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

SPI Slave and BLE on nRF51822

Hi,

I'm trying to build a SPI Slave <-> BLE bridge, so that everything received over SPI bus is forwarded on BLE and vice versa. In order for the reverse direction to work, the master has to continously pull data from the nRF51822. The problem is that if the BLE part of the code is compiled in and running, the SPI bus just locks up after a random time, between 2 and 60 seconds depending on data rate). I'm using ARM mbed, and the SPISlave::receive() function, which is supposed to tell if any data has been received from the master, starts returning false after that time no matter what the master actually sends to it. My SPI settings are 1 MHz, 8 bit, mode 0.

I've found a couple of posts here about incompatibilities between the PPI and the BLE stack. (Here and here and here.) However it's not clear if what I'm trying to achieve is possible or not. Some comments say that a SPI clock below 2 MHz should have no bad effects. I'm using only 1 MHz. Another comment says this is a problem with the S110 SoftDevice. (Well, I don't know which SoftDevice I have, how can I check it?) There's a mention of "using SPI over the SoftDevice API" but no information how to actually do that. Do I have give up on mbed to use the SoftDevice API? Is SPIS and the BLE stack totally incompatible and hopeless to work together? Thanks!

Parents
  • The problem was data jam on the SPI bus. Once the SPI peripheral hits a buffer underrun (or "overread error" as the Reference Manual calls it), it becomes essentially unusable under mbed because it blocks all data reception until a reply is prepared in the send buffer. (But since this normally only happens after a reception, this becomes a deadlock). If you fill the buffer with an "unsolicited" reply, the bus will start working again.

    In my case the overrun was caused by the delay introduced by mbed's BLE implementation. A working BLE radio can block the CPU for as long as 1495 microseconds (that's not the theoretical maximum, just the highest value I've seen). If the SPI peripheral has to execute more than one transaction (as dictated by the master) during the time when the BLE stack (or anything else) uses the CPU, you enter the above deadlock.

Reply
  • The problem was data jam on the SPI bus. Once the SPI peripheral hits a buffer underrun (or "overread error" as the Reference Manual calls it), it becomes essentially unusable under mbed because it blocks all data reception until a reply is prepared in the send buffer. (But since this normally only happens after a reception, this becomes a deadlock). If you fill the buffer with an "unsolicited" reply, the bus will start working again.

    In my case the overrun was caused by the delay introduced by mbed's BLE implementation. A working BLE radio can block the CPU for as long as 1495 microseconds (that's not the theoretical maximum, just the highest value I've seen). If the SPI peripheral has to execute more than one transaction (as dictated by the master) during the time when the BLE stack (or anything else) uses the CPU, you enter the above deadlock.

Children
  • I assume this issue comes from the mbed implementation. What exactly happens when it "blocks all data reception until a reply is prepared in the send buffer " ?

    As from the hardware point of view, what i can see is that after you hit "buffer underrun" you will receive OVERREAD event, and on the master it will receive the ORC byte. There shouldn't be a lock up here.

    Would increasing the RX buffer help avoiding the issue ?

  • Yes, it definitely looks like a problem (or feature) related to mbed. In mbed, the send/receive buffers are one byte long. One requirement of a successful transaction is that there's some data already prepared in the send buffer. This requires calling the SPISlave::relpy() function before the transaction starts. If there's a reply already prepared in the send buffer, it's clocked out by the next transaction. But in order to keep the SPI bus running (from mbed's point of view), SPISlave::reply() has to be called again before the next transaction. If the master initiates the next transaction before SPISlave::reply() is called on the slave, mbed enters an (undetectable by API) error state when SPISlave::receive() (that tells if there's a completed transaction) will never again return true unless you call SPISlave::reply(). Then you're good again until the next missed transaction.

  • Of course the problem can be solved by using larger send/receive buffers so that the CPU has more time to prepare a reply, but unfortunately that's not possible with mbed's SPISlave API.

  • What do you have on the master side on the second transaction ? Would it be the same byte as the first transaction ?

    I am thinking it can discard the transaction if it detects the data from the slave is exactly the same as the last one. But this require you to add 1 bit as the flow control bit. So you only have 7 bit for data.

    Another option is to export the project or use mbed CLI and you can modify the library on your purpose.

  • Well, discarding the transaction is not a good idea in this situation since I'm only sending continuously from the master side to poll if the slave has any data to send to the master. The very simple protocol I'm using on the bus is to send the number of bytes available so the master knows how many bytes to pull from the slave after (this can be zero). That's why I need to keep the bus running all the time, so sending a zero to the slave will allow the master to see if the slave has anything to send to it.

Related