1.タスク関数xTaskCreateの作成
タスクはそれほど複雑なものではなく、関数 xTaskCreate です。簡単に言うと、タスクを作成するには、その実行関数を提供する必要があり、そのスタック サイズ、関数の実行スペース、関数の優先順位などの重要な条件を提供する必要があります。タスク関数は実行中であるため、タスク関数の呼び出し関係やローカル変数がタスクのスタックに格納されており、タスクの切り替えや一時停止が発生する場合がありますが、その際、CPUレジスタの割り込みシーンデータがスタックに格納されます。 。
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName,
/*lint !e971 Unqualified char types are allowed for strings and single characters only. */
const configSTACK_DEPTH_TYPE usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
パラメータの説明:
(1) TaskFunction_t: typedef void (*TaskFunction_t)( void *); 関数ポインタ
(2) const char * const pcName: タスク名
(3) configSTACK_DEPTH_TYPE: #define configSTACK_DEPTH_TYPE uint16_t は、符号なし 2 バイト値であることを示します。スタックの深さ (実際には malloc 関数によって割り当てられます)
(4) void * const pvParameters: で渡されるパラメータです
(5) UBaseType_t uxPriority: typedef unsigned short UBaseType_t; は符号なし整数で、 priority 、値が大きいほど優先度が高くなります
(6) TaskHandle_t * const pxCreatedTask: TCB 構造体ポインターがあり、パラメーターが渡されます。
TCB_t の完全名は Task Control Block で、タスク制御ブロックです。この構造にはタスクのすべての情報が含まれていますが、ソース コードには多数の条件付き構成オプションがあります。次のマスクされたオプションはすべて、条件を介して設定できます。条件は、どの定義が使用されるかどうかを決定するために使用されます。これらは当面は必要ありません。また、条件付き設定項目はシールドされます。TCB の最も重要なパラメータは上記で定義されており、関連するパラメータの説明は次のとおりです。変数は次のとおりです
typedef struct tskTaskControlBlock
{
// 这里栈顶指针必须位于TCB第一项是为了便于上下文切换操作,详见xPortPendSVHandler中任务切换的操作。
volatile StackType_t *pxTopOfStack;
// 表示任务状态,不同的状态会挂接在不同的状态链表下
ListItem_t xStateListItem;
// 事件链表项,会挂接到不同事件链表下
ListItem_t xEventListItem;
// 任务优先级,数值越大优先级越高
UBaseType_t uxPriority;
// 指向堆栈起始位置,这只是单纯的一个分配空间的地址,可以用来检测堆栈是否溢出
StackType_t *pxStack;
// 任务名
char pcTaskName[ configMAX_TASK_NAME_LEN ];
/*
//以下屏蔽掉的都是可以通过条件来配置的选项,通过条件来决定哪些定义使用或者不用,暂时不需要用到这
//些,屏蔽掉,TCB最主要的参数在上面
//#####################################################################################
// MPU相关暂时不讨论
#if ( portUSING_MPU_WRAPPERS == 1 )
xMPU_SETTINGS xMPUSettings;
#endif
// 指向栈尾,可以用来检测堆栈是否溢出
#if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
StackType_t *pxEndOfStack;
#endif
// 记录临界段的嵌套层数
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
UBaseType_t uxCriticalNesting;
#endif
// 跟踪调试用的变量
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxTCBNumber;
UBaseType_t uxTaskNumber;
#endif
// 任务优先级被临时提高时,保存任务原本的优先级
#if ( configUSE_MUTEXES == 1 )
UBaseType_t uxBasePriority;
UBaseType_t uxMutexesHeld;
#endif
// 任务的一个标签值,可以由用户自定义它的意义,例如可以传入一个函数指针可以用来做Hook 函数调用
#if ( configUSE_APPLICATION_TASK_TAG == 1 )
TaskHookFunction_t pxTaskTag;
#endif
// 任务的线程本地存储指针,可以理解为这个任务私有的存储空间
#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )
void *pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
#endif
// 运行时间变量
#if( configGENERATE_RUN_TIME_STATS == 1 )
uint32_t ulRunTimeCounter;
#endif
// 支持NEWLIB的一个变量
#if ( configUSE_NEWLIB_REENTRANT == 1 )
struct _reent xNewLib_reent;
#endif
// 任务通知功能需要用到的变量
#if( configUSE_TASK_NOTIFICATIONS == 1 )
// 任务通知的值
volatile uint32_t ulNotifiedValue;
// 任务通知的状态
volatile uint8_t ucNotifyState;
#endif
// 用来标记这个任务的栈是不是静态分配的
#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
uint8_t ucStaticallyAllocated;
#endif
// 延时是否被打断
#if( INCLUDE_xTaskAbortDelay == 1 )
uint8_t ucDelayAborted;
#endif
// 错误标识
#if( configUSE_POSIX_ERRNO == 1 )
int iTaskErrno;
#endif
//###################################################################################
*/
} tskTCB;
typedef tskTCB TCB_t;
================================================= =======================
2.タスクの具体的な内部詳細を作成します。
単純なタスク作成機能を例として、ここでは 3 つの単純なタスク vTask1、vTask2、vTask3 が作成されます。
void vTask1( void *pvParameters )
{ /* 任务函数的主体一般都是无限循环 */
for( ;; )
{
flagIdleTaskrun = 0;
flagTask1run = 1;
flagTask2run = 0;
flagTask3run = 0;
/* 打印任务的信息 */
printf("T1\r\n");
}
}
void vTask2( void *pvParameters )
{
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
flagIdleTaskrun = 0;
flagTask1run = 0;
flagTask2run = 1;
flagTask3run = 0;
/* 打印任务的信息 */
printf("T2\r\n");
}
}
void vTask3( void *pvParameters )
{
const TickType_t xDelay5ms = pdMS_TO_TICKS( 5UL );
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
flagIdleTaskrun = 0;
flagTask1run = 0;
flagTask2run = 0;
flagTask3run = 1;
/* 打印任务的信息 */
printf("T3\r\n");
// 如果不休眠的话, 其他任务无法得到执行
vTaskDelay( xDelay5ms );
}
}
//主函数的实现
int main( void )
{
prvSetupHardware();
xTaskCreate(vTask1, "Task 1", 1000, NULL, 0, NULL);
xTaskCreate(vTask2, "Task 2", 1000, NULL, 0, NULL);
xTaskCreate(vTask3, "Task 3", 1000, NULL, 2, NULL);
/* 启动调度器 */
vTaskStartScheduler();
/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
return 0;
}
3. RAMメモリ内のTCBタスク構造の存在
以下の図では、tsTaskControlBlock 構造体のコード データ全体が RAM メモリに配置されており、メモリ内に TCB 構造体を割り当てる効果を示しています (図のみが効果を示しており、実際にはまったく同じではない可能性があります)。 RAM メモリに割り当てられたスタック領域に保存されたデータは次のとおりです。
スタックトップポインタpxTopOfStack; 分割メモリ空間の最後のデータ格納位置へのポインタ
; 状態リストxStateListItem;
イベントリスト xEventListItem;
タスク優先度uxPriority;
スタック開始位置へのポインタpxStack; 分割メモリ空間タスクの開始アドレス位置へのポインタ
名前pcTaskName[ configMAX_TASK_NAME_LEN ];
================================================= =======================
4. タスク関数作成時のスタック領域の確保とサイズの問題
//任务创建函数
xTaskCreate(vTask1, "Task 1", 1000, NULL, 0, NULL);
このタスクを作成するときに、渡されたパラメーターはどこに保存されますか? まず、渡されたスタックのサイズのパラメーター 1000 を確認します。この時点で、2 つの問題を解決する必要があります。1 つ目は、スタックがどこから割り当てられるかということです。2 つ目は、スタックのサイズを決定する方法です。
最初の質問は、スタックがどこから割り当てられるのかということです。スタックは実際には空きメモリの一部です。FreeRTOS の heap2.cpp ファイルは、巨大なグローバル配列 ucHeap を定義します。この配列は誰にも使用されません。空きメモリの一部として、将来スタック領域の割り当てが開始されます。上図に示すように、利用可能なメモリは特定のタスクのスタックとして使用するために配列に分割されます; 配列のサイズはマクロ定義から 17*1024 バイトです。
2 番目の質問は、例のスタックのサイズをどのように決定するかです。プログラマが入退出する値の大きさに応じて割り当てることができ、例では1000*4バイトのメモリを分割し、メモリの初期空間をpxStackポインタに格納しています。
================================================= =======================
5. タスク関数作成時の関数ポインタとパラメータ格納の問題点と機能
//任务创建函数
xTaskCreate(vTask1, "Task 1", 1000, NULL, 0, NULL);
最初のパラメータは関数ポインタです。xTaskCreat は実際には関数のアドレス addrF です。このタスクを開始するか、この関数を呼び出したいときは、CPU の R15 レジスタ、つまり PC レジスタの値を次と等しくすることができます。関数のアドレス。addrF だけです。
4 番目のパラメータは関数とともに渡されるパラメータ値で、通常は R0 レジスタに格納されます。
================================================= =======================
6. タスクはサスペンド状態から再開されます
システムは、定義されたグローバル配列 ucHeap からタスク関数 xTaskCreate に利用可能な領域を割り当てます。この例では、1000*4 のメモリ領域が割り当てられ、pxStack はメモリの開始アドレスを指し、pxTopOfStack は最後のデータ保存場所を指します。記憶の中で。
タスクを作成するとき、タスク関数 xTaskCreate はすでにメモリのこの部分を作成しており、RAM メモリの内容を変更するのに役立ちます。TCB 構造体では、パラメータと関数ポインタが渡されていません。タスク関数が設定したこれらの値は、タスクを再開するときに使用するために、CPU メモリの R15 や R0 などのレジスタに書き込まれます。
タスクが作成されたばかりのとき、タスクはまだ実行されておらず、一時停止状態に属します。タスクが中断されると、システムはサイト保護のために、戻りアドレスを含む現在のタスクのレジスタをスタックに置きます。 R15 関数、R0 パラメータなどの実行を開始し、割り込みタスクが終了し、スタック内のさまざまなレジスタを RAM メモリに復元し始め、R15 関数の戻りアドレスから実行し、ジャンプバックします。最後に中断されたタスクに戻り、実行を継続します。図1に示すように、