Skip to main content
M G_2
Associate III
July 11, 2018
Solved

STM32F746 - SPI sends twice problem

  • July 11, 2018
  • 1 reply
  • 2124 views
Posted on July 11, 2018 at 11:43

The original post was too long to process during our migration. Please click on the attachment to read the original post.
    This topic has been closed for replies.
    Best answer by M G_2
    Posted on July 12, 2018 at 08:32

    The original post was too long to process during our migration. Please click on the provided URL to read the original post. https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006I6vw&d=%2Fa%2F0X0000000byB%2F4W5WyI1UpqGSrCOqMSJPThu_n7129pFVGLKDcZF0A2E&asPdf=false

    1 reply

    waclawek.jan
    Super User
    July 11, 2018
    Posted on July 12, 2018 at 00:14

    I assume the two halfwords are being sent in a loop, and the presented waveform is not from the first run after reset (I can't at the moment explain 'doubling' immediately after reset, nevertheless the whole concept is flawed and has to be rethought).

    So let's assume that in the first call of Send_a_2Bytes(), the halfword has been started to be transmitted and, thanks to waiting for the BUSY flag to be cleared, it is completely transmitted by the end of the routine, which also means that RXNE gets set at the moment.

    In the next call of Send_a_2Bytes(),   LL_SPI_EnableIT_RXNE(SPIx); is performed first.

    What happens? RXNE is already set, so immediately it goes into the interrupt. In that, the mcu receives data but also sees, hey, TXE is set, let's transmit. There's nothing being transmitted at this point, so this write goes immediately from holding to the shift register, thus TXE is set immediately.

    Returning from the interrupt, the next line performs LL_SPI_EnableIT_TXE(SPIx);. TXE is set, so interrupt fires. Depending on the accidental ratio between execution speed of this code (given by compiler optimization, SPI baudrate, FLASH latency and similar factors) the previous halfword might have been already shifted out/in, in that case RXNE is set and the newly received halfword overwrites the previously received one in MaRxBuffer; otherwise it gets overwritten by the next received data in the SPI_Rx register and there's an overflow indicated. Next, TXE is set, so the same MaTxBuffer gets transmitted again.

    After returning from this interrupt, waiting for BUSY ensures that RXNE gets set before Send_a_2Bytes() is called again, starting the whole process all over again.

    The conventional solution is to keep a count of data to be transmitted (more precisely, this is kept indirectly, as difference between the head and tail pointer of the circular buffer used to store the data to be transmitted), enabling the Tx interrupt when main stores data into the buffer, and the interrupt disabling it when the buffer gets empty. Similarly, employing circular buffers for Rx is more 'normal'; there is no need to enable/disable that interrupt.

    JW

    M G_2
    M G_2AuthorBest answer
    Associate III
    July 12, 2018
    Posted on July 12, 2018 at 08:32

    The original post was too long to process during our migration. Please click on the provided URL to read the original post. https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006I6vw&d=%2Fa%2F0X0000000byB%2F4W5WyI1UpqGSrCOqMSJPThu_n7129pFVGLKDcZF0A2E&asPdf=false
    waclawek.jan
    Super User
    July 12, 2018
    Posted on July 12, 2018 at 09:00

    I’ve checked how often the interrupt routine was called after enabling both the interrupts TX and RX.

    How? By placing a breakpoint and observing it in the debugger? The debugger is intrusive - if nyou display the content of SPI registers, it will read SPI->DR thus clear Rx.

    Restore the original code. Place one counter into the ISR and other into Send_a_Byte(), run, and then compare after several calls to Send_a_Byte(); while *not* displaying the SPI registers in the debugger.

    What you did is a symptomatic fix, which will bite you later.

    JW