This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

nRF51 SPI slave: queuing new buffers during a potential SPI transaction

Hi, I am using the nRF51822 with SDK 12.3.0

I am trying to make use of nrf_drv_spis driver included in the SDK.

My purpose is to have nRF51822 constantly be RX-ing over SPI - which means it will TX exactly 1B: 0x00.
However every now and then, it will need to TX some data too. This raises the question:

While buffers are already set, after the BUFFERS_SET_DONE has been received, how can I SAFELY change buffers?
This is not about safely changing DMA buffers etc. This is about not losing data during a potential SPI session. Let me explain.

There are two possible scenarios:

1. SPI is idle, I call nrf_drv_spis_buffers_set with the new RX and TX buffer
    - semaphore is idle, instantly ACQUIRED by CPU and BUFFERS_SET_DONE event is received even before nrf_drv_spis_buffers_set.
    - when a future SPI transaction completes, COMPLETED event is received, for the new buffers
In other words, previous buffers are discarded since they were never exchanged. This makes sense.

2. SPI is in the middle of a transaction (CS low) therefore semaphore is owned by SPI when I call nrf_drv_spis_buffers_set  with new RX and TX buffers
    - nrf_drv_spis_buffers_set returns
    - SPI transaction completes (CS high)
    - semaphore is ACQUIRED by CPU as a result of END_ACQUIRE short being enabled in driver
    - BUFFERS_SET_DONE event is received for the new buffers (since it is checked first in ISR)
    - immediately after, COMPLETED event is received for the original buffers
    - when a future SPI transaction completes, COMPLETED event is received, for the new buffers
In other words, previous buffers contain valid data and need to be processed.

Therefore the question becomes:

Programatically, how do I differentiate between 1 and 2? How do I know if the first COMPLETED event is referring to the original set of buffers or to the ones just posted?
This also matters whether I need to call nrf_drv_spis_buffers_set during COMPLETED event - since for case 2, I don't need to for the first COMPLETED. Getting this wrong would mean SPIS lockout with semaphore always held by CPU if I fail to call nrf_drv_spis_buffers_set.

I thought about poisoning the RX buffer with impossible values (protocol-dependent) but then I realized a SPI transaction can have 0 bytes read and is still a valid transaction, buffers contain valid data etc. And this just doesn't feel right.

What I would really need is either:
    - COMPLETED called before BUFFERS_SET_DONE in 2nd scenario - but driver comment states that would be wrong, likely due to driver state machine limitations
    - BUFFERS_SET_DONE reporting whether they are due to a COMPLETED event - not sure if this can be made safe against race conditions
    - COMPLETED reporting whether there was an outstanding buffer set that was queued in the same ISR by ACQUIRED event - again this may be racey

Is there any technique that I missed which can be used to achieve reliable TX during a potential SPI transaction 

Thanks!

  • Hi,

    I'm not sure I understood your question correctly, but it sounds like an issue that you should be able to solve by setting a flag whenever you set the second buffer and a flag that indicates that you should check the default buffer on the first COMPLETED event?

    When you receive the first COMPLETED event, you process the first buffer and avoid setting new buffers, then clear this flag. On the next COMPLETED event, you check the flag that indicates that the second buffer is used, process this, set up the first buffer again, before clearing this flag as well.

    Best regards,
    Jørgen

  • Hi Jorgen,

    After calling nrf_drv_spis_buffers_set , I will receive the following events:

    - BUFFERS_SET

    - XFER_DONE (sorry, I used COMPLETED by mistake)

    I receive these events in both scenarios: when SPI is idle (semaphore not held) and when SPI is busy (holds semaphore)

    I know for sure the BUFFERS_SET refers to my new buffers, no matter what scenario we're in.

    I can't tell if XFER_DONE refers to the original set of buffers (which were exchanged if SPI was busy when calling nrf_drv_spis_buffers_set) or the new buffers (which were exchanged later on next SPI transaction if SPI was idle when calling nrf_drv_spis_buffers_set)

    Therefore there is no way to know which RX buffer to process and which TX buffer was sent.

    Hope this helps,

    Cristian

Related