ソフトウェア タイマーは、タスクを定期的または定期的に実行できるようにするために使用されます。たとえば、一定の時間を設定してから特定の機能を実行したり、一定の間隔で特定の機能を実行したりすることができます。ソフトウェアタイマによって実行される関数をソフトウェアタイマのコールバック関数と呼びます。
参考文献:
《FreeRTOS™ リアルタイム カーネルをマスターする》——第 5 章 ソフトウェア タイマー管理
目次
1.3 ソフトウェアタイマーの実行環境(Context Context)
1.3.1 RTOS デーモンタスク (タイムサービス Time Service)
4. タイマーの周期を変更し、ソフトウェア タイマーをリセットします。
1. ソフトウェアタイマーのプロパティとステータス
FreeRTOS でソフトウェア タイマー機能を有効にします。
1. プロジェクトの一部として FreeRTOS ソース ファイル FreeRTOS/source/timers.c をビルドします。
2. 「FreeRTOSConfig.h」の「configUSE_TIMERS」を 1 に設定します。
ソフトウェアタイマーコールバック関数
void ATimerCallback(TimerHandle_t xTimer)
ソフトウェア タイマー コールバック関数はタイマー サービスで実行されます。関数は短く保つ必要があり、ブロッキング状態に入ることはできません。タイマー サービスのブロックはカーネルに影響するため、vTaskDelay() などのブロックを引き起こす関数を呼び出すことはできません。xQueueReceive() などの関数を呼び出すことができますが、関数の xTicksToWait パラメーター (指定された関数のブロック時間) が 0 に設定されている場合に限ります。
1.1 ソフトウェアタイマーの周期
ソフトウェア タイマーの「期間」とは、ソフトウェア タイマーが開始されてから、ソフトウェアタイマーのコールバック関数が実行されるまでの時間です。
ワンショット タイマー (ワンショット) および定期タイマー (自動/-リロード):
1. ワンタイムタイマーはコールバック関数を 1 回だけ実行します。手動で再起動できますが、自動的には再起動できません。
2. 定期タイマーは期限が切れるたびに再起動し、そのコールバック関数を定期的に実行します。
1.2 ソフトウェアタイマーのステータス
ソフトウェア タイマーは、次の 2 つの状態のいずれかになります。
1.睡眠
ソフトウェア タイマーが休止状態にあるということは、ソフトウェア タイマーが存在し、タイマー ハンドルを通じて参照できるが、実行されていないため、そのコールバック関数は実行されないことを意味します。
2.走る
実行状態のソフトウェア タイマーは、設定されたパラメータに従って、期限切れになるとコールバック関数を 1 回または定期的に実行します。
定期タイマーは、コールバック関数の実行後、自動的に実行状態に戻ります。
ワンタイムタイマーはコールバック関数の実行後に休止状態に入ります。
1.3 ソフトウェアタイマーの実行環境(Context Context)
1.3.1 RTOS デーモンタスク (タイムサービス Time Service)
すべてのソフトウェア タイマー コールバック関数は、同じRTOS デーモン (または「タイマー サービス」) タスクのコンテキストで実行されます。(Linuxではデーモンプロセス、FreeRTOSではタスクと呼ばれます)
デーモン タスクは、スケジューラの起動時に自動的に作成される標準の FreeRTOS タスクです。その優先度とスタック サイズは、FreeRTOSConfig.h の configTIMER_TASK_PRIORITY と configTIMER_TASK_STACK_DEPTH によってそれぞれ設定されます。
ソフトウェア タイマー コールバック関数は、呼び出しタスクをブロッキング状態にする FreeRTOS API 関数を呼び出すことはできません。デーモン タスクがブロッキング状態になるためです。
1.3.2 タイマコマンドキュー
タスクはソフトウェア タイマーの API 関数を呼び出してガーディアン タスクにコマンドを送信します。このコマンドはタイマーコマンド キューと呼ばれるキューに格納されます。
コマンド例「タイマー開始」、「タイマー停止」、「タイマーリセット」。
タイマー コマンド キューは、スケジューラの起動時に自動的に作成される標準の FreeRTOS キューです。タイマー コマンド キューの長さは、FreeRTOSConfig.h の configTIMER_QUEUE_LENGTH によって設定されます。
1.3.3 ガーディアンタスクのスケジュール設定
デーモン タスクは、他の FreeRTOS タスクと同様にスケジュールされます。コマンドを処理するか、実行できる最も優先度の高いタスクである場合にタイマー コールバック関数を実行するだけです。
図に示すように、Task1 は Task1 で実行され、タイマー API 関数を呼び出して、タイマーを開始するコマンドをデーモン タスクに送信します。ガーディアン タスクの優先度はタスク 1 ほど高くないため、ガーディアン タスクはコマンドをすぐには処理しませんが、t4 まで待ちます。タスク 1 がブロッキング状態になり、ガーディアン タスクがコマンドの処理を開始します。
ガーディアン タスクの優先度が高い場合は、コマンドが送信されるとガーディアン タスクに切り替わるため、すぐにタイマーが開始されます。
タイマーのタイムアウトは、デーモン タスクがコマンドを受信した時点からカウントされるのではなく、コマンドが送信された時点からカウントされることに注意してください。
実際、送信されたコマンドにはタイムスタンプが含まれています。タイムスタンプは送信日時を記録します。たとえば、10 ミリ秒の周期でタイマーを開始するコマンドが送信された場合、タイムスタンプは、デーモン タスクがコマンドを処理した後の 10 ミリ秒ではなく、送信後の 10 ミリ秒であることが保証されます。
2. ソフトウェアタイマーを作成して開始する
xTimerCreate() は、ソフトウェア タイマーを作成し、TimerHandle_t (ソフトウェア タイマー ハンドル) を返すために使用されます。ソフトウェア タイマーは作成されると休止状態になり、すぐには開始されません。
ソフトウェア タイマーは、スケジューラの実行前に作成することも、スケジューラの起動後にタスク内から作成することもできます。
TimerHandle_t xTimerCreate( const char * const pcTimerName,
TickType_t xTimerPeriodInTicks,
UBaseType_t uxAutoReload,
void * pvTimerID,
TimerCallbackFunction_t pxCallbackFunction );
パラメータ | 効果 |
pcタイマー名 | ソフトウェアタイマーの名前、FreeRTOS では使用しません、覚えやすいです |
xTimerPeriodInTicks | ティック単位で指定されたタイマー期間。pdMS_TO_TICKS() マクロを使用すると、ミリ秒単位で指定された時間をティック単位で指定された時間に変換できます。 |
uxAutoReload | 定期的な (自動リロード) タイマーを作成するには、pdTRUE に設定します。ワンショット (ワンショット) タイマーを作成するには、pdFALSE に設定します。 |
pvTimerID | 各ソフトウェアタイマーには ID 値があります。ID は、アプリケーション作成者があらゆる目的に使用できる null ポインターです。ID は、タイマー固有のストレージを提供するために使用できるため、同じコールバック関数が複数のソフトウェア タイマーで使用される場合に特に便利です。デモは後ほど。 |
pxコールバック関数 | コールバック関数ポインタ |
戻り値 | NULL が返された場合は、ヒープ メモリが不足しているため、ソフトウェア タイマーを作成できませんでした。NULL 以外の値が返された場合は、ソフトウェア タイマーが正常に作成されたことを示します。戻り値は、作成されたタイマーへのハンドルです。 |
xTimerStart() は、スリープ状態でソフトウェア タイマーを開始するか、実行状態でソフトウェア タイマーをリセット (再起動) するために使用されます。スケジューラを開始する前に xTimerStart() を呼び出すことは可能ですが、ソフトウェア タイマーはスケジューラが開始されるまで実際には開始されません。
BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait );
この関数の最下層は、実際には前号で述べた FreeRTOS を送信するキューの完全な分析です-5. キュー (Queue)
パラメータの意味も明らかです。
パラメータ | 効果 |
xタイマー | ソフトウェアタイマーハンドル。タイマーの戻り値を作成するためです。 |
xTicksToWait | キューがいっぱいの場合に、呼び出し側タスクがブロック状態で待機し続ける必要がある最大時間を指定します。 xTicksToWait がゼロでタイマー コマンド キューがいっぱいの場合、xTimerStart() はすぐに戻ります。 次に、xTicksToWait を portMAX_DELAY に設定すると、呼び出しタスクは無期限に (タイムアウトなしで) ブロック状態のままになり、タイマー コマンド キューの空きスペースを待ちます。 portMAX_DELAY マクロをキューのように使用するには、まず FreeRTOSConfig.ht の INCLUDE_vTaskSuspend を 1 に設定する必要があります。 スケジューラが開始される前に xTimerStart() が呼び出された場合、xTicksToWait の値は無視され、xTimerStart() は xTicksToWait がゼロに設定されているかのように動作します。 |
戻り値 | 1. pdPASS コマンドが正常に送信されました。 2.pdFALSE キューがいっぱいなので書き込めません。 |
xTimerStop() は、実行中のソフトウェア タイマーを停止するために使用されます。ソフトウェア タイマーの停止は、タイマーをスリープに移行することと同じです。
例は次のとおりです。
プログラムは 2 つのタイマーを作成し、1 つは単一タイマー、もう 1 つは定期タイマーであり、時刻はコールバック関数に出力されます。
#define mainONE_SHOT_TIMER_PERIOD pdMS_TO_TICKS( 3333 )
#define mainAUTO_RELOAD_TIMER_PERIOD pdMS_TO_TICKS( 500 )
static void prvOneShotTimerCallback( TimerHandle_t xTimer )
{
TickType_t xTimeNow;
xTimeNow = xTaskGetTickCount();
vPrintStringAndNumber( "One-shot timer callback executing", xTimeNow );
ulCallCount++;
}
static void prvAutoReloadTimerCallback( TimerHandle_t xTimer )
{
TickType_t xTimeNow;
xTimeNow = uxTaskGetTickCount();
vPrintStringAndNumber( "Auto-reload timer callback executing", xTimeNow );
ulCallCount++;
}
int main( void )
{
TimerHandle_t xAutoReloadTimer, xOneShotTimer;
BaseType_t xTimer1Started, xTimer2Started;
xOneShotTimer = xTimerCreate("OneShot",mainONE_SHOT_TIMER_PERIOD,pdFALSE,0,prvOneShotTimerCallback );
xAutoReloadTimer = xTimerCreate("AutoReload",mainAUTO_RELOAD_TIMER_PERIOD,pdTRUE,0,prvAutoReloadTimerCallback );
if( ( xOneShotTimer != NULL ) && ( xAutoReloadTimer != NULL ) )
{
xTimer1Started = xTimerStart( xOneShotTimer, 0 );
xTimer2Started = xTimerStart( xAutoReloadTimer, 0 );
if( ( xTimer1Started == pdPASS ) && ( xTimer2Started == pdPASS ) )
{
vTaskStartScheduler();
}
}
for( ;; );
}
効果:
3.タイマーID(タイマーID)
前述したように、各ソフトウェア タイマーには ID 値があります。ID は、アプリケーション作成者があらゆる目的に使用できる null ポインターです。ID は void ポインター (void *) に格納されるため、整数値を直接格納したり、他のオブジェクトをポイントしたり、関数ポインターとして使用したりできます。
xTimerCreate関数でソフトウェアタイマーを作成する場合、IDには初期値が割り当てられます。その後、vTimerSetTimerlD() API 関数を使用して ID を更新でき、pvTimerGetTimerID() を使用して ID を照会できます。
他のソフトウェア タイマー API 関数とは異なり、vTimerSetTimerlD() および pvTimerGetTimerlD() はソフトウェア タイマーに直接アクセスします。これらはコマンドをタイマー コマンド キューに送信しません。
void vTimerSetTimerID( const TimerHandle_t xTimer, void *pvNewID );
void *pvTimerGetTimerID( TimerHandle_t xTimer );
例:
static void prvTimerCallback( TimerHandle_t xTimer )
{
TickType_t xTimeNow;
uint32_t ulExecutionCount;
ulExecutionCount = ( uint32_t ) pvTimerGetTimerID( xTimer );
ulExecutionCount++;
vTimerSetTimerID( xTimer, ( void * ) ulExecutionCount );
xTimeNow = xTaskGetTickCount();
if( xTimer == xOneShotTimer ) {
vPrintStringAndNumber( "One-shot timer callback executing", xTimeNow );
} else {
vPrintStringAndNumber( "Auto-reload timer callback executing", xTimeNow );
if( ulExecutionCount == 5 ) {
xTimerStop( xTimer, 0 );
}
}
}
タイマー コールバック関数を上記に変更し、コールバック関数の実行回数として ID を使用し、ID を取り出して実行ごとに 1 を追加して ID を更新し、5 になったらタイマーを停止します。 、効果は次のとおりです。
4. タイマーの周期を変更し、ソフトウェア タイマーをリセットします。
ソフトウェア タイマーの周期は、xTimerChangePeriod() 関数を使用して変更できます。
xTimerChangePeriod() を使用して既に実行中のタイマーの期間を変更すると、タイマーは新しい期間の値を使用して有効期限を再計算します。再計算された有効期限は、タイマーが最初に開始された時刻ではなく、xTimerChangePeriod() が呼び出された時刻を基準としています。
xTimerChangePeriod() を使用してスリープ (タイマーが実行されていない) の期間を変更すると、タイマーは有効期限を計算し、実行状態に移行します (タイマーは実行を開始します)。
BaseType_t xTimerChangePeriod( TimerHandle_t xTimer,
TickType_t xNewTimerPeriodInTicks,
TickType_t xTicksToWait );
xTimerReset() は、タイマーをリセットするために使用されます。ソフトウェア タイマーをリセットすると、タイマーが再起動されます。タイマーの有効期限は、タイマーが最初に開始された時間ではなく、タイマーがリセットされた時間を基準にして再計算されます。
BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait );
過去のハイライト:
組み込みC言語のいくつかの重要なポイント(const、static、volatile、ビット演算)
クロスコンパイル環境、ブートローダー、カーネル、ルート ファイル システムとは何ですか? どういう関係があるのでしょうか?
組み込みLinuxドライバ学習-7.デバイスツリーとは何ですか?