Hello,
I am using a nrf52840-dk and communicate via SPI with an external board (NUCLEO-L476RG from ST) during development.
For the development itself I am using the nRF Connect SDK v1.9.1. The communication over SPI is actually working quiet well.
My goal is to run the nrf52840 as a slave, which has multi-byte read/write capabilities with auto-address increment. So something like a burst write/read.
That means: The master wants to write data e.g. into several registers. So it initiates the transaction with:
- Toggle CS-Line
- Transmit 1 Byte as start register address
- Transfer n Bytes as long as desired (I don't care about a rollover of the address at the moment)
- Toggle CS-Line
The logical sequence of the slave would look somewhat like this:
uint8_t rx_buffer[1] = {}; struct spi_buf rx_buf = {.buf = rx_buffer, .len = sizeof(rx_buffer)}; struct spi_buf_set rx = {.buffers = &rx_buf, .count = 1}; uint8_t tx_buffer[1] = {}; struct spi_buf tx_buf = {.buf = tx_buffer, .len = sizeof(tx_buffer)}; struct spi_buf_set tx = {.buffers = &tx_buf, .count = 1}; struct k_poll_signal signal; k_poll_signal_init(&signal); struct k_poll_event event; k_poll_event_init(&event, K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY, &signal); k_poll_signal_reset(&signal); while(1) { // Read Data if(read_action) { spi_read_async(spi_dev, &spi_cfg, &rx, &signal); } else { spi_write_async(spi_dev, &spi_cfg, &tx, &signal); } rc = k_poll(&event,1,K_USEC(10)); if(rc != 0) { // Something is wrong // Reset signal event.signal->signaled = 0; event.state = K_POLL_STATE_NOT_READY; k_poll_signal_reset(event.signal); // Reset state state = READ_REG_ADDR; if(rc == -EAGAIN) { printk("Communication timed out.\n"); } continue; } k_poll_signal_check(event.signal, &signaled, &signal_result); if(rc == 0) { if(signaled != 0) // signal is given as long as its value is non-zero { state_machine(rx); } } // Reset signal event.signal->signaled = 0; event.state = K_POLL_STATE_NOT_READY; k_poll_signal_reset(event.signal); }
I use a simple state machine to check the content of the read/write buffer. Thereby rx/tx is also changed sometimes, e.g.
switch(state) { case READ_REG_ADDR: auto addr = rx_buffer[0]; auto read_action = (addr & 0x80u) == 0x80u; // msb set, so client wants to read from address addr &= 0x7fu; // actual register address (7-bit) if(read_action) { state = WRITE_DATA; tx_buf.buf = registers[addr]->data; tx_buf.len = registers[addr]->size; } else { state = READ_DATA; rx_buf.buf = registers[addr]->data; rx_buf.len = registers[addr]->size; } break; case WRITE_DATA: // do stuff like address increment // switch state break; case READ_DATA: // do stuff like address increment // switch state break; }
Unfortunately, this does not work. From the behavior of the functions spi_read_async/spi_write_async it's like they expect the CS-Line to toggle first. However, that's exactly what I don't want.
Is there any way to achieve my goal?