How does an EGU register get a value?

I have been trying to decipher the NRF specification documents to simply understand how PPI, interrupts, Tasks, Events, and EGUs are supposed to work together. I am still confused.

Searching the DevZone, I came up with this post which refers to this nordic example.

In this code, we have a handler function which is supposed to do stuff when EVENTS_TRIGGER[x] register has a "true" value.

void SWI1_EGU1_IRQHandler(void)
{
    if (NRF_EGU1->EVENTS_TRIGGERED[0] != 0)
    {
        NRF_EGU1->EVENTS_TRIGGERED[0] = 0;
        NRF_GPIOTE->TASKS_CLR[PWM1_GPIOTE_CH] = 1;      // Turn on LED2.
    }
    if (NRF_EGU1->EVENTS_TRIGGERED[1] != 0)
    {
        NRF_EGU1->EVENTS_TRIGGERED[1] = 0;
        NRF_GPIOTE->TASKS_SET[PWM1_GPIOTE_CH] = 1;      // Turn off LED2.
    }
}

Could anyone tell how this EVENT_TRIGGERED[x] part of "NRF_EGU1" gets a value? What writes that value?

As already mentioned by other people, the NGU documentation is confusing and minimalistic; it does not clearly explain the read/write flow (e.g. what writes where and such information). 

  • I can give a guide following my own understanding, probably not following the documentation .. anyway Hardware Events can trigger other Hardware Events. Any Hardware Event can optionally be linked to an interrupt vector. A Hardware Event is not an interrupt; Hardware Events and interrupts are enabled differently, one does not infer the other.

    Here is my example of using an EGU to move some data processing out of a critical timing interrupt into a less-critical timing EGU interrupt to avoid missing the other time-critical timer events. I put this first (not what you are seeking) as it shows the exact way the EGU Hardware Event with corresponding EQU interrupt is triggered, in this case by a software instruction (instigating the Hardware Task):

    // Event Generator Unit (EGUn)
    static NRF_EGU_Type* pNRF_EGU = NRF_EGU4;
    #define SWI_EGU_IRQn SWI4_EGU4_IRQn
    
    void SWI4_EGU4_IRQHandler(void)
    {
        // Handle the character ready to process event
        if (pNRF_EGU->EVENTS_TRIGGERED[0])
        {
            // Clear the character ready to process event
            pNRF_EGU->EVENTS_TRIGGERED[0] = 0;
            // Slow stuff goes here moved from timer interupt
        }
    }
    
    void init(void)
    {
        // EGU setup Clear the character ready to process event
        pNRF_EGU->EVENTS_TRIGGERED[0] = 0;
        pNRF_EGU->INTENSET = 1;
        // Set interrupt priority lower priority than timer and enable interrupt
        NVIC_SetPriority(SWI_EGU_IRQn, 6+1);
        NVIC_ClearPendingIRQ(SWI_EGU_IRQn);
        NVIC_EnableIRQ(SWI_EGU_IRQn);
    }
    // This IRQ handler will trigger 3 times every Rx character bit
    void TIMER3_IRQHandler(void)
    {
        .. snip other events
        if (pTimer->EVENTS_COMPARE[4] == 1)
        {
            pTimer->EVENTS_COMPARE[4] = 0;
            if (blahblah
            {
                // Trigger the character ready to process event by activating the lower-priority EGU interrupt
                pNRF_EGU->TASKS_TRIGGER[0] = 1;
        }
    }

    Example of using an EGU to process a Hardware Event directly (not via software instruction) by instigating an EGU Hardware Event (Task) from the timer (or other) Hardware Event. This EGU Hardware Event can optionally trigger the associated EGU software interrupt:

    // Event Generator Unit (EGUn)
    static NRF_EGU_Type* pNRF_EGU = NRF_EGU4;
    #define SWI_EGU_IRQn SWI4_EGU4_IRQn
    
    void SWI4_EGU4_IRQHandler(void)
    {
        // Handle the character ready to process event
        if (pNRF_EGU->EVENTS_TRIGGERED[0])
        {
            // Clear the character ready to process event
            pNRF_EGU->EVENTS_TRIGGERED[0] = 0;
            // etc
        }
    }
    
    void init(void)
    {
        // Configure PPI channel with connection between pTIMER->EVENTS_COMPARE[4] and pNRF_EGU->TASKS_TRIGGER[0]
        NRF_PPI->CH[PPI_CHANNEL].EEP = (uint32_t)&pTimer->EVENTS_COMPARE[4];
        NRF_PPI->CH[PPI_CHANNEL].TEP = (uint32_t)&pNRF_EGU->TASKS_TRIGGER[0];
        // EGU setup Clear the character ready to process event
        pNRF_EGU->EVENTS_TRIGGERED[0] = 0;
        pNRF_EGU->INTENSET = 1;
        // Set interrupt priority lower priority than timer and enable interrupt
        NVIC_SetPriority(SWI_EGU_IRQn, 6+1);
        NVIC_ClearPendingIRQ(SWI_EGU_IRQn);
        NVIC_EnableIRQ(SWI_EGU_IRQn);
    }

    Any Hardware Event can similarly be linked to an EGU Hardware Event (and thus an EGU software interrupt) by this PPI mechanism.

  • Thanks for your answer  , could you also tell me what calls the "SWI4_EGU4_IRQHandler"? what tiggers that? 

    My educated guess is that whenever the timer call back executes the timer handler it would write on the "EVENTS_TRIGGERED" register of the NRF_EGU4, upon which the "SWI4_EGU4_IRQHandler" will be automatically executed. (In other words, there is a mechanism continually monitoring the EVENTS_TRIGGERED register and, the moment there is a registered event, it will trigger the handler.)

    Also, shouldn't the "init()" function be called inside the main {} function? (I guess you assumed that an obvious thing, just asking for more clarity)

  • The "SWI4_EGU4_IRQHandler" is an interrupt function accessed only by an implicit assembly language call instruction from the vector interrupt table, which resides at physical address 0000'0000 unless a bootloader or softdevice is used in which case the vector interrupt table can reside elsewhere and a vector register is used by the bootloader or softdevice to point at that vector table. That SWI4_EGU4_IRQHandler vector is instigated in hardware by EVENTS_TRIGGERED going active ('0' to '1'), which in turn goes to active '1' either by an enabled Hardware Event going active or by executing a software instruction TASKS_TRIGGER; the result is identical whether invoked via hardware event or software trigger.

    __vector_table
            DCD     sfe(CSTACK)
            DCD     Reset_Handler
            DCD     NMI_Handler
            DCD     HardFault_Handler
            DCD     MemoryManagement_Handler
            DCD     BusFault_Handler
            DCD     UsageFault_Handler
            DCD     0                         ; Reserved
            DCD     0                         ; Reserved
            DCD     0                         ; Reserved
            DCD     0                         ; Reserved
            DCD     SVC_Handler
            DCD     DebugMon_Handler
            DCD     0                         ; Reserved
            DCD     PendSV_Handler
            DCD     SysTick_Handler
    
            ; External Interrupts
            DCD     POWER_CLOCK_IRQHandler
            DCD     RADIO_IRQHandler
            DCD     UARTE0_UART0_IRQHandler
            DCD     SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler
            DCD     SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQHandler
            DCD     NFCT_IRQHandler
            DCD     GPIOTE_IRQHandler
            DCD     SAADC_IRQHandler
            DCD     TIMER0_IRQHandler
            DCD     TIMER1_IRQHandler
            DCD     TIMER2_IRQHandler
            DCD     RTC0_IRQHandler
            DCD     TEMP_IRQHandler
            DCD     RNG_IRQHandler
            DCD     ECB_IRQHandler
            DCD     CCM_AAR_IRQHandler
            DCD     WDT_IRQHandler
            DCD     RTC1_IRQHandler
            DCD     QDEC_IRQHandler
            DCD     COMP_LPCOMP_IRQHandler
            DCD     SWI0_EGU0_IRQHandler
            DCD     SWI1_EGU1_IRQHandler
            DCD     SWI2_EGU2_IRQHandler
            DCD     SWI3_EGU3_IRQHandler
            DCD     SWI4_EGU4_IRQHandler
            DCD     SWI5_EGU5_IRQHandler
            DCD     TIMER3_IRQHandler
            DCD     TIMER4_IRQHandler
            DCD     PWM0_IRQHandler
            DCD     PDM_IRQHandler
            DCD     0                         ; Reserved
            DCD     0                         ; Reserved
            DCD     MWU_IRQHandler
            DCD     PWM1_IRQHandler
            DCD     PWM2_IRQHandler
            DCD     SPIM2_SPIS2_SPI2_IRQHandler
            DCD     RTC2_IRQHandler
            DCD     I2S_IRQHandler
            DCD     FPU_IRQHandler
    

    Yes, init() only executes once usually from main().

Related