Currently I'm working on the implementation of a driver for a SPI sensor using nRF Connect SDK v2.4.0.
The sensor uses an interrupt line to signal a measurement is ready at about 1 KHz. At this frequency 4 registers of the sensors are read and printed over a UART using printk using the work queue. Each transfer is 4 bytes. I noticed the sensor would stop asserting the interrupt line after a while. After some investication I found some issue in the SPI transfer. It sometimes seems to repeat the last byte of the previous transfer in the new transfer and than clocks out 3 of the 4 bytes of the new transfer. This issue appears within seconds or 10s of seconds after resetting the mcu.
Next to SPI3 the following peripherals are used:
- USB - Input/Output for the Zephyr Shell
- BLE (just advertising, not connected)
- UART0 - Output for printk - baudrate 921600
- I2C0
- GPIO
The clock frequency is 5 MHz.
A logic analyzer shows the following:
*Note* I used to clock out 0x00 after the first byte, but for testing purposes I changed the bytes to: {[register address], 0x23, 0x34, 0x45}
As a test I rewrote the code to be triggered by a timer and the routine runs in a custom work queue (with a priority of 5 and a stack size of 1024 bytes). The timeout is set to an interval of 1 ms. I moved to a development kit of panasonic (with a nRF52840) with the MISO and MOSI connected together. I added a few lines of code to the sensor_read_reg routine so it compares the output with the input..
int sensor_read_reg(const struct device *spi, struct spi_config *config, uint8_t reg, uint16_t *data) { int err; uint8_t tx_data[4] = {0x00, 0x23, 0x34, 0x45}; uint8_t rx_data[4] = {0}; tx_data[0] = 0x80 | reg; // Initialize tx-buffer struct spi_buf tx_buf = { .buf = tx_data, .len = 4 }; struct spi_buf_set tx = { .buffers = &tx_buf, .count = 1 }; // Initialize rx-buffer struct spi_buf rx_buf = { .buf = rx_data, .len = 4 }; struct spi_buf_set rx = { .buffers = &rx_buf, .count = 1 }; err = spi_transceive(spi, config, &tx, &rx); if(err) { printk("SPI: Transaction error [%d]\n", err); return err; } *data = ((uint16_t)rx_data[1]) << 8 | rx_data[2]; // Compare tx/rx data buffers if(memcmp(tx_data, rx_data, 4) != 0) { printk("tx_data != rx_data\n"); } return err; }
The following routine is called in the custom work queue item which is placed in the queue when the timer expires.
void sensor_irq_worker(struct k_work *item) { int16_t val_x, val_y, val_z; uint16_t status; sensor_read_reg(spi, &spi_cfg, 0x09, &val_x); sensor_read_reg(spi, &spi_cfg, 0x0A, &val_y); sensor_read_reg(spi, &spi_cfg, 0x0B, &val_z); sensor_read_reg(spi, &spi_cfg, 0x0E, &status); printk("X: %d;", val_x); printk("Y: %d;", val_y); printk("Z: %d; ", val_z); printk("Status: %04X\n", status); }
SPI3 Configuration (based on nRF52840 dk dts file):
spi3: arduino_spi: spi@4002f000 { compatible = "nordic,nrf-spim"; #address-cells = <1>; #size-cells = <0>; reg = <0x4002f000 0x1000>; interrupts = <47 NRF_DEFAULT_IRQ_PRIORITY>; max-frequency = <DT_FREQ_M(32)>; rx-delay-supported; rx-delay = <2>; status = "okay"; cs-gpios = <&gpio0 26 GPIO_ACTIVE_LOW>; pinctrl-0 = <&spi3_default>; pinctrl-1 = <&spi3_sleep>; pinctrl-names = "default", "sleep"; clock-frequency = <5000000>; };
I ran this test and concluded that after a while it sometimes shifts the output by one byte as well. It regularly prints "tx_data != rx_data". The shift of one byte is also visible using a logic analyzer.
Other things I tested:
1) Run the code using nRF Connect SDK v2.2.0 - problem persists
2) Make the tx_data and rx_data buffers global or static - problems disappears (or at least does not trigger as fast).
To me it's unclear why the last byte of a previous transfer ends up in the next transfer.