This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

SPIM STOP task isn't getting triggered by PPI

Hey all, I'm new here.  So far things have been going pretty well, I am just having an issue with PPI that I can't quite figure out, but I think I'm close.  I currently have an nRF52840 DK hooked up to a 6 axis sensor with a FIFO buffer.  I get the number of records in FIFO from the device, then try to use PPI to send a STOP to SPIM once I have read that many records.  To accomplish this I have Timer1 in compare mode and tied the SPIM END event to the Timer1 COUNT task with PPI, then I have the Timer1 COMPARE event hooked to the SPIM STOP task.

The problem is that I see the SPIM STOPPED register set to 1 in the debugger, but the SPIM is still reading on my scope.

Here are some snippets:

const nrfx_timer_t m_timer1 = NRFX_TIMER_INSTANCE(1);
const nrfx_spim_t m_spim3 = NRFX_SPIM_INSTANCE(3);

...

nrfx_spim_config_t spim_cfg = NRFX_SPIM_DEFAULT_CONFIG;
spim_cfg.frequency  = NRF_SPIM_FREQ_8M;
spim_cfg.ss_pin     = SPIM_CS;
spim_cfg.sck_pin    = SPIM_SCK;
spim_cfg.mosi_pin   = SPIM_MOSI;
spim_cfg.miso_pin   = SPIM_MISO;
spim_cfg.orc        = SPIM_ORC;
spim_cfg.mode       = NRF_SPIM_MODE_0;
spim_cfg.use_hw_ss  = true;
APP_ERROR_CHECK(nrfx_spim_init(&m_spim3, &spim_cfg, spim_event_handler, NULL));

...

nrfx_timer_config_t timer_config = NRFX_TIMER_DEFAULT_CONFIG;
timer_config.mode = (nrf_timer_mode_t)NRF_TIMER_MODE_COUNTER;
err_code = nrfx_timer_init(p_timer1, &timer_config, timer_handler);
APP_ERROR_CHECK(err_code);

nrfx_timer_compare(p_timer1, NRF_TIMER_CC_CHANNEL5, 0xFFFF, true);
nrfx_timer_enable(p_timer1);

/* PPI Configuration - NOTE that we DO NOT enable PPI here, but in the SPIM FIFO read
 * Configure PPI channel 0 to increment TIMER1 counter on SPIM END event.
 */
NRF_PPI->CH[0].EEP  = (uint32_t)((uint32_t)NRF_SPIM3_BASE   + (uint32_t)NRF_SPIM_EVENT_END);  // EVENTS_END
NRF_PPI->CH[0].TEP  = (uint32_t)((uint32_t)NRF_TIMER1_BASE  + (uint32_t)NRF_TIMER_TASK_COUNT);  // TASKS_COUNT

/* Configure PPI channel 2 to initiate a SPIM STOP task on TIMER1 COMPARE event.
 */
NRF_PPI->CH[2].EEP  = (uint32_t)((uint32_t)NRF_TIMER1_BASE  + (uint32_t)NRF_TIMER_EVENT_COMPARE0);  // EVENTS_COMPARE
NRF_PPI->CH[2].TEP  = (uint32_t)((uint32_t)NRF_SPIM3_BASE   + (uint32_t)NRF_SPIM_TASK_STOP);  // TASKS_STOP

...

/* Get buffer cardinality from FIFO_STATUS1 and 2 */
nrfx_timer_compare(&p_timer1, NRF_TIMER_CC_CHANNEL5, (((rx_buff[2] & 0x0F) << 8 ) | rx_buff[1]), true);

/* Reset TIMER1 counter. */
nrfx_timer_clear(&p_timer1);

/* Set up buffers */
p_instance.p_reg->TXD.PTR    = (uint32_t)tx_buff;
p_instance.p_reg->TXD.MAXCNT = 1;
p_instance.p_reg->RXD.PTR    = (uint32_t)fifo_buff;
p_instance.p_reg->RXD.MAXCNT = 3;
p_instance.p_reg->RXD.LIST   = (SPIM_RXD_LIST_LIST_ArrayList << SPIM_RXD_LIST_LIST_Pos); // Array List mode
p_instance.p_reg->SHORTS     = (SPIM_SHORTS_END_START_Enabled << SPIM_SHORTS_END_START_Pos);   //Short END to START

/* ENABLE PPI connections
 * SPIM END event is connected to START task by SHORTS.
 * SPIM END event is connected to TIMER1 COUNT task in PPI0,
 * then TIMER1 COMPARE event is connected to SPIM STOP task for = fifo size in PPI2
 */
NRF_PPI->CHENSET    = (PPI_CHENSET_CH0_Enabled << PPI_CHENSET_CH0_Pos);
NRF_PPI->CHENSET    = (PPI_CHENSET_CH2_Enabled << PPI_CHENSET_CH2_Pos);

p_instance.p_reg->INTENCLR = 0xFFFFFFFFUL;

/* Kick off SPIM START Task */
p_instance.p_reg->TASKS_START = 1;

And yes, I am aware that I am mixing nrfx driver and registers, but there are no nrfx functions that I see for SHORTS or LIST mode (and I don't see them in the xfer_desc structure) so I am guessing that you are intended to use the buffers.  And besides, without PPI it works great as it is.  As for PPI, I only use the registers so I am guessing that is fine.  (<rant>Also, why are there so many different options for drivers?  I see so many questions where the answer is "You should use XXX driver instead" that it is almost as if they were invented to prevent the need of answering questions.</rant>)

  • Just had a look at the nrf52840 Product Specification and section 6.5.30 "Registers" indicates that TIMER0-TIMER2 have 4 CC registers, and TIMER3-TIMER4 have 6 CC registers.  So COMPARE5 doesn't exist for TIMER1.  Why not try COMPARE3 and see if that works, or try COPMARE5 with TIMER3 or TIMER4? (BTW ii seems remarkable to me that CH[2].EEP reads back "0" when you wrote 0x40009154 to it (where TIMER1.EVENTS_COMPARE[5] would be, if it were to exist)..... I can't begin to think how that might be implemented in the silicon?)

    BTW2, I noticed in your debugger output that SPIM3.SHORTS = 0x0020000, which means "Shortcut between event END and task START".... so maybe in fact your COMPARE0 really is stopping SPIM3, but as soon as it ends then this shortcut immediately restarts it? (EVENTS_STARTED also shows "1")

  • Just had a moment of inspiration to solve the SPIM SHORTS problem.  Instead of using SHORTS, I just made a new PPI channel from SPIM END Event to SPIM START Task, then added is to PPI CHG[0].  Then I added a FORK to PPI[2] to trigger a PPI CHG[0].DIS Task, thus disconnecting the short from SPIM END to SPIM START.

    Still curious about COMPARE5 though.  If I don't hear back by tomorrow on that I will close this out since I answered my own question and open a new ticket for COMPARE5.

    EDIT: Per Daniel Chisholm, the datasheet indicates that there is no COMPARE5 for TIMER0-2, so that answers that.

  • Ahhhhh, I saw that 4 CC register thing, but somehow it didn't sink in at the time.  I already switched to COMPARE0, but it was still bothering me  Thanks for that!

    As for the shorts, I think I posted an "answer" about the same time you posted this, but yes, that was the issue.  I didn't realize that the SHORTS can't be turned off in hardware, so I came up with a solution just using PPI and channel groups (since those have enable/disable tasks that can be triggered).

    Thanks for all of your help!

Related