C

Qt Quick Ultralite freertos_multitask Example

Shows how the communication between Qt Quick Ultralite and the background FreeRTOS task can be implemented.

Overview

The freertos_multitask demonstrates how multiple FreeRTOS tasks can communicate each other. It shows how events from QML can be used to control another tasks activity. There are two independent tasks running: one responsible for running Qt Quick Ultralite, second responsible for blinking the onboard LED. It has a simple QML UI where a fan image is spinning in the central part of the screen while the onboard LED is blinking. By tapping the screen user is able to change fan speed rotation and LED blinking frequency.

Target platforms

Code overview

Project structure is divided into directories:

  • board_utils - static library that implements LED control routines for a given platform
  • freertos - static library that provides FreeRTOS kernel for a given platform
  • images - graphical resources used by the Project
  • src - C++ source code used by the application

Code overview

CMake project file

Main CMake file checks whether an example is built for one of the supported platforms. It's done by including freertos directory. freertos CMake target is defined only for supported platforms. When building for a desktop backend a simplified freertos_multitask_desktop application is built.

...
add_subdirectory(freertos)

if(TARGET freertos_kernel) # FreeRTOS support implemented for this platform
    add_subdirectory(board_utils)

    qul_add_target(freertos_multitask
         src/main.cpp
         src/fancontrol.cpp
         QML_PROJECT
         mcu_freertos_multitask.qmlproject
    )
...
elseif(NOT CMAKE_CROSSCOMPILING) # No FreeRTOS here - fallback for building on desktop platform
    qul_add_target(freertos_multitask_desktop
        src/fancontrol.cpp
        QML_PROJECT
        mcu_freertos_multitask.qmlproject
        GENERATE_ENTRYPOINT
        )
    target_compile_definitions(freertos_multitask_desktop PRIVATE NO_LED)
...
else()
    message(STATUS "Skipping generating target: freertos_multitask")
endif()
BoardUtils library

This library provides the most basic, HW specific implementation of LED control. It ships an simplistic API to initialize and toggle the LED on a supported board. The board_utils/include/board_utils/led.h contains API of the library.

...
namespace BoardUtils {
void initLED();
void toggleLED();
} // namespace BoardUtils
Application entry point

main.cpp file is used only for freertos_multitask target (it's not used when building for desktop). main() function takes care of the platform initialization, additionally initializes the LED using BoardUtils library.

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

Next, a FreeRTOS tasks are created. One responsible for running Qt Quick Ultralite, the other controls the LED.

    if (xTaskCreate(Qul_Thread, "QulExec", QUL_STACK_SIZE, 0, 4, &QulTask) != pdPASS) {
        Qul::PlatformInterface::log("Task creation failed!.\r\n");
        configASSERT(false);
    }

    if (xTaskCreate(Led_Thread, "LedToggle", configMINIMAL_STACK_SIZE, 0, 4, &LedTask) != pdPASS) {
        Qul::PlatformInterface::log("LED task creation failed!.\r\n");
        configASSERT(false);
    }

    vTaskStartScheduler();
    ...

Task running Qt Quick Ultralite is as simple as setting the root item of the application and calling the exec() function.

static void Qul_Thread(void *argument)
{
    (void) argument;
    Qul::Application app;
    static freertos_multitask item;
    app.setRootItem(&item);
    app.exec();
}

Task controlling the LED blinking frequency uses BoardUtils::toggleLED() API to turn the LED on and off. This API is called in the infinite loop which waits for the task notification for a right number of clock ticks. xTaskNotifyWait FreeRTOS API is used both for waiting and receiving incoming data using task notification mechanism. newSpeed parameter will carry the received data (new fan speed) if the notification was received by the LED task.

static void Led_Thread(void *argument)
{
    (void) argument;
    uint32_t speed = 1;
    uint32_t newSpeed = 0;
    while (true) {
        const TickType_t ticks = speed > 0 ? (350 / (portTICK_PERIOD_MS * speed)) : portMAX_DELAY;
        if (xTaskNotifyWait(0, ULONG_MAX, &newSpeed, ticks) == pdTRUE) {
            speed = newSpeed;
        }
        BoardUtils::toggleLED();
        taskYIELD();
    }
}
FanControl object

The src/fancontrol.h declares a FanControl singleton object that is accessible from the QML. This is enabled by adding the file to InterfaceFiles.files in the main CMakeLists.txt. Responsibility of this class is to report current speed value with speed property and to change the fan speed using updateSpeed() function.

...
class FanControl : public Qul::Singleton<FanControl>
{
public:
    FanControl();
    Qul::Property<int> speed;
    void updateSpeed(int newSpeed);

private:
    void updateLedSpeed();
};

The updateSpeed() method takes care of updating the speed property value, and calling private updateLedSpeed() API.

...
void FanControl::updateSpeed(int newSpeed)
{
    speed.setValue(newSpeed);
    updateLedSpeed();
}

The updateLedSpeed() sends notification to the LED task using xTaskNotify FreeRTOS API. Current speed property value is sent as a notification data payload.

void FanControl::updateLedSpeed()
{
#ifndef NO_LED
    xTaskNotify(LedTask, speed.value(), eSetValueWithOverwrite);
#endif
}
QML part

The UI of the application is implemented in freertos_multitask.qml. A FanControl object is used as the main integration point. Current speed value is displayed using Text item with text property bound to the FanControl.speed.

        ...
        Text {
            color: "white"
            text: FanControl.speed
            font.pixelSize: 30;
        }
        ...

The most relevant part of the QML implementation is a reaction to screen touch events to change fan and LED speed. Calling FanControl.updateSpeed results in sending notification to the FreeRTOS LED task and updating text property.

    MouseArea {
        id: ta
        anchors.fill: parent
        onPressed: {
            FanControl.updateSpeed((FanControl.speed + 1) % 6);
        }
    }

Data flow diagram

Given sequence diagram summarizes how an event from the QML affects background task that is in charge of the hardware.

Files:

Images:

See also FreeRTOS application build process.

Available under certain Qt licenses.
Find out more.