DSP

手把手教UCOS II移植到DSP 2803x(28035为例)

2019-07-13 10:07发布


移植前说明:
相信学过UCOS的人都看过下面这张图,这张图讲解了UCOS移植的方法。这是ARM芯片移植UCOS的体系结构图,当然对于其它芯片也是适用的,只是个别文件名不同而已。移植的时候,图示红 {MOD}部分是不需要去修改的,需要修改的是蓝 {MOD}部分(这部分与芯片的型号是相关的,不同芯片是不一样的),绿 {MOD}部分也可以适当更改。可以通过os_cfg.h裁剪UCOS代码,因为UCOS相对来说还是比较臃肿的。


这里写图片描述

移植前准备:
一、 准备一个28035的开发例程(也可以自己写的,注:我使用的是CCS7.2版本,不同版本文件结构可能不同)
下面是一个28035的例程整体框架,例程功能是定时器time0每500ms中断一次,中断程序中翻转GPIO口的输出状态,以此来让开发板上的LED灯闪烁。文件说明:include是头文件,在这里没有展开。这是一个最基本的28035开发例程。

这里写图片描述

二、准备UCOS II移植文件
在Micrium官网下载micrium提供的关于28335的移植例程(到目前为止只有28335),整体框架如下图所示。
文件很多,这里先不用理会,后面会讲各个文件的功能。
这里写图片描述

三、 开始移植
1、 把前面两个工程文件合并起来。
官网下载的移植文件说明:一共就这几个文件,分别是APP, BSP, UC_CPU, UC_LIB, UCOS_II。
APP包含了用户应用代码,其中各种.h文件是对ucos的一些配置。.h文件保留,并新建一个CONFIG文件夹保存起来。app.c文件删除,用自己写的文件代替。
BSP称作板级支持包,这个相当于官方提供的例程中所使用的各种.c文件。这个文件也要删除,用.c文件代替。为什么要删除BSP文件,因为这个是为28335写的文件,在28035已经不适用了。还有一个问题是,官网28335这个例程只是定义了一些用到的寄存器,还有很多寄存器没有定义,如果要用到那些寄存器则要自己去定义,这就相当于本来提供的各种.c文件都要自己重新写,很烦锁。
uC_CPU是基于 micrium 官方评估板的 ucosii 移植代码,这个直接复制过来。
uC_LIB这个是micrium 官方的一个库代码,也复制过来。
uCOS_II这个文件是ucos_ii源代码,移植的时候需要修改的文件。但是官方已经帮我们修改好了。所以直接复制过来用就可以了。

可能有人会有疑问,这个28335的CPU文件适用于28035么?答案是肯定的。看到上面的C28X目录么,就是说这个移植文件是适用于所有C28X芯片的。因为28335和28035都属于C28X系列,所以此文件也适用于28035芯片。

把上面几个文件复制到第一个工程例程,修改后的框架如下图所示:
这里写图片描述

文件说明:CONFIG文件夹文件是从上面APP文件夹里复制而来的.h文件。main文件的Example_2803xLEDBlink.c是要修改的,下一步会介绍。SXD28035_common及SXD28035_headers文件,是原来工程的文件,没有修改。uC_CPU, uC_LIB, uCOS_II这三个文件是从官网例程复制过来的。F28035.cmd文件也要自己写。
2、 修改文件路径
这里写图片描述

