Author Topic: Problems with SPI on STM8 - hanging waiting for RXNE that never comes  (Read 2008 times)

0 Members and 1 Guest are viewing this topic.

Offline HwAoRrDkTopic starter

  • Super Contributor
  • ***
  • Posts: 1561
  • Country: gb
As a further part of my experiments with the STM8, I've been trying to write some code for the SPI peripheral, but I've run into a problem I can't quite fathom.

Initially, I wrote some simple, naive code for a function that takes pointers to two buffers (plus a size) and transmits the 'input' data buffer and writes received data to the 'output' buffer. That all worked fine, but was unfortunately rather slow, with too much delay in between bytes. I am aiming to have a constant uninterrupted stream of bytes traversing the SPI bus, to make transactions as quick as possible.

Then I thought I would try following the exact procedure laid out in the STM8S Reference Manual (section 20.3.5, 'Full Duplex Transmit and receive procedure in master or slave mode', p270), where they advise the following:

Quote
1. Enable the SPI by setting the SPE bit
2. Write the first data to be transmitted in the SPI_DR register (this clears the TXE flag).
3. Wait until TXE = 1 and write the second data to be transmitted. Then wait until RXNE = 1 and read the SPI_DR to get the first received data (this clears the RXNE bit). Repeat this operation for each data to be transmitted/received until the n-1 received data.
4. Wait until RXNE = 1 and read the last received data.
5. Wait until TXE = 1 and then wait until BSY = 0 before disabling the SPI.

As the SPI peripheral data writes and reads to SPI_DR register are buffered, it seems they advise this slightly unusual sequence in order to keep the send buffer full throughout the transaction.

I ended up with the following code for my transfer function:

Code: [Select]
void spi_transfer_buffer(const void *in_buf, void *out_buf, size_t count) {
const uint8_t *in_data = (const uint8_t *)in_buf;
uint8_t *out_data = (uint8_t *)out_buf;
uint8_t dummy = 0x00;

if(count > 0) {
if(in_data != NULL && out_data != NULL) {
// Write first byte of data.
SPI_DR = *in_data++;

while(count-- > 1) {
// Wait until previous data byte has been sent, then write next data byte.
led_txe_on(); // TEST
loop_until_bit_is_set(SPI_SR, SPI_SR_TXE);
led_txe_off(); // TEST
SPI_DR = *in_data++;

// Wait until reception of next data byte, then read it.
led_rxne_on(); // TEST
loop_until_bit_is_set(SPI_SR, SPI_SR_RXNE);
led_rxne_off(); // TEST
*out_data++ = SPI_DR;
}

// Wait until reception of last data byte, and read it.
led_rxne_on(); // TEST
loop_until_bit_is_set(SPI_SR, SPI_SR_RXNE);
led_rxne_off(); // TEST
*out_data++ = SPI_DR;
} else if(in_data != NULL && out_data == NULL) {
// Same as above, but reading into dummy instead of out_data.
} else if(in_data == NULL && out_data != NULL) {
// Same again, but writing from dummy instead of in_data.
}
}
}

The lines commented 'TEST' were to aid my debugging efforts by toggling GPIO lines so I can see (with a logic analyser) how long it is spending waiting for TXE or RXNE. My main loop is just repeatedly formatting a string, printing it (via UART), sending a few fixed bytes plus the contents of the string out via SPI, and a short delay.

The problem I am having though, is that this would work fine for a few iterations of the main loop, but then hang after 35 iterations! :wtf: After inspecting things with a logic analyser, it appears that the hang is caused by it never exiting the loop where it is waiting for TXNE to be set from reception of the final byte. TXNE apparently never gets set, so the loop continues forever. :(



As you can see above on the green trace (positive pulses represent time spent waiting) shows that on the final byte, the RXNE flag never seems to be set, causing it to loop forever.

As a shot in the dark - because it was the only thing I could think of that could potentially interrupt things (no pun intended) - was to globally disable interrupts during my SPI transaction. I have one regular interrupt in my test code, which is a 1ms timer that increments a timestamp (timestamp value gets inserted in the aforementioned string). Doing so prevents the hang, and it has been happily running for a couple of hours now as I type.

Anyone got any ideas what's going on? Obviously, I can't practically use such code that requires no interruption, so I am keen to know if there is some flaw in my code.
 

Offline HwAoRrDkTopic starter

  • Super Contributor
  • ***
  • Posts: 1561
  • Country: gb
Re: Problems with SPI on STM8 - hanging waiting for RXNE that never comes
« Reply #1 on: January 14, 2020, 06:33:59 pm »
As an aside, as I was looking at this problem some more this afternoon, I happened to be looking at the generated assembly for my SPI code, and I noticed something odd where I think the compiler, SDCC, is behaving oddly. Doesn't have any bearing on my SPI issues, but odd nonetheless.

My debugging macro statements that I'd put in to trace things on the logic analyser took the form of:

Code: [Select]
#define led_txe_on() do { PC_ODR |= _BV(PC_ODR_ODR2); } while(0)
Normally, SDCC is smart and converts statements like this to a single bset instruction (and bres, bcpl for &= and ^= operations). But, in this particular piece of my test code it's... not. ??? What I am getting is this:

Code: [Select]
ld a, 0x500a ; <-----
ldw x, (0x08, sp) ; start of 'while(count-- > 1)' loop
cpw x, #0x0001
jrule 00228$
ldw x, (0x08, sp)
decw x
ldw (0x08, sp), x
or a, #0x04 ; <-----
ld 0x500a, a ; <-----

It's kept it as a read-modify-store operation, but split the read and the modify-store, with other code inserted in between. Like, WTF is that all about? That seems to be asking for bugs. I know this particular function isn't actually doing anything with that IO register in the time between, but what if I had an ISR that did? It could screw things right up. :wtf:

I don't know what to make of it. Is this a compiler bug?

Anyway, I changed my macro to some inline assembly with explicit bsetand bres instructions:

Code: [Select]
#define led_txe_on() __asm__("bset 0x500A, #2")
One really doesn't need potential problem-causing shenanigans like this when already trying to diagnose an extant problem. :palm:
 

Offline rhodges

  • Frequent Contributor
  • **
  • Posts: 329
  • Country: us
  • Available for embedded projects.
    • My public libraries, code samples, and projects for STM8.
Re: Problems with SPI on STM8 - hanging waiting for RXNE that never comes
« Reply #2 on: January 15, 2020, 12:13:24 am »
What chip are you using? Did you check the errata? I just checked stm8s103 (no SPI errata) and stm8s105 (SPI issue, but does not seem relevant.)
Currently developing STM8 and STM32. Past includes 6809, Z80, 8086, PIC, MIPS, PNX1302, and some 8748 and 6805. Check out my public code on github. https://github.com/unfrozen
 

Offline HwAoRrDkTopic starter

  • Super Contributor
  • ***
  • Posts: 1561
  • Country: gb
Re: Problems with SPI on STM8 - hanging waiting for RXNE that never comes
« Reply #3 on: January 15, 2020, 11:46:23 am »
I am using an STM8S208RB on a Nucleo board.

Good idea about the errata, I hadn't thought of that. But I checked and there doesn't seem to be anything concerning SPI that applies.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf