3. Implementation

Using the project created in the Clone FreeRTOS retarget Project section, this section demonstrates how to catch the external event and use it in the RTOS.

3.1. Existing Application

The freertos_retarget project exhibits a timer-based application task that sends a character over the UART interface every second. This behavior is implemented in main.c file.

static void prvTemplateTask( void *pvParameters )
{
        OS_TICK_TIME xNextWakeTime;
        static uint32_t test_counter=0;

        /* Initialize xNextWakeTime - this only needs to be done once. */
        xNextWakeTime = OS_GET_TICK_COUNT();

   for( ;; ) {
                /* Place this task in the blocked state until it is time to run again.
                   The block time is specified in ticks, the constant used converts ticks
                   to ms.  While in the Blocked state, this task will not consume any CPU
                   time. */
                vTaskDelayUntil( &xNextWakeTime, mainCOUNTER_FREQUENCY_MS );
                test_counter++;

                if (test_counter % (1000 / OS_TICKS_2_MS(mainCOUNTER_FREQUENCY_MS)) == 0){
                        printf("#");
                        fflush(stdout);
                }
        }
}

3.2. Configure the GPIO Generating Interrupt

On the DA1469x Pro DevKit, the push button K1 is mapped on P0_6 pin. In order to detect an interrupt, it is necessary to configure the GPIO as an input. The button connects the pin to the ground. For demonstration purposes we will enable an internal pull-up resistor on that pin to be able to detect the falling edge when the button is pressed. To do this, in main.c file, replace the prvSetupHardware routine with the following code:

/**
 * @brief Hardware Initialization
 */
static void prvSetupHardware(void)
{
        /*
         * The IRQ produced by the KEY sub block of the wakeup controller (debounced IO
         * IRQ) is multiplexed with other trigger sources (VBUS IRQ, SYS2CMAC IRQ, JTAG
         * present) in a single PDC peripheral trigger ID (HW_PDC_PERIPH_TRIG_ID_COMBO).
         */
#if !defined(CONFIG_USE_BLE) && (!dg_configENABLE_DEBUGGER) && (!dg_configUSE_SYS_CHARGER)

        uint32_t pdc_wkup_combo_id = hw_pdc_add_entry(HW_PDC_LUT_ENTRY_VAL(
                                                            HW_PDC_TRIG_SELECT_PERIPHERAL,
                                                            HW_PDC_PERIPH_TRIG_ID_COMBO,
                                                            HW_PDC_MASTER_CM33, 0));
        OS_ASSERT(pdc_wkup_combo_id != HW_PDC_INVALID_LUT_INDEX);

        /*  */
        hw_pdc_set_pending(pdc_wkup_combo_id);
        hw_pdc_acknowledge(pdc_wkup_combo_id);
#endif

        /* Init hardware */
        pm_system_init(periph_init);


        /* Enable the COM power domain before handling any GPIO pin */
        hw_sys_pd_com_enable();

        /* Configure the KEY1 push button on Pro DevKit */
        HW_GPIO_SET_PIN_FUNCTION(KEY1);
        HW_GPIO_PAD_LATCH_ENABLE(KEY1);

        /* Lock the mode of the target GPIO pin */
        HW_GPIO_PAD_LATCH_DISABLE(KEY1);

        /* Disable the COM power domain after handling the GPIO pins */
        hw_sys_pd_com_disable();
}

3.3. Set up the Wake-Up Timer

The next step is to initialize the Wake-Up Timer block and configure it to generate an interrupt on the falling edge. Then, we need to register an interrupt handler to handle the interrupt, the function will be button_interrupt_cb().

/* Initialize the WKUP controller */
static void wkup_init(void)
{

        /* Initialize the WKUP controller */
        hw_wkup_init(NULL);

        /*
         * Set debounce time expressed in ms. Maximum allowable value is 63 ms.
         * A value set to 0 disables the debounce functionality.
         */
        hw_wkup_set_debounce_time(10);

        /*
         * Enable interrupts produced by the KEY block of the wakeup controller (debounce
         * circuitry) and register a callback function to hit following a KEY event.
         */
        hw_wkup_register_key_interrupt(button_interrupt_cb, 1);


        /*
         * Set the polarity (rising/falling edge) that triggers the WKUP controller.
         *
         * \note The polarity is applied both to KEY and GPIO blocks of the controller
         *
         */
        hw_wkup_configure_pin(KEY1_PORT, KEY1_PIN, 1, HW_WKUP_PIN_STATE_LOW);


        /* Enable interrupts of WKUP controller */
        hw_wkup_enable_irq();
}

The NULL parameter in hw_wkup_init() resets the wake-up block to its initial state. hw_wkup_configure_pin() configures the GPIO P0_6 to generate the interrupt.

3.4. Handle the Interrupt

button_interrupt_cb() will be executed in handling mode. In order to defer the handling of the interrupt inside the RTOS, we will generate a task notification. The callback also needs to clear the current interrupt flag.

void button_interrupt_cb(void)
{
        hw_wkup_reset_interrupt();
        OS_TASK_NOTIFY_FROM_ISR(task_h, 0x1, OS_NOTIFY_SET_BITS);
}

The task_h handler is declared in the system_init() function and needs to be made global to allow button_interrupt_cb() to use it.

/* Task handle */
__RETAINED static OS_TASK task_h;

3.5. Using the Notification

The notification can be used by the prvTemplateTask(). We can modify the loop inside the application task so that, instead of sending a character on regular basis, we wait for the button push notification. Effectively, the character will be sent every time the button is pushed. In main.c file, replace the prvTemplateTask application task with the following code:

#include "hw_wkup.h"
#include "hw_pdc.h"

/**
 * @brief Template task increases a counter every mainCOUNTER_FREQUENCY_MS ms
 */
static void prvTemplateTask( void *pvParameters )
{
        uint32_t ulNotifiedValue;

        wkup_init();

        for( ;; ) {

                     /* Wait for the external interruption notification */
                     OS_TASK_NOTIFY_WAIT(0x0, OS_TASK_NOTIFY_ALL_BITS, &ulNotifiedValue,
                                                                  OS_TASK_NOTIFY_FOREVER);

                     /* Check the notification is the expected value */
                      if(ulNotifiedValue & 0x1){
                              /* Send the character on the UART */
                             printf("#");
                             fflush(stdout);
                     }
             }
}

3.6. Testing

Build the project either in Debug_QSPI or Release_QSPI mode and burn the generated image to the chip. Open a serial terminal of your choice with the following parameters:

  • 115200 bps

  • 8 bits

  • no flow control

  • 1 stop bit

After each K1 press you should see an additional # character appeared on your serial console as shown in Figure 2.

_images/serial_term.png

Figure 2 Terminal Interface