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!