C
Qt Quick ウルトラライトマルチタスクの例
Qt Quick UltraliteとバックグラウンドRTOSスレッド間の通信がどのように実装できるかを示します。
概要
multitask の例では、複数のRTOSスレッドがどのように通信できるかを示しています。この例では、QMLからのイベントを使用して他のスレッドの活動を制御する方法と、非GUIスレッドがQMLプロパティの更新をトリガする方法を示しています。1つはQt Quick Ultraliteの実行、1つはオンボードLEDの点滅、1つはQMLアプリケーションの要求に応じてファンの回転周期を計算するものです。シンプルなQML UIがあり、画面中央でファンの画像が回転し、オンボードLEDが点滅します。画面にはLEDの点滅回数(LEDが点滅した合計回数)も表示されます。画面をタップすることで、ファンの回転数やLEDの点滅回数を変更することができる。

対象プラットフォーム
プロジェクト構造
プロジェクトの構造はディレクトリに分かれています:
board_utils- 指定されたプラットフォームのLED制御ルーチンを実装する静的ライブラリ。freertos- 与えられたプラットフォーム用のFreeRTOS カーネルを提供する静的ライブラリ。images- プロジェクトが使用するグラフィカル・リソースsrc- アプリケーションで使用されるC++ソースコード。desktop- デスクトップ版アプリケーションのC++ソースコード。freertos-FreeRTOS ポートの C++ ソースコード。zephyr-Zephyr ポートの C++ ソースコード。
コードの概要
CMakeプロジェクトファイル
メインのCMakeファイルは、freertos ディレクトリを含めることで、サンプルがサポートされているプラットフォームの1つ用にビルドされているかどうかをチェックします。freertos CMake ターゲットは、サポートされているプラットフォームに対してのみ定義されます。デスクトップバックエンド用にビルドする場合、簡略化されたmultitask_desktop アプリケーションがビルドされます。
注: Zephyr のビルド・プロセスでは、CMake プロジェクト・ファイルは使用されません。 Zephyr でQt Quick Ultralite を使用する を参照してください。
...
add_subdirectory(freertos)
if(TARGET freertos_kernel) # FreeRTOS support implemented for this platform
add_subdirectory(board_utils)
qul_add_target(multitask
src/freertos/main.cpp
src/freertos/hardwarecontrol.cpp
src/freertos/threads/led_thread.cpp
src/freertos/threads/qul_thread.cpp
src/freertos/threads/fan_thread.cpp
QML_PROJECT
mcu_multitask.qmlproject
)
target_compile_definitions(multitask PRIVATE FREERTOS)
target_include_directories(multitask PRIVATE src src/freertos/threads)
...
elseif(NOT CMAKE_CROSSCOMPILING) # No FreeRTOS here - fallback for building on desktop platform
qul_add_target(multitask_desktop
src/desktop/hardwarecontrol.cpp
QML_PROJECT
mcu_multitask.qmlproject
GENERATE_ENTRYPOINT
)
target_compile_definitions(multitask_desktop PRIVATE DESKTOP)
target_include_directories(multitask_desktop PRIVATE src)
...
else()
message(STATUS "Skipping generating target: multitask")
endif()BoardUtils ライブラリ
このライブラリは、LED制御の最も基本的でハードウェア固有の実装を提供します。サポートされているボード上でLEDを初期化し、トグルするための単純化されたAPIを出荷します。board_utils/include/board_utils/led.hにライブラリーのAPIが含まれています。
...
namespace BoardUtils {
void initLED();
void toggleLED();
} // namespace BoardUtilsアプリケーション・エントリ・ポイント
main.cppソースファイルは、FreeRTOS およびZephyr ポートでのみ使用されます(デスクトップ用にビルドする場合は使用されません)。
main() 関数は、Qt Quick Ultraliteプラットフォーム、LED制御スレッド用のハードウェアLED、ファン制御スレッド用のFreeRTOS キューを初期化する。
...
int main()
{
Qul::initHardware();
Qul::initPlatform();
BoardUtils::initLED();
initFanControlQueue();
...次に、LED制御、Qt Quick Ultraliteエンジン、ファン制御用のFreeRTOS タスクが作成されます。
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();
...main() 関数は、Qt Quick Ultralite プラットフォーム、LED 制御スレッド用のハードウェア LED、ファンおよび LED 制御スレッド用のZephyr キューを初期化します。
...
int main()
{
Qul::initHardware();
Qul::initPlatform();
BoardUtils::initLED();
initFanControlQueue();
initLedControlQueue();
...次に、Qt Quick Ultraliteエンジン、LED制御、ファン制御用にZephyr スレッドが作成される。この後、メインスレッドは使用されなくなるため終了する。
struct k_thread QulTaskData, LedTaskData, FanControlTaskData;
QulTask = k_thread_create(&QulTaskData,
qul_stack_area,
K_THREAD_STACK_SIZEOF(qul_stack_area),
Qul_Thread,
NULL,
NULL,
NULL,
4,
0,
K_NO_WAIT);
LedTask = k_thread_create(&LedTaskData,
led_stack_area,
K_THREAD_STACK_SIZEOF(led_stack_area),
Led_Thread,
NULL,
NULL,
NULL,
4,
0,
K_NO_WAIT);
FanControlTask = k_thread_create(&FanControlTaskData,
fancontrol_stack_area,
K_THREAD_STACK_SIZEOF(fancontrol_stack_area),
FanControl_Thread,
NULL,
NULL,
NULL,
4,
0,
K_NO_WAIT);
// Exit the main thread because it is not needed anymore.
return 0;
...Qt Quick ウルトラライトスレッド
Qt Quick Ultraliteスレッドがアプリケーション・インスタンスを作成し、exec() ループを実行する。
...
void Qul_Thread(void *argument)
{
(void) argument;
Qul::Application app;
static multitask item;
app.setRootItem(&item);
app.exec();
}...
void Qul_Thread(void *arg1, void *arg2, void *arg3)
{
(void) arg1;
(void) arg2;
(void) arg3;
Qul::Application app;
static 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 postEventsToUI(HardwareEvent &event)
{
static HardwareControlEventQueue eventQueue;
eventQueue.postEvent(event);
}これらのイベントは、onEvent() コールバックによって以下のように処理される:
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);
}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);
}以下のスニペットに示すように、HardwareControlEventQueue はQul::EventQueue から派生したものである:
class HardwareControlEventQueue : public Qul::EventQueue<HardwareEvent>
{
void onEvent(const HardwareEvent &event) override;
};注意: 他のスレッドから Qul プロパティsetValue() メソッドを直接使用することはスレッドセーフではありません。Qul::EventQueue Qul::EventQueue はプラットフォーム層で実装されており、オペレーティングシステム固有のキューを利用します。提供されているQt Quick Ultralite リファレンスポートでは、Qul::EventQueue はネイティブのスレッドセーフ RTOS キューを使用して実装されています。
LEDスレッド
起動時、LEDスレッドはFreeRTOS タスク通知を待つ。Qt Quick Ultraliteスレッドがタッチイベントを処理すると、新しい速度値をキューに送信し、LEDスレッドのブロックを解除して点滅速度を更新する。この後、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スレッドはZephyr キューから新しいLED速度値を待つ。Qt Quick Ultraliteスレッドがタッチイベントを処理すると、新しい速度値をキューに送信し、LEDスレッドのブロックを解除して点滅速度を更新する。この後、LEDスレッドは新しい速度値に従ってLEDを点滅させる。
void Led_Thread(void *arg1, void *arg2, void *arg3)
{
...
while (true) {
const k_timeout_t ticks = speed > 0 ? K_MSEC(350 / speed) : K_FOREVER;
if (k_msgq_get(&ledControlQueue, &newSpeed, ticks) == 0) {
speed = newSpeed;
}
BoardUtils::toggleLED();
...LED スレッドはまた、点滅回数を計算し、postEventsToUI() 関数を使用してこの情報を QML アプリケーションに送信します。QMLアプリケーションはこのカウント数を画面上で更新します。
...
ledEvent.id = HardwareEventId::LedCycleCount;
ledEvent.data = ledCycleCount;
postEventsToUI(ledEvent);
taskYIELD();
}
}...
ledEvent.id = HardwareEventId::LedCycleCount;
ledEvent.data = ledCycleCount;
postEventsToUI(ledEvent);
k_yield();
}
}ファン制御スレッド
ファン制御スレッドはRTOSイベントキューで待機し、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);
}
}
}...
void FanControl_Thread(void *arg1, void *arg2, void *arg3)
{
...
while (true) {
if (k_msgq_get(&fanControlQueue, &newSpeed, K_FOREVER) == 0) {
int rotationPeriod = newSpeed == 0 ? 0 : 5000 / (newSpeed * 3);
fanEvent.id = HardwareEventId::FanRotationPeriod;
fanEvent.data = rotationPeriod;
postEventsToUI(fanEvent);
}
}
}データフロー図
以下のシーケンス図は、QMLアプリケーションからのイベントが、ハードウェアを担当するバックグラウンドスレッドにどのように影響するかをまとめたものです。


