C
Qt Quick Ultraliteを以下の用途で使用する場合FreeRTOS
FreeRTOS は、組み込みデバイスおよびマイクロコントローラプラットフォーム向けに設計されたリアルタイムオペレーティングシステムカーネルです。スレッド(FreeRTOS では「タスク」と呼ばれます)、ミューテックス、セマフォ、およびソフトウェアタイマーを提供します。
このガイドでは、FreeRTOS およびQt Quick Ultraliteを使用してQt Quick Ultraliteの開発を開始するために必要な事項、ならびにFreeRTOS に関する背景情報を解説します。
サポートされているアーキテクチャ、プラットフォーム、およびFreeRTOS のバージョン
Qt Quick Ultraliteは以下のハードウェアをサポートしています:
| ハードウェアボード | MCU | アーキテクチャ | コンパイラ | 対応FreeRTOS |
|---|---|---|---|---|
| NXP IMXRT1050-EVKB | MIMXRT1052DVL6A | ARM Cortex-M7 | GNU Arm GCC 12.3.rel1、IAR Build Tools for Arm V9.40 | FreeRTOS V10.0.1 |
| NXP IMXRT1064-EVK | MIMXRT1064DVL6A | ARM Cortex-M7 | GNU Arm GCC 12.3.rel1、IAR Build Tools for Arm V9.40 | FreeRTOS V10.0.1 |
| STM32F769I-DISCOVERY | STM32F769NI | ARM Cortex-M7 | GNU Arm GCC 12.3.rel1、IAR Build Tools for Arm V9.40 | FreeRTOS V10.0.1 |
| NXP IMXRT1170-EVKB | MIMXRT1176DVMAA | ARM Cortex-M7 および ARM Cortex-M4 | GNU Arm GCC 12.3.rel1、IAR Build Tools for Arm V9.40 | FreeRTOS V10.0.1 |
| Renesas EK-RA8D1 | R7FA8D1BHECBD | ARM Cortex-M85 | GNU Arm GCC 12.3.rel1 | FreeRTOS V10.6.1 |
| Espressif ESP32-S3-BOX-3 | ESP32-S3 MCU | デュアルコア Xtensa LX7 | GNU Xtensa GCC 13.2.0 | FreeRTOS V10.5.1 |
| Infineon PSOC™ Edge E84 評価キット | PSE846GPS2DBZC4 | ARM Cortex-M55 | GNU Arm GCC 14.2.1、Arm 向け LLVM 組み込みツールチェーン 19.1.5 | FreeRTOS V10.6.202 |
これらのリファレンスボードは、Qt 標準サポートの対象となっています。
注: 特定のハードウェア・プラットフォーム用にプリコンパイル済みのプラットフォーム・ライブラリを使用している場合 、FreeRTOS のソースコードの一部はすでにそこにコンパイルされています。FreeRTOS のバージョンを変更したい場合は、プラットフォーム・ライブラリを再ビルドする必要があります。
開発環境の要件
前提条件
FreeRTOS 向けのQt Quick Ultraliteをビルドするには、以下のものが必要です:
- Qt Quick Ultralite版FreeRTOS パッケージがインストールされていること。詳細は、「 FreeRTOS 用Qt Quick Ultraliteの入手方法」を参照してください。
- FreeRTOS ソースコード。FreeRTOS はFreeRTOS のWebサイトから入手できます。
- Qt Quick Ultralite の前提条件およびターゲットプラットフォーム(「サポートされているターゲットボードおよび開発ホスト」を参照)。
環境のセットアップ
使用しているボードに応じて、『 NXP のはじめに(BareMetal およびFreeRTOS )』および『STM のはじめに』で定義されているプラットフォーム固有の環境変数を設定してください。
もし app_commonFreeRTOS のソースへのパスも設定する必要があります:
-DFREERTOS_DIR=< FreeRTOS directory path >cmakeを実行する際は、プラットフォーム名に「FreeRTOS 」というサフィックスを付けて、FreeRTOS プロジェクトのビルドファイルを生成してください。
例:
cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=MinSizeRel -DCMAKE_TOOLCHAIN_FILE=<Qul install path>/lib/cmake/Qul/toolchain/armgcc.cmake -DQUL_PLATFORM=<target platform>-freertos -DFREERTOS_DIR=<FreeRTOS directory path>cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=MinSizeRel -DCMAKE_TOOLCHAIN_FILE=<Qul install path>\lib\cmake\Qul\toolchain\armgcc.cmake -DQUL_PLATFORM=<target platform>-freertos -DFREERTOS_DIR=<FreeRTOS directory path>サポート対象のプラットフォームにおけるデバイスごとの環境設定については、「サポート対象のアーキテクチャ、プラットフォーム、およびFreeRTOS のバージョン」に記載されているハードウェアボードのリンクを参照してください。
Qt Quick Ultralite の使用方法FreeRTOS
Qt Quick Ultralite の入手方法FreeRTOS
サポート対象のプラットフォームでは、Qt Quick Ultraliteのインストールに、FreeRTOS のサポートが標準で含まれています。FreeRTOS 用のサンプルをコンパイルするには、「 app_common の使用方法」を参照してください。
Qt Quick Ultralite スレッドの開始
Qt Quick Ultraliteをターゲット上で動作させるには、以下の2つの関数を呼び出す必要があります:
Qul::initPlatform();この関数は、プラットフォームのハードウェアとオペレーティングシステムを初期化します。この関数は、できるだけ早い段階で、遅くとも
Qul::appMain()が実行される前、またはデバイス固有の関数が呼び出される前に呼び出す必要があります。FreeRTOS におけるさまざまなメモリ割り当ての実装や、Qt Quick Ultraliteプロジェクトでのそれらの使用方法については、「ヒープポリシーの変更」を参照してください。
Qul::appMain();この関数は、Qt Quick Ultralite を初期化するだけでなく、メインループとしても機能します。FreeRTOS では、この関数はタスクから実行する必要があります。
カスタムエントリポイントの設定に関する詳細については、「アプリケーションでのQt Quick Ultralite の実行」を参照してください。
また、 FreeRTOS タスクでQt Quick Ultraliteを実行する例については、「main.cpp の例」を参照してください。
メモリアロケータの提供
FreeRTOS には、デフォルトで 5 種類のメモリアロケータの実装が用意されています。これらはFreeRTOS の `MemMang` ディレクトリにあり、ヒープ管理のためのさまざまな戦略を提供します。各実装の詳細については、FreeRTOS 開発者ドキュメントの「メモリ管理」を参照してください。
プロジェクトでapp_common を使用している場合、プロジェクトのQUL_FREERTOS_HEAP_POLICY ターゲットプロパティを設定することで、使用される実装を変更できます。詳細については、「ヒープポリシーの変更」を参照してください。
FreeRTOS に付属するメモリアロケータは、pvPortMalloc およびvPortFree を実装しています。これらのアロケータは、Qt Quick Ultralite プラットフォームポートで内部的に使用されており、ほとんどの場合、アプリケーションコードでもこれらを使用する必要があります。リンカ設定で、標準ライブラリとFreeRTOS のアロケータ用に別々のヒープ領域を設定することが可能です。
アプリケーションでFreeRTOS のヒープアロケータを使用する方法の一つは、malloc 、free 、およびrealloc のデフォルト実装をオーバーロードすることです。new およびdelete というC++キーワードが内部でmalloc およびfree を使用している場合、アプリケーション側で個別のオーバーロードを用意する必要はありません。
realloc を含むメモリ割り当て関数の C コード例:
#include <FreeRTOS.h> #include <portable.h> #if defined(__ICCARM__) #include <string.h> #else #include <memory.h> #endif extern void *pvPortMalloc(size_t xWantedSize); extern void vPortFree(void *pv); void *malloc(size_t sz) { void *ptr = pvPortMalloc(sizeof(size_t) + sz); if (ptr == NULL) { return NULL; } *((size_t *) ptr) = sz; return ((char *) ptr) + sizeof(size_t); } void free(void *p) { if (p != NULL) { vPortFree(((char *) p) - sizeof(size_t)); } } void *realloc(void *ptr, size_t sz) { if (ptr == NULL) return malloc(sz); size_t oldSize = *(size_t *) ((unsigned long) ptr - sizeof(size_t)); if (sz == oldSize) return ptr; void *newPtr = NULL; if (sz > 0) { newPtr = malloc(sz); memcpy(newPtr, ptr, (sz > oldSize) ? oldSize : sz); } free(ptr); return newPtr; }
Qt Quick Ultralite は、CMakeLists.txt でアプリケーションにプロパティを追加することで有効にできるデフォルトのアロケータオーバーライドを提供しています。
qul_add_target(my_application …) set_target_properties(my_application PROPERTIES FREERTOS_PROVIDE_MEMORY_ALLOCATOR_OVERLOADS TRUE)
このプロパティは、app_target_setup_os を呼び出す前に設定する必要があります。これにより、実行ファイルにCおよびC++用のアロケータのオーバーロードが追加されます。
注: Qt Quick Ultralite が提供するアロケーターのオーバーロードは 、FreeRTOS heap_3.c とは互換性がありません。これは、同ライブラリのpvPortMalloc 関数が内部でmalloc を使用しているためです。
注: malloc の 内部呼び出しにより 、printf などの一部の関数では、FreeRTOS によって既に割り当てられている可能性のあるメモリを再度割り当てる場合があります。これにより予期しない動作が発生しますが、app_common でオーバーロードを有効にすることでこれを回避できます。
スレッドのスタックサイズ
FreeRTOS では、個々のスレッド(またはタスク)ごとに独自のスタックが割り当てられます。Qt Quick Ultraliteが必要とするスタック容量は、プロジェクトの複雑さに大きく依存します。デフォルトでは、Qt Quick UltraliteリファレンスFreeRTOS プラットフォームでは、6キロワード(24 KiBに相当)のスレッドスタックサイズが使用されます。
注: FreeRTOS では 、スタックサイズはバイトではなくワード単位で定義されています。
FreeRTOS タスクでQt Quick Ultraliteを実行するためのmain.cpp の例
以下のコードは、Qt Quick Ultralite 用の基本的なFreeRTOS スレッドを作成し、それを実行する方法を示しています。
#include <qul/qul.h> #include <FreeRTOS.h> #include <task.h> static void Qul_Thread(void *argument); int main() { Qul::initPlatform(); if (xTaskCreate(Qul_Thread, "QulExec", 32*1024, 0, 4, 0) != pdPASS) { configASSERT(false); // Task creation failed } vTaskStartScheduler(); configASSERT(false); } static void Qul_Thread(void *argument) { (void) argument; Qul::appMain(); }
他のアプリケーションからQt Quick Ultraliteと連携する
「FreeRTOS マルチタスクの例」および「C++ コードと QML の統合」を参照してください。
ビルドFreeRTOS
「FreeRTOS アプリケーションのビルドプロセス」を参照してください。
app_common に関する詳細については、「FreeRTOS アプリケーションのビルドプロセス」を参照してください。
FreeRTOS マルチタスクの例
FreeRTOS およびQt Quick Ultralite を使用した複数のタスクの実行。

