最近在做一些關於一些驅動向TI板子的移植集成的事情,在這裏簡單的記錄一下。本文主要敘述如何進行多任務的編程,以及我在編程中遇到的一些不解。
- Task組成部分:任務函數,任務參數(屬性),任務堆棧.
實際上Task是sys/bios提供的一種併發編程手段,和我們常見到的線程是一個道理。
- Task類型:硬中斷HWI,軟中斷SWI,任務Task,空閒任務IDLE.
- 優先級排列:硬中斷HWI>軟中斷SWI>任務Task>空閒任務IDLE.
- 四種類型任務釋義:
硬中斷:實時環境中,它是響應外部觸發的異步事件(中斷)-----優先級不由sys/bios管理,由cpu的特性決定。
雖然優先級最高,當然也可以被中斷。
軟中斷:軟件中斷服務例程 ------優先級可以達到 32 級,默認只有 16 級。
SWI 可以被更高優先級的 SWI 和 HWI 搶佔,但是 SWI 是不會阻塞的。
任務:用戶任務,運行過程中,可以被阻塞直到必要的資源可用;
TASK 要求每個任務要有自己獨立的棧空間。
SYS/BIOS 提供了一些機制用於 TASK 間的同步和通信,它們有信號量(Semphore),事件(Events),隊列(Queue)和郵箱(Mailboxes)。
優先級也可以達到 32 級,默認是 16 級。
Task 優先級數值越大,其優先級就越高,0 是優先級最低的。Task_setPri 函數動態的更改。新的優先級必須在 1 到 TnumPriorities – 1 之間。當新優先級數低於當前優先級時,可能會發生任務的切換。
空閒任務:優先級最低的任務。
當 SYS/BIOS 沒有比空閒任務更高的任務運行時,SYS/BIOS 就會執行空閒任務,並且是連續執行,直到有更高的任務進入就緒狀態。
其運行在Task任務 0 優先級上。
- 創建一個task過程:
#include <app.h>
#include <utils/console_io/include/app_log.h>
#include <xdc/runtime/Error.h>
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <app_ipc_rsctable.h>
static Void appMain(UArg arg0, UArg arg1){ //任務函數
//do something
printf("this is thread function\n");
}
void StartupEmulatorWaitFxn (void){
volatile uint32_t enableDebug = 0;
do{
}while (enableDebug);
}
static uint8_t gTskStackMain[4*1024] //任務使用的棧空間
__attribute__ ((section(".bss:taskStackSection")))
__attribute__ ((aligned(4096)))
;
int main(void)
{
Task_Params tskParams;
Error_Block eb;
Task_Handle task;
/* This is for debug purpose - see the description of function header */
StartupEmulatorWaitFxn();
Error_init(&eb);
Task_Params_init(&tskParams);
tskParams.arg0 = (UArg) NULL;
tskParams.arg1 = (UArg) NULL;
tskParams.priority = 10u; //指定任務優先級
tskParams.stack = gTskStackMain; //指定任務使用的堆棧
tskParams.stackSize = 4096; //指定堆棧的大小
task = Task_create(appMain, &tskParams, &eb); //動態創建一個task
/*
You can continue to create other tasks from here......
*/
if(NULL == task){
BIOS_exit(0);
}
BIOS_start(); //運行到這裏程序不會退出,相當於是個死循環,操作系統將會調度執行任務
return 0;
}
- SDK版本以及引用路徑:
/psdk_rtos_auto_j7_06_01_00_15/bios_6_76_03_01/packages/ti/sysbios/knl
- 如何在程序中獲取任務的信息以及狀態:
// bios_6_76_03_01/packages/ti/sysbios/knl/Task.h:105:struct ti_sysbios_knl_Task_Stat ; 任務信息聲明處
//解釋幾個我用到的成員:
struct ti_sysbios_knl_Task_Stat {
xdc_Int priority; //優先級
xdc_Ptr stack; //使用的堆棧地址
xdc_SizeT stackSize; //使用的堆棧的大小
xdc_runtime_IHeap_Handle stackHeap;
xdc_Ptr env;
ti_sysbios_knl_Task_Mode mode;//狀態:阻塞 就緒 運行 不活躍的
xdc_Ptr sp;
xdc_SizeT used; //已經使用了的堆棧空間大小
};
如何在一個任務中動態獲取當前任務的狀態信息:
Task_Stat statbuf;
Task_stat(Task_self(),&statbuf);
printf("In appMain task,stack buff used = %d\n",statbuf.used);//查看當前任務已經使用的
printf("In appMain task,stack total size = %d\n",statbuf.stackSize);//查看當前任務分配的總stack
printf("In appMain task,task priority =%d\n",statbuf.priority); //查看當前任務的優先級
- 關於任務堆棧:
大小:
儘量使用自定義數組來充當任務執行時的堆棧,不要依靠於系統的堆棧,如果內存不足,將發生致命錯誤;
關於給任務分配多大內存爲合適,我查看了一些資料,有說到一個任務最少需要分配512個字節,這是理論值,或者說這個任務的函數是一個空函數,啥都不幹,這512是用來滿足上下文的保存的,不符合實際開發,如果條件允許,簡單的函數給予2k,稍微複雜的給予4k.(具體值還請各位嘗試,這裏作爲參考)
注意:我這裏是跑在R5F mcu2_0上的,和使用的SDK版本也有關係。
對齊方式:(不添加對齊應該也可以運行)
我理解cpu爲了提高取指速率,需要進行內存對齊。
一般而言,選擇對齊數num * n = stackSize,其中n%2 == 0,num可能更滿足要求,如
static uint8_t TskStackMain[4*1024]
attribute ((section(".bss:taskStackSection")))
attribute ((aligned(4096)));
這個我並沒有找到官方的文檔的來解釋說明,僅僅作爲一種猜測,當然不是憑空猜測,是經過實驗的。
- 任務阻塞方式:
Task_sleep():使用:Task_sleep(5300 / Clock_tickPeriod);// 延時5.3ms Clock_tickPeriod定義在ti/sysbios/knl/Clock.h
Task_yield():使得任務主動讓出自己的時間片
Semaphore_pend():信號量,獲取臨界資源
- 同步方式:
信號量、郵箱、事件等,如需要可以參考官方文檔。
最後說一下我的遇到幾個很奇怪的現象:
1.替換固件,板子斷電重啓,系統運行的結果是前一次,爲什麼上一次數據沒有被擦除?
2.相同優先級的任務存在某一個任務一直運行,不釋放cpu的現象,只好通過一些接口
讓他主動釋放,並沒有像一些文檔中描述的,類似於時間片輪轉?
這裏猜測可能是SDK版本的原因,也可能是板子還是不夠成熟吧