文章目录
基于stm32f103C8T6移植uc/OS-III实时操作系统
一、任务要求
学习嵌入式实时操作系统(RTOS),以uc/OS-III为例,将其移植到stm32F103上,构建至少3个任务(task)。
task1: 以1s为周期对LED0进行点亮—-熄灭控制;
task2: 以 3s为周期对LED1进行点亮—-熄灭控制;
task3: 以2s周期通过串口发送“hello uc/OS! 欢迎来到RTOS多任务环境!”
二、系统原理介绍
1.RTOS实时操作系统
实时操作系统通常包含实时内核和其他高级服务,例如文件管理、协议栈、图形用户界面(GUI)和其他组件。大多数附加服务都围绕 I/O 设备。
实时内核是管理微处理器、微控制器或数字信号处理器 (DSP) 的时间和资源的软件。实时应用程序的设计过程涉及将工作分解为任务,每个任务负责一部分工作。任务(也称为线程)是一个简单的程序,它认为自己完全拥有中央处理单元 (CPU)。在单个 CPU 上,在任何给定时间仅执行一个任务。任务通常也被实现为无限循环。
内核负责任务的管理。这称为多任务处理。多任务处理是在多个任务之间调度和切换 CPU 的过程。CPU 在多个连续任务之间切换注意力。多任务处理提供了拥有多个 CPU 的错觉,并最大限度地利用了 CPU。多任务处理还有助于创建模块化应用程序。多任务处理最重要的方面之一是它允许应用程序程序员管理实时应用程序固有的复杂性。使用多任务处理时,应用程序更易于设计和维护。
实时操作系统在使用时间片的方式,通过中断跳转到不同任务中执行,设置时间片可以控制任务的跳转频率,因此,在实时操作系统中,每一个任务都有自己单独的堆栈区,且有对应的寄存器来记录任务执行的状态,当切换到其他任务又切换回来时,可以通过寄存器的状态继续执行代码,通过这种方式能够保证其实时性,且其也存在低级任务、高级任务等优先级,通过控制ISR可以控制任务的调度。
2.uc/OS-III操作系统
(1)特性和功能
uC/OS 是一个免费公开源代码、结构小巧、具有可剥夺实时内核的实时操作系统。它分为uC/OS-II 和uC/OS-III 两个版本。uC/OS-II 是一个实时操作系统内核,仅包含任务调度、任务管理、时间管理、内存管理和任务间的通信和同步等基本功能。uC/OS-III 是一个可升级的、可固化的、基于优先级的实时内核,它对任务的个数无限制,并且支持现代的实时内核所期待的大部分功能,例如资源管理、同步、任务间的通信等。
µC/OS-III 是一个高度可移植、可ROM、可扩展、抢占式、实时、确定性、多任务处理内核,适用于微处理器、微控制器和 DSP。
注释:
(1) 低优先级任务正在执行。
(2)中断发生,CPU转向负责服务中断设备的ISR。
(3)ISR为中断设备提供服务,但实际上做的工作很少。ISR 通常会向较高优先级任务发出信号或发送消息,该任务将负责中断设备的大部分处理。例如,如果中断来自以太网控制器,ISR 只是发出任务信号,该任务将处理接收到的数据包。
(4) 当ISR完成时,μC/OS-III注意到ISR已经准备好运行一个更重要的任务,并且不会返回到被中断的任务,而是上下文切换到更重要的任务。
(5)高优先级任务响应中断设备执行并进行必要的处理。
(6) 当优先级较高的任务完成其工作时,它会循环回到任务代码的开头,并调用 µC/OS-III 函数来等待来自设备的下一个中断。
(7) 低优先级任务正好从中断的地方恢复,不知道发生了什么。
以下是 µC/OS-III 提供的功能列表:
源代码: μC/OS-III 以 ANSI-C 源代码形式提供。µC/OS-III 的源代码可以说是最干净、最一致的内核代码。清洁来源是 Micriµm 企业文化的一部分。尽管许多商业内核供应商为其产品提供源代码,但除非代码遵循严格的编码标准,并附有完整的文档和示例来展示代码的工作原理,否则这些产品可能会很麻烦且难以利用。
直观的应用程序编程接口(API): μC/OS-III 非常直观。一旦熟悉了所使用的一致的编码约定,就可以很容易地预测调用所需服务的函数,甚至预测需要哪些参数。例如,指向对象的指针始终是第一个参数,而指向错误代码的指针始终是最后一个参数。
抢占式多任务处理: μC/OS-III是抢占式多任务内核,因此,μC/OS-III始终运行最重要的准备运行任务。
同等优先级的任务循环调度: μC/OS-III允许多个任务以相同的优先级运行。当具有相同优先级的多个任务准备运行并且该优先级是最重要的级别时,μC/OS-III 会在用户指定的时间(称为时间量子)内运行每个任务。每个任务都可以定义自己的时间量子,如果一个任务不需要完整的时间量子,也可以将CPU让给另一个具有相同优先级的任务。
低中断禁用时间: μC/OS-III 有许多需要原子访问的内部数据结构和变量。为了确保这一点,μC/OS-III 能够通过锁定调度程序而不是禁用中断来保护这些关键区域。因此中断被禁用的时间很短。这确保了 µC/OS-III 能够响应一些最快的中断源。
确定性: μC/OS-III 的中断响应是确定性的。此外,μC/OS-III 提供的大多数服务的执行时间都是确定的。
可扩展:可以根据应用程序的要求调整占用空间(代码和数据)。添加和删除功能(即服务)是在编译时执行大约 60 个 #defines(请参阅参考资料os_cfg.h)。µC/OS-III 还对传递给 µC/OS-III 服务的参数执行许多运行时检查。具体来说,μC/OS-III 验证用户没有传递NULL指针、没有从 ISR 调用任务级服务、参数是否在允许的范围内以及指定的选项是否有效等。可以禁用这些检查(在编译时)进一步减少代码占用并提高性能。µC/OS-III 的可扩展性使其能够用于广泛的应用和项目。
可移植性: μC/OS-III可以移植到大量的CPU架构上。大多数 µC/OS-II 端口都可以轻松转换为在 µC/OS-III 上工作,只需几分钟即可进行最小的更改,因此可以受益于 µC/OS-II 已支持的超过 45 个 CPU 架构。
可ROMable: μC/OS-III 专为嵌入式系统而设计,可与应用程序代码一起进行ROM 化。
运行时可配置: μC/OS-III允许用户在运行时配置内核。具体来说,所有内核对象,例如任务、堆栈、信号量、事件标志组、消息队列、消息数量、互斥信号量、内存分区和计时器,都是由用户在运行时分配的。这可以防止在编译时过度分配资源。
无限数量的任务: μC/OS-III支持无限数量的任务。然而,从实际角度来看,任务数量实际上受到处理器可以访问的内存量(代码和数据空间)的限制。每个任务都需要自己的堆栈空间,μC/OS-III 提供的功能允许在运行时监视任务的堆栈增长。
µC/OS-III 对每个任务堆栈的大小没有任何限制,只是根据所使用的 CPU 有一个最小大小。
无限数量的优先级: μC/OS-III 支持无限数量的优先级。然而,将 µC/OS-III 配置为 32 到 256 个不同的优先级对于大多数应用来说已经足够了。
无限数量的内核对象: μC/OS-III 允许任意数量的任务、信号量、互斥信号量、事件标志、消息队列、定时器和内存分区。用户在运行时分配所有内核对象。
服务: μC/OS-III提供高端实时内核所期望的所有服务,例如任务管理、时间管理、信号量、事件标志、互斥体、消息队列、软件定时器、固定大小内存池等。
**互斥信号量(互斥信号量):**互斥信号量用于资源管理。互斥体是特殊类型的信号量,具有内置优先级继承,可消除无限制的优先级反转。对互斥体的访问可以嵌套,因此一个任务最多可以获取相同的互斥体 250 次。当然,互斥锁拥有者需要释放相同次数的互斥锁。
嵌套任务挂起: μC/OS-III 允许任务挂起自身或另一个任务。挂起一个任务意味着该任务将不被允许执行,直到该任务被另一个任务恢复。悬架最多可嵌套 250 层。换句话说,一个任务最多可以挂起另一个任务 250 次。当然,任务必须恢复相同次数才能在 CPU 上运行。
软件计时器:您可以定义任意数量的“一次性”和/或“周期性”计时器。定时器是倒数计数器,在倒数到 0 时执行用户可定义的操作。每个定时器都可以有自己的操作,如果定时器是周期性的,则定时器会自动重新加载,并在每次倒数达到零时执行该操作。
任务信号: μC/OS-III 允许 ISR 或任务直接向任务发出信号。这避免了仅仅为了发出任务信号而创建中间内核对象(例如信号量或事件标志),并带来更好的性能。
任务消息: μC/OS-III 允许 ISR 或任务直接向任务发送消息。这避免了创建和使用消息队列,并且还获得了更好的性能。
任务寄存器:每个任务可以有用户可定义数量的“任务寄存器”。任务寄存器与 CPU 寄存器不同。任务寄存器可用于保存“errno”类型变量、ID、每个任务的中断禁用时间测量等等。
错误检查: μC/OS-III 验证是否NULL未传递指针、用户是否未从 ISR 调用任务级服务、参数是否在允许的范围内、指定的选项是否有效、是否传递了指向正确对象的指针作为操纵所需对象的服务参数的一部分等等。每个 µC/OS-III API 函数都会返回一个与函数调用结果相关的错误代码。
内置性能测量: μC/OS-III具有内置功能,可以测量每个任务的执行时间、每个任务的堆栈使用情况、任务执行次数、CPU使用率、ISR-to-task和task-to -任务响应时间、某些列表中的峰值条目数、每个任务的中断禁用和调度程序锁定时间等等。
内置跟踪点: μC/OS-III 在整个代码中内置了跟踪点,可以使用一些最流行的跟踪分析工具(例如 Percepio 的 TraceAlyzer 和 SEGGER SystemView)实时记录所有内核事件和中断。
易于优化: μC/OS-III 的设计使其可以基于 CPU 架构轻松优化。µC/OS-III 中使用的大多数数据类型都可以更改,以更好地利用 CPU 的自然字大小。此外,优先级解析算法可以轻松地用汇编语言编写,以受益于特殊指令,例如位设置和清除,以及计数前导零 (CLZ) 或查找第一个 (FF1) 指令。
死锁预防:所有 µC/OS-III“挂起”服务都包含超时,这有助于避免死锁。
任务级的节拍处理: μC/OS-III 中的时钟节拍管理器是通过从 ISR 接收触发的任务来完成的。通过任务处理延迟和超时可以大大减少中断延迟。此外,μC/OS-III使用散列增量列表机制,进一步减少了处理任务延迟和超时的开销。
用户可定义的钩子: μC/OS-III 允许端口和应用程序员定义“钩子”函数,由μC/OS-III 调用。挂钩只是一个定义的函数,允许用户扩展 µC/OS-III 的功能。在上下文切换期间调用一个这样的钩子,在创建任务时调用另一个钩子,在删除任务时调用另一个钩子,等等。
时间戳:对于时间测量,μC/OS-III 要求提供 16 位或 32 位自由运行计数器。可以在运行时读取该计数器以对某些事件进行时间测量。例如,当 ISR 将消息发布到任务时,时间戳计数器会自动读取并保存为发布消息的一部分。当接收者接收到消息时,时间戳被提供给接收者,通过读取当前时间戳,可以确定接收到消息所花费的时间。
对内核感知调试器的内置支持:此功能允许内核感知调试器以用户友好的方式检查和显示 µC/OS-III 变量和数据结构。µC/OS-III 中的内核感知支持可被 µC/Probe 用于在运行时显示此信息。
对象名称:每个 µC/OS-III 内核对象都可以有一个与其关联的名称。这使得很容易识别对象被分配给什么。因此,您可以为任务、信号量、互斥体、事件标志组、消息队列、内存分区和计时器分配 ASCII 名称。对象名称可以具有任意长度,但必须以 NUL 结尾。
(2)系统架构
注释:
(1) 应用代码由项目或产品文件组成。为了方便起见,这些文件被简单地称为app.c和app.h,但是应用程序可以包含任意数量的文件,而不必称为 app.*。应用程序代码通常是可以找到main().
(2) 半导体制造商通常以源代码形式提供库函数,用于访问其 CPU 或 MCU 上的外设。这些库非常有用,通常可以节省宝贵的时间。由于这些文件没有命名约定,.c因此.h是假设的。
(3) µC/OS-III 所需的板支持包 (BSP) 代码通常非常简单,一般来说,µC/OS-III 仅需要初始化一个用于时间延迟和超时的周期性中断源。该功能应与其相应的头文件 一起放置在名为 os_bsp.c 的文件中bsp_os.h。
(4) 这是与 µC/OS-III 处理器无关的代码。该代码是用高度可移植的 ANSI C 编写的。
(5) 这是适应特定CPU架构的μC/OS-III代码,称为端口。µC/OS-III 源于 µC/OS-II,并受益于能够使用 µC/OS-II 可用的 45 个左右端口中的大部分。然而,μC/OS-II 端口需要进行一些小的更改才能与μC/OS-III 配合使用。
(6) 在 Micriμm,我们封装了 CPU 功能。这些文件定义了禁用和启用中断的函数、CPU_???独立于所使用的 CPU 和编译器的数据类型以及更多函数。
(7) µC/LIB 是一系列源文件,提供内存复制、字符串和 ASCII 相关函数等常用功能。有些偶尔用来替换stdlib编译器提供的函数。提供这些文件是为了确保它们在应用程序之间、特别是在编译器之间完全可移植。µC/OS-III 不使用这些文件,但 µC/CPU 使用。
(8)可选模块:一些编译器提供了Thread-local Storage的扩展,它提供了一个变量区域,线程可以在其中存储它们的状态,以使多线程环境更安全。这些功能的调整是在os_tls.c该uCOS-III\TLS<tool>文件夹下的文件中执行的。其中是工具制造商的名称或工具名称。
(9)可选模块:μC/OS-III在整个代码中内置了跟踪点,可以使用一些最流行的跟踪分析工具(例如Percepio的TraceAlyzer和SEGGER SystemView)实时记录所有内核事件和中断。这些跟踪调用默认被禁用,并且只能在编译时通过将配置常量设置OS_CFG_TRACE_EN为DEF_ENABLEDin来启用os_cfg.h。这些跟踪函数在该文件夹下的多个文件中定义uCOS-III\Trace<tool>。跟踪工具的名称在哪里。
(10) 配置文件用于定义要包含os_cfg.h在应用程序中的 µC/OS-III 功能 ( ),指定 µC/OS-III ( os_cfg_app.h) 期望的某些变量和数据结构的大小,例如空闲任务堆栈大小、tick速率、消息池大小、配置应用程序员可用的 µC/CPU 功能 ( cpu_cfg.h) 以及配置 µC/LIB 选项 ( lib_cfg.h)。
三、实验过程
1.建立CubeMX工程
(1)选择芯片
STM32F103C8T6
(2)配置RCC
(3)配置SYS
(4)串口配置USART1
(5)GPIO口设置
设置PA3 PC13作为两个LED灯的端口,将与LED相连的两个端口PA3 PC13配置为GPIO_Output,可根据LED现象作为是否移植成功的依据。
(6)设置路径
按之前教程设置好到处keil文件即可。
2.获取uCOS-III源码
进入 Micrium 公司官网下载中心http://micrium.com/downloadcenter/
选择ST系列,点击 View all STMicroelectronics
,点击 STMicroelectronics STM32F107之后按照提示注册下载即可。
3.移植前的文件准备
(1)打开下载好的源码
文件夹下的文件已经复制添加好了,如下图所示
(2)将uCOS的5个相关文件复制到cubeMX工程的MDK-ARM文件夹下
4.移植过程
打开cubeMX生成的keil文件
(1)将uCOS文件添加到项目中
点击Manage Project Items
,按下图所示操作,为项目新建文件夹,在对应文件夹下添加文件。
(2)点击CPU–>Add Files…
MDK-ARM\uC-CPU路径下选中以下文件,Add添加
MDK-ARM\uC-CPU\ARM-Cortex-M3\RealView路径下选中以下文件,Add添加
(3)点击LIB–>Add Files…
MDK-ARM\uC-LIB
路径下选中下图红框文件,Add添加
MDK-ARM\uC-LIB\Ports\ARM-Cortex-M3\RealView
路径下选中下图绿框文件,Add添加
(4)点击PORT–>Add Files…
MDK-ARM\uCOS-III\Ports\ARM-Cortex-M3\Generic\RealView
路径下选中以下文件,Add添加
(5)点击SOURCE–>Add Files…
MDK-ARM\uCOS-III\Source
路径下选中以下全部 .c .h 文件,Add添加
(6)点击CONFIG–>Add Files…
MDK-ARM\uC-CONFIG
路径下选中以下全部文件,Add添加
(7)点击BSP–>Add Files…
MDK-ARM\uC-BSP
路径下选中以下全部文件,Add添加
全部添加完毕后,点击OK
5.导入文件路径
按下图所示步骤操作
6.构建三个任务
(1)代码添加
bsp.h
#ifndef __BSP_H__
#define __BSP_H__
#include "stm32f1xx_hal.h"
void BSP_Init(void);
#endif
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
(2)修改部分文件相关代码
打开startup_stm32f103xb.s
文件
在以下位置处将PendSV_Handler改为OS_CPU_PendSVHandler,
SysTick_Handler改为OS_CPU_SysTickHandler
打开app_cfg.h
文件
DEF_ENABLED 改为 DEF_DISABLED
#define APP_TRACE BSP_Ser_Printf 改为 #define APP_TRACE(void)
打开includes.h
文件
在#include <bsp.h>下面添加 #include “gpio.h” #include “app_cfg.h”
将#include <stm32f10x_lib.h> 改为 #include “stm32f1xx_hal.h”
打开lib_cfg.h
文件
修改为5(该处宏定义设置堆空间的大小,STM32F103C8T6的RAM只有20K,所以要改小一点)
打开usart.c
文件,添加代码完成printf重定向
/* USER CODE BEGIN 1 */
typedef struct __FILE FILE;
int fputc(int ch,FILE *f){
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xffff);
return ch;
}
/* USER CODE END 1 */
(3)初始化管脚
在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);
}
(4)撰写主函数
修改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 \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****/
编译烧录。
四、实验结果
led 1s和2s
发送
五、总结
当我们需要处理器完成的任务比较多,为了便于管理可以引入实时操作系统,这能很方便准确的将所有任务进行整合。
学习了这个操作系统为后续大作业有了很大的帮助。但是在进行移植时要仔细,否则在编译时很容易报一大堆错误。
参考博客
https://blog.csdn.net/qq_46467126/article/details/121441622
https://blog.csdn.net/sjsnsnsnsi/article/details/134612198