欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STM32迷你板UCOSII系统移植

程序员文章站 2022-03-16 18:21:35
...

写在前面:
所需要下载的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体系结构如图所示
STM32迷你板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文件夹
STM32迷你板UCOSII系统移植
UCOSII文件夹下新建三个文件夹
STM32迷你板UCOSII系统移植

2.向core文件夹添加UCOSII的源码
在这个路径下,找到UCOSII的源码,也就是正点原子所带的软件资料:

6,软件资料\2,UCOS学习资料\UCOSII资料\UCOS II源码\Micrium\Software\uCOS-II\Source

STM32迷你板UCOSII系统移植
将上面所有文件复制到CORE文件夹下,复制完的CORE文件夹下内容:
STM32迷你板UCOSII系统移植

3.向CONFIG文件添加内容
此处的内容需要在该路径下找到,同样为正点原子所带的资料

4,程序源码\3,扩展例程\4,UCOS扩展例程\例1-1 UCOSII移植\UCOSII\CONFIG

添加好文件的CONFIG文件夹
STM32迷你板UCOSII系统移植
4.向PORT文件夹下添加文件
文件路径:

4,程序源码\3,扩展例程\4,UCOS扩展例程\例1-1 UCOSII移植\UCOSII\PORT

添加完的PORT文件夹
STM32迷你板UCOSII系统移植

5.将上述三个文件添加到工程中
打开工程
点击Manage Project Items,新建三个分组:UCOSII-CORE,UCOSII-CONFIG,UCOSII-PORT
STM32迷你板UCOSII系统移植
STM32迷你板UCOSII系统移植
向分组中添加文件
UCOSII-CORE分组添加如下,但是需要删除掉ucos_ii.c文件,否则会报错。
STM32迷你板UCOSII系统移植
删除掉ucos_ii.c文件之后
STM32迷你板UCOSII系统移植
UCOSII-PORT分组添加如下
只需要添加如下三个文件os_cpu.h和os_cpu_a.asm和os_cpu_c.c
STM32迷你板UCOSII系统移植
UCOSII-CONFIG分组添加如下
添加includes.h和os_cfg.h文件
STM32迷你板UCOSII系统移植

添加完成后会发现工程目录下多了三个文件夹
STM32迷你板UCOSII系统移植
但是我们发现UCOSII-CORE文件下都是加锁的
STM32迷你板UCOSII系统移植

解决办法是:找到CORE文件夹,更改文件夹属性:去掉只读
STM32迷你板UCOSII系统移植
回过头来查看发现文件枷锁已经去掉
STM32迷你板UCOSII系统移植

下面需要将路径包含进来
点击魔法棒,选择C/C++,找到include paths
STM32迷你板UCOSII系统移植

点击后面的添加按钮
STM32迷你板UCOSII系统移植
添加进来CORE,PORT,CONFIG文件
STM32迷你板UCOSII系统移植

下面进行编译,发现报错

..\UCOSII\CORE\ucos_ii.h(44): error:  #5: cannot open source input file "app_cfg.h": 
No such file or directory

STM32迷你板UCOSII系统移植
双击该错误,进入到ucos_ii.h文件,找到该头文件,由于我们不需要该头文件,注释掉即可
STM32迷你板UCOSII系统移植
STM32迷你板UCOSII系统移植
然后再次编译
又发现另一个错误:PendSV_Handler 函数被重定义

..\OBJ\LED.axf: Error: L6200E: Symbol PendSV_Handler multiply defined (by os_cpu_a.o and stm32f10x_it.o).

STM32迷你板UCOSII系统移植
解决办法:删除其中一个定义,由于汇编语言执行起来更快,所以删除stm32f10x_it.c文件下PendSV_Handler 函数的定义,这里采用注释掉的方法

先找到重定义的地方
os_cpu_a.asm这是一个汇编文件
STM32迷你板UCOSII系统移植

在stm32f10x_it.c文件下
STM32迷你板UCOSII系统移植

解决:注释掉
STM32迷你板UCOSII系统移植
再次编译,发现没有错误
STM32迷你板UCOSII系统移植

6.修改sys.h文件

打开该文件:位于SYSTEM文件夹下,找到sys.c文件
STM32迷你板UCOSII系统移植

右击sys.h选择Open document sys.h
STM32迷你板UCOSII系统移植

将SYSTEM_SUPPORT_OS 改为1,表示支持UCOS系统
修改前:
STM32迷你板UCOSII系统移植

修改后:

STM32迷你板UCOSII系统移植
再次编译,有报错:还是重定义的问题,同样的,这里我们还是注释掉stm32f10x_it.c文件下SysTick_Handler 函数

..\OBJ\LED.axf: Error: L6200E: Symbol SysTick_Handler multiply defined (by delay.o and stm32f10x_it.o).

STM32迷你板UCOSII系统移植
再次编译发现没有错误
这里需要注意的是os_cpu_c.h文件夹下的OSTaskStkInit函数,当移植时发现和下面的不一致时,需要替换成下面的OSTaskStkInit函数。
STM32迷你板UCOSII系统移植

/*
*********************************************************************************************************
*                                        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);
	}
}


编译之后没有错误,下载到开发板上
STM32迷你板UCOSII系统移植
开发板出现led灯的交替闪烁,
再进行串口测试,也就是浮点测试:每个500ms进行+0.01的操作
STM32迷你板UCOSII系统移植

至此,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上面运行。

相关标签: 嵌入式系统