前言
本教程是基于STM32F103RBT6基础工程进行移植,同系列的stm32同样适用,只不过基础工程要另外准备,其中本教程使用到的基础工程里面有个SYSTEM的文件夹(如果使用其他工程建议可以把该文件夹复制过去使用),里面的函数是由正点原子团队提供,这里也是参考原子出品的“STM32F1UCOS开发手册”,想了解更加具体的教程可以自己去网上搜一下。这是stm32学习论坛,里面有丰富的学习资源。 本教程使用到的文件:工程文件与word; 访问密码:912b(如果你准备按照该教程进行移植,开始之前请先下载该文件,链接失效可以评论回复,看到后会处理)注意:请使用MDK5打开工程! 1、准备工作 (1)stm32F103RBT6基础工程:这里以跑马灯为例 (2)UCOSII源码:官网下载 (3)准备文件:PORT和CONFIG 2、移植 (1)在基础工程中建立新文件夹用来存放源码:
(2)向三个文件夹添加UCOSII源码,首先向CORE文件夹添加(UCOSII源码):至于为什么不要那两个文件,后续再谈,先照着做。 然后向CONFIG文件夹添加文件:将“准备工作”里面“必需文件”下的CONFIG全部复制到这里来: 其中includes.h里面定义了一些头文件,如图,:

os_cfg.h主要用来配置和裁剪UCOSII的。 最后是向PORT文件夹添加文件,如上所述,直接将“必需文件”下的PORT直接复制过来: 这里的文件是关键,移植需要修改几乎都在这里,因为这里PORT下的文件已经是修改好的,下一步添加到工程时不用再修改就能在stm32上跑,这里后续再讲,先移植好一个成功的工程先。
(3)配置工程
a 我们打开基础工程,将UCOSII添加到工程里面:


 添加成功后:  别忘了添加路径: b 编译工程,处理错误 上面步骤成功之后,现在可以编译工程了, 提示打不开“app_cfg.h”文件,跟踪错误: 再重新编译一下, 发现错误,重复定义了,我们stm32f10x_it.h里面的注释掉: 这时候编译,发现工程已经没有错误了,因为我们是引用了正点原子提供的system函数,所以还要按照他们的规则去修改一下一些文件。 c 修改sys.h头文件 打开sys.h:  因为 system 里面的文件适用于裸板也适用移植了操作系统的,所以宏定义要按需更改。 再编译,提示 ”TRUE” 没有定义,把 ”TRUE” 改为“ OS_TRUE ”,再编译: 同理,将stm32f10x.h里面的注释掉:

