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