Bluepill need help with interrupt

Post here first, or if you can't find a relevant section!
Post Reply
jwiggams
Posts: 6
Joined: Mon Nov 23, 2020 8:31 pm

Bluepill need help with interrupt

Post by jwiggams »

Hi all,

Just getting started with the Bluepill, and so far things have been going well. But I've run into a snag with using interrupts.

I have this interfaced to a Sega Genesis console, and what I want is whenever I write to a certain address the byte value is grabbed and sent out to my serial monitor. This works...except the data is always wrong. The timing has to be fast enough to clock the data pin values on the rising edge of the nTIME signal coming from the Genesis. I have this set up as an ISR, but it's still not working. This cartridge has been wired up to an FPGA, and that works so I know the hardware is OK. The Bluepill is the wildcard here, and my coding experience with these is very minimal.

Here is my code so far, just trying with one data pin right now - to see if it's a 0 or 1 based on the 0x00 or 0x01 byte i'm writing with my test ROM game. I'm using software serial as my dedicated serial pins are being used for some of the data pins.

Code: Select all

#include <SoftwareSerial.h>
#define RX  PB1
#define TX  PB0

SoftwareSerial serial(RX, TX);

//nTIME and nWR pins from Genesis
#define nTIME PB15
#define nWR PB2 

//data pins from Genesis
#define d0 PA8
#define d1 PA9
#define d2 PA10
#define d3 PA11
#define d4 PA12
#define d5 PB5
#define d6 PB6
#define d7 PB7

bool val;
volatile int notTime;

void setup() {

  //start serial
  serial.begin(9600);

  //set pins for Software Serial communication
  pinMode(RX, INPUT);
  pinMode(TX, OUTPUT);

  //set nWR, nTIME and data pins
  pinMode(nTIME, INPUT);
  pinMode(nWR, INPUT);
  pinMode(d0, INPUT);
  pinMode(d1, INPUT);
  pinMode(d2, INPUT);
  pinMode(d3, INPUT);
  pinMode(d4, INPUT);
  pinMode(d5, INPUT);
  pinMode(d6, INPUT);
  pinMode(d7, INPUT);

  //use interrupt for nTIME
  attachInterrupt(digitalPinToInterrupt(nTIME),time_ISR,RISING);
  
}

void time_ISR(){
  notTime = 0;
}

void loop() {
  //set nTIME interrupt high by default - wait for it to trigger
  notTime = 1;
  while (notTime){      
  //wait in here until interrupt triggers and boots us out of the while block
  }
  //as soon as interrupt is triggered, grab bit info from data[0]
  val = GPIOA->IDR & 0x0100;     

  //print info to monitor
  serial.println(val);
}
Any help with this rather niche problem would be greatly appreciated!
by Bakisha » Mon Nov 23, 2020 10:35 pm
Window is very small. If (by Wiki) Sega runs at 7.6MHz, that means 1 cycle is 131nS, and if it's 50/50 clock, that means 65nS after rising edge. That's like 5 cycles on bluepill (72MHz - 13.88nS).
Maybe use

Code: Select all

noInterrupts();
interrupts();
while fetching data (and/or address)bus.

Or fill whole memory of bluepill with fetched values, send it with serial and see if you are getting correct values for all?.

IMHO, timing is so small. Maybe in assembler and cycle-count...
Or maybe add external latch, 74LS273?
Go to full post
Last edited by jwiggams on Wed Nov 25, 2020 5:18 pm, edited 1 time in total.
mlundin
Posts: 94
Joined: Wed Nov 04, 2020 1:20 pm
Answers: 6
Location: Sweden

Re: Bluepill need help with interrupt coding with Sega Genesis

Post by mlundin »

The code looks sound. For how long is the input data valid after the rising edge of nTime ? The attached pin interrupts have fairly long latency.

Since you are using a blocking loop waiting for the notTime flag from the ISR, you might as well wait for the nTime pin to go high and cut out the interrupt overhead. You can also try to read and save GPIOA->IDR and GPIOB->IDR in the ISR, and then sort out the bits in the main loop after receiving the notTime low notification.
jwiggams
Posts: 6
Joined: Mon Nov 23, 2020 8:31 pm

Re: Bluepill need help with interrupt coding with Sega Genesis

Post by jwiggams »

I've got a pretty small window. The data pins are valid at the rising edge of nTIME, and that could be my issue - that I'm not getting it fast enough once that edge happens. But when i sample the data before the rising edge, it's wrong as well.

Based on your suggestion to remove the interrupts:

Code: Select all

 
  bool isTime = GPIOB->IDR & 0x8000;
  while(isTime){
    isTime = GPIOB->IDR & 0x8000;
  }
  while((isTime)==0){
    isTime = GPIOB->IDR & 0x8000;
    val = GPIOA->IDR & 0x0100;
  }
  serial.println(val);
  
