FreeRTOS ----スケジューラ
プロセススケジューラの分析を開始します
あなたは、タスクを作成したら、タスクスケジューラを起動するvTaskStartScheduler()関数を呼び出します。
void vTaskStartScheduler( void )
{
/* 部分代码如下: */
BaseType_t xReturn;
xReturn = xTaskCreate( prvIdleTask,
configIDLE_TASK_NAME,
configMINIMAL_STACK_SIZE,
( void * ) NULL,
portPRIVILEGE_BIT,
&xIdleTaskHandle );
#if ( configUSE_TIMERS == 1 )
{
if( xReturn == pdPASS )
{
xReturn = xTimerCreateTimerTask();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_TIMERS */
if( xReturn == pdPASS )
{
#ifdef FREERTOS_TASKS_C_ADDITIONS_INIT
{
freertos_tasks_c_additions_init();
}
#endif
portDISABLE_INTERRUPTS();
#if ( configUSE_NEWLIB_REENTRANT == 1 )
{
_impure_ptr = &( pxCurrentTCB->xNewLib_reent );
}
#endif /* configUSE_NEWLIB_REENTRANT */
xNextTaskUnblockTime = portMAX_DELAY;
xSchedulerRunning = pdTRUE;
xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT;
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();
traceTASK_SWITCHED_IN();
if( xPortStartScheduler() != pdFALSE )
{
/* Should not reach here as if the scheduler is running the
function will not return. */
}
else
{
/* Should only reach here if a task calls xTaskEndScheduler(). */
}
}
else
{
configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );
}
/* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0,
meaning xIdleTaskHandle is not used anywhere else. */
( void ) xIdleTaskHandle;
}
- あなたは、スタティックメモリを使用する場合、あなたはアイドルタスクで0、最も低い優先度の優先順位を作成する機能xTaskCreateStatic()を使用し、アイドルタスクを作成します。
- あなたがソフトウェアタイマを使用する場合は、我々は()関数xTimerCreateTimerTaskによってタイマサービスジョブを作成する必要があります。
- 閉じる中断。
- 変数は、実行中のスケジューラが開始さを示す、xSchedulerRunning pdTRUEに設定されています。
- マクロが時間統計機能が有効であることを示す、configGENERATE_RUN_TIME_STATS時間1であれば、この時点では、ユーザーがタイマ/カウンタを設定するには、マクロportCONFIGURE_TIMER_FOR_RUN_TIME_STATSを実装する必要があります。
- 関連するハードウェアを開始するためのスケジューラを初期化する機能xPortStartScheduler()を呼び出します。
FreeRTOSシステムクロックティックタイマが設けられており、xPortStartScheduler()関数初期化ハードウェアによって行われるタスク切り替え、割り込みPendSVに使用される次のように、特定のコードがあります。
/* 部分重要代码如下: */
/* Make PendSV and SysTick the lowest priority interrupts. */
portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;
/* Start the timer that generates the tick ISR. Interrupts are disabled
here already. */
vPortSetupTimerInterrupt();
/* Initialise the critical nesting count ready for the first task. */
uxCriticalNesting = 0;
/* Start the first task. */
prvStartFirstTask();
- PendSV割り込み優先順位最低の優先度を設定します。
- セットのSysTick割り込み優先レベルが最も低い優先度です。
- タイマーのタイミングサイクルがダニに設定し、タイマ割り込み缶をチェックする機能vPortSetupTimerInterrupt()を呼び出します。
- 初期化のネストされたクリティカルセクションカウンタがゼロです。
- 最初のタスクを開始する機能prvStartFirstTask()を呼び出します。
最初のタスクを開始します
次のように、ソース関数を最初のタスクを開始するための機能prvStartFirstTask():
__asm void prvStartFirstTask( void )
{
PRESERVE8
/* Use the NVIC offset register to locate the stack. */
ldr r0, =0xE000ED08
ldr r0, [r0]
ldr r0, [r0]
/* Set the msp back to the start of the stack. */
msr msp, r0
/* Globally enable interrupts. */
cpsie i
cpsie f
dsb
isb
/* Call SVC to start the first task. */
svc 0
nop
nop
}
PRESERVE8ザ・アライン8バイトを意味し、ディレクティブ、。
一般ベクタテーブルは、開始アドレス(0x00000000の)格納を開始しなければならないが、いくつかのアプリケーションは、実行時または再定義ベクタテーブルに変更する必要があり、この目的のために提供のCortex-Mプロセッサは、再配置特徴ベクトルテーブルと呼ばれています、プログラマブル・レジスタは、ベクタテーブル(VTOR)をオフセットレジスタと呼ばれるレジスタのアドレス0xE000ED08、ベクタテーブルは、レジスタによって再定義することができるで提供します。
先頭のアドレスは、MSP(一次スタックポインタ)の初期値スケールに格納され、及びMSPの次の行の動作は、初期値を取得することであり、MSP、今後の初期にR0、R0に直ちに割り当て値を割り当てられていますMSP、リセットする値。
ldr r0, =0xE000ED08 ldr r0, [r0] ldr r0, [r0] msr msp, r0
命令は、I CPSIEおよびF CPSIE、次のような意味を:
- 命令は、DSBとISBは意味を次のとおりです。
- 0 SVCは、 SVC SVC命令を呼び出すこともSVCの呼び出し要求管理として知られている割り込みをトリガし、SVCとPendSV例外は、OSの設計のために非常に重要であり、唯一のプログラムの背面に、最初のタスクを開始するにはFreeRTOS例外でSVCを使用しますSVCよりも決して少ないです。
今後、ハンドラ割り込みSVC例外に入り、SVC割り込みサービスルーチンは、次のように、通常はvPortSVCHandler()のための#defineによって再定義SVC_Handler()が、FreeRTOSConfig.hする必要があります:
#define vPortSVCHandler SVC_Handler
次のようにvPortSVCHandler()がport.cファイルのソースコードで定義される関数です。
__asm void vPortSVCHandler( void )
{
PRESERVE8
ldr r3, =pxCurrentTCB /* Restore the context. */
ldr r1, [r3] /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */
ldr r0, [r1] /* The first item in pxCurrentTCB is the task top of stack. */
ldmia r0!, {r4-r11} /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */
msr psp, r0 /* Restore the task stack pointer. */
isb
mov r0, #0
msr basepri, r0
orr r14, #0xd
bx r14
}
pxCurrentTCBは、タスクを実行する針点の実装ポインタTCB_tであり、ここで記憶されたアドレスへのポインタを取得することで、操作の次の命令のシリーズは、TCBアドレスpxCurrentTCBポインタポイントを得るために最初に、そしてその後、このアドレスを介して取得しますTCBは、すなわちタスクスタックpxTopOfStackスタックポインタが指している、最初のフィールドです。
ldr r3, =pxCurrentTCB ldr r1, [r3] ldr r0, [r1]
究極の目標は、タスクのスタックポインタを実行するための最初のタスクを取得することです。
あるタスクに対応するレジスタの値は、これらの値はコンテクストセーブに格納されているので、必要がこれらのレジスタを復元するために、タスクスイッチの部位、すなわち、復元します。
LDMIA R0!、{R4-R11}、LDMIA命令は、複数のロード/ストア命令であり、ここでは役割がライトバックと複数のロード/ストア命令が使用され、レジスタR0に格納されたマルチアドレスを下記値の連続したアドレスが割り込み回復は、R4〜R11は、手動でポップアップし、ユーザーが必要なときに、これらのレジスタが自動的にスタックのうちに終了しますレジスタR4-R11に割り当てられ、R0〜R3、R12、PC、XPSR MCUなどのために、
- MSR PSP、R0プロセススタックポインタPSPタスクスタックに設定します。
- MSR BASEPRI、R0すなわちBASEPRI = 0は、割り込みをイネーブルレジスタと
- ORR R14、#の0xd、R14はリンクレジスタ(LR)で、R14は、彼が異常なCPUは、スレッドモードに入り、プロセススタックを使用やめると言って、最後の4または0x0Dのビット。
BX R14、命令が実行された後、ハードウェアが自動的にレジスタR0〜R3、R12、LR、PCとXPSR、プロセススタックスタック使用量PSPの値を復元した後、PC保存タスク機能に登録し実行します。
この時点で、FreeRTOSタスクスケジューラが実行を開始しました。
タスクの切り替え
コアシステムがRTOSタスク管理であり、タスク管理は、コアタスク切り替えで、タスク切り替えは、タスクの実行順序を決定し、タスク切り替えの効率レベルもRTOSの性能を決定します。
PendSV例外
PendSV(懸濁システムコール)を除いて、優先度がICSRステータスレジスタによってプログラムすることができ、割り込み制御1 bit28割り込みPendSVをトリガするように設定されている;及びSVC異常は、状態を停止することができます設定を処理する優先度の高い例外内、および、割り込み処理が終了した後、他のすべての実行中のPendSV例外処理を許可する、最も低い優先度の例外にPendSVセット、この機能を利用するために処理し、優先度の高い例外が完了した後に実行されますこれは、コンテキスト切り替えのために有用であるキーデザイン、様々なOSです。
交換システムFreeRTOSタスクが完了しPendSV割り込みサービス機能に最終的にあります。
サービスルーチン割り込みPendSV)は(PendSV_Handlerに持っているはずですが、FreeRTOSで次のように再定義:
#define xPortPendSVHandler PendSV_Handler
機能xPortPendSVHandler()のソースは次のよう:
__asm void xPortPendSVHandler( void )
{
extern uxCriticalNesting;
extern pxCurrentTCB;
extern vTaskSwitchContext;
PRESERVE8
mrs r0, psp
isb
ldr r3, =pxCurrentTCB /* Get the location of the current TCB. */
ldr r2, [r3]
stmdb r0!, {r4-r11} /* Save the remaining registers. */
str r0, [r2] /* Save the new top of stack into the first member of the TCB. */
stmdb sp!, {r3, r14}
mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
msr basepri, r0
dsb
isb
bl vTaskSwitchContext
mov r0, #0
msr basepri, r0
ldmia sp!, {r3, r14}
ldr r1, [r3]
ldr r0, [r1] /* The first item in pxCurrentTCB is the task top of stack. */
ldmia r0!, {r4-r11} /* Pop the registers and the critical nesting count. */
msr psp, r0
isb
bx r14
nop
}
処理操作のコード:
- 保存場所(現在実行中のタスク)。
- 割り込みを無効には、クリティカルセクションを入力してください。
- 次に実行するタスクを取得するために呼び出すvTaskSwitchContext()関数で、タスク制御ブロックを実行するには、このタスクのための意志のpxCurrentTCB更新。
- スタックポインタは、新しいタスクを取得します。
- 復旧サイト(サイトは、タスクを実行しようとしています)。
- PSPの更新処理スタックポインタ値。
- 最後の実行命令BX R14は、新しいタスクが実行を開始します。
次のタスクが実行されるようにして下さい
コールvTaskSwitchContext(実行するために次のタスクを検索する機能)、関数内で最終的に完了するために、taskSELECT_HIGHEST_PRIORITY_TASK()を呼び出している間。
マクロが1のとき、すなわち、特定の使用を必要とし、コモンモードおよびハードウェアモードは、このマクロの設定configUSE_PORT_OPTIMISED_TASK_SELECTIONかどうかによって決定されますが、二つの方法でFreeRTOSを実行したいタスクは、使用されている検索ハードウェア、そうでない場合は、それが一般的な方法です。
次のように2つの違いはアプローチ:
一般的な方法
アレイpxReadyTasksLists []配列は、対応するリストに連接されている優先度の準備タスクを準備タスクリスト、リストに対応する優先度です。
UxTopReadyPriority変数がレディ状態で最も優先度の高い値を示し、状況は2つの方法で更新された値は次のとおりです。
- それは新しいタスクuxTopReadyPriorityに割り当てられた優先順位の値よりも大きい場合は、タスクの優先順位は、現在の値uxTopReadyPriorityよりも大きい場合には、新しいタスクを作成するたびに決定されます。
- 新しい準備タスクがレディリストに追加された場合uxTopReadyPriorityの値を決定し、更新します。
一般的に、それはuxTopReadyPriorityからそれを優先準備タスクを持ってショーを空でないリスト、最優先の価値判断を、開始する準備が整っのこの現在の状態を参照してください、です。
ハードウェア
ハードウェアは、先行ゼロの命令の計算された数とのCortex-Mプロセッサのようなハードウェア命令、実装するためにプロセッサを使用して構築される:CLZと、
ハードウェアを使用する場合、uxTopReadyPriority変数がレディ状態が最優先ではなく、優先順位の変数代表のすべてのビットを使用することを表し、代わっ優先順位0にBIT0は、31の優先度、優先度を表すビット31そこであろう次いで準備作業各位置1、従って、最大で32の優先度を有していなければならないハードウェアを使用する場合、
先行ゼロの数を計算するためのCLZ命令は、すなわち1ビットのビットに最上位ビットから開始し、0の間で、その後、数は、準備完了状態にある、次に31を引いたとの数であります最高のプライオリティ値。
リスト項目から対応するリストを見つけるためにlistGET_OWNER_OF_NEXT_ENTRY()を使用して、最高の優先順位準備完了状態を得た後、リストの項目は、このように実行されるべき次のタスクを決定し、pxCurrentTCBブロックに割り当てるタスクに対応します。
タスクの切り替え
どちらの場合も、タスク切り替えトリガします:システムコールやダニのタイマ割り込みの実装を。
システムコール
システムコールは、タスク切替機能taskYIELD()および他の間接呼び出しAPIのtaskYIELD()のようなシステムによって提供される関連するAPIのFreeRTOSの実装です。
関数taskYIELD()は、実際には以下のように定義されるマクロです。
#define taskYIELD() portYIELD()
/* Scheduler utilities. */
#define portYIELD() \
{ \
/* Set a PendSV to request a context switch. */ \
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; \
\
/* Barriers are normally not required but do ensure the code is completely \
within the specified behaviour for the architecture. */ \
__dsb( portSY_FULL_READ_WRITE ); \
__isb( portSY_FULL_READ_WRITE ); \
}
最終的な動作は、ステータスレジスタと割り込み制御bit28 ICSR 1に書き込むことにより、上記ソースである、懸濁PendSV PendSVは、タスク切替機能サービスPendSVを中断することができるように、割込みトリガ。
最終的関数による割り込みレベルのタスク切替機能portYIELD_FROM_ISR()、完全にportYIELD()。
ティックタイマー割り込み
次のようにタイマーチック割り込みサービス機能を変更する必要があります。
void SysTick_Handler(void)
{
if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
{
xPortSysTickHandler();
}
}
コールxPortSysTickHandler()次のようにタイマーサービス機能、機能のソースコードをカチカチ:
void xPortSysTickHandler( void )
{
vPortRaiseBASEPRI(); //关中断
{
if( xTaskIncrementTick() != pdFALSE ) //增加时钟计数器xTickCount的值
{
//向中断控制和状态寄存器ICSR的bit28写入1,挂起PendSV来触发PendSV中断
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
}
}
vPortClearBASEPRIFromISR(); //打开中断
}
タイムスライス
FreeRTOSは、複数のタスクをサポートすると同時にタイムスライスの動作に優先度のタスク(クロックパルスの長さ)は、次のタスクが優先的に存在するように、CPUの使用を許可されているのと同じ優先順位を有します実行します。
それをスライス時間を使用し、マクロマクロconfigUSE_PREEMPTION configUSE_TIME_SLICING 1が構成されなければなりません。
タイムスライスの長さは、マクロconfigTICK_RATE_HZによって決定され、割り込みサイクルティックタイムスライスタイマの長さであり、そのようなconfigTICK_RATE_HZ値が1000に設定され、その後、タイムスライスの長さは1msです。
タイムスライスは、サービス機能タイマ割り込みダニを発生します。