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>)

  • In line 35 you are connecting to event COMPARE0, however in the code you provide nothing us setting CC[0] to 6; by default it will be 0, i.e. effectively 65536.  And in line 24 you setting CC[5] to 0xffff but it (COMPARE5) seems unused?

    (BTW a lot of sympathy for your rant.  And I think timer SHORTS can be accessed through the SDK function "...extended_compare...", which is not an immediately intuitive naming choice!)

  • Ah yes I changed from Compare0 to Compare5 but never updated the PPI address (earlier when something else want working I got to thinking that Compare0 might be reserved by the SoftDevice like Timer0 is, I don't think that's the case though).  I'll have to give this a try.

  • Hmm I tried this and just noticed something weird, PPI CH[2].EEP never gets set, it just stays =0x00.  Maybe there is something that blocks using PPI CH[2]?  It is the only thing I can see that isn't set correctly when I step through the debugger.

  • OK, so after a little testing, I am very confused.  Per Daniel Chisholm, I fixed my COMPARE0 bug, but now I notice that cannot set PPI to the COMPARE5 event.  Is there some reason that this event cannot be used?

    Here is what happens if I use COMPARE0:

        /* 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)0x40009140UL; // Address for COMPARE0
        NRF_PPI->CH[2].TEP  = (uint32_t)((uint32_t)NRF_SPIM3_BASE   + (uint32_t)NRF_SPIM_TASK_STOP);  // TASKS_STOP

    But with COMPARE5:

        /* 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)0x40009154UL; // Address for COMPARE5
        NRF_PPI->CH[2].TEP  = (uint32_t)((uint32_t)NRF_SPIM3_BASE   + (uint32_t)NRF_SPIM_TASK_STOP);  // TASKS_STOP

    I also notice that the app does a full rebuild when I make this change, indicating that something bigger has changed with the app as a whole.  So... what's the deal COMPARE5?

    EDIT: I should also add that even after changing back to COMPARE0, SPIM3 is still not stopping even though the STOP event has occurred:

    I break in the TIMER1 compare handler (so COMPARE0 has happened, and therefore PPI CH[2] should have stopped SPIM3):

    and here are the SPIM3 registers:

    Even if I uncomment the TASKS_STOP trigger at the top of the handler, it still doesn't stop. The only way it stops is if I disable SPIM3 in the handler.

  • So I think I finally grok the problem here.  Kicking off the STOP take for SPIM does nothing if SHORTS are still set.  Somehow you have to disable SHORTS, which I'm guessing requires software intervention?  I disconnect it first thing in my TIMER1 COMPARE0 handler, but I still get a two byte overrun since SPIM is still running in the background.  I can just discard this data for my purposes here, but is there a better way?  It feels like there should be a DISCONNECT SHORTS task in SPIM, or some other equivalent but I'm not finding it.

    Still would like to know why COMPARE5 doesn't work though...

Related