このページでは

C

基本機能の実装

プラットフォームコンテキストの概念

Qt Quick Ultraliteコアは、Qul::Platform::PlatformContext 仮想クラスを介してプラットフォームと通信します。このクラスには、ハードウェアリソースへのアクセスを提供するために実装が必要な一連の仮想メソッドが含まれています。各プラットフォームは、Qul::Platform::PlatformContext を継承することで、独自のプラットフォームコンテキストクラスの実装を作成します。

Qul::Platform::PlatformContext のプラットフォーム固有の実装は、UltraliteコアがQul::Platform::getPlatformInstance 関数を通じてQt Quick から取得します。

基本機能の実装

この章では、Qt Quick Ultralite コアを実行するために実装が必要な基本関数およびクラスメソッドを列挙します。platform/boards/qt/example-baremetal/platform_context.cpp の内容を独自の実装の基礎として利用し、指定された場所にコードを追加することができます。

プラットフォームコンテキストの実装

プラットフォームは、Qul::Platform::PlatformContext 仮想クラスを継承して実装する必要があります。

struct ExamplePlatform : PlatformContext
{
    void initializeHardware() QUL_DECL_OVERRIDE;
    void initializePlatform() QUL_DECL_OVERRIDE;
    void exec() QUL_DECL_OVERRIDE;
    uint64_t update() QUL_DECL_OVERRIDE;
    double rand() QUL_DECL_OVERRIDE;
    void scheduleEngineUpdate(uint64_t timestamp) QUL_DECL_OVERRIDE;
    uint64_t currentTimestamp() QUL_DECL_OVERRIDE;
    void consoleWrite(char character) QUL_DECL_OVERRIDE;

注:これらの メソッドとその実装例については、『Platform porting guide』の各章で説明されています。

Qul::Platform::getPlatformInstance は、プラットフォーム固有のコンテキストクラスのインスタンスを返さなければなりません。

PlatformContext *getPlatformInstance()
{
    static ExamplePlatform platform;
    return &platform;
}

Qt Quick Ultralite コアは、返されたインスタンスを使用し、コンテキストメソッドを呼び出して、実行時にハードウェアやプラットフォームと通信します。

ハードウェアの初期化

ハードウェアコンポーネントの初期化は、PlatformContext::initializeHardware メソッドで行うことができます。

これには、クロック、ピン、周辺機器、バス、メモリの設定などが含まれます。また、これを実装せず、代わりに、たとえばアプリケーションのmain() 関数などで、独自のハードウェア初期化を行うことも可能です。

void ExamplePlatform::initializeHardware()
{
#if 0
    Replace the following code with actual code for your device.

    setup_system_clocks();
    setup_flash_memory();
    setup_sd_ram();
    setup_usart();
    setup_rnd_generator();
#endif
}
    ...

プラットフォームの初期化

PlatformContext::intializePlatform は、Qt Quick Ultralite エンジンの実行前に呼び出され、お使いのボードに固有のリソースを設定する必要があります。

これは主にグラフィックスリソースに使用されるため、現時点では空のままにしておくことができます。

void PlatformContext::intializePlatform() {
    Qul::PlatformInterface::log("Called platform initialization\n");
}

現在のタイムスタンプ

レンダリングやタイマーにとって不可欠な要素は、システムの現在のタイムスタンプを取得することです。PlatformContext::currentTimestamp メソッドは、このシステム時刻を返さなければなりません。

uint64_t ExamplePlatform::currentTimestamp()
{
    // Replace this line to make it return the system timestamp in milliseconds.
    return 0;
}

レンダリングループのコールバックスケジュール

Qt Quick Ultralite のレンダリングループとイベント処理は、指定されたタイミングで呼び出される必要があります。

Qt Quick Ultralite コアは、タスクを実行するためにコールバックが必要となる次のタイムスタンプをプラットフォームに通知するために、PlatformContext::scheduleEngineUpdate を呼び出します。

void ExamplePlatform::scheduleEngineUpdate(uint64_t timestamp)
{
    nextUpdate = timestamp;
}

timestamp 引数は、レンダリングループが次に実行されるべき時刻を指定します。プラットフォームが提供している場合は、ハードウェアタイマーや OS が提供するタイマーを使用して、スケジューリングのタイムアウトを実装できます。簡単な解決策として、このタイムアウト値を変数に格納し、以下の関数で利用するようにします。

警告: `PlatformContext::scheduleEngineUpdate `の実装は 割り込みから呼び出される可能性があるため、割り込みコンテキスト内で実行しても安全でなければなりません。

レンダリングループの更新

Qt Quick Ultralite エンジンは、所定の回数呼び出される必要があります。

PlatformContext::update メソッドは、PlatformInterface::updateEngine を呼び出してタイマーを更新し、アニメーションを表示することで、シングルコアエンジンの更新を処理します。このメソッドは、前のセクションで説明したように、Qt Quick Ultralite コアエンジンによって設定されたタイムスタンプを使用します。

uint64_t ExamplePlatform::update()
{
    const uint64_t timestamp = this->currentTimestamp();

    if (timestamp >= nextUpdate) {
        // Handle deadline or pending events
        Qul::PlatformInterface::updateEngine(timestamp);
    }

    return nextUpdate;
}

この関数は、次に呼び出される予定のタイムスタンプを返します。現在のタイムスタンプよりも小さい値、あるいは0が返された場合は、できるだけ早く再度呼び出す必要があります。 現在のタイムスタンプよりも大きい値が指定された場合、プラットフォームの実装は指定された時刻に `PlatformContext::update ` を呼び出す必要があります。スケジュールされた時刻までは、デバイスは処理を譲渡したり、スリープモードに入ったりすることがあります。

この関数は、PlatformContext::scheduleEngineUpdate によってスケジュールされたタイムスタンプがすでに経過したかどうかを確認するため、可能な限り頻繁に呼び出すことができます。スケジュールされた時刻の処理方法を変更したい場合は、PlatformInterface::updateEngine の呼び出しのみが不可欠です。

メインループ

PlatformContext::exec にはexecループが含まれており、アプリケーションが実行されている限り、あるいは少なくともその間ずっと実行され続け、適切なタイミングでPlatformContext::update を呼び出す役割を担っています。コアエンジンによる更新呼び出しが必要ない場合、デバイスは可能であればスリープ状態に入る場合があります。

Qt Quick Ultralite プラットフォームではなく、アプリケーション側からメインループを駆動する場合は、その実装をスキップし、代わりにアプリケーションのメインループに同様のコードを記述し、PlatformContext::exec を一切呼び出さないようにすることができます。

void ExamplePlatform::exec()
{
    while (true) {
        logFlush(); // Flush partially filled log buffer
        const uint64_t timestamp = this->update();

        if (timestamp > Platform::getPlatformInstance()->currentTimestamp()) {
            // The device may yield or go into sleep mode
        }
    }
}

イールドやスリープに使用される関数は、タッチイベントなどのイベントが割り込みからQt Quick Ultraliteコアに配信された場合、または次回のスケジュールされた更新時刻に達した場合に、処理を再開できるものでなければなりません。

注: スリープモードや省電力モードが実装されていない場合 、CPU は常にフル負荷で動作します。

注: デモ、サンプル、テストを使用しない場合、または Application::exec() を呼び出さない場合、あるいは app_common フレームワークを使用しない場合にのみ、関数本体を空にすることができます。

乱数

QML アプリケーションに乱数を供給するには、プラットフォーム側で `PlatformContext::rand ` をオーバーライドする必要があります。

double ExamplePlatform::rand()
{
    // Replace std::rand() by the proper call to the random number generator on your device, if available.
    const uint32_t number = std::rand();
    return number / (std::numeric_limits<uint32_t>::max() + 1.0);
}

基本的なメモリ割り当て

Qt Quick Ultralite コアライブラリは、以下の関数を使用して少量のメモリ割り当てを処理します。

これらの呼び出しを、以下に示すようにプラットフォーム固有の関数に転送することができます。

void *qul_malloc(std::size_t size)
{
    return std::malloc(size);
}

void qul_free(void *ptr)
{
    std::free(ptr);
}

void *qul_realloc(void *ptr, size_t s)
{
    return std::realloc(ptr, s);
}

グラフィックス関連のメモリ割り当てについては、このガイドの後半で説明します。

IAR コンパイラ用の追加関数

タイムインフラストラクチャを機能させるには、IARライブラリで追加の関数を実装する必要があります。これらの関数は、.c ファイル内、またはextern "C" セクション内に配置することで、Cリンク方式でリンクされる必要があります。詳細については、IAR社の「IAR C/C++ Developer Guide」を参照してください。

// The number of times an internal timing event occurs per second
int const CLOCKS_PER_SECOND = 1000;

clock_t clock(void)
{
    QUL_UNUSED(CLOCKS_PER_SECOND);

    // This function should return a value, which after division by CLOCKS_PER_SECOND,
    // is the processor time in seconds.
    return (clock_t) HAL_GetTick();
}

// The time_t type is defined in bxarm/arm/inc/c/time{32,64}.h
#if _DLIB_TIME_USES_64
time_t __time64(time_t *t)
#else
time_t __time32(time_t *t)
#endif
{
    uint64_t timeAtStartup = 0; // Read this from a time source like a real time clock;
    uint64_t currentTimestamp = timestamp();
    // same timestamp as _gettimeofday
    time_t curtime = (time_t) (timeAtStartup + (currentTimestamp / 1000));

    if (t)
        *t = curtime;

    return curtime;
}

char const *__getzone()
{
    // See <IAR>/src/lib/time/getzone.c for documentation
    // For Germany as a default timezone
    return ":GMT+1:GMT+2:0100:032502+0:102502+0";
}

__ATTRIBUTES char *_DstMalloc(size_t);
__ATTRIBUTES void _DstFree(char *);

char *_DstMalloc(size_t s)
{
    // Return a buffer that can hold the maximum number of DST entries of
    // of any timezone available on the device.
    // Each DST entry takes up a structure of 5 bytes plus regular alignment.
    // Instead of a static buffer a dynamically allocated memory can be used as well.

    // With the two entries shown above the required buffer size would be
    // 2 * (5 bytes size + 3 bytes alignment) = 16 bytes

    static char buffert[8 * 4];
    return buffert;
}

void _DstFree(char *p)
{
    // Nothing required here because of static buffer in _DstMalloc
    QUL_UNUSED(p);
}

特定のQtライセンスの下で利用可能です。
詳細はこちら。