嵌入式实时操作系统RTOS_使用CubeMX进行FreeRTOS的任务创建_软件定时器的使用
FreeRTOS简介
CMSIS-RTOS v2
- 在CubeMX软件的中间件已经集成了FreeRTOS,可以自动生成FreeRTOS的程序框架,省去了移植RTOS的步骤,因此以FreeRTOS为例来讲解RTOS的具体使用
- 此外,我们并不使用FreeRTOS原有的接口函数,而是使用经过封装的函数,这一封装称为CMSIS-RTOS v2,这是一套由ARM提出,针对Cortex-M处理器的RTOS软件接口标准,使得跨操作系统的调用与移植更加方便
FreeRTOS的初始化配置
-
启用FreeRTOS
使用CubeMX配置使用FreeRTOS步骤如下:
- 在Pinout & configuration 页面的左侧栏中展开Middleware and Software Pack,勾选FREERTOS,进入FreeRTOS的配置页面
- 在Mode栏中选择Interface 为CMSIS-V2
- 在Configuration 栏的config parameters 标签页配置RTOS的基本属性
注意:由于新版本的keil将编译器升级为了AC6,因此要进行如下操作让编译正常:
将
C:\Users\你的用户名\STM32Cube\Repository\你要设置的单片机型号\Middlewares\Third_Party\FreeRTOS\Source\portable\GCC\ARM_CM4F中的两个文件port.c和portmacro.h复制到C:\Users\你的用户名\STM32Cube\Repository\你要设置的单片机型号\Middlewares\Third_Party\FreeRTOS\Source\portable\RVDS\ARM_CM4F中替换原来的文件如果安装路径不一样找不到
Repository文件夹,可以在CubeMX中在Help菜单栏中打开Connection&Updates来看Repository文件夹路径 -
FreeRTOS的属性
-
USE_PREEMPTION:是否启用抢占式调度,默认支持
-
TICK_RATE_HZ:定义系统节拍数,默认为1000Hz,即1ms
-
MAX_PRIORTIES:定义可供用户使用的最大优先级数,默认为56,即0~55可用,数字越大优先级越高(与中断优先级编号越小越高相反)
-
MINIMAL_STACK_SIZE:任务栈的大小,默认为512B
-
TOTAL_HEAP_SIZE:堆空间的大小,默认为15360字节,用于创建任务、信号量、消息队列以及软件计数器的使用;
任务的创建、任务栈的分配、各种系统服务的创建都需要从堆空间分配内存,如果用户创建了较多的任务或系统服务或者定义了较多的局部变量,可以根据实际情况增加该值
-
TIMER_TASK_PRIORITY:设置软件定时器的优先级,默认为2
-
-
时钟节拍
时钟节拍是一个周期性的定时中断,也即前文介绍的SysTick,是延时、超时、时间片流转的基本单位,如延时函数的功能便是将任务延时若干整数倍的时钟节拍
需要注意的是,前文介绍的SysTick已被用作HAL库的时间基准,为了避免HAL库与RTOS相冲突,需要修改HAL库的时间基准为其他定时器,把SysTick留给RTOS
修改方法如下:
-
在Pinout & configuration 页面的左侧栏中展开System Core,选择SYS
-
将Timebase Source从Systick修改为某个TIM,如TIM10
-
为设定的定时器编写实现HAL库时间基准的中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance==TIM10)//使用TIM10产生时间基准 { HAL_IncTick(); } }
-
任务管理的接口函数
-
任务创建函数osThreadNew
用于创建一个新任务
-
任务优先级设置函数osThreadSetPriority
设置任务的优先级,在CMSIS-RTOS2的封装中将56个优先级分为六大类:
- osPriorityIdle:最低优先级,为空闲任务保留,对应1
- osPriorityLowN,低优先级;osPriorityBelowNormalN,低于正常优先级;osPriorityNormalN,正常优先级;osPriorityAboveNormalN,高于正常优先级;osPriorityHighN,高优先级,以上分类各含8个优先级编号
- osPriorityRealtimeN,实时优先级,对应48-55
- osPriorityISR,最高优先级,为ISR延迟任务,即由中断触发的任务保留,不能用于用户的任务,对应56
-
以上两个函数都可由CubeMX自动生成,在接下来任务创建一节中介绍如何配置
-
延时函数osDelay
调用了延时函数的任务将进入阻塞态,并立即进行任务调度,经过指定的时间节拍后,任务会自动返回就绪态
该函数不能在中断服务程序中使用
任务创建
任务创建的初始化配置
-
使用CubeMX来创建任务
- 在FreeRTOS的配置窗口中,选择Task and Queues 标签页,进行任务的创建(默认已经创建了一个defaultTask)
- 点击Add按钮创建新任务
- 双击创建的任务,弹出Edit Task,可对任务名、任务函数Entry Function、优先级、任务栈大小进行修改
-
系统内存使用情况
在完成了所有的任务和系统服务的配置后,系统内存的最终使用情况可在FreeRTOS Heap Usage标签页中看到
- HEAP STILL AVAILABLE 表示可用的堆空间大小
- TOTAL HEAP USED 表示已经使用的堆空间大小
- 其余各行是各个系统服务所占用的堆空间大小,可根据使用情况修改TOTAL_HEAP_SIZE
任务创建的代码实现
-
任务创建的主程序
以下内容由CubeMX自动生成
#include "main.h" #include "cmsis_os.h"//使用CMSIS-RTOS2封装后接口函数的头文件 int main() { osKernelInitialize();//初始化RTOS内核 MX_FREERTOS_Init();//此函数中包含了创建任务的函数 { defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes); //在CubeMX中创建任务后,自动生成这个任务的句柄以及属性结构体 } osKernelStart();//启动内核,开始任务调度 //启动内核后while(1)中的代码将不会执行 } -
任务函数
void StartDefaultTask(void *argument) { /* USER CODE BEGIN StartDefaultTask */ /* Infinite loop */ for(;;) { //此处为任务代码 osDelay(1); } /* USER CODE END StartDefaultTask */ }
软件定时器
软件定时器概述
-
硬件与软件定时器
某个任务周期性地/延时确定时间执行是常用的功能需求,我们往往需要定时器来实现这一点,而我们之前介绍的硬件定时器虽然精确,但有数量有限,不同硬件操作代码不同不便于移植的问题
-
而软件定时器基于系统节拍,模拟硬件的功能,具备一定的通用性,数量也不受限制,FreeRTOS中提供了软件定时器的功能,经CMSIS-RTOS v2 封装有后文介绍的接口函数
-
软件定时器的分类
-
单次触发
osTimerOnce:执行一次回调函数后不再运行 -
周期触发
osTimerPeriodic:重复执行回调函数,直到用户停止或删除该定时器
-
软件定时器的接口函数
-
软件定时器创建函数osTimerNew
创建一个单次触发
osTimerOnce或周期触发osTimerPeriodic的软件定时器,并将其与回调函数相连,创建后的定时器处于停止状态不能从中断服务程序中调用
osTimerId_t osTimerNew(osTimerFunc_t func, osTimerType_t type, void *argument, const osTimerAttr_t *attr) //func:指向回调函数的函数指针 //type:osTimerOnce表示单次触发定时器, osTimerPeriodic表示周期触发定时器 //argument:回调函数的入口参数 //attr:定时器属性的指针,默认值为NULL //返回值为定时器的句柄,用于标识软件定时器 -
软件定时器启动函数osTimerStart
不能从中断服务程序中调用
osStatus_t osTimerStart(osTimerId_t timer_id, uint32_t ticks) //timer_id:要启动的软件定时器 //ticks:对单次触发的定时器表示延时,对周期触发的定时器表示周期 //具体数值根据系统节拍与需求决定 -
软件定时器停止函数
不能从中断服务程序中调用
osStatus_t osTimerStop(osTimerId_t timer_id)
软件定时器的代码实现
-
创建两个软件定时器,一个每隔1s触发一次,另一个用于控制指示灯在系统启动5s后再开启
//创建并启动软件定时器,写在main里 osTimerId_t ComTimeHandle; osTimerId_t LEDTimeHandle; ComTimeHandle = osTimerNew(ComCallback, osTimerPeriodic, NULL, NULL); LEDTimeHandle = osTimerNew(LEDCallback, osTimerOnce, NULL, NULL); osTimerStart(ComCallback,1000); osTimerStart(LEDCallback,5000); -
还要写回调函数
void ComCallback(void *argument) { //每隔1s触发一次 } void LEDCallback(void *argument) { //只触发一次 }
本文来自博客园,作者:无术师,转载请注明原文链接:https://www.cnblogs.com/artlessist/p/19415203
本文使用知识共享4.0协议许可 CC BY-NC-SA 4.0
特别说明版权归属的文章以及不归属于本人的转载内容(如引用的文章与图片)除外

浙公网安备 33010602011771号