大家好,欢迎来到IT知识分享网。
目录
一、前提
1.1 任务要求
学习嵌入式实时操作系统(RTOS),以uc/OS-III为例,将其移植到stm32F103上,构建至少3个任务(task)。
task1: 以1s为周期对LED0进行点亮—-熄灭控制;
task2: 以 3s为周期对LED1进行点亮—-熄灭控制;
task3: 以2s周期通过串口发送“hello uc/OS! 欢迎来到RTOS多任务环境!”
记录详细的移植过程。
1.2 超级循环系统
在之前的实验中,没有操作系统的实时性很差,不能实时的传输数据,通过一个while死循环来进行程序的运行,这种系统被称为前后台系统或者超级循环系统,这种系统一般只适用于小项目,低复杂度的小型系统。应用程序由调用模块(即任务)来执行所需操作(后台)的无限循环组成。中断服务例程 (ISR) 处理异步事件(前台)。前台也叫中断级;背景称为任务级别。应在任务级别执行的关键操作必须由 ISR 处理,以确保及时处理。这会导致 ISR 花费的时间比应有的时间长。此外,ISR 提供的后台模块的信息在后台例程轮到执行之前不会被处理,这称为任务级响应。最坏情况的任务级响应时间取决于后台循环执行所需的时间,并且由于典型代码的执行时间不是恒定的,因此连续通过循环部分的时间是不确定的。此外,如果更改代码,循环的时间也会受到影响。
1.3 RTOS实时操作系统
实时操作系统通常包含实时内核和其他高级服务,例如文件管理、协议栈、图形用户界面(GUI)和其他组件。大多数附加服务都围绕 I/O 设备。
实时内核是管理微处理器、微控制器或数字信号处理器 (DSP) 的时间和资源的软件。实时应用程序的设计过程涉及将工作分解为任务,每个任务负责一部分工作。任务(也称为线程)是一个简单的程序,它认为自己完全拥有中央处理单元 (CPU)。在单个 CPU 上,在任何给定时间仅执行一个任务。任务通常也被实现为无限循环。
内核负责任务的管理。这称为多任务处理。多任务处理是在多个任务之间调度和切换 CPU 的过程。CPU 在多个连续任务之间切换注意力。多任务处理提供了拥有多个 CPU 的错觉,并最大限度地利用了 CPU。多任务处理还有助于创建模块化应用程序。多任务处理最重要的方面之一是它允许应用程序程序员管理实时应用程序固有的复杂性。使用多任务处理时,应用程序更易于设计和维护。
实时操作系统在使用时间片的方式,通过中断跳转到不同任务中执行,设置时间片可以控制任务的跳转频率,因此,在实时操作系统中,每一个任务都有自己单独的堆栈区,且有对应的寄存器来记录任务执行的状态,当切换到其他任务又切换回来时,可以通过寄存器的状态继续执行代码,通过这种方式能够保证其实时性,且其也存在低级任务、高级任务等优先级,通过控制ISR可以控制任务的调度。
1.4 uc/OS-III操作系统简介
1.4.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 企业文化的一部分。尽管许多商业内核供应商为其产品提供源代码,但除非代码遵循严格的编码标准,并附有完整的文档和示例来展示代码的工作原理,否则这些产品可能会很麻烦且难以利用。通过本书,您将深入了解μC/OS-III的内部工作原理,从而保护您的投资。
直观的应用程序编程接口(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 结尾。
1..4.2 系统架构
下图展示了μC/OS-III架构及其与硬件的关系。当然,除了定时器和中断控制器之外,硬件很可能还包含其他设备,例如通用异步接收发送器 (UART)、模数转换器 (ADC)、以太网控制器等。
(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>文件夹下的文件中执行的。其中<tool>是工具制造商的名称或工具名称。 (9)可选模块:μC/OS-III在整个代码中内置了跟踪点,可以使用一些最流行的跟踪分析工具(例如Percepio的TraceAlyzer和SEGGER SystemView)实时记录所有内核事件和中断。这些跟踪调用默认被禁用,并且只能在编译时通过将配置常量设置OS_CFG_TRACE_EN为DEF_ENABLEDin来启用os_cfg.h。这些跟踪函数在该文件夹下的多个文件中定义uCOS-III\Trace\<tool>。<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)。
二、STM32CubeMX配置
2.1. 选择stm32f103c8t6芯片
2.2. 配置RCC
2.3.配置SYS
2.4. 配置UART1
2.5. 配置GPIO
2.6. 配置时钟
2.7. 配置工程
三、移植过程
3.1、下载us/OS3源码
官网下载地址为:
Micrium Software and Documentation – Silicon Labs (silabs.com)
注册和登录账号,Q邮箱是不合法的,可以使用Google的邮箱
下载完成后,解压,里面有如下文件:
3.2、将源码加入工程中
将文件中的uC-CPU、uC-LIB、uCOS-III复制到第二步中生成的工程文件目录下:
复制bsp.c和bsp.h两个文件到上面新建的文件夹uC-BSP中:
路径为:Micrium\Software\EvalBoards\Micrium\uC-Eval-STM32F107\BSP
复制一下7个文件到上面新建的文件夹uC-CONFIG中
该7个文件目录为:
Micrium\Software\EvalBoards\Micrium\uC-Eval-STM32F107\uCOS-III
3.3、在Keil中加入上述文件
打开工程,在工程中新添分组:
在CPU分组中添加一下文件:
前三个文件位置在:MDK-ARM\uC-CPU
后三个文件的位置在:MDK-ARM\uC-CPU\ARM-Cortex-M3\RealView
进入Manage Project Items,点Add Files 然后在上面的目录中添加上面六个文件
在lib分组中加入十个文件,前9个文件位置为:MDK-ARM\uC-LIB
第十个文件位置为 MDK-ARM\uC-LIB\Ports\ARM-Cortex-M3\RealView
在PORT分组中加入3个文件,位置为:MDK-ARM\uCOS-III\Ports\ARM-Cortex-M3\Generic\RealView
在SOURCE分组中加入源码,位置为:MDK-ARM\uCOS-III\Source
在CONFIG分组中加入配置文件,位置为: MDK-ARM\uCOS-CONFIG
在BSP分组中加入bsp.c和bsp.h文件,位置为: MDK-ARM\uCOS-BSP
完成上述配置之后,还需要添加头文件路径:
通过上述配置,完成源码的移植,但是还需要对源码进行更改。
3.4、代码编写
3.4.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
3.4.2 添加串口printf重定义函数
在usart.c中添加重定义函数:
typedef struct __FILE FILE; int fputc(int ch,FILE *f){ HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xffff); return ch; }
3.4.3 编写主函数
/* 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/
3.4.4 更改部分代码
在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)
3.4.4 配置内存
四、实验效果
4.1 task1&&task2
左边的灯位LED0,可以看出1s闪烁一次
右边的灯位LED1,3s闪烁一次
4.2 task3
五、总结与参考
学会了us/OS的RTOS移植方法,创建多任务,并且执行,清楚RTOS与Superloop两种系统的区别,以及RTOS的优越性。当项目复杂度较小时,通常采用Superloop的架构比较简单,因为其代码量少,实现简单。当项目复杂度很高时,需要使用操作系统,RTOS具备非常重要的实时性特性,且也支持优先级功能。
源码下载:
交流群 :
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/141357.html