この例では、互いに連携する複数のタスクを作成する方法を示します。このアプリケーションには 3 つのタスクがあります:
Qul_ThreadQMLアプリケーションインスタンスに対してQt Quick Ultraliteのexec()ループを実行し、HardwareControlメソッドを使用して他のタスクにタッチイベントを通知します。Led_ThreadLEDの点滅を行い、最新のLED点滅回数をQMLアプリケーション(Qt Quick Ultraliteスレッド)に反映します。FanControl_Threadファンアニメーションの回転周期を再計算し、QMLアプリケーション(Qt Quick Ultraliteスレッド)を更新します。
HardwareControl クラス
HardwareControl は、QMLアプリケーションが他のスレッドとやり取りするためのインターフェースを提供するQul::Singleton クラスです。このクラスは、Led_Thread (FreeRTOS タスク通知を使用)およびFanControl_Thread (FreeRTOS キューを使用)との通信を処理します。
class HardwareControl : public Qul::Singleton<HardwareControl> { public: HardwareControl(); Qul::Property<int> fanSpeed; Qul::Property<int> ledCycleCount; Qul::Signal<void(int rotationPeriod)> fanRotationPeriodChanged; void updateSpeed(int newSpeed); private: void updateLedSpeed(); void updateFanSpeed(); ...
ここでは、まずspeed プロパティに新しい値を設定します。その後、updateFanSpeed およびupdateLedSpeed() を呼び出し、QMLアプリケーションからのonPressed イベントによる速度の変更についてタスクを更新します。
void HardwareControl::updateSpeed(int newSpeed) { fanSpeed.setValue(newSpeed); updateFanSpeed(); updateLedSpeed(); }
LEDの点滅速度は、updateSpeed() メソッドを使用して更新されます:
void HardwareControl::updateLedSpeed() { xTaskNotify(LedTask, fanSpeed.value(), eSetValueWithOverwrite); }
QMLアプリケーションは、updateFanSpeed() を呼び出すことで、新しいファンの回転周期を要求します:
void HardwareControl::updateFanSpeed() { xQueueSend(getFanControlQueueHandle(), (void *) &(fanSpeed.value()), portMAX_DELAY); }
qul_thread.cpp
Qt Quick Ultraliteスレッドは、アプリケーションインスタンスを作成し、exec() ループを実行します。
... void Qul_Thread(void *argument) { (void) argument; 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); }
これらのイベントは、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); }
注: 他のスレッドから直接 Qul プロパティの値を更新することはスレッドセーフではありません 。代わりに、上記のコードスニペットに示されているようにQul::EventQueue を使用してください。
led_thread.cpp
起動時、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スレッドは点滅回数を計算し、Qul::EventQueue に基づくpostEventsToUI() 関数を使用して、この情報をQMLアプリケーションに送信します。このカウント値は、QMLアプリケーションによって画面上で更新されます。
ledEvent.id = HardwareEventId::LedCycleCount; ledEvent.data = ledCycleCount; postEventsToUI(ledEvent); taskYIELD(); }
fan_thread.cpp
ファン制御スレッドは、FreeRTOS イベントキューを待機し、Qt Quick Ultraliteスレッドからのタッチイベントに応じてファンの回転速度を更新します。ファンアニメーション用のrotationPeriod を再計算し、postEventsToUI() 関数を使用してこの値をUIに送信します。UIは、rotationPeriod の値に基づいてアニメーション速度を更新します。
... void FanControl_Thread(void *argument) { (void) argument; int newSpeed; HardwareEvent fanEvent; 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); }
multitask.qml
multitask.qml デバイスの画面に表示されるUIを宣言します。
import QtQuick 2.15 Rectangle { id: root Image { id: background source: "images/background-dark.png" anchors.fill: root } Column { anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.topMargin: 10 Text { anchors.horizontalCenter: parent.horizontalCenter horizontalAlignment: Text.AlignHCenter color: "white" text: "Speed: " + HardwareControl.fanSpeed font.pixelSize: 34 } Text { topPadding: 10 anchors.horizontalCenter: parent.horizontalCenter horizontalAlignment: Text.AlignHCenter color: "silver" text: "LED cycle count:" font.pixelSize: 17 } Text { anchors.horizontalCenter: parent.horizontalCenter horizontalAlignment: Text.AlignHCenter color: "white" text: HardwareControl.ledCycleCount font.pixelSize: 17 font.bold: true } } Text { horizontalAlignment: Text.AlignHCenter anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom anchors.bottomMargin: 10 color: "gray" text: "Tap to change fan and LED speed!" } Image { id: fan source: "images/fan-off.png" anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter transform: Rotation { origin.x: fan.width / 2 origin.y: fan.height / 2 RotationAnimation on angle { id: imageRotation loops: Animation.Infinite from: 0 to: 360 duration: 0 running: false } } } MouseArea { id: ta anchors.fill: parent onPressed: { HardwareControl.updateSpeed((HardwareControl.fanSpeed + 1) % 6); } } HardwareControl.onFanRotationPeriodChanged: { imageRotation.duration = rotationPeriod imageRotation.running = rotationPeriod > 0 } Component.onCompleted: { HardwareControl.updateSpeed(HardwareControl.fanSpeed) } }
BoardUtils
BoardUtilsは、ターゲットボードのLEDを初期化および制御する関数を宣言する名前空間です:
namespace BoardUtils { void initLED(); void toggleLED(); } // namespace BoardUtils
これらの関数の定義はデバイスごとに異なります。以下の例は、STM32F769I-DISCOVERY向けの実装からのものです。
namespace BoardUtils { void initLED() { BSP_LED_Init(LED1); } void toggleLED() { BSP_LED_Toggle(LED1); } } // namespace BoardUtils
main.cpp
main.cpp には、ハードウェアの初期化と、Qt Quick Ultralite、LED、およびファン制御スレッドの作成が含まれています。
int main() { Qul::initHardware(); Qul::initPlatform(); BoardUtils::initLED(); initFanControlQueue(); 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(); // Should not reach this point return 1; }
最初の関数呼び出しは、ハードウェアの初期化を行うものです。
Qul::initPlatform(); BoardUtils::initLED();
まず、Qul::initPlatform() を使用してボードを初期化します。BoardUtils::initLED() 関数は、点滅させるためのボード固有の 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); } if (xTaskCreate(FanControl_Thread, "FanControl", configMINIMAL_STACK_SIZE, 0, 4, &FanControlTask) != pdPASS) { Qul::PlatformInterface::log("Fan control task creation failed!.\r\n"); configASSERT(false); }
xTaskCreate() 関数を使用して、Qt Quick Ultraliteのメインループ用スレッド、ファン制御用スレッド、およびLED点滅用スレッドを作成します。これらのスレッドはすべて優先度4を使用します。Qt Quick UltraliteスレッドのスタックサイズはQUL_STACK_SIZE であり、これはFreeRTOSConfig.h で32×1024ワードとして定義されています。 LEDスレッドは、スタックサイズconfigMINIMAL_STACK_SIZE に設定されています。これはFreeRTOSConfig.h でも設定されており、STM32F769I-DISCOVERYの場合、128ワードとなります。xTaskCreate() 関数の詳細については、FreeRTOS APIリファレンスのxTaskCreateを参照してください。
vTaskStartScheduler();
これを呼び出すと、FreeRTOS スケジューラが起動し、以前に作成されたスレッドをスケジューリングします。詳細は、FreeRTOS APIリファレンスのvTaskStartSchedulerを参照してください。
特定のQtライセンスの下で利用可能です。
詳細はこちら。