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

How to update SPIM TXD.PTR/RXD.PTR safely?

Hi,

I am using SPIM in nrf5340 to read datas from 2 sensors. The sample rate is 400Hz, and I have to send and receive 8 bytes to each sensor every 2.5ms.  

So I set up one TX buffer and two RX buffer:

typedef struct ArrayList
{
  volatile uint8_t buffer[8];
} ArrayList_type;

#define SENSOR_FS             400     //sample rate (Hz)
#define SENSOR_MAX_SECOND     2       //max sample time (s)

static volatile ArrayList_type  ReaderList0[2 * SENSOR_FS * SENSOR_MAX_SECOND];
static volatile ArrayList_type  ReaderList1[2 * SENSOR_FS * SENSOR_MAX_SECOND]; 
static volatile ArrayList_type  WriterList[2 * SENSOR_FS * SENSOR_MAX_SECOND];

The WriterList contains the address for reading:

uint16_t i = 0;
    for(i = 0; i < SENSOR_FS * SENSOR_MAX_SECOND; i++) {
        WriterList[2 * i].buffer[0] = SENSOR_A_ADDR;
        WriterList[2 * i].buffer[1] = 0xff;
        WriterList[2 * i].buffer[2] = 0xff;
        WriterList[2 * i].buffer[3] = 0xff;
        WriterList[2 * i].buffer[4] = 0xff;
        WriterList[2 * i].buffer[5] = 0xff;
        WriterList[2 * i].buffer[6] = 0xff;
        WriterList[2 * i].buffer[7] = 0xff;

        WriterList[2 * i + 1].buffer[0] = SENSOR_B_ADDR;
        WriterList[2 * i + 1].buffer[1] = 0xff;
        WriterList[2 * i + 1].buffer[2] = 0xff;
        WriterList[2 * i + 1].buffer[3] = 0xff;
        WriterList[2 * i + 1].buffer[4] = 0xff;
        WriterList[2 * i + 1].buffer[5] = 0xff;
        WriterList[2 * i + 1].buffer[6] = 0xff;
        WriterList[2 * i + 1].buffer[7] = 0xff;
    }

and I initalized SPM with tx and rx dma list:

// Enable the use of EasyDMA ArrayList
    nrf_spim_tx_list_enable(NRF_SPIM1);
    nrf_spim_rx_list_enable(NRF_SPIM1);
    // Function for initializing the SPI master driver instance.
    nrfx_spim_xfer_desc_t xfer_desc = NRFX_SPIM_XFER_TRX(&WriterList[0], 8, &ReaderList0[0], 8);
    ReaderListReceiving = 0;
    err_code = nrfx_spim_xfer(&spi_instance, &xfer_desc, NRFX_SPIM_FLAG_TX_POSTINC | NRFX_SPIM_FLAG_RX_POSTINC|NRFX_SPIM_FLAG_HOLD_XFER);
  

I want the SPIM write/read 8 bytes for sensor A, and next 8 bytes for sensor B, repeatedly. And it works well.

Also I use a timer for timing out check. If the timer interrupt occurs (about 1.5s), I need to "swing" the RX buffer for new data receiving and reset the TX buffer.

Since I need to receive 8+8=16bytes entirely for one sample, I should check whether the SPIM has finished 16 bytes receive, if SPIM has received 8 bytes, that means

datas from sensor B has not received yet. I use TXD.PTR to determine data receive from sensor B:

void Sensor_Recvbuffer_toggle()
{
    uint32_t  recvAddr, recvBytesNum, writeAddr, sentBytesNum, sentPackageNum;
    bool timer1_done;
    do {
        //wait for spim finish 8 bytes
        timer1_done = nrf_spim_event_check(spi_instance.p_reg, NRF_SPIM_EVENT_END); 
    }while(!timer1_done);
 
    writeAddr = spi_instance.p_reg->TXD.PTR - (uint32_t)&WriterList[0]; //how many bytes sent totally
    if(writeAddr % 16){
        //remainder is not zero, means sensor B has not finished
        nrf_spim_event_clear(spi_instance.p_reg, NRF_SPIM_EVENT_END);
        //wait for receiving another 8 bytes 
        do {
            timer1_done = nrf_spim_event_check(spi_instance.p_reg, NRF_SPIM_EVENT_END);
        }while(!timer1_done);
        writeAddr = spi_instance.p_reg->TXD.PTR - (uint32_t)&WriterList[0];
    }

    spi_instance.p_reg->TXD.PTR = (uint32_t)&WriterList[0]; //reset TX
    sensor_package_num = writeAddr / 16;
    if (ReaderListReceiving == 0) {   
        spi_instance.p_reg->RXD.PTR = (uint32_t)&ReaderList1[0];  //swing RX
        ReaderListReceiving = 1;
        nrf_gpio_pin_clear(LED3);
        SEGGER_RTT_printf(0,"sensor buffer0 RX TX = %d\n", writeAddr);
    }
    else {
        spi_instance.p_reg->RXD.PTR = (uint32_t)&ReaderList0[0];  //swing RX
        ReaderListReceiving = 0;
        nrf_gpio_pin_set(LED3);
        SEGGER_RTT_printf(0,"sensor buffer1 RX TX = %d\n", writeAddr);
    }
    
}

Most of time, the code works fine, however, SPIM will exchange the TX 8 bytes data occasionally, it will send

SENSOR_B_ADDR, 0xff, 0xff,0xff, 0xff, 0xff,0xff, 0xff firstly, and then SENSOR_A_ADDR, 0xff, 0xff,0xff, 0xff, 0xff,0xff, 0xff

this will lead to wrong data reading. 

I guess maybe I do not update the TXD.PTR/RXD.PTR correctly, so how to update them saftly?

I am using nrf5340 and nrf Connect SDK 1.5.0.

Thanks!

Parents
  • Hi,

    It should be safe to update the pointers between a END event and triggering of the START task. It is not fully clear from the posted code snippets how you handle this transition. Is this handled by the default IRQ handler in the driver, or do you handle it manually in your application?

    You may need to temporarily pause the triggering of the START task while updating the pointers to make sure you can update the pointer safely. One possible way to do this is to use a PPI channel to automatically trigger the START task on reception of the END event, and concurrently count the number of generated END events from the SPIM using a TIMER in count mode. You can then use COMPARE event from this counter to disable to PPI channel that triggers the SPIM START task when you have reached the number og available buffers, and at the same time trigger an interrupt that is used to update the buffer pointers (instead of using the 1.5s timer interrupt for this).

    Best regards,
    Jørgen

Reply Children
No Data
Related