C

Qt Quick Ultralite freertos_app_switch Example

Shows how to switch between a Qt Quick Ultralite application and another one via soft reset.

Overview

The freertos_app_switch demonstrates how it is possible to switch between a Qt Quick Ultralite application and another one via soft reset and a memory location that holds its state in between soft resets. This example also shows how to restart a Qt Quick Ultralite application, re-triggering the application's initialization steps.

A specific memory address is used at startup to select between two applications:

  • Application #1 is the Qt Quick Ultralite application itself, offering a user interface to the boot selection mechanism.

  • Application #2 is another application that directly writes to the framebuffer, blinks the LED for a few seconds and then restarts the device into the first application.

Note: Be careful when using soft resets. Make sure that the hardware peripherals can be correctly re-initialized. Some peripherals might require a reset signal or to be in a specific state before they can be programmed.

Note: The same reset mechanism presented here could be adapted to use hard resets (triggering a power cycle) and a non-volatile memory to hold state.

Target platforms

Code overview

Application entry point

The main() function takes care of hardware and platform initialization, and additionally it initializes the LED using the board-specific InitLED() function.

...
int main()
{
    Qul::initHardware();
    Qul::initPlatform();
    initLED();
    ...

Next, the boot value is read from memory.

    int boot = readBootValue();

Where the readBootValue() method simply reads from a specific memory location and it's defined in boardutils.c.

int readBootValue()
{
    int *ptr = &__BOOT_VAL_ADDR;
    return *ptr;
}

The address of the boot variable can be set via a linker-defined symbol in the board.cmake file.

set(BOOT_VAL_ADDR 0x81400000)
if (IAR)
    target_link_options(freertos_app_switch PRIVATE --define_symbol __BOOT_VAL_ADDR=${BOOT_VAL_ADDR})
else()
    target_link_options(freertos_app_switch PRIVATE -Xlinker --defsym=__BOOT_VAL_ADDR=${BOOT_VAL_ADDR})
endif()

Based on the boot value a different FreeRTOS task is created. Either a Qt Quick Ultralite task running Application #1 or another task implementing Application #2.

    if (boot != APP2) {
        Qul::PlatformInterface::log("Application #1...QUL Application Switch\r\n");
        if (xTaskCreate(App1_thread, "QulExec", QUL_STACK_SIZE, 0, 4, &QulTask) != pdPASS) {
            Qul::PlatformInterface::log("Task creation failed!\r\n");
            configASSERT(false);
        }
    } else {
        Qul::PlatformInterface::log("Application #2...blinking LED\r\n");
        if (xTaskCreate(App2_thread, "LedToggle", configMINIMAL_STACK_SIZE, 0, 4, &LedTask) != pdPASS) {
            Qul::PlatformInterface::log("LED task creation failed!\r\n");
            configASSERT(false);
        }
    }

    vTaskStartScheduler();
    ...

The Application #1 task running Qt Quick Ultralite simply sets the root item of the application and calls the exec() function.

static void App1_thread(void *argument)
{
    (void) argument;

    Qul::Application app;
    static appswitch item;
    app.setRootItem(&item);
    app.exec();
}

The Application #2 task directly writes a raw image to the framebuffer via displayApp2Background(), then blinks the LED for a few cycles.

static void App2_thread(void *argument)
{
    (void) argument;
    displayApp2Background();

    //Blink for N_BLINKS times then reboot into App1
    for (int i = 0; i < N_BLINKS; ++i) {
        xTaskNotifyWait(0, ULONG_MAX, NULL, pdMS_TO_TICKS(500));
        toggleLED();
        taskYIELD();
    }
    reset(APP1);
}
QML

The Qt Quick Ultralite user interface is implemented in freertos_app_switch.qml. The application to be executed at the next boot is selected via two radio buttons and a reset button triggers the soft reset.

                ...
                Button {
                    id: resetButton
                    text: "Reset!"
                    anchors.centerIn: parent
                    onClicked: {
                        console.log("Chosen application #", DevControl.application, "...Resetting system!")
                        DevControl.resetDevice();
                    }
                }
                ...

A DevControl object is used as the interface between the UI and the abstracted hardware.

void DevControl::HWResetDevice()
{
    reset(application.value());
}

The public method resetDevice() allows to write the value of Qul::Property<int> application to the boot memory location and then triggers a soft reset via NVIC_SystemReset().

void writeBootValue(int app)
{
    int *ptr = &__BOOT_VAL_ADDR;
    *ptr = app;
    QUL_CleanInvalidateDCache_by_Addr((void *) ptr, 4);
}

void reset(int app)
{
    writeBootValue(app);
    NVIC_SystemReset();
}

Note: on NXP i.MX RT1170 right after flashing the binary the device should be reset once more. Otherwise the soft reset via NVIC_SystemReset() might not work properly.

Files:

See also FreeRTOS application build process.

Available under certain Qt licenses.
Find out more.