Hi,
On a NRF52840, I try to perform `x` SPI transactions of 3 bytes each using EasyDMA Arraylist feature fully controlled by hardware. The CS must be toggled between each block of 3 bytes so I use CS HW of SPIM3.
Example of a transaction:

I need everything to be hardware controlled as SoftDevice is running and has higher interrupts priority than my app, I do not control the software timing precisely. My slot window is very tiny and a precise timing is required.
The start is not a problem, triggered by a GPIOTE but I don't know how to stop the SPI in hardware.
Here is the code I tried:
#define NUM_SPI_TRANSACTION 10
#define SPI_CMD_SIZE 3
uint8_t spi_buf[NUM_SPI_TRANSACTION * SPI_CMD_SIZE];
void configure_timer_ppi(void) {
/* Configure timer1 in counter mode */
NRF_TIMER1->TASKS_STOP = 1;
NRF_TIMER1->TASKS_CLEAR = 1;
NRF_TIMER1->MODE = TIMER_MODE_MODE_LowPowerCounter;
NRF_TIMER1->BITMODE = TIMER_BITMODE_BITMODE_08Bit;
/* Stop timer when it reaches the expected value */
NRF_TIMER1->SHORTS = TIMER_SHORTS_COMPARE1_STOP_Enabled
<< TIMER_SHORTS_COMPARE1_STOP_Pos;
/* Configure number of SPI transactions to trigger */
NRF_TIMER1->CC[0] = NUM_SPI_TRANSACTION;
NRF_TIMER1->CC[1] = NRF_TIMER1->CC[0];
NRF_TIMER1->TASKS_START = 1;
}
void configure_ppi(void) {
/* Increment timer counter when SPI transaction is done */
sd_ppi_channel_assign(0, &NRF_SPIM3->EVENTS_END, &NRF_TIMER1->TASKS_COUNT);
/* Stop SPI when counter reaches the right amount of SPI transactions */
sd_ppi_channel_assign(1, &NRF_TIMER1->EVENTS_COMPARE[0],
&NRF_SPIM3->TASKS_STOP);
sd_ppi_channel_enable_set(0);
sd_ppi_channel_enable_set(1);
}
void configure_spi_dma(void) {
NRF_SPIM3->TASKS_STOP = 1;
NRF_SPIM3->TXD.MAXCNT = SPI_CMD_SIZE;
NRF_SPIM3->RXD.MAXCNT = 0;
/* Configure DMA in array_list */
NRF_SPIM3->TXD.LIST = SPIM_TXD_LIST_LIST_ArrayList << SPIM_TXD_LIST_LIST_Pos;
NRF_SPIM3->TXD.PTR = (uint32_t)(spi_buf);
NRF_SPIM3->EVENTS_END = 0;
/* When a SPI transacation is done, starts SPI again */
NRF_SPIM3->SHORTS = SPIM_SHORTS_END_START_Enabled
<< SPIM_SHORTS_END_START_Pos;
/* Also configure the rest of SPI including the CS HW */
}
int main(void) {
/* Init */
ble_stack_init();
gap_params_init();
gatt_init();
advertising_init();
services_init();
conn_params_init();
configure_timer_ppi();
configure_ppi();
configure_spi_dma();
/* Trigger spi start */
NRF_SPIM3->TASKS_START = 1;
/* Enter main loop */
for (;;) {
}
return 0;
}
With this code, the SPI never stops, I guess this is due to the short of the SPI, it has to be disabled before stopping it but this is not mentioned in the datasheet. How to deactivate shorts with hardware? Why the SPI does not stop? I also tried to increment counter timer on `NRF_SPIM3->EVENTS_STARTED` and `NRF_SPIM3->EVENTS_STOPPED`, same result.
Then I tried to pause the SPI with PPI instead of stopping it `NRF_SPIM3->TASKS_SUSPEND`
void SWI0_IRQHandler(void) {
/* Clear SWI event */
NRF_EGU0->EVENTS_TRIGGERED[0] = 0;
/* Disable short before stopping SPI */
NRF_SPIM3->SHORTS = SPIM_SHORTS_END_START_Disabled;
NRF_SPIM3->TASKS_STOP;
}
void configure_ppi(void) {
/* Increment timer counter when SPI transaction is done */
sd_ppi_channel_assign(0, &NRF_SPIM3->EVENTS_END, &NRF_TIMER1->TASKS_COUNT);
/* Stop SPI when counter reaches the right amount of SPI transactions */
sd_ppi_channel_assign(1, &NRF_TIMER1->EVENTS_COMPARE[0],
&NRF_SPIM3->TASKS_SUSPEND);
/* Generate a software interrupt when counter reaches the right amount of SPI
* transactions */
NRF_EGU0->INTENSET = EGU_INTENSET_TRIGGERED0_Enabled
<< EGU_INTENSET_TRIGGERED0_Pos;
NVIC_SetPriority(SWI0_EGU0_IRQn, IRQ_PRIO_LOWEST);
NVIC_ClearPendingIRQ(SWI0_EGU0_IRQn);
NVIC_EnableIRQ(SWI0_EGU0_IRQn);
NRF_PPI->FORK[1].TEP = (unsigned int)&NRF_EGU0->TASKS_TRIGGER[0];
sd_ppi_channel_enable_set(0);
sd_ppi_channel_enable_set(1);
}
It does not work as expected, it pauses but after sending a byte. I think this is due to short of SPI being executed before PPI, right? From datasheet, chapter 6.1.7 Shortcuts:
> [...] However, the propagation delay through the shortcut is usually shorter than the propagation delay through the PPI.
Transaction example:
Then, the SPI block seems stuck, calling `NRF_SPIM3->TASKS_STOP` in the app has no effect, the CS remains asserted. Also I cannot start another SPI transaction. Why calling `NRF_SPIM3->TASKS_STOP` or `NRF_SPIM3->TASKS_RESUME` has no effect, how to unlock the SPI block?
I tried another solution involving software interrupt to stop the SPI but this is not accurate, the handle timing of the interrupt by the app is not predictable.
I also tried without short by connecting the `NRF_SPIM3->EVENTS_END` to `NRF_SPIM3->EVENTS_START` in PPI but the result is the same, SPI block seems stuck at some point.
Is it possible to perform multiple SPI transaction fully hardware controlled, without involving software interrupt?
