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

Clarification on SPI double buffer and EVENTS_READY

First: nRF52840 dongle or nRF52840-Preview_DK (doesn't matter which I get the same issue), SDK17.0.2, Segger, Windows 10.  I am trying to dump a FIFO buffer from a sensor as fast as possible.  I thought I had it working, but then I realized that my rx_buffer in the nrf52840 was all 0s.  Then I saw that I might need to use the EVENTS_READY flag in this post.  I have been trying to figure out how to get this to work using the datasheet so that I don't need to use the interrupt system (too slow), but the code I have doesn't quite do it.  I get 24 clocks, I see the correct data on the MOSI pin, and I'm pretty sure on the MISO pin as well, but I never see the CS go high.  I guess this tells me that somehow the last while (!EVENTS_READY) is hanging, but if I remove it I don't get the correct result either.

// First time through, fill the double buffer
NRF_SPI0->TXD = FIFO_DATA_OUT_L | 0x80; // TXD = FIFO, RXD-1 = 0xFF
NRF_SPI0->TXD = 0xFF;  // TXD = 0xFF, TXD+1 = FIFO, RXD = 0xFF, RXD-1 = Byte1

// Read garbage (0xFF) from address TX when Event is ready
while (!NRF_SPI0->EVENTS_READY);
NRF_SPI0->EVENTS_READY = 0;

(void)NRF_SPI0->RXD; // After this line: TXD = 0xFF, TXD+1 = FIFO, RXD = Byte1, RXD-1 = ?

// Now we can push the last TX into the double buffer
NRF_SPI0->TXD = 0xFF;

// Now read data when Event is ready
while (!NRF_SPI0->EVENTS_READY);
NRF_SPI0->EVENTS_READY = 0;

ble_buff[ble_idx++] = (uint8_t)NRF_SPI0->RXD;

// Now read data when Event is ready
// while (!NRF_SPI0->EVENTS_READY);
// NRF_SPI0->EVENTS_READY = 0;

ble_buff[ble_idx++] = (uint8_t)NRF_SPI0->RXD;

nrf_delay_us(3);
NRF_GPIO->OUTSET = 1 << SPIM_CS;

How can I get three bytes from SPI as fast as possible without using the interrupts?  (obviously(?) this will go into a loop to clear out the sensor FIFO)

Parents
  • Seems you trying to use the old legacy implementation of SPI where you read and write individual bytes, it is better to use the SPIM module where you can prepare two buffers (rx and tx) and only execute the start task and the SPIM module will handle the rest until all data is sent/received.

    If you want to read and write low level registers using SPI peripheral you will need to follow the description in the datasheet carefully, you can also find an old implementation of spi_master_tx_rx() in for instance nRF5 SDKv5.2 (\spi_master) from 2015 that you may find useful:
    http://developer.nordicsemi.com/nRF5_SDK/nRF51_SDK_v5.x.x/ 

  • I have used the SPIM module, but it is very slow by comparison.  The biggest problem is that the sensor I use requires CS to go high and low between each set of three bytes to read the FIFO, the three bytes being a TX of the FIFO address, then a RX of two bytes of data.  Using the SPIM module, each transaction of three bytes takes something like 14us, which is then multiplied about 8000 times.  Using the registers, it looks like I can get that down to around 6us (if I can get them to read correctly), for a time savings of about 10ms - which in turn adds up to a huge power savings.

    As far as following the datasheet carefully goes.  You can see that the pattern I implement follows the datasheet (the link is in my original post) precisely, although the datasheet doesn't include an example with so few bytes of transfer.  Going by the datasheet, it would seem that you can only transfer a minimum of 6 bytes using the double buffer, but that doesn't seem right.  In my application, where I'm transferring 3 bytes, my first TX byte is simultaneously the first byte (TX = 0 in the example from the datasheet) and the TX = n-2 byte from the example in the datasheet.  As a result, the datasheet isn't very helpful.

    Now if you could point out how my code differs from what the datasheet says, that would be very helpful - in fact, it's why I posted this question asking for clarification on exactly that part of the datasheet.

Reply
  • I have used the SPIM module, but it is very slow by comparison.  The biggest problem is that the sensor I use requires CS to go high and low between each set of three bytes to read the FIFO, the three bytes being a TX of the FIFO address, then a RX of two bytes of data.  Using the SPIM module, each transaction of three bytes takes something like 14us, which is then multiplied about 8000 times.  Using the registers, it looks like I can get that down to around 6us (if I can get them to read correctly), for a time savings of about 10ms - which in turn adds up to a huge power savings.

    As far as following the datasheet carefully goes.  You can see that the pattern I implement follows the datasheet (the link is in my original post) precisely, although the datasheet doesn't include an example with so few bytes of transfer.  Going by the datasheet, it would seem that you can only transfer a minimum of 6 bytes using the double buffer, but that doesn't seem right.  In my application, where I'm transferring 3 bytes, my first TX byte is simultaneously the first byte (TX = 0 in the example from the datasheet) and the TX = n-2 byte from the example in the datasheet.  As a result, the datasheet isn't very helpful.

    Now if you could point out how my code differs from what the datasheet says, that would be very helpful - in fact, it's why I posted this question asking for clarification on exactly that part of the datasheet.

Children
No Data
Related