This does seem to be a lot more accurate (8 out of 10 writes is correct), but still getting bad values here and there. Basically, I wait until nTIME goes low, and then I keep reading the data pin until nTIME goes high again. This is the best I can think of on how to get as close to that early part of that rising edge.

Is there a better way to write the code out than what I'm trying here? I tried putting the GPIO IDR reads in the ISR but the data was all over the place.
User avatar
Bakisha
Posts: 139
Joined: Fri Dec 20, 2019 6:50 pm
Answers: 5
Contact:

Re: Bluepill need help with interrupt coding with Sega Genesis

Post by Bakisha »

Window is very small. If (by Wiki) Sega runs at 7.6MHz, that means 1 cycle is 131nS, and if it's 50/50 clock, that means 65nS after rising edge. That's like 5 cycles on bluepill (72MHz - 13.88nS).
Maybe use

Code: Select all

noInterrupts();
interrupts();
while fetching data (and/or address)bus.

Or fill whole memory of bluepill with fetched values, send it with serial and see if you are getting correct values for all?.

IMHO, timing is so small. Maybe in assembler and cycle-count...
Or maybe add external latch, 74LS273?
stevestrong
Posts: 502
Joined: Fri Dec 27, 2019 4:53 pm
Answers: 8
Location: Munich, Germany
Contact:

Re: Bluepill need help with interrupt coding with Sega Genesis

Post by stevestrong »

The solution would be to use DMA to save the data into memory. All data pins should be mapped to the same port (A or B), best the low or high nibble.
The DMA can be triggered by a timer, which can be set to have clock input (or reset) on a pin (edge direction programabble).
I used that technique to sample OV7670 camera output here.
jwiggams
Posts: 6
Joined: Mon Nov 23, 2020 8:31 pm

Re: Bluepill need help with interrupt coding with Sega Genesis

Post by jwiggams »

Bakisha wrote: Mon Nov 23, 2020 10:35 pm
Maybe use

Code: Select all

noInterrupts();
interrupts();
while fetching data (and/or address)bus.
That works a lot better! At least for reading a single bit (bit 0), its showing correct values every time. I wasn't thinking about turning off the interrupts because that kills my software serial, but I'd forgotten you can turn off and on again before using serial. Thanks for that!

What is the best way to read pins from both A and B ports and add them together into a byte? If I try to read each pin individually, the bits from the console are no longer valid by the time I reach the 3rd or 4th bit, never mind getting to bits 5-7 on the B port. On Arduino, I know you can use port manipulation to OR the bytes all together, but when I try it with the STM32 I just get "0" values, mostly. It only wants to register a 1 or 0 properly when I make it a bool...and I'm not sure why that is.

I tried:

Code: Select all

//OR bits 7-0 together into byte 'value' - each IDR read should register a 1 if true, else a 0
byte value = (GPIOB->IDR & 0x0080 | GPIOB->IDR & 0x0040 | GPIOB->IDR & 0x0020 | GPIOA->IDR & 0x1000 | GPIOA->IDR & 0x0800 | GPIOA->IDR & 0x0400 | GPIOA->IDR & 0x0200 | GPIOA->IDR & 0x0100);
How can I do the above correctly with the STM32? It needs to read the pin values ideally in one shot.
stevestrong
Posts: 502
Joined: Fri Dec 27, 2019 4:53 pm
Answers: 8
Location: Munich, Germany
Contact:

Re: Bluepill need help with interrupt coding with Sega Genesis

Post by stevestrong »

read once the GPIOA and B IDR ports quickly, then put the value together:

Code: Select all

uint32 pa = GPIOA->IDR;
uint32 pb = GPIOB->IDR;
byte value = (pa & 0x...) | (pb & 0x...)
But I recommend to use only pins of one port, e.g. GPIOA or GPIOB.
jwiggams
Posts: 6
Joined: Mon Nov 23, 2020 8:31 pm

Re: Bluepill need help with interrupt coding with Sega Genesis

Post by jwiggams »

stevestrong wrote: Tue Nov 24, 2020 6:30 pm read once the GPIOA and B IDR ports quickly, then put the value together:

Code: Select all

uint32 pa = GPIOA->IDR;
uint32 pb = GPIOB->IDR;
byte value = (pa & 0x...) | (pb & 0x...)
But I recommend to use only pins of one port, e.g. GPIOA or GPIOB.
That's the ticket, it works! You're right, one port would be best. I'll rearrange my pins and that should clear up any remaining glitches (still get bad values on the B port if I write in really fast succession).

Thanks everyone for the help and the pointers :D
Post Reply

Return to “General discussion”