STM32迷你板UCOSII系统移植
写在前面:
所需要下载的UCOSII代码链接:https://pan.baidu.com/s/1D_IyXhODEa5oVUdDv-GJrQ 提取码:mte3
1.UCOSII简介
UCOSII 是一个可以基于ROM 运行的、可裁减的、抢占式、实时多任务内核,具有高度可
移植性,特别适合于微处理器和控制器,是和很多商业操作系统性能相当的实时操作系统
(RTOS)。
UCOSII 是专门为计算机的嵌入式应用设计的, 绝大部分代码是用C 语言编写的。CPU 硬
件相关部分是用汇编语言编写的、总量约200 行的汇编语言部分被压缩到最低限度,为的是便
于移植到任何一种其它的CPU 上。用户只要有标准的ANSI 的C 交叉编译器,有汇编器、连
接器等软件工具,就可以将UCOSII 嵌人到开发的产品中。UCOSII 具有执行效率高、占用空间
小、实时性能优良和可扩展性强等特点, 最小内核可编译至 2KB 。UCOSII 已经移植到了几
乎所有知名的CPU 上。
UCOSII体系结构如图所示
从上图可以看出,UCOSII 的移植,我们只需要修改:os_cpu.h、os_cpu_a.asm 和os_cpu.c
等三个文件,其中:os_cpu.h,进行数据类型的定义,以及处理器相关代码和几个函数原
型;os_cpu_a.asm,是移植过程中需要汇编完成的一些函数,主要就是任务切换函数;os_cpu.c,定义一些用户HOOK函数。
2.UCOSII移植具体步骤
新建基础工程,这里以跑马灯工程为例。
UCOSII移植具体步骤
1.在基础工程下建立相应的文件夹:CONFIG,CORE,PORT
新建UCOSII文件夹
UCOSII文件夹下新建三个文件夹
2.向core文件夹添加UCOSII的源码
在这个路径下,找到UCOSII的源码,也就是正点原子所带的软件资料:
6,软件资料\2,UCOS学习资料\UCOSII资料\UCOS II源码\Micrium\Software\uCOS-II\Source
将上面所有文件复制到CORE文件夹下,复制完的CORE文件夹下内容:
3.向CONFIG文件添加内容
此处的内容需要在该路径下找到,同样为正点原子所带的资料
4,程序源码\3,扩展例程\4,UCOS扩展例程\例1-1 UCOSII移植\UCOSII\CONFIG
添加好文件的CONFIG文件夹
4.向PORT文件夹下添加文件
文件路径:
4,程序源码\3,扩展例程\4,UCOS扩展例程\例1-1 UCOSII移植\UCOSII\PORT
添加完的PORT文件夹
5.将上述三个文件添加到工程中
打开工程
点击Manage Project Items,新建三个分组:UCOSII-CORE,UCOSII-CONFIG,UCOSII-PORT
向分组中添加文件
UCOSII-CORE分组添加如下,但是需要删除掉ucos_ii.c文件,否则会报错。
删除掉ucos_ii.c文件之后
UCOSII-PORT分组添加如下
只需要添加如下三个文件os_cpu.h和os_cpu_a.asm和os_cpu_c.c
UCOSII-CONFIG分组添加如下
添加includes.h和os_cfg.h文件
添加完成后会发现工程目录下多了三个文件夹
但是我们发现UCOSII-CORE文件下都是加锁的
解决办法是:找到CORE文件夹,更改文件夹属性:去掉只读
回过头来查看发现文件枷锁已经去掉
下面需要将路径包含进来
点击魔法棒,选择C/C++,找到include paths
点击后面的添加按钮
添加进来CORE,PORT,CONFIG文件
下面进行编译,发现报错
..\UCOSII\CORE\ucos_ii.h(44): error: #5: cannot open source input file "app_cfg.h":
No such file or directory
双击该错误,进入到ucos_ii.h文件,找到该头文件,由于我们不需要该头文件,注释掉即可
然后再次编译
又发现另一个错误:PendSV_Handler 函数被重定义
..\OBJ\LED.axf: Error: L6200E: Symbol PendSV_Handler multiply defined (by os_cpu_a.o and stm32f10x_it.o).
解决办法:删除其中一个定义,由于汇编语言执行起来更快,所以删除stm32f10x_it.c文件下PendSV_Handler 函数的定义,这里采用注释掉的方法
先找到重定义的地方
os_cpu_a.asm这是一个汇编文件
在stm32f10x_it.c文件下
解决:注释掉
再次编译,发现没有错误
6.修改sys.h文件
打开该文件:位于SYSTEM文件夹下,找到sys.c文件
右击sys.h选择Open document sys.h
将SYSTEM_SUPPORT_OS 改为1,表示支持UCOS系统
修改前:
修改后:
再次编译,有报错:还是重定义的问题,同样的,这里我们还是注释掉stm32f10x_it.c文件下SysTick_Handler 函数
..\OBJ\LED.axf: Error: L6200E: Symbol SysTick_Handler multiply defined (by delay.o and stm32f10x_it.o).
再次编译发现没有错误
这里需要注意的是os_cpu_c.h文件夹下的OSTaskStkInit函数,当移植时发现和下面的不一致时,需要替换成下面的OSTaskStkInit函数。
/*
*********************************************************************************************************
* INITIALIZE A TASK'S STACK
*
* Description: This function is called by either OSTaskCreate() or OSTaskCreateExt() to initialize the
* stack frame of the task being created. This function is highly processor specific.
*
* Arguments : task is a pointer to the task code
*
* p_arg is a pointer to a user supplied data area that will be passed to the task
* when the task first executes.
*
* ptos is a pointer to the top of stack. It is assumed that 'ptos' points to
* a 'free' entry on the task stack. If OS_STK_GROWTH is set to 1 then
* 'ptos' will contain the HIGHEST valid address of the stack. Similarly, if
* OS_STK_GROWTH is set to 0, the 'ptos' will contains the LOWEST valid address
* of the stack.
*
* opt specifies options that can be used to alter the behavior of OSTaskStkInit().
* (see uCOS_II.H for OS_TASK_OPT_xxx).
*
* Returns : Always returns the location of the new top-of-stack once the processor registers have
* been placed on the stack in the proper order.
*
* Note(s) : 1) Interrupts are enabled when your task starts executing.
* 2) All tasks run in Thread mode, using process stack.
*********************************************************************************************************
*/
OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt)
{
OS_STK *stk;
(void)opt; /* 'opt' is not used, prevent warning */
stk = ptos; /* Load stack pointer */
/* Registers stacked as if auto-saved on exception */
*(stk) = (INT32U)0x01000000L; /* xPSR */
*(--stk) = (INT32U)task; /* Entry Point */
*(--stk) = (INT32U)0xFFFFFFFEL; /* R14 (LR) (init value will cause fault if ever used)*/
*(--stk) = (INT32U)0x12121212L; /* R12 */
*(--stk) = (INT32U)0x03030303L; /* R3 */
*(--stk) = (INT32U)0x02020202L; /* R2 */
*(--stk) = (INT32U)0x01010101L; /* R1 */
*(--stk) = (INT32U)p_arg; /* R0 : argument */
/* Remaining registers saved on process stack */
*(--stk) = (INT32U)0x11111111L; /* R11 */
*(--stk) = (INT32U)0x10101010L; /* R10 */
*(--stk) = (INT32U)0x09090909L; /* R9 */
*(--stk) = (INT32U)0x08080808L; /* R8 */
*(--stk) = (INT32U)0x07070707L; /* R7 */
*(--stk) = (INT32U)0x06060606L; /* R6 */
*(--stk) = (INT32U)0x05050505L; /* R5 */
*(--stk) = (INT32U)0x04040404L; /* R4 */
return (stk);
}
3.移植后的测试
下面进行UCOSII操作系统的测试
将下面代码复制到main.c中,替换掉原来的代码
#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "includes.h"
//START 任务
//设置任务优先级
#define START_TASK_PRIO 10 ///开始任务的优先级为最低
//设置任务堆栈大小
#define START_STK_SIZE 128
//任务任务堆栈
OS_STK START_TASK_STK[START_STK_SIZE];
//任务函数
void start_task(void *pdata);
//LED0任务
//设置任务优先级
#define LED0_TASK_PRIO 7
//设置任务堆栈大小
#define LED0_STK_SIZE 64
//任务堆栈
OS_STK LED0_TASK_STK[LED0_STK_SIZE];
//任务函数
void led0_task(void *pdata);
//LED1任务
//设置任务优先级
#define LED1_TASK_PRIO 6
//设置任务堆栈大小
#define LED1_STK_SIZE 64
//任务堆栈
OS_STK LED1_TASK_STK[LED1_STK_SIZE];
//任务函数
void led1_task(void *pdata);
//浮点测试任务
#define FLOAT_TASK_PRIO 5
//设置任务堆栈大小
#define FLOAT_STK_SIZE 128
//任务堆栈
//如果任务中使用printf来打印浮点数据的话一点要8字节对齐
__align(8) OS_STK FLOAT_TASK_STK[FLOAT_STK_SIZE];
//任务函数
void float_task(void *pdata);
int main(void)
{
delay_init(); //延时初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组配置
uart_init(115200); //串口波特率设置
LED_Init(); //LED初始化
OSInit(); //UCOS初始化
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_SR cpu_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任务
OSTaskCreate(float_task,(void*)0,(OS_STK*)&FLOAT_TASK_STK[FLOAT_STK_SIZE-1],FLOAT_TASK_PRIO);//创建浮点测试任务
OSTaskSuspend(START_TASK_PRIO);//挂起开始任务
OS_EXIT_CRITICAL(); //退出临界区(开中断)
}
//LED0任务
void led0_task(void *pdata)
{
while(1)
{
LED0=0;
delay_ms(80);
LED0=1;
delay_ms(100);
}
}
//LED1任务
void led1_task(void *pdata)
{
while(1)
{
LED1=0;
delay_ms(300);
LED1=1;
delay_ms(300);
}
}
//浮点测试任务
void float_task(void *pdata)
{
OS_CPU_SR cpu_sr=0;
static float float_num=0.01;
while(1)
{
float_num+=0.01f;
OS_ENTER_CRITICAL(); //进入临界区(关闭中断)
printf("float_num的值为: %.4f\r\n",float_num); //串口打印结果
OS_EXIT_CRITICAL(); //退出临界区(开中断)
delay_ms(500);
}
}
编译之后没有错误,下载到开发板上
开发板出现led灯的交替闪烁,
再进行串口测试,也就是浮点测试:每个500ms进行+0.01的操作
至此,UCOSII操作系统移植成功。
4.总结
1) 移植UCOSII
要想UCOSII在STM32正常运行,首先是需要移植UCOSII,正点原子提供的SYSTEM文件夹里面的系统函数直接支持UCOSII,只需要在sys.h文件里面将:SYSTEM_SUPPORT_UCOS宏定义改为1,即可通过delay_init函数初始化UCOSII的系统时钟节拍,为UCOSII提供时钟节拍。
2) 编写任务函数并设置其堆栈大小和优先级等参数。
编写任务函数,以便UCOSII调用。
设置函数堆栈大小,需要根据函数的需求来设置,如果任务函数的局部变量多,嵌套层数多,那么相应的堆栈就得大一些,如果堆栈设置小了,很可能出现的结果就是CPU进入HardFault,遇到这种情况,就必须把堆栈设置大一点了。另外,有些地方还需要注意堆栈字节对齐的问题,如果任务运行出现莫名其妙的错误(比如用到sprintf出错),请考虑是不是字节对齐的问题。
设置任务优先级,这个需要根据任务的重要性和实时性设置,记住高优先级的任务有优先使用CPU的权利。
3) 初始化UCOSII,并在UCOSII中创建任务
调用OSInit
初始化UCOSII,通过调用OSTaskCreate
函数创建任务。
4) 启动UCOSII
调用OSStart
启动UCOSII。
通过以上4个步骤,UCOSII就开始在STM32上面运行。
上一篇: 字符串匹配
下一篇: php查找字符串出现次数的方法