再编译,这时候发现已经没有错误了: 如果还有错误,请根据错误提示自行解决。 如此,UCOSII就已经移植完成了,下面写个个测试程序看看能不能成功即可。 d 测试是否移植成功, 程序:(将下面代码直接复制到main函数里面,原先的都删掉,编译,下载验证,如果LED0和LED1以不同的频率闪烁说明移植成功) #include"led.h" #include"delay.h" #include"sys.h" #include"usart.h" #include"includes.h" //开始任务 #defineSTART_TASK_PRIO 10 #defineSTART_STK_SIZE 128 OS_STKSTART_TASK_STK[START_STK_SIZE]; void start_task(void*pdata); //LED0任务 #defineLED0_TASK_PRIO 7 #defineLED0_STK_SIZE 64 OS_STKLED0_TASK_STK[LED0_STK_SIZE]; void led0_task(void*pdata); //LED1任务 #defineLED1_TASK_PRIO 6 #defineLED1_STK_SIZE 64 OS_STKLED1_TASK_STK[LED1_STK_SIZE]; void led1_task(void*pdata); intmain(void) { delay_init(); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); uart_init(115200); LED_Init(); OSInit(); //UCOSII初始化 OSTaskCreate(start_task,(void*)0,(OS_STK*)&START_TASK_STK[START_STK_SIZE-1],START_TASK_PRIO);//创建开始任务 OSStart(); //系统启动 } void start_task(void*pdata) { OS_CPU_SRcpu_sr=0; pdata=pdata; OSStatInit(); OS_ENTER_CRITICAL(); //临界 OSTaskCreate(led0_task,(void*)0,(OS_STK*)&LED0_TASK_STK[LED0_STK_SIZE-1],LED0_TASK_PRIO);//创建LED0任务 OSTaskCreate(led1_task,(void*)0,(OS_STK*)&LED1_TASK_STK[LED1_STK_SIZE-1],LED1_TASK_PRIO);//创建LED1任务 OSTaskSuspend(START_TASK_PRIO);//开始任务挂起 OS_EXIT_CRITICAL(); } //LED0任务 void led0_task(void*pdata) { while(1) { GPIO_ResetBits(GPIOA,GPIO_Pin_8); delay_ms(80); GPIO_SetBits(GPIOA,GPIO_Pin_8); delay_ms(80); } } //LED1任务 void led1_task(void*pdata) { while(1) { GPIO_ResetBits(GPIOD,GPIO_Pin_2); delay_ms(300); GPIO_SetBits(GPIOD,GPIO_Pin_2); delay_ms(300); } }
3 移植过程讲解 (1)如果参考上面的步骤你已经完成了系统的移植的话,接下来讲解一下移植过程中主要需要修改的文件,首先是os_cpu_a.asm,前面已经说过这些需要修改的文件都是官方提供的代码,我们只需要根据自身的硬件体系结构进行修改即可: os_cpu_a.asm主要是一些汇编代码,我们在工程中打开,主要关注的是这几个函数: EXPORT的意思这些函数在本文件定义供其他文件调用。
a OSStartHighRdy函数
OSStartHighRdy是由OSStart()函数调用的,这是第一个任务,用来开启多任务,多任务开启失败的话调用OSStartHang函数。需要移植的原因是这里的寄存器都要根据STM32的结构来进行修改,这里已经是修改好了的。
bOSCtxSw函数和OSIntCtxSw函数
两个函数都是用来做任务切换,区别在于OSCtxSw是任务级,OSIntCtxSw是中断级切换。OSCtxSw函数在 OSSched函数中负责保存当前任务对应的处理器寄存器到堆栈中,并将任务中需要恢复的处理器寄存器从堆栈中恢复出来。OSIntCtxSw函数主要保存当前任务堆栈指针,并将新任务对应的处理器寄存器从堆栈中恢复出来。
 这里也是已经移植好了的。其他的都可以暂时先不用理会。
(2)移植os_cpu.h
os_cpu.h主要定义了一些数据类型,μCOS-II为了保证可移植性,程序中没有直接使用 int,unsignedint 等定义,而是自己定义了一套数据类型,如 INT16U 表示 16 位无符号整型,对于 STM32 这样的 32 位内核,INT16U 是unsigned short型,如果是16位的处理器,则是unsignedint型。如图示:
这里尤其注意OS_STK类型,这里是32位的。
接下来是定义栈的增长方向,CM3栈是由高地址向低地址增长的,
定义 OS_TASK_SW 宏。OS_TASK_SW 宏是 uC/OS-II 从低优先级任务切换到高优先级任务时的调用,可以采用下面两种方式定义:如果处理器支持软中断,可以使用软中断将中断向量指向OSCtxSw 函数;
不同的硬件的位数不一样,栈的增长方向也不一样,自然都要修改。
(3)移植os_cpu_c.c文件
os_cpu_c.c 主要定义了系统需要的钩子函数,其中必须需要移植的是OSTaskStkInit函数,这个函数在任务创建时被调用,它负责初始化任务的堆栈结构,其他的钩子函数可以暂时不用理会,有兴趣的可以自行了解。
堆栈初始化函数OSTaskStkInit是由任务创建函数OSTaskCreate()和OSTaskCreateExt()这两个函数调用,用于在创建任务的时候初始堆栈,从上面的代码中可以看出就是在任务堆栈中保存寄存器的值。
这里要注意一下入栈顺序,要根据CM3的体系,其中CM3硬件会自动将FPSCR、XPSR、PC、LR、R12、R0-R3入栈,入栈顺序遵照Stackframe,剩下的R4-R11需要手动入栈。
移植UCOSII到stm32三个文件改动基本不大,主要是OS_CPU_A.ASM需要改动,其他的都是默认和CM3的体系相近,不用做出大的改动即可移植成功。
至此,UCOSII移植就已经全部完成了。
本教程主要是为了对系统移植的过程有个大概了解,其中的细节没有进行分析,因技术水平有限,有错漏之处欢迎指出或有更好的学习建议也希望可以回复交流,共同学习、进步。
谢谢!
|