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

nRF52840 - giving userdefined function at PPI task end

Hello,

We were just wondering if it is possible to give user-defined function at the PPI TEP instead of starting timer or generating some GPIOTE task.

Is it possible to configure a PPI channel where there is a GPIOTE event and as per the occurrence of that event, some userdefined function executes?

nrf_drv_ppi_channel_assign(m_ppi_channel1,
                           gpiote_event_addr,
                           &user_defined_function);

I tried above way, it compiled but the function is not getting executed on GPIOTE event.

I can't use the timer interrupt for this as the frequency of GPIOTE event is really high (around 3MHz). And I want to capture 8-bit parallel port data in 'user_defined_function'

So, Can I make above logic work anyhow? Is it possible in PPI? Any suggestions how can I achieve this one?

Thanks in advance.

Parents
  • Hello,

    Sorry, the previous reply was from a spam-bot. I reported it, and the reply was deleted, along with your answer to it. 

    You can try to use EGU (event generator unit) to trigger a SW interrupt event from the PPI. To get some help to set it up, check out this ticket, and let me know if you are stuck somewhere.

  • Okay, understood the concept of EGU.

    But didn't get how to make it functional.

    Can you please provide a demo code for that? or any further reference regarding that will be useful.

    Thanks.

  • See the attached project. It should work in SDK15.3.0. 

    It is a bare-metal approach for PWM using GPIOTE, TIMER and PPI. I changed one of the pins to trigger an EGU to get a callback and toggle the pin instead of toggling directly from the GPIOTE. 

    Note that there aren't any EGU drivers, so I think this bare-metal approach is the best I can give.

    PS: it is a modified blinky project. Since it doesn't use any drivers, it should work by replacing the main.c file in any SDK example.PPI_EGU_example.zip

    Best regards,

    Edvin

  • Thanks for the demo code,

    I used in my application and it works as expected. However while debugging in Segger Embedded Studio we found something interesting. 

    void SWI1_EGU1_IRQHandler(void)
    {
        ++m_counter;
    }
    
    void egu_init(void)
    {
        NVIC_ClearPendingIRQ(SWI1_EGU1_IRQn);
        NVIC_EnableIRQ(SWI1_EGU1_IRQn);
        NRF_EGU1->INTENSET = (1 << 0) | (1 << 1);
    }
    
    static void ppi_init(void)
    {
        uint32_t err_code = NRF_SUCCESS;
        nrf_drv_gpiote_in_config_t config = NRFX_GPIOTE_RAW_CONFIG_IN_SENSE_LOTOHI(true);
        config.pull = NRF_GPIO_PIN_NOPULL;
        uint32_t gpiote_event_addr;
        nrfx_gpiote_uninit();
    
        err_code = nrf_drv_ppi_init();
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_gpiote_init();
        APP_ERROR_CHECK(err_code);
        //nrfx_gpiote_in_uninit(PIN_NUMBER);
    
        /* Configure 1st available PPI channel to stop TIMER0 counter on TIMER1 COMPARE[0] match,
         * which is every even number of seconds.
         */
        err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel1);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_gpiote_in_init(PIN_NUMBER, &config, gpiote_event_handler);
        APP_ERROR_CHECK(err_code);
    
        nrf_drv_gpiote_in_event_enable(PIN_NUMBER, false);
    
        gpiote_event_addr = nrf_drv_gpiote_in_event_addr_get(PIN_NUMBER);
    
    
        err_code = nrf_drv_ppi_channel_assign(m_ppi_channel1,
                                              gpiote_event_addr,
                                              (uint32_t)&NRF_EGU1->TASKS_TRIGGER[0]);
    
        APP_ERROR_CHECK(err_code);
    
        NRF_PPI->CHENSET                 = (1 << m_ppi_channel1);
    }

    Above is the code snippet which we made by the help of the demo you share. Below is our observation:

    • The egu_init() is successful. ppi_init is also successful.
    • When the routine of ppi_init get's completed specially when ppi channel is enabled, the code gets stuck (I guess in IRQHandler) and m_counter keeps on increment. The thing to be noted is there is continuous input event on the GPIO configured in GPIOTE at the frequency of 1MHz.

    Is this a expected behaviour? As we have another logic to be performed after ppi_init, we might not want to get stuck in IRQHandler. Is there any configuration which we missed? or am I understanding it incorrectly?

    Thanks

  • I am not sure, but I think I saw something similar when I tested the project that I sent you.

    I thought that it stopped the other PPI events from happening (LED1 stopped, but LED2 kept on blinking). I figured this was because the EGU triggered too often, so that the application layer never got any time to update the TIMER->CC[] registers. If you have interruptions at 1MHz, then you don't have many clock cycles to run your SWI_EGU1_IRQHandler() before the interrupt triggers again.

    However, I see that you don't reset the EGU event. Can you try to add:

    NRF_EGU1->TASKS_TRIGGER[0] = 0;

    in your SWI1_EGU1_IRQHandler()?

  • I added below line of code in IRQ Handler.

    NRF_EGU1->TASKS_TRIGGER[0] = 0;

    But still there is no change in behaviour. It continuously executes the IRQ handler and none of the other logic is executed.

    Is there any document for clock cycle, such that we can understand the machine cycle consumed in each instruction?

  • can you try to trigger the PPI event less often, i.e. have an input pulse with a lower frequency to see if this is actually the issue? (you can use a timer to generate a pulse on one of the GPIOs that you input to your PPI pin)

Reply Children
  • I don't think that is the issue, this is because when I remove the input pulse from the GPIO then also the counter keeps on increment.

    I tried with timer of 10ms. Then also, when first event occurs at PPI event end, the execution of SWI1_EGU1_IRQHandler() starts endlessly. If I remove the input pulse at runtime (by removing input from GPIO pin) then also the counter keeps on increment.

  • Is it possible to reproduce this on a DK? If so, would it be possible to send a project that can reproduce this, so that I can test it and see what's going on?

    Best regards,

    Edvin

  • Yes, it is possible to reproduce on DK. Timer interrupt toggles pin 0.05 on DK and EGU is triggered by the event on pin 0.02

    Attached is the project.

    Thanks.

    nrf52-ble-image-transfer-demo.zip

  • Hello,

    Sorry. I should have noticed.

    In your egu event handler you should use:

    NRF_EGU1->EVENTS_TRIGGERED[0] = 0;

    not

    NRF_EGU1->TASKS_TRIGGER[0] = 0;

    I also see that something triggered NRF_EGU1->EVENTS_TRIGGERED[1]. Not sure exactly what that is. I didn't get the time to look properly at it, but maybe you figure it out before tomorrow (Not sure where you are situated). 

    So I added:

    NRF_EGU1->EVENTS_TRIGGERED[0] = 0;
    NRF_EGU1->EVENTS_TRIGGERED[1] = 0;

    And then it stopped calling the EGU event handler. Maybe some other library is using EGU1, and you can try another EGU (to prevent EVENTS_TRIGGERED[1] from triggereng the event handler.

    Best regards,

    Edvin

  • Hello,

    Thanks for your suggestion. Now there is no unnecessary interrupts when the pulse is removed from the GPIO.

    As we have a very narrow timeline for capturing the image, we want to calculate the machine cycle of the subroutine and total available machine cycles for the controller. Is there any document/reference from where we can get this information?

    Also, we are capturing parallel port data. Is there any better way for storing the image on device? I have gone through the demo of flash data storage. Will it be appropriate to use flash storage considering very tight timeline (high rate of input data)?

    Can we write subroutine for EGU in assembly? Will it reduce number of machine cycles it takes to execute the subroutine?

    Thanks.

Related