本文目的是主要介绍通过嵌入式实时操作系统(RTOS),以uc/OS为例,将其移植到stm32F103上,构建至少3个任务(task):其中两个task分别以1s和3s周期对LED灯进行点亮-熄灭的控制;另外一个task以2s周期通过串口发送“hello uc/OS! 欢迎来到RTOS多任务环境!”。记录并详细的讲解移植过程。
文章目录
前言
※ 嵌入式实时操作系统(RTOS)的详解,可参考博客进行学习:https://blog.csdn.net/qq_42605300/article/details/105215866
※ 通过嵌入式实时操作系统(RTOS),以uc/OS为例,将其移植到stm32F103上,构建至少3个任务(task):其中两个task分别以1s和3s周期对LED灯进行点亮-熄灭的控制;另外一个task以2s周期通过串口发送“hello uc/OS! 欢迎来到RTOS多任务环境!”。记录并详细的讲解移植过程。
梦远路长,总是念了一天又一天,盼了一年又一年。

(一)需求分析
※ 采用嵌入式实时操作系统(RTOS),以uc/OS为例,将其移植到stm32F103上,构建至少3个任务(task):其中两个task分别以1s和3s周期对LED灯进行点亮-熄灭的控制;另外一个task以2s周期通过串口发送“hello uc/OS! 欢迎来到RTOS多任务环境!”。记录并详细的讲解移植过程。
所需工具:
1、芯片: STM32F103C8T6
2、STM32CubeMx软件
3、IDE: MDK-Keil软件
4、STM32F1xx/STM32F4xxHAL库
5、LED灯
(二)实时操作系统(RTOS)——uc/OS介绍
1.什么是嵌入式RTOS?
🎈 OS(操作系统,operating system)
,是嵌入式系统中的灵魂,是其软件系统中的重要组成部分,嵌入式系统的智能化程度与其密不可分。
🎈 从嵌入式系统的应用领域来看,嵌入式操作系统可以分为实时操作系统(RTOS)和非实时操作系统(NRTOS)。但在嵌入式应用领域,很多场合对系统的实时性要求严格,因此操作系统的选择要基于实时系统。实时多任务操作系统(Real Time Operating System,简称RTOS)是根据操作系统的工作特性而言的,实时是指物理进程的真实时间。
2.RTOS具体功能
RTOS具有的功能 | 描述 |
---|---|
任务管理 | 包含多任务和基于优先级的任务调度 |
任务间的同步和通信 | 基于Semaphore 、MailBox等 |
优化的内存管理单元 (MMU) | 负责处理中央处理器(CPU)的内存访问请求 |
实时时钟服务 | 用以设置相应的寄存器来控制实时时钟的状态 |
中断管理服务 | 处理器处理“急件”的服务 |
Semaphore:
信号量 。又被称为信号灯,用信号量进行通信就像我们只拨通别人的手机而不与之通话。
MailBox:
邮箱。用邮箱进行通信则可达到既拨通别人的手机又与之通话的效果。换句话说,消息队列和邮箱可以及时传送事件的内容。
MMU:
Memory Management Unit
RTC:
Real Time Clock,可以。这些状态包括使实时时钟开始工作,也包括使实时时钟停止工作。
3.常见的嵌入式操作系统(RTOS)
🎈 目前市场上的嵌入式操作系统有几十种,中国近年出现了不少国产嵌入式操作系统,如 HopenOs、DeltaOs 等。国际上嵌入式操作系统种类更多,除了最常用的 uC/OS-II、嵌入式 Linux、 Vxworks 和 WinCE 外,还包括 QNX、PSOS 等。
1、uC/OS-II
uC/OS-II ,一个实时多任务的微内核,为 Jean J. Labrosse 在 1992 年编写的一个开源代码。由于内核小(仅由十几个 C 语言和汇编文件构成),应用范围宽,目前可应用在许多工业控制器、通信设备和交换机中。它既是实时多任务的微内核,又是开源的代码。
2、Linux 操作系统
Linux 操作系统,有强大的网络功能及其代码的开源性, 但其内核具有不可剥夺特点,其实时性受到一定的限制。因此,为改善其实时性,人们提出了各种嵌入式 Linux 操作系统,如:RT-Linux、Kurt-Linux 和 Red-Linux。
3、Vxworks
Vxworks,作为一种商用的嵌入式操作系统,具有优秀的实时性和稳定性,其代码是不公开的, 而且价格昂贵。Vxworks 主要用于对实时性和稳定性有严格要求的航空航天项目中。
4、 WinCE
WinCE(Window Compact Edition),由美国微软公司设计的,也可用于手持设备的操作系统中, 其主要特点是用户界面非常漂亮,但由于其针对不同种类的手持设备,导致生成的目标代码“超长”(redundant codes)。
4.uc/OS系统介绍
🎈 uC/OS
是一个可以基于ROM运行的、可裁减的、抢占式、实时多任务内核,具有高度可移植性
,特别适合于微处理器和控制器,适合很多商业操作系统的实时操作系统(RTOS)。
🎈 uC/OS可简单的视为一个多任务调度器,在这个任务调度器之上完善并添加了和多任务操作系统相关的系统服务,如信号量、邮箱等;其主要特点有公开源代码,代码结构清晰、明了,注释详尽,组织有条理,可移植性好,可裁剪,可固化。
内核属于抢占式,最多可以管理60个任务;从1992年开始,由于高度可靠性、鲁棒性和安全性,uC/OS已经广泛使用在从照相机到航空电子产品的各种应用中。
🎈 μC/OS实时多任务操作系统被广泛应用于微处理器、微控制器和数字信号处理器
;μC/OS最早出自于1992 年美国嵌入式系统专家Jean J.Labrosse 在《嵌入式系统编程》杂志的5 月和6 月刊上刊登的文章连载,并把μC/OS的源码发布在该杂志的BBS 上。
5.uCOS-II中的任务
5.1 内核的主要工作
🎈 操作系统的内核的主要工作就是对任务进行管理和调度。
🎈 一个任务相当于一个死循环,相当于一个线程。
这个操作系统如何管理任务:任务控制块TCB。
这个操作系统的任务分为系统任务和用户任务
,系统任务是系统内核自带的,提供系统正常工作的保障。
uCOS-II系统设置了两个系统任务,空闲任务和统计任务。Cpu空闲状态下执行空闲任务。目前任务必须存在。统计任务就是统计一下在单位时间内CPU的使用时间,也叫CPU占用率。统计任务可以通过设置使它不存在。
🎈 任务的状态:睡眠状态、就绪状态、运行状态、等待状态、中断服务状态。
🎈 任务相当于一个线程,函数负责创建和启动这些任务,操作系统负责管理调度这些任务。
uCOS-II系统使用优先级来表明系统任务,每个任务的优先级必须不一样,老版本中最多支持64个任务,新版本可以有256个,空闲任务优先级倒数第一,统计任务优先级倒数第二,系统任务已经占用了两个优先级。优先级素质越大则优先级越低,优先级为0的最优先执行。
任务堆栈
:每个任务都有自己的人物堆栈,相当于申请了一块线程的内存资源。
任务控制块
:相当于是每一个任务的ID,操作系统通过任务控制块来获取任务的全部信息,并调度它们。
调度器
:操作系统的核心算法,采用一定算法去调做任务,做到让每个任务都能够执行到,而且每个任务还能根据轻重缓急来执行,有各式各样的算法,windows系统的任务调度算法非常复杂,uCOS-II系统比较简单。
uCOS-II系统的任务调度器会根据任务就绪表 OSRdyTbl[7]来调度任务。任务就绪表的位置和优先级别的数字成一一对应关系。
5.2 任务睡眠状态
🎈 睡眠状态下的任务,驻留在内存中,或者可以理解为用户已经在代码实现上完成了coding,但是并没有让ucos接管该任务的管理权。即在这种状态下,ucos系统是不会对这类任务进行调度和处理的。通过ucos系统的指令接口OsTaskCreate和OsTaskDel,实现用户和ucos间对任务管理权的交接。
5.3 任务就绪状态
🎈 当任务被创建并由ucos接管后,即进入到就绪状态。换句话说,就是等待CPU资源进行任务的运行。但是什么时候能够获取到CPU资源,将由ucos系统调度来决定。
5.4 任务运行状态
🎈 当任务获取到CPU资源,即进入到运行状态。在该中状态下,任务占用CPU和其他资源按照任务的设定功能执行相关代码,提供相关任务的服务。
5.5 任务等待状态
🎈 多任务系统中,很多资源都是所有任务共享的。当任务试图获取某项资源失败后,都会进入到等待状态。在等待状态中,该任务会释放CPU资源,进入被等待资源的等待队列中。当被等待资源被释放后,ucos会从等待队列中,找到优先级最高的等待任务恢复到运行状态继续执行。
🎈 同时,ucos系统也提供了另一种等待策略:任务延时等待
。即运行状态下的任务,可以通过延时等待功能进入到等待状态,此时释放CPU资源。当等待超时后,会重新抢夺CPU资源,至于那个时候能不能抢到,就看超时后调度的时刻是否有优先级更高的任务了。
5.5 任务中断状态
🎈 中断状态,即任务在执行过程中,被硬件中断处理过程打断执行过程,被迫放弃CPU资源。进入到中断服务函数中。
当中断结束后,重新对CPU资源进行抢占。同理,能否抢到CPU资源,就要看任务调度时刻,是否有更高优先级的任务就绪。下图优先级B>C>A。所以当ISR结束后,CPU资源被任务B抢占。
6.uc/OS的优势
🎈 μC/OS-II是一个基于抢占式的实时多任务内核,可固化、可剪裁、具有高稳定性和可靠性
,除此以外,μC/OS-II的鲜明特点就是源码公开,便于移植和维护。
① 随着信息化技术的发展和数字化产品的普及,以计算机技术、芯片技术和软件技术为核心的嵌入式系统再度成为当前研究和应用的热点;对功能、可靠性、成本、体积和功耗严格要求的嵌入式系统一般由
嵌入式微处理器、外围硬件设备、嵌入式操作系统以及用户的应用程序
等四个部分组成,
② 其中嵌入式微处理器和嵌入式操作系统分别是其硬件和软件的核心
③ 市场上主流的嵌入式实时操作系统有Vxworks、pSos、WinCE、Linux等,基于实时性、成本以及开发难度方面的考虑
,我们选择uC/OS——开放源代码的嵌入式实时操作系统
7.任务、时间及内存管理
7.1 uc/OS任务管理
🎈 uC/OS中最多可以支持64个任务
,分别对应优先级0~63
,其中0为最高优先级,63为最低级;系统保留了4个最高优先级的任务和4个最低优先级的任务,所有用户可以使用的任务数有56个。
🎈 uC/OS提供了任务管理的各种函数调用
,包括创建任务,删除任务,改变任务的优先级,任务挂起和恢复等。
🎈 系统初始化时会自动产生两个任务:一个是空闲任务
,它的优先级最低,该任务仅给一个整型变量做累加运算;另一个是统计任务
,它的优先级为次低,该任务负责统计当前cpu的利用率。
7.2 uc/OS时间管理
🎈 uC/OS的时间管理是通过定时中断
来实现的,该定时中断一般为10毫秒或100毫秒发生一次,时间频率取决于用户对硬件系统的定时器编程来实现;中断发生的时间间隔是固定不变的,该中断也成为一个时钟节拍;uC/OS要求用户在定时中断的服务程序中,调用系统提供的与时钟节拍相关的系统函数,例如中断级的任务切换函数,系统时间函数。
7.3 内存管理
🎈 在ANSI C中是使用malloc和free
两个函数来动态分配和释放内存;
🎈 但在嵌入式实时系统中,多次这样的操作会导致内存碎片,且由于内存管理算法的原因,malloc和free的执行时间也是不确定;uC/OS把连续的大块内存按分区管理
;每个分区中包含整数个大小相同的内存块,但不同分区之间的内存块大小可以不同;用户需要动态分配内存时,系统选择一个适当的分区,按块来分配内存;释放内存时将该块放回它以前所属的分区,这样能有效解决碎片问题,同时执行时间也是固定的
。
8.内存的动态分配
🎈 在c语言中,最终无非就是要实现两个方法:malloc()和free().
🎈 uCOS-II系统对内存进行了两级管理,一大片连续的内存空间被分区,然后每个区又分成了若干的内存块。操作系统以区为级别管理内存,任务以内存块为级别申请内存和释放内存。
整体的使用情况由 -内存控制快OS_MEM- 这种数据结构来记录。
为什么要分块儿?
malloc()和free()虽然是连续的,但会造成内存碎片存在。
9.关于uc/OS其他
代码的临界段:关闭中断的那一段,临界段一定要设置的很小。
uCOS-II系统利用OSSched()来实现任务的调度,OSSched()是临界段代码。
🎈 共享数据能够满足互斥条件的几种处理方式:关中断、使用测试并置位指令、禁止做任务切换、利用信号量。
根据实际情况处理。
🎈 uCOS-II系统任务切换的细节:
🎈 uC/OS II有系统时钟,每5毫秒中断一次,执行一次OS_Sched()。
OS_Sched ()在系统提供的延时函数里面也有,在任务恢复里面也有,在各种需要他的地方都有调用。他的主要代码如下,把正在执行的任务压入任务堆栈,然后根据优先级去执行新的任务。OS_Sched ()里面调用了OS_TASK_SW(),这个是根据处理器不同的汇编。
*********************************************************************************************************
* SCHEDULER
*
* Description: This function is called by other uC/OS-II services to determine whether a new, high
* priority task has been made ready to run. This function is invoked by TASK level code
* and is not used to reschedule tasks from ISRs (see OSIntExit() for ISR rescheduling).
*
* Arguments : none
*
* Returns : none
*
* Notes : 1) This function is INTERNAL to uC/OS-II and your application should not call it.
* 2) Rescheduling is prevented when the scheduler is locked (see OS_SchedLock())
*********************************************************************************************************
*/
void OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
OS_ENTER_CRITICAL();
if (OSIntNesting == 0u) { /* Schedule only if all ISRs done and ... */
if (OSLockNesting == 0u) { /* ... scheduler is not locked */
OS_SchedNew();
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */
#if OS_TASK_PROFILE_EN > 0u
OSTCBHighRdy->OSTCBCtxSwCtr++; /* Inc. # of context switches to this task */
#endif
OSCtxSwCtr++; /* Increment context switch counter */
OS_TASK_SW(); /* Perform a context switch */
}
}
}
OS_EXIT_CRITICAL();
}
(三)“昙花一现”——工程项目新创
1.工程新创
🎈 点击ACCESS TO MCU SELECTOR
🎈 选择STM32F103C8T6芯片
2.引脚排列和配置
2.1 配置RCC与SYS
2.1.1 配置RCC
🎈 点System Cor
,选择RCC
,在右侧弹出的菜单栏中选Crystal/Ceramic Resonator:
🎈 发现部分引脚高亮。
2.1.1 配置SYS
🎈 选择调试接口,点System Cor
,选择SYS
。在右侧弹出的菜单栏中选Serial Wire:
🎈 部分引脚高亮。
3.配置USART1
🎈 配置串口USART1:
4.设置LED端口
🎈 设置PA3、PC13
作为两个LED灯的端口:
5.生成工程
🎈 配置如下:
🎈 最后生成过程。
(四)准备uCOSIII源码
🎈进入官网下载:http://micrium.com/downloadcenter/
🎈或链接:https://pan.baidu.com/s/10RqsDRecbmVteWmDv2oUNQ
🎈提取码:1234
网盘下载,打开目录如下(文件夹uC-BSP
和uC-CONFIG
是自己新建的两个文件夹)
(五)移植前准备
1.文件内部移植
🎈 为uC-BSP文件夹新建bsp.c
和bsp.h
文件。
🎈 .给文件夹uC-CONFIG添加以下文件(从以下路径复制过来)
🎈 粘贴:
2.将uCOS相关文件复制到HAL工程的MDK-ARM文件夹下
🎈 操作如下:
(六)开始移植
回到Keil打开的HAL工程
1.将uCOS文件添加到项目
🎈 点击Manage Project Items
:
🎈 为项目新建文件夹如下:
🎈 点击CPU–>Add Files…
,选中以下文件,Add
🎈 再打开MDK-ARM\uC-CPU\ARM-Cortex-M3\RealView
路径,选中以下文件,Add
添加:
🎈 点击LIB–>Add Files…
,在MDK-ARM\uC-LIB
路径下选中下图文件,Add
添加:
🎈 再打开MDK-ARM\uC-LIB\Ports\ARM-Cortex-M3\RealView
路径,选中下图框文件,Add
添加:
🎈 点击PORT–>Add Files…
,打开MDK-ARM\uCOS-III\Ports\ARM-Cortex-M3\Generic\RealView
路径,选中以下文件,Add
添加:
🎈 点击SOURCE–>Add Files…
,MDK-ARM\uCOS-III\Source
路径下选中以下全部.c .h
文件,Add
添加:
🎈 点击CONFIG–>Add Files…
,MDK-ARM\uC-CONFIG
路径下选中以下全部文件,Add
添加:
🎈 点击BSP–>Add Files…
,LMDK-ARM\uC-BSP
路径下选中以下全部文件,Add
添加:
!!!一定记得点击OK,不然就白干了
🎈 此时项目结构会发生变化:
2.导入文件路径
🎈 导入文件路径:
🎈 项目路径下寻找:
3.为bsp.c和bsp.h添加代码
🎈 为bsp.c
和bsp.h
添加代码
3.1 bsp.c代码
🎈 bsp.c
代码:
#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; /* Enable Cortex-M3's DWT CYCCNT reg. */
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
3.2 bsp.h代码
🎈 bsp.h
代码:
#ifndef __BSP_H__
#define __BSP_H__
#include "stm32f1xx_hal.h"
void BSP_Init(void);
#endif
4.修改startup_stm32f103xb.s
文件代码
🎈 在以下位置处:
PendSV_Handler
改为OS_CPU_PendSVHandler,
SysTick_Handler
改为OS_CPU_SysTickHandler
5.修改app_cfg.h文件代码
🎈 DEF_ENABLED
改为 DEF_DISABLED
🎈 #define APP_TRACE BSP_Ser_Printf
改为 #define APP_TRACE(void)
6.修改includes.h文件代码
🎈 在#include <bsp.h>
下面添加 #include “gpio.h”
和 #include “app_cfg.h”
🎈 将#include <stm32f10x_lib.h>
改为 #include “stm32f1xx_hal.h”
7.修改lib_cfg.h文件代码
🎈 修改为5u:
8.修改usart.c文件代码
🎈 添加代码完成printf
重定向:
/* USER CODE BEGIN 1 */
int fputc(int ch,FILE *f){
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xffff);
return ch;
}
/* USER CODE END 1 */
9.修改usart.h文件代码
🎈 添加定义代码:
typedef struct __FILE FILE;
10.初始化管脚
🎈 在gpio.c
文件中修改代码:
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET);
/*Configure GPIO pin : PC13|PA3 */
GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
(七)撰写代码与效果实现
1.撰写主函数
🎈 修改main.c
文件代码:
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"
#include "usart.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <includes.h>
#include "stm32f1xx_hal.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* 任务优先级 */
#define START_TASK_PRIO 3
#define LED0_TASK_PRIO 4
#define MSG_TASK_PRIO 5
#define LED1_TASK_PRIO 6
/* 任务堆栈大小 */
#define START_STK_SIZE 96
#define LED0_STK_SIZE 64
#define MSG_STK_SIZE 64
#define LED1_STK_SIZE 64
/* 任务栈 */
CPU_STK START_TASK_STK[START_STK_SIZE];
CPU_STK LED0_TASK_STK[LED0_STK_SIZE];
CPU_STK MSG_TASK_STK[MSG_STK_SIZE];
CPU_STK LED1_TASK_STK[LED1_STK_SIZE];
/* 任务控制块 */
OS_TCB StartTaskTCB;
OS_TCB Led0TaskTCB;
OS_TCB MsgTaskTCB;
OS_TCB Led1TaskTCB;
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* 任务函数定义 */
void start_task(void *p_arg);
static void AppTaskCreate(void);
static void AppObjCreate(void);
static void led_pc13(void *p_arg);
static void send_msg(void *p_arg);
static void led_pa3(void *p_arg);
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/**Initializes the CPU, AHB and APB busses clocks
*/
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();
}
/**Initializes the CPU, AHB and APB busses clocks
*/
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();
}
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
OS_ERR err;
OSInit(&err);
HAL_Init();
SystemClock_Config();
//MX_GPIO_Init(); 这个在BSP的初始化里也会初始化
MX_USART1_UART_Init();
/* 创建任务 */
OSTaskCreate((OS_TCB *)&StartTaskTCB, /* Create the start task */
(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,
(OS_TICK ) 0,
(void *) 0,
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
(OS_ERR *)&err);
/* 启动多任务系统,控制权交给uC/OS-III */
OSStart(&err); /* Start multitasking (i.e. give control to uC/OS-III). */
}
void start_task(void *p_arg)
{
OS_ERR err;
CPU_SR_ALLOC();
p_arg = p_arg;
/* YangJie add 2021.05.20*/
BSP_Init(); /* Initialize BSP functions */
//CPU_Init();
//Mem_Init(); /* Initialize Memory Management Module */
#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 * )"led_pc13",
(OS_TASK_PTR )led_pc13,
(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 * )"led_pa3",
(OS_TASK_PTR )led_pa3,
(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);
/* 创建MSG任务 */
OSTaskCreate((OS_TCB * )&MsgTaskTCB,
(CPU_CHAR * )"send_msg",
(OS_TASK_PTR )send_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(); //进入临界区
}
/**
* 函数功能: 启动任务函数体。
* 输入参数: p_arg 是在创建该任务时传递的形参
* 返 回 值: 无
* 说 明:无
*/
static void led_pc13 (void *p_arg)
{
OS_ERR err;
(void)p_arg;
BSP_Init(); /* Initialize BSP functions */
CPU_Init();
Mem_Init(); /* Initialize Memory Management Module */
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err); /* Compute CPU capacity with no task running */
#endif
CPU_IntDisMeasMaxCurReset();
AppTaskCreate(); /* Create Application Tasks */
AppObjCreate(); /* Create Application Objects */
while (DEF_TRUE)
{
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);
OSTimeDlyHMSM(0, 0, 1, 0,OS_OPT_TIME_HMSM_STRICT,&err);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);
OSTimeDlyHMSM(0, 0, 1, 0,OS_OPT_TIME_HMSM_STRICT,&err);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
static void led_pa3 (void *p_arg)
{
OS_ERR err;
(void)p_arg;
BSP_Init(); /* Initialize BSP functions */
CPU_Init();
Mem_Init(); /* Initialize Memory Management Module */
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err); /* Compute CPU capacity with no task running */
#endif
CPU_IntDisMeasMaxCurReset();
AppTaskCreate(); /* Create Application Tasks */
AppObjCreate(); /* Create Application Objects */
while (DEF_TRUE)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_RESET);
OSTimeDlyHMSM(0, 0, 3, 0,OS_OPT_TIME_HMSM_STRICT,&err);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET);
OSTimeDlyHMSM(0, 0, 3, 0,OS_OPT_TIME_HMSM_STRICT,&err);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
static void send_msg (void *p_arg)
{
OS_ERR err;
(void)p_arg;
BSP_Init(); /* Initialize BSP functions */
CPU_Init();
Mem_Init(); /* Initialize Memory Management Module */
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err); /* Compute CPU capacity with no task running */
#endif
CPU_IntDisMeasMaxCurReset();
AppTaskCreate(); /* Create Application Tasks */
AppObjCreate(); /* Create Application Objects */
while (DEF_TRUE)
{
printf("hello uc/OS!欢迎来到RTOS多任务环境! \r\n");
OSTimeDlyHMSM(0, 0, 2, 0,OS_OPT_TIME_HMSM_STRICT,&err);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/* USER CODE BEGIN 4 */
/**
* 函数功能: 创建应用任务
* 输入参数: p_arg 是在创建该任务时传递的形参
* 返 回 值: 无
* 说 明:无
*/
static void AppTaskCreate (void)
{
}
/**
* 函数功能: uCOSIII内核对象创建
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
static void AppObjCreate (void)
{
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
2.环境配置
🎈 如图所示:
3.电路搭建
🎈 烧录无报错。
🎈 电路搭建如下
4.效果实现如下
🎈 最后我们实现了两个task分别以1s和3s周期对LED灯进行点亮-熄灭的控制。
🎈 UC/OS实现串口数据周期发送:
(八)总结
🎈 本文介绍了通过嵌入式实时操作系统(RTOS),以uc/OS为例,将其移植到stm32F103上,构建至少3个任务(task):其中两个task分别以1s和3s周期对LED灯进行点亮-熄灭的控制;另外一个task以2s周期通过串口发送“hello uc/OS! 欢迎来到RTOS多任务环境!”。记录并详细的讲解移植过程。
🎈 本次实操不难,主要是难在去理解uc/OS实时操作系统,移植uc/OS文件这一点操作需要非常仔细,出错一点,就牵一发动全身,需要修改的文件代码非常多,需要一步一步的去完成,最后编写成功的时候,还是特别有成就感,实验效果是一遍就出来,但过程却比较艰难,需要细心仔细。
🎈 同时,学嵌入式系统开发以来,不仅对课程的理解有所提升,对自己的生活态度与人生规划也带来了颇多感悟。
愿每一个孩子成长后明白,无论贫穷,无论富贵,无论教育如何,开心健康才是最最最重要的!!!
寄语:或许从未有长大一说
只有学会不断成长
这一次真情流露
即使生活难如泥,但总有阳光照入心灵

(九)参考文献
[1]https://blog.csdn.net/qq_52171727/article/details/127656806
[2]https://blog.csdn.net/qq_52791446/article/details/127638893
[3]https://blog.csdn.net/x1131230123/article/details/106485849
[4]https://blog.csdn.net/qq_45659777/article/details/121570886