基于HAL库的uC/OS-III多任务系统移植
板子:STM32F103C8
移植系统:uc/OS-III
编译工具:Keil&STM32CubeMX
烧录工具:FlyMcu
一、uC/OS-III
1. 简介:
uC/OS-III(Micro C OS Three 微型的C 语言编写的操作系统第3版)是一个可升级的,可固化的,基于优先级的实时内核。它对任务的个数无限制。uC/OS-III 是一个第3 代的系统内核,支持现代的实时内核所期待的大部分功能。例如资源管理,同步,任务间的通信等等。然而,uC/OS-III 提供的特色功能在其它的实时内核中是找不到的,比如说完备的运行时间测量性能,直接地发送信号或者消息到任务,任务可以同时等待多个内核对象等。
同时也是一个可扩展的,可固化的,抢占式的实时内核,它管理的任务个数不受限制。它是第三代内核,提供了现代实时内核所期望的所有功能包括资源管理、同步、内部任务交流等。uC/OS-III 也提供了很多特性是在其他实时内核中所没有的。比如能在运行时测量运行性能,直接得发送信号或消息给任务,任务能同时等待多个信号量和消息队列。
2. 目标:
uC/OS-III 最主要的目标是提供一流的实时内核以适应更新很快的嵌入式产品。使用像uC/OS-III 那样具有雄厚的基础和稳定的框架的商业实时内核,能够帮助设计师们处理日益复杂的嵌入式设计。
3. 特点:
源代码
uC/OS-III 完全根据ANSI-C 标准写的。代码的规范是Micrium 团队的一种文化。虽然很多商业内核供应商提供他们产品的源代码,但是这些产品很有可能是笨重且难以利用的。除非代码严格地遵循标准并且产品有完整的带例子的说明书以展示代码是怎样工作的。
应用程序接口(API)
uC/OS-III 是很直观的。如果你熟悉类似的编码规范,你能轻松地知道函数名所对应的服务,以及需要怎样的参数。例如:指向对象的指针通常是第一个参数,指向错误代码的指针通常是最后一个参数。
抢占式多任务处理
uC/OS-III 是一个抢占式多任务处理内核,因此,uC/OS-III 正在运行的经常是最重要的就绪任务。
时间片轮转调度
uC/OS-III 允许多个任务拥有相同的优先级。当多个相同优先级的任务就绪时,并且这个优先级是目前最高的。uC/OS-III 会分配用户定义的时间片给每个任务去运行。每个任务可以定义不同的时间片。当任务用不完时间片时可以让出CPU 给另一个任务。
快速响应中断
uC/OS-III 有一些内部的数据结构和变量。uC/OS-III 保护临界段可以通过锁定调度器代替关中断。因此关中断的时间会非常少。这样就使uC/OS-III 可以响应一些非常快的中断源了。
确定性的
uC/OS-III 的中断响应时间是可确定的,uC/OS-III 提供的大部分服务的执行时间也是可确定的。
可扩展的
根据应用的需求,代码大小可以被调整。编译时通过调整uC/OS-III 源代码中的大约40 个#define(见OS_CFG.H)可以在添加或移除一些功能。uC/OS-III 的服务还提供一些实时检查功能。特别的,uC/OS-III 能检传递的参数是否为NULL 指针,ISR 是否就绪了任务级服务。参数有允许范围,指定选项都是有用的。检测功能可以被关闭(在编译时)以提供更好的性能和缩减代码大小。实际上,可扩展的uC/OS-III 支持更广泛的应用和项目。
易移植的
uC/OS-III 可以被移植到大部分的CPU 架构中。大部分的支持uC/OS-II 的器件通过改动就能支持uC/OS-III。而uC/OS-II已经移植到45 种CPU 架构中了。
可固化的
uC/OS-III 专为嵌入式系统设计,它可以跟应用程序代码一起被固化。
可实时配置的
uC/OS-III 允许用户在运行时配置内核。特别的,所有的内核对象如任务、堆栈、信号量、事件标志组、消息队列、消息、互斥信号量、内存分区、软件定时器等都是在运行时分配的,以免在编译时的过度分配。
任务数无限制
uC/OS-III 对任务数量无限制。实际上,任务的数量限制于处理器能提供的内存大小。每一个任务需要有自己的堆栈空间,uC/OS-III 在运行时监控任务堆栈的生长。uC/OS-III 对任务的大小无限制,
优先级数无限制
uC/OS-III 对优先级的数量无限制。然而,配置uC/OS-III 的优先级在32 到256 之间已经满足大多数的应用了。
内核对象数无限制
uC/OS-III 支持任何数量的任务、信号量、互斥信号量、事件标志组、消息队列、软件定时器、内存分区。用户在运行时分配所有的内核对象。
服务
uC/OS-III 提供了高档实时内核所需要的所有功能,例如任务管理、时间管理、信号量、事件标志组、互斥信号量、消息队列、软件定时器、内存分区等。
互斥信号量
互斥信号量用于资源管理。它是一个内置优先级的特殊类型信号量,用于消除优先级反转。互斥信号量可以被嵌套,因此,任务可申请同一个互斥信号量多达250 次。当然,互斥信号量的占有者需要释放同等次数。
嵌套的任务停止
uC/OS-III 允许任务停止自身或者停止另外的任务。停止一个任务意味着这个任务将不再执行直到被其他的任务恢复。停止可以被嵌套到250 级。换句话说,一个任务可以停止另外的任务多达250 次。当然,这个任务必须被恢复同等次数才有资格再次获得CPU。
软件定时器
可以定义任意数量的一次性的、周期性的、或者两者兼有的定时器。定时器是倒计时的,执行用户定义的行为一直到计数减为0。每一个定时器可以有自己的行为,如果一个定时器是周期性的,计数减为0 时会自动重装计数值并执行用户定义的行为。
挂起多个对象
uC/OS-III 允许任务等待多个事件的发生。特别的,任务可以同时等待多个信号量和消息队列被提交。等待中的任务在事件发生的时候被唤醒。
任务信号量
uC/OS-III 允许ISR 或者任务直接地发送信号量给其它任务。这样就避免了必须产生一个中间级内核对象如一个信号量或者事件标志组只为了标记一个任务。提高了内核性能。
任务消息
uC/OS-III 允许ISR 或者任务直接发送消息到另一个任务。这样就避免产生一个消息队列,提高了内核性能。
任务寄存器
每一个任务可以拥有用户可定义的任务寄存器,不同于CPU 寄存器。
错误检测
uC/OS-III 能检测指针是否为NULL、在ISR 中调用的任务级服务是否允许、参数在允许范围内、配置选项的有效性、函数的执行结果等。每一个uC/OS-III 的API 函数返回一个对应于函数调用结果的错误代号。
内置的性能测量
uC/OS-III 有内置性能测量功能。能测量每一个任务的执行时间,每个任务的堆栈使用情况,任务的执行次数,CPU的使用情况,ISR 到任务的切换时间,任务到任务的切换时间,列表中的峰值数,关中断、锁调度器平均时间等。
可优化
uC/OS-III 被设计于能够根据CPU 的架构被优化。uC/OS-III 所用的大部分数据类型能够被改变,以更好地适应CPU 固有的字大小。优先级调度法则可以通过编写一些汇编语言而获益于一些特殊的指令如位设置、位清除、计数清零指令(CLZ),find-first-one(FF1)指令。
死锁预防
uC/OS-III 中所有的挂起服务都可以有时间限制,预防死锁。
任务级的时基处理
uC/OS-III 有时基任务,时基ISR 触发时基任务。uC/OS-III 使用了哈希列表结构,可以大大减少处理延时和任务超时所产生的开支。
用户可定义的钩子函数
uC/OS-III 允许程序员定义hook 函数,hook 函数被uC/OS-III 调用。hook 函数允许用户扩展uC/OS-III 的功能。有的hook 函数在任务切换的时候被调用,有的在任务创建的时候被调用,有的在任务删除的时候被调用。
时间戳
为了测量时间,uC/OS-III 需要一个16 位或者32 位的时时间戳计数器。这个计数器值可以在运行时被读取以测量时间。例如:当ISR 提交消息到任务时,时间戳计数器自动读取并保存作为消息。当接收者接收到这条消息,时间戳被提供在消息内。通过读取现在的时间戳,消息的响应时间可以被确定。
嵌入的内核调试器
这个功能允许内核调试器查看uC/OS-III 的变量和数据结构通过一个用户定义的通道。(但是只能在调试器遇到断点的时候查看)。uC/OS-III 内核也支持uC/Probe(探针)在运行时显示信息。
对象名称
每个uC/OS-III 的内核对象有一个相关联的名字。这样就能很容易的识别出对象所指定的作用。分配一个ASCII 码的名字给任务、信号量、互斥信号量、事件标志组、消息队列、内存块、软件定时器。对象的名字长度没有限制,但是必须以空字符结束。
以上内容摘抄于《百度百科:uC/OS-III》
二、系统移植
1. STM32CubeMX创建项目
选择对应的F103C8tx系统版,激活串口一,并设置PB0,PB1为OUTPUT,用于点亮两个LED
2. 下载官方程序包
①:进入 Micrium 公司官网下载中心
②:度盘直接下载:
官网下载较慢,而且对于英语不好的同学来说不太好找,这里有大佬直接提供了百度网盘连接 链接:https://pan.baidu.com/s/1Btj7foEXdXjjJWoZQsN-OQ 提取码:mleh
3. 将程序包移植至Keil
3.1 解压工程文件并移至项目目录
下载完成后将uC/OS-III文件复制到工程文件夹 在生成的keil工程文件夹f103c8_uCOSIII_1_test中新建一个 UCOSIII 的文件夹,将我们下载的源代码中三个文件夹: Uc-CPU、 uC-LIB、 Ucos-III 复制到我们新建的文件夹中:
并在Src文件夹下新建一个OS文件夹
将刚才下载源码打开,将路径:\EvalBoards\Micrium\uC-Eval-STM32F107\uCOS-III 下的文件: app.c 、 app_cfg.h 、 cpu_cfg.h 、 includes.h 、 lib_cfg.h 、 os_app_hooks.c 、os_app_hook.h 、os_cfg.h 、os_cfg_app.h 复制到上一步建立的OS文件夹中,同时新建三个空白文件: bsp.c 、bsp.h 、app.h
3.2 添加工程组件
- 打开f103c8_uCOSIII_1_test工程, 按照如图所示添加六个新的组:
bsp、uCOSIII_CPU 、uCOSIII_LIB 、 uCOSIII_Ports 、 uCOSIII_Source、 OS_cfg
- 如图将工程文件中的各文件添加到对应的组别中:
3.3 添加头文件路径
4. 修改部分程序内容
4.1 startup_stm32f103xb.s
在如下位置将其替换为OP_CPU_XXXXXX
4.2 app_cfg.h
第一处修改: #define APP_CFG_SERIAL_EN DEF_ENABLED 改为#define APP_CFG_SERIAL_EN DEF_DISABLED 第二处修改: #define APP_TRACE BSP_Ser_Printf 改为#define APP_TRACE (void)
4.3 includes.h
第一处修改: 添加相关头文件
#include "gpio.h"
#include "app_cfg.h"
#include "app.h"
第二处修改: 添加HAL 库 将#include <stm32f10x_lib.h> 替换为#include "stm32f1xx_hal.h"
4.4 bps.c与bps.h
#include "includes.h"
#define DWT_CR *(CPU_REG32 *)0xE0001000
#define DWT_CYCCNT *(CPU_REG32 *)0xE0001004
#define DEM_CR *(CPU_REG32 *)0xE000EDFC
#define DBGMCU_CR *(CPU_REG32 *)0xE0042004
#define DEM_CR_TRCENA (1 << 24)
#define DWT_CR_CYCCNTENA (1 << 0)
CPU_INT32U BSP_CPU_ClkFreq (void)
{
return HAL_RCC_GetHCLKFreq();
}
void BSP_Tick_Init(void)
{
CPU_INT32U cpu_clk_freq;
CPU_INT32U cnts;
cpu_clk_freq = BSP_CPU_ClkFreq();
#if(OS_VERSION>=3000u)
cnts = cpu_clk_freq/(CPU_INT32U)OSCfg_TickRate_Hz;
#else
cnts = cpu_clk_freq/(CPU_INT32U)OS_TICKS_PER_SEC;
#endif
OS_CPU_SysTickInit(cnts);
}
void BSP_Init(void)
{
BSP_Tick_Init();
MX_GPIO_Init();
}
#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED)
void CPU_TS_TmrInit (void)
{
CPU_INT32U cpu_clk_freq_hz;
DEM_CR |= (CPU_INT32U)DEM_CR_TRCENA;
DWT_CYCCNT = (CPU_INT32U)0u;
DWT_CR |= (CPU_INT32U)DWT_CR_CYCCNTENA;
cpu_clk_freq_hz = BSP_CPU_ClkFreq();
CPU_TS_TmrFreqSet(cpu_clk_freq_hz);
}
#endif
#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED)
CPU_TS_TMR CPU_TS_TmrRd (void)
{
return ((CPU_TS_TMR)DWT_CYCCNT);
}
#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_H__
#define __BSP_H__
#include "stm32f1xx_hal.h"
void BSP_Init(void);
#endif
4.5 app.c和app.h
// app.c
#include <includes.h>
// app.h
#ifndef __APP_H__
#define __APP_H__
#include <includes.h>
#endif /* __APP_H__ */
4.6 lib_cfg.h
此处修改为5(该处宏定义设置堆空间的大小,STM32F103C8T6的RAM只有20K,所以要改小一点)
4.7 usart.c
由于我们使用了printf函数,需要在usart.c文件中添加以下代码完成printf重定向(不知道放哪放到文件末尾即可)
int fputc(int ch,FILE *f){
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xffff);
return ch;
}
5. Keil参数配置
魔法棒配置:
三、创建多任务程序
以下所有修改都再main.c中完成。
1. 前置代码
为了提高代码可读性,这里要进行一系列的函数声明和变量声明
1.1 添加头文件
#include <includes.h>
#include "stm32f1xx_hal.h"
1.2 设置任务相关变量
#define START_TASK_PRIO 3
#define LED0_TASK_PRIO 4
#define LED1_TASK_PRIO 5
#define MSG_TASK_PRIO 6
#define START_STK_SIZE 96
#define LED0_STK_SIZE 64
#define LED1_STK_SIZE 64
#define MSG_STK_SIZE 64
CPU_STK START_TASK_STK[START_STK_SIZE];
CPU_STK LED0_TASK_STK[LED0_STK_SIZE];
CPU_STK LED1_TASK_STK[LED1_STK_SIZE];
CPU_STK MSG_TASK_STK[MSG_STK_SIZE];
OS_TCB StartTaskTCB;
OS_TCB Led0TaskTCB;
OS_TCB Led1TaskTCB;
OS_TCB MsgTaskTCB;
static OS_TCB AppTaskStartTCB;
1.3 声明任务函数
static CPU_STK AppTaskStartStk[APP_TASK_START_STK_SIZE];
static void AppTaskCreate(void);
static void AppObjCreate(void);
static void AppTaskStart(void *p_arg);
static void Task_LED_PB0(void *p_arg);
static void Task_LED_PB1(void *p_arg);
static void Task_MSG(void *p_arg);
1.4 main.c 启动多任务系统
int main(void)
{
OS_ERR err;
OSInit(&err);
HAL_Init();
SystemClock_Config();
OSInit(&err);
MX_GPIO_Init();
MX_USART1_UART_Init();
OSTaskCreate((OS_TCB *)&StartTaskTCB,
(CPU_CHAR *)"start task",
(OS_TASK_PTR ) AppTaskStart,
(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,
(OS_TICK ) 0,
(void *) 0,
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
(OS_ERR *)&err);
OSStart(&err);
}
1.5 必要的函数声明
static void AppTaskCreate (void)
{
}
static void AppObjCreate (void)
{
}
void Error_Handler(void)
{
}
2. 点亮LED
针对PB0和PB1创建两个不同的LED电量程序,一个以100ms的频率不断闪烁,另一个以600ms的频率不断闪烁
static void Task_LED_PB0 (void *p_arg)
{
OS_ERR err;
(void)p_arg;
BSP_Init();
CPU_Init();
Mem_Init();
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err);
#endif
CPU_IntDisMeasMaxCurReset();
AppTaskCreate();
AppObjCreate();
while (DEF_TRUE)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET);
OSTimeDlyHMSM(0, 0, 0, 500,OS_OPT_TIME_HMSM_STRICT,&err);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET);
OSTimeDlyHMSM(0, 0, 0, 500,OS_OPT_TIME_HMSM_STRICT,&err);
}
}static void Task_LED_PB1 (void *p_arg)
{
OS_ERR err;
(void)p_arg;
BSP_Init();
CPU_Init();
Mem_Init();
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err);
#endif
CPU_IntDisMeasMaxCurReset();
AppTaskCreate();
AppObjCreate();
while (DEF_TRUE)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);
OSTimeDlyHMSM(0, 0, 0, 100,OS_OPT_TIME_HMSM_STRICT,&err);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET);
OSTimeDlyHMSM(0, 0, 0, 100,OS_OPT_TIME_HMSM_STRICT,&err);
}
}
3. 串口通信
使用Usart1向主机发送信息“Welcome to uC/OS-III!”
static void Task_MSG (void *p_arg)
{
OS_ERR err;
(void)p_arg;
BSP_Init();
CPU_Init();
Mem_Init();
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err);
#endif
CPU_IntDisMeasMaxCurReset();
AppTaskCreate();
AppObjCreate();
while (DEF_TRUE)
{
printf("Welcome to uc/OS-III! \r\n");
OSTimeDlyHMSM(0, 0, 0, 500,OS_OPT_TIME_HMSM_STRICT,&err);
}
}
4. 任务启动函数
有几个任务就按照格式写几个任务
static void AppTaskStart (void *p_arg)
{
OS_ERR err;
CPU_SR_ALLOC();
p_arg = p_arg;
BSP_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
OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);
#endif
OS_CRITICAL_ENTER();
OSTaskCreate((OS_TCB * )&Led0TaskTCB,
(CPU_CHAR * )"TASK_LED",
(OS_TASK_PTR )Task_LED_PB0,
(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);
OSTaskCreate((OS_TCB * )&Led1TaskTCB,
(CPU_CHAR * )"TASK_LED2",
(OS_TASK_PTR )Task_LED_PB1,
(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 * )&MsgTaskTCB,
(CPU_CHAR * )"TASK_MSG",
(OS_TASK_PTR )Task_MSG,
(void * )0,
(OS_PRIO )MSG_TASK_PRIO,
(CPU_STK * )&MSG_TASK_STK[0],
(CPU_STK_SIZE)MSG_STK_SIZE/10,
(CPU_STK_SIZE)MSG_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();
}
5. main.c 完整代码
#include "main.h"
#include "usart.h"
#include "gpio.h"
#include <includes.h>
#include "stm32f1xx_hal.h"
#define START_TASK_PRIO 3
#define LED0_TASK_PRIO 4
#define LED1_TASK_PRIO 5
#define MSG_TASK_PRIO 6
#define START_STK_SIZE 96
#define LED0_STK_SIZE 64
#define LED1_STK_SIZE 64
#define MSG_STK_SIZE 64
CPU_STK START_TASK_STK[START_STK_SIZE];
CPU_STK LED0_TASK_STK[LED0_STK_SIZE];
CPU_STK LED1_TASK_STK[LED1_STK_SIZE];
CPU_STK MSG_TASK_STK[MSG_STK_SIZE];
OS_TCB StartTaskTCB;
OS_TCB Led0TaskTCB;
OS_TCB Led1TaskTCB;
OS_TCB MsgTaskTCB;
static OS_TCB AppTaskStartTCB;
static CPU_STK AppTaskStartStk[APP_TASK_START_STK_SIZE];
static void AppTaskCreate(void);
static void AppObjCreate(void);
static void AppTaskStart(void *p_arg);
static void Task_LED_PB0(void *p_arg);
static void Task_LED_PB1(void *p_arg);
static void Task_MSG(void *p_arg);
void SystemClock_Config(void);
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
int main(void)
{
OS_ERR err;
OSInit(&err);
HAL_Init();
SystemClock_Config();
OSInit(&err);
MX_GPIO_Init();
MX_USART1_UART_Init();
OSTaskCreate((OS_TCB *)&StartTaskTCB,
(CPU_CHAR *)"start task",
(OS_TASK_PTR ) AppTaskStart,
(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,
(OS_TICK ) 0,
(void *) 0,
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
(OS_ERR *)&err);
OSStart(&err);
}
static void AppTaskStart (void *p_arg)
{
OS_ERR err;
CPU_SR_ALLOC();
p_arg = p_arg;
BSP_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
OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);
#endif
OS_CRITICAL_ENTER();
OSTaskCreate((OS_TCB * )&Led0TaskTCB,
(CPU_CHAR * )"TASK_LED",
(OS_TASK_PTR )Task_LED_PB0,
(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);
OSTaskCreate((OS_TCB * )&Led1TaskTCB,
(CPU_CHAR * )"TASK_LED2",
(OS_TASK_PTR )Task_LED_PB1,
(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 * )&MsgTaskTCB,
(CPU_CHAR * )"TASK_MSG",
(OS_TASK_PTR )Task_MSG,
(void * )0,
(OS_PRIO )MSG_TASK_PRIO,
(CPU_STK * )&MSG_TASK_STK[0],
(CPU_STK_SIZE)MSG_STK_SIZE/10,
(CPU_STK_SIZE)MSG_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();
}
static void Task_LED_PB0 (void *p_arg)
{
OS_ERR err;
(void)p_arg;
BSP_Init();
CPU_Init();
Mem_Init();
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err);
#endif
CPU_IntDisMeasMaxCurReset();
AppTaskCreate();
AppObjCreate();
while (DEF_TRUE)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET);
OSTimeDlyHMSM(0, 0, 0, 500,OS_OPT_TIME_HMSM_STRICT,&err);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET);
OSTimeDlyHMSM(0, 0, 0, 500,OS_OPT_TIME_HMSM_STRICT,&err);
}
}static void Task_LED_PB1 (void *p_arg)
{
OS_ERR err;
(void)p_arg;
BSP_Init();
CPU_Init();
Mem_Init();
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err);
#endif
CPU_IntDisMeasMaxCurReset();
AppTaskCreate();
AppObjCreate();
while (DEF_TRUE)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);
OSTimeDlyHMSM(0, 0, 0, 100,OS_OPT_TIME_HMSM_STRICT,&err);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET);
OSTimeDlyHMSM(0, 0, 0, 100,OS_OPT_TIME_HMSM_STRICT,&err);
}
}
static void Task_MSG (void *p_arg)
{
OS_ERR err;
(void)p_arg;
BSP_Init();
CPU_Init();
Mem_Init();
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err);
#endif
CPU_IntDisMeasMaxCurReset();
AppTaskCreate();
AppObjCreate();
while (DEF_TRUE)
{
printf("Welcome to uc/OS-III! \r\n");
OSTimeDlyHMSM(0, 0, 0, 500,OS_OPT_TIME_HMSM_STRICT,&err);
}
}
static void AppTaskCreate (void)
{
}
static void AppObjCreate (void)
{
}
void Error_Handler(void)
{
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif
6. 烧录
BOOT1置零,按下Reset重启,然后使用FlyMCU烧录工具将此程序烧录进最小系统板中
四、运行结果:
图中我们可以看到三个不同的任务在同时进行:两个LED灯以不同的频率进行闪烁,同时串口的RXD也在不停地闪烁,证明最小系统板正在对主机发送信息:
如图再经过串口调试工具确实验证了RXD在不停接受最小系统板发送的信息的事实。
五、总结
通过学习uC/OS-III硬件操作系统,我知道了如何让硬件进行多线程工作,如此便能大大提高嵌入式的开发效率。
六、参考文章
机智的橙子: STM32F103C8T6移植uCOS基于HAL库
带火星的木头: STM32F103C8T6移植uC/OS-III基于HAL库超完整详细过程
百度百科: uC/OS-III
|