**广西河池学院
广西高校重点实验室培训基地
系统控制与信息处理重点实验室
本篇文章来自河池学院:OpenWRT无线路由组
写作时间:2021年07月22日**
uC/OS-III移植到STM32F103
4.4新建一个工程文件夹
本教程以uC/OS-III移植到正点原子STM31F103为例,首先将一个STM32的例程文件夹重命名为“STM32_UCOSIII_LED”,先以最基础的点亮LED例程来验证。 图1-1新建工程文件
4.2 向工程中添加相应的文件
(1)下载Micrium官方移植源码,http://micrium.com/ 百度网盘文件可直接下载: 链接:https://pan.baidu.com/s/1oMy2YT2G6PjGYHhkl1Po_g 提取码:dg05 下载好以后我们打开这个“Micrium官方移植”->Sofeware的文件夹,可以看到这里只有四个子文件夹,选择“uC-xxx”的三个文件夹copy到我们所建立的工程文件夹“STM32_UCOSIII_LED”中。 (2)新建“APP”、“BSP”两个空文件夹。 图1-2.1复制UCOSIII相关文件到工程中 (3)向BSP添加文件 复制官方移植好的工程中的相关文件到BSP中,路径: Micrium官方移植\Software\EvalBoards\Micrium\uC-Eval-STM32F107\BSP
图1-2.2向BSP添加文件 (4)向APP添加文件 复制官方移植好的工程中的相关文件到BSP中,路径: Micrium官方移植\Software\EvalBoards\Micrium\uC-Eval-STM32F107\uCOS-III 图1-2.3向APP添加文件
4.3向工程中添加分组以及相应的头文件路径
(1)向工程中添加分组以及文件 打开KEIL工程,新建如下图的分组。
图1-3.1工程添加分组 工程中分组建立完成后,向新建的各个分组添加相应的文件,要选择“All files”全部添加。
图1-3.2向工程中添加分组以及文件 1、APP分组的路径: STM32_UCOSIII_LED\APP; 2、BSP分组的路径: STM32_UCOSIII_LED\BSP; 3、UCOSIII-LIB分组的路径: STM32_UCOSIII_LED\uC-LIB(包括Port文件夹的 STM32_UCOSIII_LED\uC-LIB\Ports\ARM-Cortex-M3\RealView); 3、UCOSIII-CPU分组的路径: STM32_UCOSIII_LED\uC-CPU(包括ARM-Cortex-M3文件夹的 STM32_UCOSIII_LED\uC-CPU\ARM-Cortex-M3\RealView); 4、UCOSIII-Source分组的路径: STM32_UCOSIII_LED\uCOS-III\Source 6、UCOSIII_Port分组的路径: STM32_UCOSIII_LED\uCOS-III\Ports\ARM-Cortex-M3\Generic\RealView (2)添加相应的头文件路径
图1-3.3添加相应的头文件路径
4.4具体的工程文件修改
4.4.1删除main.c文件 因为APP文件夹中的app.c文件包含main函数,所以打开USR文件夹,删除main.c文件。
4.4.2修改bsp.c和bsp.h文件 bsp.h以及includes.h文件中,源码中使用的头文件是#include <stm32f10x_lib.h>,将其改成我们使用的库函数头文件#include <stm32f10x.h>。在bsp.h文件中添加我们要使用的板级驱动头文件,如#include <led.h>
将bsp.c中的BSP_Init()函数的内容删掉(因为我并没有使用BSP部分函数),在里面添加自己写的外设初始化函数,如LED_Init(),将该文件中没有用到的函数都删掉。
添加时钟周期数转换为us所需要的代码:
(1)#define BSP_REG_DEM_CR (*(CPU_REG32 *)0xE000EDFC) //DEMCR寄存器
#define BSP_REG_DWT_CR (*(CPU_REG32 *)0xE0001000) //DWT控制寄存器
#define BSP_REG_DWT_CYCCNT (*(CPU_REG32 *)0xE0001004) //DWT时钟计数寄存器
#define BSP_REG_DBGMCU_CR (*(CPU_REG32 *)0xE0042004)
//DEMCR寄存器的第24位,如果要使用DWT ETM ITM和TPIU的话DEMCR寄存器的第24位置1
#define BSP_BIT_DEM_CR_TRCENA DEF_BIT_24
//DWTCR寄存器的第0位,当为1的时候使能CYCCNT计数器,使用CYCCNT之前应当先初始化
#define BSP_BIT_DWT_CR_CYCCNTENA DEF_BIT_00
(2)//CPU_TS32_to_uSec()和CPU_TS64_to_uSec()是将读取到时钟周期数,转换为us
#if (CPU_CFG_TS_32_EN == DEF_ENABLED)
CPU_INT64U CPU_TS32_to_uSec (CPU_TS32 ts_cnts)
{
CPU_INT64U ts_us;
CPU_INT64U fclk_freq;
fclk_freq = BSP_CPU_ClkFreq();
ts_us = ts_cnts / (fclk_freq / DEF_TIME_NBR_uS_PER_SEC);
return (ts_us);
}
#endif
#if (CPU_CFG_TS_64_EN == DEF_ENABLED)
CPU_INT64U CPU_TS64_to_uSec (CPU_TS64 ts_cnts)
{
CPU_INT64U ts_us;
CPU_INT64U fclk_freq;
fclk_freq = BSP_CPU_ClkFreq();
ts_us = ts_cnts / (fclk_freq / DEF_TIME_NBR_uS_PER_SEC);
return (ts_us);
}
#endif
4.4.3、修改os_cpu_a.asm文件(UCOSIII-Port分组) SysTick_Handler和PendSV_Handler两个函数: SysTick_Handler是滴答定时器中断,这个中断相当于操作系统的心脏,在它的中断服务函数中,提供进程/任务的上下文切换和任务调度的工作; PendSV_Handler函数作用是当操作系统(OS)检测到某IRQ正在活动,并且被SysTick抢占,它将触发一个PendSV异常,以便缓期执行上下文切换。 打开os_cpu_a.s文件,将OS_CPU_PendSVHandler修改为PendSV_Handler
添加任务切换器,任务级切换以及中断级切换的代码 代码如下:
(1)
;任务级切换,从任务A切换到任务B
OSCtxSw
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
(2)
;中断级切换,从中断退出时切换到一个任务时,从中断切换到任务时,CPU的寄存器入栈工作已完成,无需做第二次
OSIntCtxSw
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
修改系统优先级寄存器4
打开stm32f10x_it.c文件(USER分组中),注释掉PendSV_Handler()以及SysTick_Handler()函数;
4.4.4修改os_cpu_c.c文件(UCOSIII-Port分组) 在os_cpu_c.c文件中,添加#include <includes.h>头文件
4.4.5修改sys.h文件(SYSTEM分组) 定义系统文件支持UCOS,将0改为1;
4.4.6 修改os_cfg_app.h文件(UCOSIII-Source) 根据自己需要修改系统任务的优先级,一般情况下,系统任务的优先级(由高到低): 中断服务管理任务–>时钟节拍任务–>定时任务->统计任务–>空闲任务
4.5测试验证移植
APP分组中的main函数全部删除替换,使用LED以及串口测试移植是否成功,代码如下:
#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "includes.h"
/************************************************
ALIENTEK战舰STM32开发板UCOS实验
技术支持:www.openedv.com
淘宝店铺:http://eboard.taobao.com
关注微信公众平台微信号:"正点原子",免费获取STM32资料。
广州市星翼电子科技有限公司
作者:正点原子 @ALIENTEK
************************************************/
//UCOSIII中以下优先级用户程序不能使用,ALIENTEK
//将这些优先级分配给了UCOSIII的5个系统内部任务
//优先级0:中断服务服务管理任务 OS_IntQTask()
//优先级1:时钟节拍任务 OS_TickTask()
//优先级2:定时任务 OS_TmrTask()
//优先级OS_CFG_PRIO_MAX-2(倒数第二):统计任务 OS_StatTask()
//优先级OS_CFG_PRIO_MAX-1(最后一个):空闲任务 OS_IdleTask()
//技术支持:www.openedv.com
//淘宝店铺:http://eboard.taobao.com
//广州市星翼电子科技有限公司
//作者:正点原子 @ALIENTEK
//任务优先级
#define START_TASK_PRIO 3
//任务堆栈大小
#define START_STK_SIZE 512
//任务控制块 用来保存任务的信息
OS_TCB StartTaskTCB;
//任务堆栈 用来在切换任务和调用其他函数的时候保存现场,每个任务都应该有自己的堆栈
CPU_STK START_TASK_STK[START_STK_SIZE];
//任务函数
void start_task(void *p_arg);
//任务优先级
#define LED0_TASK_PRIO 4
//任务堆栈大小
#define LED0_STK_SIZE 128
//任务控制块
OS_TCB Led0TaskTCB;
//任务堆栈
CPU_STK LED0_TASK_STK[LED0_STK_SIZE];
void led0_task(void *p_arg);
//任务优先级
#define LED1_TASK_PRIO 5
//任务堆栈大小
#define LED1_STK_SIZE 128
//任务控制块
OS_TCB Led1TaskTCB;
//任务堆栈
CPU_STK LED1_TASK_STK[LED1_STK_SIZE];
//任务函数
void led1_task(void *p_arg);
//任务优先级
#define FLOAT_TASK_PRIO 6
//任务堆栈大小
#define FLOAT_STK_SIZE 128
//任务控制块
OS_TCB FloatTaskTCB;
//任务堆栈
__align(8) CPU_STK FLOAT_TASK_STK[FLOAT_STK_SIZE];
//任务函数
void float_task(void *p_arg);
int main(void)
{
OS_ERR err;
CPU_SR_ALLOC();
delay_init(); //延时初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组配置
uart_init(115200); //串口波特率设置
LED_Init(); //LED初始化
OSInit(&err); //*初始化UCOSIII
OS_CRITICAL_ENTER();//*进入临界区
//*创建开始任务,使用OSTaskCreate函数创建任务必须让OS_CRITICAL_ENTER()进入临界区,任务创建完后OS_CRITICAL_ENTER()退出临界区
OSTaskCreate((OS_TCB * )&StartTaskTCB, //任务控制块
(CPU_CHAR * )"start task", //任务名字
(OS_TASK_PTR )start_task, //任务函数
(void * )0, //传递给任务函数的参数
(OS_PRIO )START_TASK_PRIO, //任务优先级
(CPU_STK * )&START_TASK_STK[0], //任务堆栈基地址
(CPU_STK_SIZE)START_STK_SIZE/10, //任务堆栈深度限位,,通常为堆栈大小的1/10,用来检测堆栈是否为空
(CPU_STK_SIZE)START_STK_SIZE, //任务堆栈大小
(OS_MSG_QTY )0, //任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
(OS_TICK )0, //当使能时间片轮转时的时间片长度,为0时为默认长度,
(void * )0, //用户补充的存储区
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项
(OS_ERR * )&err); //存放该函数错误时的返回值
OS_CRITICAL_EXIT(); //*退出临界区
OSStart(&err); //*开启UCOSIII,使用OSStart()之前一定要至少创建一个任务,在调用OSSInit()函数初始化时已经创建一个空闲任务了
while(1);
}
//开始任务函数
void start_task(void *p_arg)
{
OS_ERR err;
CPU_SR_ALLOC();
p_arg = p_arg;
CPU_Init();
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err); //统计任务
#endif
#ifdef CPU_CFG_INT_DIS_MEAS_EN //如果使能了测量中断关闭时间
CPU_IntDisMeasMaxCurReset();
#endif
#if OS_CFG_SCHED_ROUND_ROBIN_EN //当使用时间片轮转的时候
//使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms
OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);
#endif
OS_CRITICAL_ENTER(); //进入临界区
//创建LED0任务
OSTaskCreate((OS_TCB * )&Led0TaskTCB,
(CPU_CHAR * )"led0 task",
(OS_TASK_PTR )led0_task,
(void * )0,
(OS_PRIO )LED0_TASK_PRIO,
(CPU_STK * )&LED0_TASK_STK[0],
(CPU_STK_SIZE)LED0_STK_SIZE/10,
(CPU_STK_SIZE)LED0_STK_SIZE,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void * )0,
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR * )&err);
//创建LED1任务
OSTaskCreate((OS_TCB * )&Led1TaskTCB,
(CPU_CHAR * )"led1 task",
(OS_TASK_PTR )led1_task,
(void * )0,
(OS_PRIO )LED1_TASK_PRIO,
(CPU_STK * )&LED1_TASK_STK[0],
(CPU_STK_SIZE)LED1_STK_SIZE/10,
(CPU_STK_SIZE)LED1_STK_SIZE,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void * )0,
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR * )&err);
//创建浮点测试任务
OSTaskCreate((OS_TCB * )&FloatTaskTCB,
(CPU_CHAR * )"float test task",
(OS_TASK_PTR )float_task,
(void * )0,
(OS_PRIO )FLOAT_TASK_PRIO,
(CPU_STK * )&FLOAT_TASK_STK[0],
(CPU_STK_SIZE)FLOAT_STK_SIZE/10,
(CPU_STK_SIZE)FLOAT_STK_SIZE,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void * )0,
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR * )&err);
OS_TaskSuspend((OS_TCB*)&StartTaskTCB,&err); //挂起开始任务
OS_CRITICAL_EXIT(); //进入临界区
}
//led0任务函数
void led0_task(void *p_arg)
{
OS_ERR err;
p_arg = p_arg;
while(1)
{
LED0=0;
OSTimeDlyHMSM(0,0,0,200,OS_OPT_TIME_HMSM_STRICT,&err); //延时200ms
LED0=1;
OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_HMSM_STRICT,&err); //延时500ms
}
}
//led1任务函数
void led1_task(void *p_arg)
{
OS_ERR err;
p_arg = p_arg;
while(1)
{
LED1=~LED1;
OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_HMSM_STRICT,&err); //延时500ms
}
}
//浮点测试任务
void float_task(void *p_arg)
{
CPU_SR_ALLOC();
static float float_num=0.01;
while(1)
{
float_num+=0.01f;
OS_CRITICAL_ENTER(); //进入临界区
printf("float_num的值为: %.4f\r\n",float_num);
OS_CRITICAL_EXIT(); //退出临界区
delay_ms(500); //延时500ms
}
}
效果图:LED灯交替闪烁,串口显示数字
附页 参考文献: [1]正点原子 STM32F1 UCOS开发手册_V2.0 [2]野火EmbedFire [野火]uCOS-III内核实现与应用开发实战指南——基于STM32 20210122
|