[SOLVED] SPI and GPIO pin race condition

I’m using the SPI to talk to a display that uses a DC pin to tell the display if a byte is command or data/param.

I’ve noticed flakeyness so I debugged it further and found out that if I transfer 2 command bytes (together) and then set the DC pin to high, the capture shows that the DC pin is set too soon see here.

I’m using the txmark set to 1 and checking for it after each transfer block is done in a busyloop but it seems it “sets” too soon?

Is there some other way I should check that the SPI writes are completely pushed out of the FIFO?

What works for me @almindor is something like the five steps of this uint8_t spi_transfer (uint8_t x) function:

  1. Wait for tx flush – while( spi_ip_txwm() == 0 );
  2. Enqueue data – while( spi_tx_data( x ) == 0 );
  3. Wait for tx flush – while( spi_ip_txwm() == 0 );
  4. Wait for rx char – while( spi_ip_rxwm() == 0 );
  5. Retrieve data – return spi_rx_data();

Where spi_ip_txwm is bit 0 of the IP register, at offset 0x74 from BASE, and spi_ip_rxwm is bit 1 of the IP register, at offset 0x74 from BASE.

You could easily enable the interrupts with register IE and use wfi for a more economical “sleepy busy loop”.

Though not relevant here, you might be interested in a couple of undocumented SPI block settings with regard to timing delay adjustments. These are the EXTRADEL and SAMPLEDEL registers.

1 Like

Yes this is more or less what I do. What is your txmark setting? I’m using 1 to see if the queue is empty (my understanding is that IP.txmark bit would be set once the TX pipe is done pushing the bits).

I’m not using wfi as this is a pure blocking setup. The idea is simple, send 2 “command” bytes while keeping the GPIO manual pin low, then bring the GPIO pin up and then send 1 “param” byte.

The issue is that even with waiting for the IP.txwm the GPIO pin still gets set to high too soon, before the 2nd command byte is done which means that the SPI registers don’t “wait” properly for some reason.

Not using the TXWM wait makes the GPIO pin high in the middle of the two first bytes. If I use the wait it delays by roughly half a byte of data into the 2nd byte but still before they’re done sending. This on 500khz SPI speed and 320Mhz core clocks.

My understanding was that IP.TXWM bit would be 1 only if the TX FIFO queue would be empty (as in the last byte was sent out completely) but it seems like it’s set too soon.

Also to clarify I’m doing one way only, I just discard the read bytes.

Found the issue! I was not waiting for all reads properly, short circuiting after writes were done. It was just a silly while loop change I did for unrelated refactor reasons o.O

Goes to show I guess…

1 Like

Glad you got it.
Also, don’t forget about CSMODE(0x18)[1:0] mode: 0=auto, 2=hold, 3=off, which control how and whether the CS pin is used. Might be helpful for your multi-byte “command” frame, not to drop CS in between. Just a thought.