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?