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

Parents
  • 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.

Reply
  • 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.

Children
  • 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...

  • 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