μC/OS-III(Micro C OS Three)是一个可升级的、可固化的、基于优先级的实时内核,它是Micrium公司出品的RTOS(实时操作系统)类实时操作系统的一个版本。以下是对μC/OS-III的详细描述:
1. 基本特性
- 多任务管理:μC/OS-III是一个支持多任务抢占的内核,总是优先执行任务优先级高的任务。它支持时间片调度,允许系统中有多个相同任务优先级的任务,通过时间片的方式轮流调度这些任务。
- 任务数量不限:μC/OS-III理论上支持不受限制的任务数量,但实际上,系统中任务的最大数量受处理器内存空间的限制。
- 任务优先级数量不限:μC/OS-III支持的任务优先级数量不受限制,但对于大多数应用场景而言,使用32~256个任务优先级就绰绰有余了。
- 内核对象数量不限:μC/OS-III提供了多种内核对象,如任务、信号量、事件标志、消息队列、软件定时器和内存区等,用户可以在不考虑处理器内存限制的情况下无限制地创建这些内核对象。
2. 任务管理
- 任务创建、删除、挂起和恢复:μC/OS-III支持任务的创建、删除、挂起和恢复等操作,以及任务调度和切换。
- 时间片轮转调度:当多个相同优先级的任务就绪时,μC/OS-III将以时间片的方式调度这些任务,即根据用户指定的时间(时间片)轮流调度这些任务。
3. 中断处理
- 极短的中断禁用时间:μC/OS-III通过锁定任务调度器代替禁用中断来保护一些关键区域(临界区),这确保了μC/OS-III能够快速地响应中断。
4. 信号量和消息队列
- 信号量机制:μC/OS-III提供了二值信号量、计数型信号量和互斥信号量,用于任务同步和资源管理。
- 任务内嵌信号量和消息队列:μC/OS-III允许任务直接获取来自其他任务或中断的信号和消息,而不需要任何的中间内核对象,这大大提高了系统的运行效率。
5. 其他特性
- 时间戳:μC/OS-III提供了时间戳功能,用户可以方便地测量系统在运行过程中处理器处理某些事件所消耗的时间。
- 自定义钩子函数:μC/OS-III提供了一些在内核执行操作之前、之后或过程中的钩子函数,以便用户扩展μC/OS-III的功能。
- 防死锁:μC/OS-III允许任务在等待某些内核对象前,设置一个等待的最大超时时间,以有效地防止死锁的发生。
- 软件定时器:用户可以创建任意数量的“单次”和“周期”软件定时器,并且每个软件定时器都可以有独立的超时回调函数。
6.在STM32F429移植UCOSIII
6.1 移植环境
单片机:STM32F429IGT6
固件库:HAL库
编程软件:Keil 5.38
操作系统:UCOSIII 3.04
6.2 UCOSIII源码
下载好的UCOSIII 3.04 源码如下图所示,如果找不到源码下载渠道的朋友可以评论获取。
(1)EvalBoards文件夹是关于STM32F429的工程文件
(2)uC-CPU文件夹是与CPU相关的代码。
(3)uC-LIB文件夹是一些可移植的但是和编译器没有关系的函数组成的,UCOSIII用不到,但移植的时候需要复制过去。
(4)uCOS-III文件夹里面包含CPU平台的文件和UCOSIII的源码。
6.3 开始移植
(1)本次移植以跑马灯项目为基础,在项目中创建UCOSIII文件夹,然后将源码uC-CPU、uC-LIB、uCOS-III三个文件夹复制过去。
(2)在项目中的UCOSIII文件夹中创建UCOS_BSP、UCOS_CONFIG两个文件夹。
(3)将 源码目录 EvalBoards\ST\STM32F429II-SK\BSP 文件夹下面的 bsp.c、bsp.h两个文件复制到项目UCOS_BSP文件夹下。
(4)将源码目录 EvalBoards\ST\STM32F429II-SK\uCOS-III 文件夹中的下面这些文件复制到项目的UCOS_CONFIG文件夹下。
(5)在项目工程中创建以下分组:
(6)将源码文件导入到项目中。
(7)给项目添加头文件配置信息。
(8)此时编译一下有个报错,修改 bsp.h下面的 stm32f4xx_conf.h 为 stm32f4xx_hal.h ,再编译一下报错更多了,还是因为bsp文件的问题,下面对其进行修改。
修改过程比较复杂,改完后代码如下:
bsp.c
#define BSP_MODULE
#include <bsp.h>
/*
*********************************************************************************************************
* REGISTERS
*********************************************************************************************************
*/
#define BSP_REG_DEM_CR (*(CPU_REG32 *)0xE000EDFC)
#define BSP_REG_DWT_CR (*(CPU_REG32 *)0xE0001000)
#define BSP_REG_DWT_CYCCNT (*(CPU_REG32 *)0xE0001004)
#define BSP_REG_DBGMCU_CR (*(CPU_REG32 *)0xE0042004)
/*
*********************************************************************************************************
* REGISTER BITS
*********************************************************************************************************
*/
#define BSP_BIT_DEM_CR_TRCENA DEF_BIT_24
#define BSP_BIT_DWT_CR_CYCCNTENA DEF_BIT_00
CPU_INT32U BSP_CPU_ClkFreq (void)
{
CPU_INT32U hclk_freq;
hclk_freq = HAL_RCC_GetHCLKFreq();
return (hclk_freq);
}
#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED)
void CPU_TS_TmrInit (void)
{
CPU_INT32U fclk_freq;
fclk_freq = BSP_CPU_ClkFreq();
BSP_REG_DEM_CR |= (CPU_INT32U)BSP_BIT_DEM_CR_TRCENA; /* Enable Cortex-M4's DWT CYCCNT reg. */
BSP_REG_DWT_CYCCNT = (CPU_INT32U)0u;
BSP_REG_DWT_CR |= (CPU_INT32U)BSP_BIT_DWT_CR_CYCCNTENA;
CPU_TS_TmrFreqSet((CPU_TS_TMR_FREQ)fclk_freq);
}
#endif
#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED)
CPU_TS_TMR CPU_TS_TmrRd (void)
{
CPU_TS_TMR ts_tmr_cnts;
ts_tmr_cnts = (CPU_TS_TMR)BSP_REG_DWT_CYCCNT;
return (ts_tmr_cnts);
}
#endif
#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
bsp.h
#ifndef BSP_PRESENT
#define BSP_PRESENT
/*
*********************************************************************************************************
* EXTERNS
*********************************************************************************************************
*/
#ifdef BSP_MODULE
#define BSP_EXT
#else
#define BSP_EXT extern
#endif
/*
*********************************************************************************************************
* INCLUDE FILES
*********************************************************************************************************
*/
#include <stdio.h>
#include <stdarg.h>
#include <cpu.h>
#include <cpu_core.h>
#include <lib_def.h>
#include <lib_ascii.h>
#include <stm32f4xx_hal.h>
#endif /* End of module include. */
(9)将 cpu_a.asm 里面的 OS_CPU_PendSVHandler 全部替换为 PendSV_Handler,然后将stm32f4xx_it.c里面的PendSV_Handler注释掉。
(10)修改 os_cfg_app.h 中的参数如下:
#ifndef OS_CFG_APP_H
#define OS_CFG_APP_H
/*
************************************************************************************************************************
* CONSTANTS
************************************************************************************************************************
*/
/* --------------------- MISCELLANEOUS ------------------ */
#define OS_CFG_MSG_POOL_SIZE 100u /* Maximum number of messages */
#define OS_CFG_ISR_STK_SIZE 100u /* Stack size of ISR stack (number of CPU_STK elements) */
#define OS_CFG_TASK_STK_LIMIT_PCT_EMPTY 10u /* Stack limit position in percentage to empty */
/* ---------------------- IDLE TASK --------------------- */
#define OS_CFG_IDLE_TASK_STK_SIZE 128u /* Stack size (number of CPU_STK elements) */
/* ------------------ ISR HANDLER TASK ------------------ */
#define OS_CFG_INT_Q_SIZE 10u /* Size of ISR handler task queue */
#define OS_CFG_INT_Q_TASK_STK_SIZE 100u /* Stack size (number of CPU_STK elements) */
/* ------------------- STATISTIC TASK ------------------- */
#define OS_CFG_STAT_TASK_PRIO (OS_CFG_PRIO_MAX - 2u) /* Priority */
#define OS_CFG_STAT_TASK_RATE_HZ 10u /* Rate of execution (1 to 10 Hz) */
#define OS_CFG_STAT_TASK_STK_SIZE 100u /* Stack size (number of CPU_STK elements) */
/* ------------------------ TICKS ----------------------- */
#define OS_CFG_TICK_RATE_HZ 1000u /* Tick rate in Hertz (10 to 1000 Hz) */
#define OS_CFG_TICK_TASK_PRIO 1u /* Priority */
#define OS_CFG_TICK_TASK_STK_SIZE 128u /* Stack size (number of CPU_STK elements) */
/* ----------------------- TIMERS ----------------------- */
#define OS_CFG_TMR_TASK_PRIO 2u /* Priority of 'Timer Task' */
#define OS_CFG_TMR_TASK_RATE_HZ 100u /* Rate for timers (100 Hz Typ.) */
#define OS_CFG_TMR_TASK_STK_SIZE 128u /* Stack size (number of CPU_STK elements) */
#endif
(11)修改 sys.h 文件 SYSTEM_SUPPORT_OS 改成1 支持操作系统,然后 将stm32f4xx_it.c 里面的 SysTick_Handler 函数注释掉。
#define SYSTEM_SUPPORT_OS 1 //定义系统文件夹是否支持OS
6.4 测试代码
在main函数加一个开始任务和两个灯闪动的任务,灯根据配置的时间运转即为移植成功,代码如下:
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "includes.h"
//任务优先级
#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);
int main(void)
{
OS_ERR err;
CPU_SR_ALLOC();
Stm32_Clock_Init(360,25,2,8); //设置时钟,180Mhz
HAL_Init(); //初始化HAL库
delay_init(180); //初始化延时函数
uart_init(115200); //初始化USART
LED_Init(); //初始化LED
OSInit(&err); //初始化UCOSIII
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, //任务堆栈深度限位
(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_OPT_TASK_SAVE_FP, //任务选项,为了保险起见,所有任务都保存浮点寄存器的值
(OS_ERR * )&err); //存放该函数错误时的返回值
OS_CRITICAL_EXIT(); //退出临界区
OSStart(&err); //开启UCOSIII
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 //当使用时间片轮转的时候
//使能时间片轮转调度功能,设置默认的时间片长度s
OSSchedRoundRobinCfg(DEF_ENABLED,10,&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_OPT_TASK_SAVE_FP,
(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_OPT_TASK_SAVE_FP,
(OS_ERR * )&err);
OS_CRITICAL_EXIT(); //进入临界区
OS_TaskSuspend((OS_TCB*)&StartTaskTCB,&err); //挂起开始任务
}
//led0任务函数
void led0_task(void *p_arg)
{
OS_ERR err;
p_arg = p_arg;
while(1)
{
LED0=0; //LED0打开
OSTimeDlyHMSM(0,0,0,200,OS_OPT_TIME_HMSM_STRICT,&err); //延时200ms
LED0=1; //LED0关闭
OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_HMSM_STRICT,&err); //延时500ms
}
}
//led1任务函数
void led1_task(void *p_arg)
{
p_arg = p_arg;
while(1)
{
LED1=!LED1;
delay_ms(500);//延时500ms
}
}