C
Qt Quick Ultralite freertos_multitask 示例
展示如何实现 Qt Quick Ultralite 与后台 FreeRTOS 任务的通信。
概述
freertos_multitask
示例展示了多个 FreeRTOS 任务如何相互通信。它展示了如何使用 QML 的事件来控制其他任务的活动,以及如何由非 GUI 任务触发 QML 属性的更新。有三个独立运行的任务:一个负责运行 Qt Quick Ultralite,一个用于闪烁板上 LED,一个根据 QML 应用程序的要求计算风扇旋转周期。它有一个简单的 QML UI,其中风扇图像在屏幕中心旋转,同时板上 LED闪烁。屏幕还显示 LED 闪烁次数(LED 闪烁的总次数)。通过点击屏幕,用户可以更改风扇转速和 LED 闪烁频率。
目标平台
代码概述
项目结构分为目录
board_utils
- 实现特定平台 LED 控制例程的静态库。freertos
- 为特定平台提供 FreeRTOS 内核的静态库。images
- 项目使用的图形资源。src
- 应用程序使用的 C++ 源代码。desktop
- 应用程序桌面版本的 C++ 源代码。mcu
- MCU 目标设备的 C++ 源代码。
代码概述
CMake 项目文件
主 CMake 文件检查示例是否为支持的平台之一构建。这是通过包含一个 freertos
目录来完成的。只有对于支持的平台,才会定义一个 freertos
CMake 目标。在为桌面后端构建时,将构建一个简化的 freertos_multitask_desktop
应用程序。
... add_subdirectory(freertos) if(TARGET freertos_kernel) # FreeRTOS support implemented for this platform add_subdirectory(board_utils) qul_add_target(freertos_multitask src/mcu/main.cpp src/mcu/hardwarecontrol.cpp src/mcu/threads/led_thread.cpp src/mcu/threads/qul_thread.cpp src/mcu/threads/fan_thread.cpp QML_PROJECT mcu_freertos_multitask.qmlproject ) target_compile_definitions(freertos_multitask PRIVATE FREERTOS) target_include_directories(freertos_multitask PRIVATE src src/mcu/threads) ... elseif(NOT CMAKE_CROSSCOMPILING) # No FreeRTOS here - fallback for building on desktop platform qul_add_target(freertos_multitask_desktop src/desktop/hardwarecontrol.cpp QML_PROJECT mcu_freertos_multitask.qmlproject GENERATE_ENTRYPOINT ) target_compile_definitions(freertos_multitask_desktop PRIVATE DESKTOP) target_include_directories(freertos_multitask_desktop PRIVATE src) ... else() message(STATUS "Skipping generating target: freertos_multitask") endif()
BoardUtils 库
这个库提供了最基本、特定硬件的实现 LED 控制。它提供了一个简单的 API 来初始化和切换支持板上的 LED。《board_utils/include/board_utils/led.h》包含库的 API。
... namespace BoardUtils { void initLED(); void toggleLED(); } // namespace BoardUtils
应用程序入口点
仅使用 main.cpp 源文件用于 freertos_multitask
目标(在构建桌面时未使用)。该 main()
函数初始化 Qt Quick Ultralite 平台、硬件 LED(用于 LED 控制线程)和 FreeRTOS 队列(用于风扇控制线程)。
... int main() { Qul::initHardware(); Qul::initPlatform(); BoardUtils::initLED(); initFanControlQueue(); ...
接下来,创建 FreeRTOS 任务用于 LED 控制、Qt Quick Ultralite 引擎和风扇控制。
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); } if (xTaskCreate(FanControl_Thread, "FanControl", configMINIMAL_STACK_SIZE, 0, 4, &FanControlTask) != pdPASS) { Qul::PlatformInterface::log("Fan control task creation failed!.\r\n"); configASSERT(false); } vTaskStartScheduler(); ...
Qt Quick Ultralite 线程
Qt Quick Ultralite 线程创建应用程序实例并运行 exec()
循环。
... void Qul_Thread(void *argument) { (void) argument; Qul::Application app; static freertos_multitask item; app.setRootItem(&item); app.exec(); }
源文件 qul_thread.cpp 也实现了 postEventsToUI()
函数。此函数由其他线程使用,以将事件发送到修改 Qul::Property fanSpeed
和 Qul::Property ledCycleCount
QML 属性。
void postEventsToUI(HardwareEvent &event) { static HardwareControlEventQueue eventQueue; eventQueue.postEvent(event); }
以下是对事件的处理方式
void HardwareControlEventQueue::onEvent(const HardwareEvent &event) { if (event.id == HardwareEventId::LedCycleCount) HardwareControl::instance().ledCycleCount.setValue(event.data); else if (event.id == HardwareEventId::FanRotationPeriod) HardwareControl::instance().fanRotationPeriodChanged(event.data); }
HardwarControlEventQueue
从 Qul::EventQueue 派生,如下面的代码片段所示
class HardwareControlEventQueue : public Qul::EventQueue<HardwareEvent> { void onEvent(const HardwareEvent &event) override; };
注意: 直接在其他线程中使用 Qul 属性的 setValue()
方法是不安全的。取而代之的是,使用 Qul::EventQueue 将事件发布到 QML 接口对象,如上述代码片段所示。Qul::EventQueue 是在平台层实现的,并利用特定于操作系统的队列。对于 FreeRTOS,Qul::EventQueue 基于 FreeRTOS 队列,这使得它是线程安全的。
LED 线程
在启动时,LED 线程无限期地等待 FreeRTOS 任务通知。在接收到来自 Qt Quick Ultralite 线程的触摸事件后,它将解除阻塞并更新 LED 闪烁速度。第一次事件后,它将根据新计算的速度值闪烁 LED。
void Led_Thread(void *argument) { ... 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(); ...
LED 线程还计算闪烁次数,并使用 postEventsToUI()
函数将此信息发送到 QML 应用程序。QML 应用程序将此计数更新到屏幕上。
... ledEvent.id = HardwareEventId::LedCycleCount; ledEvent.data = ledCycleCount; postEventsToUI(ledEvent); taskYIELD(); } }
风扇控制线程
风扇控制线程等待 FreeRTOS 事件队列以根据从 QML 应用程序接收到的触摸事件更新风扇速度。它重新计算风扇动画的 rotationPeriod
,并使用 postEventsToUI()
函数将此值发送回 QML 应用程序。QML 应用程序根据 rotationPeriod
值更新动画速度。
... void FanControl_Thread(void *argument) { ... while (true) { if (xQueueReceive(fanControlQueue, &newSpeed, portMAX_DELAY) == pdTRUE) { int rotationPeriod = newSpeed == 0 ? 0 : 5000 / (newSpeed * 3); fanEvent.id = HardwareEventId::FanRotationPeriod; fanEvent.data = rotationPeriod; postEventsToUI(fanEvent); } } }
数据流图
下面的顺序图总结了 QML 的事件如何影响负责硬件的后台线程。
文件
- freertos_multitask/CMakeLists.txt
- freertos_multitask/board_utils/CMakeLists.txt
- freertos_multitask/board_utils/include/board_utils/led.h
- freertos_multitask/board_utils/src/ek-ra6m3g-freertos/led.cpp
- freertos_multitask/board_utils/src/mimxrt1050-evk-freertos/led.cpp
- freertos_multitask/board_utils/src/mimxrt1064-evk-freertos/led.cpp
- freertos_multitask/board_utils/src/mimxrt1170-evkb-freertos/led.cpp
- freertos_multitask/board_utils/src/stm32f769i-discovery-freertos/led.cpp
- freertos_multitask/freertos/CMakeLists.txt
- freertos_multitask/freertos_multitask.qml
- freertos_multitask/mcu_freertos_multitask.qmlproject
- freertos_multitask/src/desktop/hardwarecontrol.cpp
- freertos_multitask/src/hardwarecontrol.h
- freertos_multitask/src/mcu/hardwarecontrol.cpp
- freertos_multitask/src/mcu/main.cpp
- freertos_multitask/src/mcu/threads/fan_thread.cpp
- freertos_multitask/src/mcu/threads/fan_thread.h
- freertos_multitask/src/mcu/threads/led_thread.cpp
- freertos_multitask/src/mcu/threads/led_thread.h
- freertos_multitask/src/mcu/threads/qul_thread.cpp
- freertos_multitask/src/mcu/threads/qul_thread.h
图片
另请参阅 FreeRTOS 应用程序构建过程.
在特定 Qt 许可证下提供。
了解更多信息。