3、 编写主程序(主程序代替APP程序)
main / Example_2803xLEDBlink.c程序如下:
#include #include #include #include #include #include "DSP28x_Project.h" #define LED0 GpioDataRegs.GPBTOGGLE.bit.GPIO34 CPU_STK_SIZE App_TaskStartStk[APP_CFG_TASK_STK_SIZE]; CPU_STK_SIZE App_TaskPendStk[APP_CFG_TASK_STK_SIZE]; CPU_STK_SIZE App_TaskPostStk[APP_CFG_TASK_STK_SIZE]; static OS_EVENT *AppTaskObjSem; static void App_TaskStart(void *p_arg); static void App_TaskPing (void *p_arg); static void App_TaskPong (void *p_arg); interrupt void cpu_timer0_isr(void); // 中断声明 void BSP_Tick_Init(void); void BSP_Tick_Init(void) { EALLOW; PieVectTable.TINT0 = &cpu_timer0_isr; // 定时器中断地址 // PieVectTable.RTOSINT = &RTOSINT_ISR; // test PieVectTable.OS_CPU_RTOSINT = &OS_CPU_RTOSINT_Handler; // RTOS // MemCopy(&RamfuncsLoadStart, &RamfuncsLoadEnd, &RamfuncsRunStart); // InitFlash(); //初始化FLASH EDIS; InitCpuTimers(); ConfigCpuTimer(&CpuTimer0, 60, 1000); // test 500000 CpuTimer0Regs.TCR.all = 0x4001; // 设置 TSS bit = 0 IER |= M_INT1; // CPU-Timer 0属于CPU INT1,使能: PieCtrlRegs.PIEIER1.bit.INTx7 = 1; // Enable TINT0 in the PIE: Group 1 interrupt 7 EINT; // 使能INTM中断 ERTM; // 使能DBGM中断 } void main(void) { InitSysCtrl(); // InitGpio(); // 本例未用到 // 第二步:初始化GPIO: DINT; InitPieCtrl(); IER = 0x0000; IFR = 0x0000; InitPieVectTable(); EALLOW; GpioCtrlRegs.GPBMUX1.bit.GPIO34 = 0; // LED0 GpioCtrlRegs.GPBDIR.bit.GPIO34 = 1; EDIS; EINT; // 使能INTM中断 ERTM; // 使能DBGM中断 OSInit(); 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)); OSStart(); while (DEF_TRUE) { ; } } static void App_TaskStart (void *p_arg) { volatile CPU_INT08U os_err; // test (void)&p_arg; BSP_Tick_Init(); 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, (INT16U )(OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR)); 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)); while (DEF_TRUE) { os_err = OSSemPost(AppTaskObjSem); OSTimeDlyHMSM(0, 0, 0, 1); } } static void App_TaskPing (void *p_arg) { CPU_INT08U os_err; (void)&p_arg; while (DEF_TRUE) { OSSemPend( AppTaskObjSem, 0, &os_err); } } static void App_TaskPong (void *p_arg) { (void)&p_arg; while (DEF_TRUE) { OSTimeDlyHMSM(0, 0, 0, 300); // test before is 300 LED0=1; CpuTimer0.InterruptCount++; // test 仿真测试用 } } interrupt void cpu_timer0_isr(void) { PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // LED0=1; // test // CpuTimer0.InterruptCount++; //test OSIntEnter(); OSTimeTick(); OSIntExit(); }
程序说明:BSP_Tick_Init(void)和interrupt void cpu_timer0_isr(void)函数为ucos系统提供时钟节拍,第一个函数是初始化timer,此处设置的是1ms定时一到就会跳入中断函数,中断函数中调用OSTimeTick()函数为ucos提供时钟节拍。注意这里设置了OS_CPU_RTOSINT_Handler,这个后面还会讲到。
后面的函数都是官网例程的代码直接复制过来的。在void App_TaskPong (void *p_arg)函数做了一些修改,改成自己开发板用的代码及测试代码。
4、 编写CMD文件。
这个很重要,如果不懂CMD文件的可以用我这个文件。千万不要乱改,否则会出现各种各样的问题,而且极难调试。
MEMORY { PAGE 0: /* Program Memory */ /* Memory (RAM/FLASH/OTP) blocks can be moved to PAGE1 for data allocation */ RAML0 : origin = 0x008000, length = 0x000800 /* on-chip RAM block L0 */ RAML1 : origin = 0x008800, length = 0x000400 /* on-chip RAM block L1 */ OTP : origin = 0x3D7800, length = 0x000400 /* on-chip OTP */ FLASHH : origin = 0x3E8000, length = 0x002000 /* on-chip FLASH */ FLASHG : origin = 0x3EA000, length = 0x002000 /* on-chip FLASH */ FLASHF : origin = 0x3EC000, length = 0x002000 /* on-chip FLASH */ FLASHE : origin = 0x3EE000, length = 0x002000 /* on-chip FLASH */ FLASHD : origin = 0x3F0000, length = 0x002000 /* on-chip FLASH */ FLASHC : origin = 0x3F2000, length = 0x002000 /* on-chip FLASH */ /* FLASHA : origin = 0x3F6000, length = 0x001F80 */ /* on-chip FLASH */ FLASHA : origin = 0x3F5000, length = 0x002F80 /* test */ CSM_RSVD : origin = 0x3F7F80, length = 0x000076 /* Part of FLASHA. Program with all 0x0000 when CSM is in use. */ BEGIN : origin = 0x3F7FF6, length = 0x000002 /* Part of FLASHA. Used for "boot to Flash" bootloader mode. */ CSM_PWL_P0 : origin = 0x3F7FF8, length = 0x000008 /* Part of FLASHA. CSM password locations in FLASHA */ IQTABLES : origin = 0x3FE000, length = 0x000B50 /* IQ Math Tables in Boot ROM */ IQTABLES2 : origin = 0x3FEB50, length = 0x00008C /* IQ Math Tables in Boot ROM */ IQTABLES3 : origin = 0x3FEBDC, length = 0x0000AA /* IQ Math Tables in Boot ROM */ ROM : origin = 0x3FF27C, length = 0x000D44 /* Boot ROM */ RESET : origin = 0x3FFFC0, length = 0x000002 /* part of boot ROM */ VECTORS : origin = 0x3FFFC2, length = 0x00003E /* part of boot ROM */ PAGE 1 : /* Data Memory */ /* Memory (RAM/FLASH/OTP) blocks can be moved to PAGE0 for program allocation */ /* Registers remain on PAGE1 */ BOOT_RSVD : origin = 0x000000, length = 0x000050 /* Part of M0, BOOT rom will use this for stack */ RAMM0 : origin = 0x000050, length = 0x0003B0 /* on-chip RAM block M0 */ RAMM1 : origin = 0x000400, length = 0x000400 /* on-chip RAM block M1 */ RAML2 : origin = 0x008C00, length = 0x000400 /* on-chip RAM block L2 */ RAML3 : origin = 0x009000, length = 0x001000 /* on-chip RAM block L3 */ FLASHB : origin = 0x3F4000, length = 0x002000 /* on-chip FLASH */ } SECTIONS { /* Allocate program areas: */ .cinit : > FLASHA PAGE = 0 .pinit : > FLASHA PAGE = 0 .text : > FLASHA PAGE = 0 codestart : > BEGIN PAGE = 0 ramfuncs : LOAD = FLASHD, RUN = RAML0, LOAD_START(_RamfuncsLoadStart), LOAD_END(_RamfuncsLoadEnd), RUN_START(_RamfuncsRunStart), PAGE = 0 csmpasswds : > CSM_PWL_P0 PAGE = 0 csm_rsvd : > CSM_RSVD PAGE = 0 /* Allocate uninitalized data sections: */ .stack : > RAMM0 PAGE = 1 .ebss : > RAML2 | RAML3 PAGE = 1 .esysmem : > RAML2 | RAML3 PAGE = 1 /* Initalized sections go in Flash */ /* For SDFlash to program these, they must be allocated to page 0 */ .econst : > FLASHA PAGE = 0 .switch : > FLASHA PAGE = 0 /* Allocate IQ math areas: */ IQmath : > FLASHA PAGE = 0 /* Math Code */ IQmathTables : > IQTABLES, PAGE = 0, TYPE = NOLOAD .reset : > RESET, PAGE = 0, TYPE = DSECT vectors : > VECTORS PAGE = 0, TYPE = DSECT }
5、 设置堆栈(stack)大小。
右键工程项目名,选择properties。出现下面框图,如图,修改为0x3b0。这个设置跟CMD文件要对应上。CMD文件上.stack设为RAMMO段,其大小正好为0x3B0。请看上面代码。
这里写图片描述

