1、移植過程
在將ucos移植到28377d平台上時主要遇見了下面幾個問題,
1) 文件怎么組織,是通過修改micrim上提供的28335一直代碼修改而成的,下載地址為:https://www.micrium.com/。
2)移植完成后發現創建任務完成后,任務無法跳轉,移植在主函數中來回循環
3)當使用ostimedly()函數對任務延時,當延時時間已經完成,系統無法跳出空任務循環,移植在IdleTask中運行
4)任務切換過程中總是跳入到異常中斷中。
移植思路:
開始移植過程時,下載了micrium官網上提供的關於28335的移植歷程,他的歷程導入后整體框架如下圖所示:
上圖中主要包含了5個文件夾分別是APP BSP UC-CPU UC-LIB UCOS-II
APP 主要包含了應用代碼,主要是官方自己編寫的一個小程序,其中各種.h文件是對ucos的一些配置
BSP稱作板級支持包,這個就按照通俗理解的官方提供的歷程中所使用的各種.c文件,比如我需要控制IO口,就要使用F2837xD_GPIO.C中的一些函數,這些就是BSP
UC-CPU暫時未用,好像有包含
UC-LIB暫時未用,貌似是一些支持庫,但是F28377D本身自帶就有一些運算支持庫
UCOS-II這個是重頭戲,里面包含了兩個文件夾
source 文件夾下是ucos的無關核代碼,這些不需要修改
prots->c28x->generic->ccs里面的代碼是和內核有關的代碼,無非也就是操作堆棧,保存cpu的當前寄存器值以及恢復等等,這些是需要修改的
但是下載的代碼已經幫我們修改好了。直接使用
最開始的移植思路是 :
BSP 板級支持包不使用micrum提供的,查閱代碼可以發現BSP中無非就是對外設的控制和上電初始化芯片的過程,這些完全可以倒入一個F28377D的歷程
使用歷程中的例子來完成初始化,這樣更加方便
提供給ucos的時間中斷,這個就人為的使用cputimer來做一個中斷,中斷函數里面調用ostimetick函數來實現。
步驟如下:
首先在28377D的歷程中導入一個blink燈閃的歷程,修改后的框架如下圖所示:
同樣包含了下面幾個文件夾,這個只是我自己用的,和歷程不一樣,這個是隨意的:
cmd driver pcore uc-cpu uc-lib ucos-ii
cmd中存放的是歷程的cmd文件
driver存放的就是那些調用外設的驅動程序,也就是用來替換BSP的
pcore是放置應用程序的。h文件的定義,直接從micrum中復制過來的
uc-cpu uc-lib ucos-ii 是和micrum提供的源碼一樣的(復制過來的)
主函數被放置在了processflow中,這里沒有打開顯示。
代碼如下所示:
修改后的mian函數如下:
#include <string.h> #include "F28x_Project.h" #include "F2837xD_Ipc_drivers.h" #include <app_cfg.h> #include <ucos_ii.h> #include <cpu_core.h> #include <lib_def.h> __interrupt void cpu_timer0_isr(void); //#pragma CODE_SECTION(App_TaskStartStk , "RAMGS0"); //#pragma CODE_SECTION(App_TaskPendStk , "RAMGS0"); //#pragma CODE_SECTION(App_TaskPostStk , "RAMGS0"); CPU_STK_SIZE App_TaskStartStk[APP_CFG_TASK_STK_SIZE]; /* Ping Task's stack.*/ CPU_STK_SIZE App_TaskPendStk[APP_CFG_TASK_STK_SIZE]; /* Pong Task's stack.*/ CPU_STK_SIZE App_TaskPostStk[APP_CFG_TASK_STK_SIZE]; static OS_EVENT *AppTaskObjSem; /********************************************************************************************* ************ * FUNCTION PROTOTYPES ********************************************************************************************** *********** */ /* Start Task.*/ static void App_TaskStart(void *p_arg); /* Ping Task. */ //static void App_TaskPing (void *p_arg); /* Pong Task. */ static void App_TaskPong (void *p_arg); void cpu_timer0_isr(void) { PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; OSIntEnter(); OSTimeTick(); OSIntExit(); } void CPU_Initfunc(void){ InitSysCtrl(); //memcpy(&RamfuncsRunStart,&RamfuncsLoadStart,(size_t)&RamfuncsLoadSize); InitFlash(); //IPCBootCPU2(C1C2_BROM_BOOTMODE_BOOT_FROM_FLASH); //CsmUnlock(); InitGpio(); DINT; InitPieCtrl(); IER = 0x0000; IFR = 0x0000; InitPieVectTable(); EINT; // Enable Global interrupt INTM ERTM; // Enable Global realtime interrupt DBG } void BSP_INIT(void){ GPIO_SetupPinMux(10, GPIO_MUX_CPU1, 0); GPIO_SetupPinOptions(10, GPIO_OUTPUT, GPIO_ASYNC); } void BSP_LED_Off(void){ GpioDataRegs.GPADAT.bit.GPIO10 = 0; } void BSP_Tick_Init(void){ EALLOW; // This is needed to write to EALLOW protected registers PieVectTable.TIMER0_INT = &cpu_timer0_isr; EDIS; // This is needed to disable write to EALLOW protected registers InitCpuTimers(); //CpuTimer0 時間為設置掃頻之間的等待時間 1000 代表1ms //CpuTimer1為采樣定時器時間設置 ConfigCpuTimer(&CpuTimer0, 200, 100000); CpuTimer0Regs.TCR.all = 0x4000; // Use write-only instruction to set TSS bit = 0 IER |= M_INT1; IER |= M_INT13; //IER |= M_INT14; CpuTimer0Regs.TCR.bit.TSS = 1; PieCtrlRegs.PIEIER1.bit.INTx7 = 1; EINT; // Enable Global interrupt INTM ERTM; // Enable Global realtime interrupt DBGM CpuTimer0Regs.TCR.all = 0x4000; } void BSP_LED_Toggle(void){ GpioDataRegs.GPATOGGLE.bit.GPIO10 = 1; }
int main (void) { /* Initialize the CPU and Board.*/ //CPU_Init(); //BSP_Init(); CPU_Initfunc(); BSP_INIT(); OSInit(); DELAY_US(1000000); /* Create the Start task.*/ OSTaskCreateExt(App_TaskStart, (void *)0, (CPU_STK *)&App_TaskStartStk[0], (INT8U )APP_CFG_TASK_START_PRIO, (INT16U )APP_CFG_TASK_START_PRIO, (CPU_STK *)&App_TaskStartStk[APP_CFG_TASK_STK_SIZE - 1u], (INT32U )APP_CFG_TASK_STK_SIZE, (void *)0, (INT16U )(OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR)); /* Start multitasking (i.e. give control to uC/OS-II). */ OSStart(); /* Should never get here.*/ while (DEF_TRUE) { ; } } /* ********************************************************************************************** *********** * App_TaskStart() * * Description : First task to be scheduled. Creates the application tasks. * * Argument(s) : p_arg the argument passed by 'OSTaskCreateExt()'. * * Return(s) : none. * * Caller(s) : This is a task. * * Note(s) : (1) This task creates the application task. The application is a simple LED blinking * demo where LD1 and LD4 blink at a 4:3 polyrhythm. ********************************************************************************************** *********** */ static void App_TaskStart (void *p_arg) { CPU_INT08U os_err; /* Prevent compiler warning for not using 'p_arg' */ (void)&p_arg; /* Clear the LEDs.*/ BSP_LED_Off(); /* Start the Ticker.*/ BSP_Tick_Init(); /* Create the Ping task.*/ AppTaskObjSem = OSSemCreate(0); // OSTaskCreateExt(App_TaskPing, // (void *)0, // (CPU_STK *)&App_TaskPendStk[0], // (INT8U )APP_CFG_TASK_PEND_PRIO, // (INT16U )APP_CFG_TASK_PEND_PRIO, // (CPU_STK *)&App_TaskPendStk[APP_CFG_TASK_STK_SIZE - 1u], // (INT32U )APP_CFG_TASK_STK_SIZE, // (void *)0, /* Create the Pongtask.*/ OSTaskCreateExt(App_TaskPong, (void *)0, (CPU_STK *)&App_TaskPostStk[0], (INT8U )APP_CFG_TASK_POST_PRIO, (INT16U )APP_CFG_TASK_POST_PRIO, (CPU_STK *)&App_TaskPostStk[APP_CFG_TASK_STK_SIZE - 1u], (INT32U )APP_CFG_TASK_STK_SIZE, (void *)0, (INT16U )(OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR)); /* All tasks should be written as an infinite loop. */ while (DEF_TRUE) { //os_err = OSSemPost(AppTaskObjSem); //OSTimeDlyHMSM(0, 0, 0, 500); OSTimeDly(80); DELAY_US(1000000); } } /* ********************************************************************************************** *********** * App_TaskPing() * * Description : 'Ping' task, toggles LD1. * * Argument(s) : p_arg the argument passed by 'OSTaskCreateExt()'. * * Return(s) : none. * * Caller(s) : This is a task. ********************************************************************************************** *********** */ //static void App_TaskPing (void *p_arg) //{ // CPU_INT08U os_err; // /* Prevent compiler warning for not using 'p_arg' */ // (void)&p_arg; // // /* Task body, always written as an infinite loop. */ // while (DEF_TRUE) { // OSSemPend( AppTaskObjSem, // 0, // &os_err); // } //} /* ********************************************************************************************** *********** * App_TaskPong) * * Description : 'Pong' task, toggles LD4. * * Argument(s) : p_arg the argument passed by 'OSTaskCreateExt()'. * * Return(s) : none. * * Caller(s) : This is a task. ********************************************************************************************** *********** */ static void App_TaskPong (void *p_arg) { /* Prevent compiler warning for not using 'p_arg' */ (void)&p_arg; /* Task body, always written as an infinite loop. */ while (DEF_TRUE) { BSP_LED_Toggle(); OSTimeDly(2); } }
micrum提供的歷程中app.c里面的mian()函數是包含了CPU_Init();和BSP_Init()兩個函數,分別是用來初始化芯片以及初始化ucos。在我的函數中
同樣提供了兩個函數分別為CPU_Initfunc()和BSP_INIT();
CPU_Initfunc()函數主要是對cpu進行必要的初始化,復制的歷程的初始化代碼流程
BSP_INIT()的代碼代碼段是初始化GPIO10,我的板子上GPIO10連着一個燈。
之后就是創建了第一個任務APP_TASKSTART任務,
之后調用OSSTART()函數去執行當前優先任務最好的任務,至於osstart()函數中的內容,請自己去看
void BSP_Tick_Init(void)
void cpu_timer0_isr(void)
函數為ucos系統提供時鍾節拍,第一個函數是初始化timer,代碼是參考28377d的歷程中有關於timer定時器的設置,此處設置的是50的定時
50ms定時一到就會跳入中斷函數,也就是第二個函數,在第二個函數中調用ostimetick()函數為ucos提供時鍾節拍,ostimetick()請
自己參考函數內容。
下面來說明產生問題的原因以及解決:
1\文件組織問題上面已經顯示了
2、移植完成后系統無法跳轉進入任務執行:
產生原因 :在任務跳轉的時候,ucos其實是模擬了返回中斷的過程,在創建任務的時候,將創建任務的各種信息(創建的時候其實只有sp指針以及返回地址有用),雖然
創建的時候cpu的值是使用的假信息,具體自己去看創建代碼。
在需要做任務切換時,系統是將對應任務tcb中的堆棧指針返回到cpu的堆棧sp中,然后通過出棧過程將信息返回,然后執行回調跳回到需要執行的任務的代碼處。
問題就出現在cpu的sp寄存器是16位的,所以在創建task過程中,指定的App_TaskPostStk的內存必須要指定在一個地址低於16位的內存當中,他的指定是在cmd中完成的。
最開始出錯是應為我將App_TaskPostStk內存映射到了0x10000的內存當中,所以16位sp獲取到的sp的值就為0x0000調轉到初始代碼處,所以一直在循環:
OSTaskCreateExt(App_TaskPong,
(void *)0, (CPU_STK *)&App_TaskPostStk[0], (INT8U )APP_CFG_TASK_POST_PRIO, (INT16U )APP_CFG_TASK_POST_PRIO, (CPU_STK *)&App_TaskPostStk[APP_CFG_TASK_STK_SIZE - 1u], (INT32U )APP_CFG_TASK_STK_SIZE, (void *)0, (INT16U )(OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR));
下面是調試過程的截圖:
圖1 :
圖2
圖1 中任務啟動后會跳轉到osstarthigtrdy函數中,然后在跳轉到OS_CTX_RESTORE這個函數在asm文件中自己看,
主要的目的就是把最高優先級的任務返回到cpu中,看反匯編代碼中0119a4地址中的指令時將sp返回的,最右邊的sp的值是DA35
如果超過16位那就被結尾了,再然后調用IRET函數將保存在任務棧中的task地址返回到pc中執行。這樣就完成了任務的切換。至於創建任務的時候,各種值是怎么保存到棧中的,請
自己查看代碼。
3、當使用ostimedly()函數對任務延時,當延時時間已經完成,系統無法跳出空任務循環,移植在IdleTask中運行
通過ostimedly函數,任務就有一個延時參數,每次中斷完成調用ostimetick函數,延遲參數減一,知道遞減到0,然后ostimetick函數將
任務又重新插入到就緒表里,具體參考ostimetick函數。
但是 但是 但是 ostimetick函數里是沒有任務切換的 ostimetick函數里是沒有任務切換的 ostimetick函數里是沒有任務切換的;
重要的事情說三遍,所以添加上osIntEnter()和OsIntExit()函數。
OsIntExit()函數里有任務切換函數。這才會導致任務從IdleTask中重新調回任務中執行。
void cpu_timer0_isr(void)
{
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; OSIntEnter(); OSTimeTick(); OSIntExit(); }
4、任務切換過程中總是跳入到異常中斷中
首先必須要明白一個問題,任務的切換還是穿件ucos都是將他作為一個中斷過程來處理的。比如在上面的程序中,任務延時2個時鍾節拍后要繼續執行,
在timer定時器的中斷中,調用osIntExit()函數。
看看看 重要的地方出現了,任務的跳轉是調用了OSiNTcTXsW這個宏定義。
這個宏定義就是 : asm(" TRAP #16");
跳轉到中斷列表中第16個中斷,請翻閱28377d的芯片手冊,第16個中斷的為位置真好是在默認包含了所有中斷表的。c文件即
F28377xD_DefaultISR.c中。
第16個中斷對應的地址就是 : inrerrupt void RTOS_ISR(void)中;
就是說,這個時候ucos通過指令就把指針調轉到了 inrerrupt void RTOS_ISR(void)中來執行。
按照ucos的設想,在這個函數中呢就包含了兩個步驟,保存當前cpu寄存器值,
將最高優先級任務sp指針返回,並當做中斷回調到任務中去。
但是坑就在這個地方;因為我使用的是TI的歷程,歷程中這個函數是空的,關於cpu寄存器的操作是在ucos-ii->prots->generic->ccs->os_cpu_a.asm
這個文件中的_os_cpu_rtosint_handler:函數,這個自己去看,是有的。
最初的做法,我是在 inrerrupt void RTOS_ISR(void)中斷函數中調用_os_cpu_rtosint_handler;這就相當於進了兩次中斷,子啊保存的時候保存了兩次,
保存在堆棧中斷返回地址就已近不是任務的返回地址而是其他的值了。
所以這個點我做了一點修改,中斷函數是按照名字來區別的(實際是名字所對應的地址),所以我就將:
ucos-ii->prots->generic->ccs->os_cpu_a.asm文件下_os_cpu_rtosint_handler的名字更換為_RTOS_ISR;讓中斷直接運行,不經過兩次調用
在F28377xD_DefaultISR.c中將 inrerrupt void RTOS_ISR(void)函數屏蔽。
運行成功。
整個移植過程就完成了,並且能夠讓這個ucos運行起來。至於他更精妙的用法我暫時還沒有領會到。
慢慢在用起來把。
如果上文有錯誤之處,請指正:
郵箱 havihouston@outlook.com