ファイル
- multitask/CMakeLists.txt
- multitask/board_utils/CMakeLists.txt
- multitask/board_utils/include/board_utils/led.h
- multitask/board_utils/src/ek-ra8d1-freertos/led.cpp
- multitask/board_utils/src/esp32s3box3/led.cpp
- multitask/board_utils/src/mimxrt1050-evk-freertos/led.cpp
- multitask/board_utils/src/mimxrt1064-evk-freertos/led.cpp
- multitask/board_utils/src/mimxrt1170-evkb-freertos/led.cpp
- multitask/board_utils/src/stm32f769i-discovery-freertos/led.cpp
- multitask/board_utils/src/zephyr/led.cpp
- multitask/freertos/CMakeLists.txt
- multitask/mcu_multitask.qmlproject
- multitask/multitask.qml
- multitask/src/desktop/hardwarecontrol.cpp
- multitask/src/freertos/hardwarecontrol.cpp
- multitask/src/freertos/main.cpp
- multitask/src/freertos/threads/fan_thread.cpp
- multitask/src/freertos/threads/fan_thread.h
- multitask/src/freertos/threads/led_thread.cpp
- multitask/src/freertos/threads/led_thread.h
- multitask/src/freertos/threads/qul_thread.cpp
- multitask/src/freertos/threads/qul_thread.h
- multitask/src/hardwarecontrol.h
- multitask/src/zephyr/hardwarecontrol.cpp
- multitask/src/zephyr/threads/fan_thread.cpp
- multitask/src/zephyr/threads/fan_thread.h
- multitask/src/zephyr/threads/led_thread.cpp
- multitask/src/zephyr/threads/led_thread.h
- multitask/src/zephyr/threads/qul_thread.cpp
- multitask/src/zephyr/threads/qul_thread.h
画像:
Zephyr アプリケーションのビルドプロセスも参照してください 。
特定の Qt ライセンスの下で利用可能です。
詳細はこちら。