6、 取消浮点运算float支持
因为28035是定点芯片,所以要取消浮点运算。
右键工程项目名,选择properties。出现下面框图,如图,选择softlib。fpu32用于支持浮点运算的芯片。
这里写图片描述

7、 设置OS_CPU_RTOSINT_Handler 中断
UCOS任务的跳转是调用了OSINTCTXSW这个宏定义。这个宏定义就是:asm(” TRAP #16”); TRAP #16定义为RTOSINT_ISR中断,在SXD28035_common->DSP2803X_DefaultIsr.c文件中可以看到interrupt void RTOSINT_ISR(void)函数是空的。关于cpu寄存器的操作是在ucos­ii­>prots­>C28x->generic­>ccs­>os_cpu_a.asm这个文件中的_OS_CPU_RTOSINT_Handler: 函数。所以在这里要把OS_CPU_RTOSINT_Handler定义为16号中断入口地址。具体做法如下:
1) 在main程序中修改RTOSINT中断的入口地址,如下,取消第一条指令,加上第二条指令。
// PieVectTable.RTOSINT = &RTOSINT_ISR; // test
PieVectTable.OS_CPU_RTOSINT = &OS_CPU_RTOSINT_Handler; // RTOS
2) 修改中断向量表。路径为SXD28035_headers->include->DSP2803x_PieVect.h。修改后如下所示:
这里写图片描述

3) 中断向量表初始化也要修改。路径为SXD28035_headers->include->DSP2803x_PieVect.c。修改后如下所示:
注意:在DSP2803x_PieVect.c 文件中添加os_cpu.h 头文件,否则编译器会找不到OS_CPU_RTOSINT_Handler 。 #include “os_cpu.h”
这里写图片描述

4)取消interrupt void RTOSINT_ISR(void)中断。路径为SXD28035_common->DSP2803X_DefaultIsr.c。修改后如下所示:
这里写图片描述

5) 好了,来看看OS_CPU_RTOSINT_Handler的定义长什么样子。路径为ucos­ii­>prots­>C28x->generic­>ccs­>os_cpu_a.asm
这里写图片描述

8、 裁剪代码
为什么要裁剪代码呢?一方面是因为UCOS代码比较臃肿;另一方面是不想去修改CMD文件。在这里直接把暂时没用到的代码取消就可以了。
1)uC_LIB文件夹中的lib_ascii.c, lib_math.c, lib_mem.c, lib_str.c。这四个文件取消编译。
这里写图片描述

2)裁剪UCOS II内核代码。路径为CONFIG->os_cfg.h。把暂时不用的功能注消。如下图
这里写图片描述

最后编译0错误,0警告,下载,运行正常。大功告成!