FreeRTOS学习(下)

九、任务通知

什么是任务通知?

FreeRTOS 从版本 V8.2.0 开始提供任务通知这个功能,每个任务都有一个 32 位的通知值。按照
FreeRTOS 官方的说法,使用消息通知比通过二进制信号量方式解除阻塞任务快 45% , 并且更加
省内存(无需创建队列)。
在大多数情况下,任务通知可以替代二值信号量、计数信号量、事件标志组,可以替代长度为 1
的队列(可以保存一个 32 位整数或指针值),并且任务通知速度更快、使用的 RAM 更少!

任务通知值的更新方式

FreeRTOS 提供以下几种方式发送通知给任务 :
发送消息给任务,如果有通知未读, 不覆盖通知值
发送消息给任务,直接覆盖通知值
发送消息给任务,设置通知值的一个或者多个位
发送消息给任务,递增通知值
通过对以上方式的合理使用,可以在一定场合下替代原本的队列、信号量、事件标志组等。

任务通知的优势和劣势

任务通知的优势
1. 使用任务通知向任务发送事件或数据,比使用队列、事件标志组或信号量快得多。
2. 使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构体
任务通知的劣势
1. 只有任务可以等待通知,中断服务函数中不可以,因为中断没有 TCB
2. 通知只能一对一,因为通知必须指定任务。
3. 等待通知的任务可以被阻塞, 但是发送消息的任务,任何情况下都不会被阻塞等待。
4. 任务通知是通过更新任务通知值来发送数据的,任务结构体中只有一个任务通知值,只能保
持一个数据。

函数

 

BaseType_t xTaskNotify ( TaskHandle_t xTaskToNotify ,
uint32_t ulValue ,
eNotifyAction eAction );
参数:
xTaskToNotify :需要接收通知的任务句柄;
ulValue :用于更新接收任务通知值, 具体如何更新由形参 eAction 决定;
eAction :一个枚举,代表如何使用任务通知的值;

 

返回值:
如果被通知任务还没取走上一个通知,又接收了一个通知,则这次通知值未能更新并返回
pdFALSE , 而其他情况均返回 pdPASS
BaseType_t xTaskNotifyAndQuery ( TaskHandle_t xTaskToNotify ,
uint32_t ulValue ,
eNotifyAction eAction ,
uint32_t * pulPreviousNotifyValue );
参数:
xTaskToNotify :需要接收通知的任务句柄; ulValue :用于更新接收任务通知值, 具体如何更新
由形参 eAction 决定; eAction :一个枚举,代表如何使用任务通知的值;
pulPreviousNotifyValue :对象任务的上一个任务通知值,如果为 NULL , 则不需要回传, 这个时
候就等价于函数 xTaskNotify()
返回值:
如果被通知任务还没取走上一个通知,又接收了一个通知,则这次通知值未能更新并返回
pdFALSE , 而其他情况均返回 pdPASS
BaseType_t xTaskNotifyGive ( TaskHandle_t xTaskToNotify ); 参数:
xTaskToNotify :接收通知的任务句柄, 并让其自身的任务通知值加 1
返回值:
总是返回 pdPASS
2. 等待通知
等待通知 API 函数只能用在任务,不可应用于中断中!

 

uint32_t ulTaskNotifyTake ( BaseType_t xClearCountOnExit ,
TickType_t xTicksToWait );
参数:
xClearCountOnExit :指定在成功接收通知后,将通知值清零或减 1 pdTRUE :把通知值清零(二
值信号量); pdFALSE :把通知值减一(计数型信号量); xTicksToWait :阻塞等待任务通知值
的最大时间;
返回值:
0 :接收失败 非 0 :接收成功,返回任务通知的通知值
BaseType_t xTaskNotifyWait ( uint32_t ulBitsToClearOnEntry ,
uint32_t ulBitsToClearOnExit ,
uint32_t * pulNotificationValue ,
TickType_t xTicksToWait );
ulBitsToClearOnEntry :函数执行前清零任务通知值那些位 。 ulBitsToClearOnExit :表示在函数退
出前,清零任务通知值那些位,在清 0 前,接收到的任务通知值会先被保存到形参
*pulNotificationValue 中。 pulNotificationValue :用于保存接收到的任务通知值。 如果 不需要使
用,则设置为 NULL 即可 。 xTicksToWait :等待消息通知的最大等待时间。

实操

1. 模拟二值信号量
2. 模拟计数型信号量
3. 模拟事件标志组
4. 模拟邮箱   
邮箱就是大小为1的队列

十、延时函数

相对延时: vTaskDelay
绝对延时: vTaskDelayUntil

vTaskDelay 作用是让任务阻塞,任务阻塞后, RTOS 系统调用其它处于就绪状态的优先级最高的任
务来执行。
HAL_Delay 一直不停的调用获取系统时间的函数,直到指定的时间流逝然后退出,故其占用了全
CPU 时间。

十一、软件定时起

什么是定时器?

简单可以理解为闹钟,到达指定一段时间后,就会响铃。
STM32 芯片自带硬件定时器,精度较高,达到定时时间后会触发中断,也可以生成 PWM 、输入
捕获、输出比较,等等,功能强大,但是由于硬件的限制,个数有限。
软件定时器也可以实现定时功能,达到定时时间后可调用回调函数,可以在回调函数里处理信
息。

软件定时器优缺点

优点: 
1. 简单、成本低;
2. 只要内存足够,可创建多个;
缺点:
精度较低,容易受中断影响。在大多数情况下够用,但对于精度要求比较高的场合不建议使用
软件定时器原理
定时器是一个可选的、不属于 FreeRTOS 内核的功能,它是由定时器服务任务来提供的。
在调用函数 vTaskStartScheduler() 开启任务调度器的时候,会创建一个用于管理软件定时器的任
务,这个任务就叫做软件定时器服务任务。
1. 负责软件定时器超时的逻辑判断
2. 调用超时软件定时器的超时回调函数
3. 处理软件定时器命令队列
FreeRTOS 提供了很多定时器有关的 API 函数,这些 API 函数大多都使用 FreeRTOS 的队列发送命令给
定时器服务任务。 这个队列叫做定时器命令队列 。定时器命令队列是提供给 FreeRTOS 的软件定时
器使用的, 用户不能直接访问
单次定时器和周期定时器
单次定时器: 只超时一次,调用一次回调函数。可手动再开启定时器;
周期定时器: 多次超时,多次调用回调函数。

函数

 

1. 创建软件定时器
TimerHandle_t xTimerCreate
( const char * const pcTimerName ,
const TickType_t xTimerPeriod ,
const UBaseType_t uxAutoReload ,
void * const pvTimerID ,
TimerCallbackFunction_t pxCallbackFunction );
参数:
pcTimerName :软件定时器名称 xTimerPeriodInTicks :定时超时时间,单位:系统时钟节拍。宏
pdMS_TO_TICKS() 可用于将以毫秒为单位指定的时间转换为以 tick 为单位指定的时间。
uxAutoReload :定时器模式, pdTRUE :周期定时器, pdFALSE :单次定时器 pvTimerID :软件
定时器 ID ,用于多个软件定时器公用一个超时回调函数 pxCallbackFunction :软件定时器超时回
调函数
返回值:
成功:定时器句柄
失败: NULL 2. 开启软件定时器
BaseType_t xTimerStart ( TimerHandle_t xTimer ,
TickType_t xBlockTime );
参数:
xTimer :待开启的软件定时器的句柄 xTickToWait :发送命令到软件定时器命令队列的最大等待时
返回值:
pdPASS :开启成功 pdFAIL :开启失败
3. 停止软件定时器
BaseType_t xTimerStop ( TimerHandle_t xTimer ,
TickType_t xBlockTime );
参数与返回值同上。
4. 复位软件定时器
BaseType_t xTimerReset ( TimerHandle_t xTimer ,
TickType_t xBlockTime );
参数与返回值同上。
该功能将使软件定时器的重新开启定时,复位后的软件定时器以复位时的时刻作为开启时刻重新
定时。
5. 更改软件定时器定时时间
BaseType_t xTimerChangePeriod ( TimerHandle_t xTimer ,
TickType_t xNewPeriod ,
TickType_t xBlockTime );
xNewPeriod :新的定时超时时间,单位:系统时钟节拍。
其余参数与返回值同上。

实操

实验需求
创建两个定时器:
定时器 1 ,周期定时器,每 1 秒打印一次 liangxu shuai
定时器 2 ,单次定时器,启动后 2 秒打印一次 laochen shuai
cubeMX 配置

 

void StartDefaultTask ( void const * argument )
{
/* USER CODE BEGIN StartDefaultTask */
osTimerStart ( myTimer01Handle , 1000 );
// xTimerChangePeriod(myTimer01Handle, pdMS_TO_TICKS(1000), 0);
osTimerStart ( myTimer02Handle , 2000 );
/* Infinite loop */
for (;;)
{
osDelay ( 1 );
}
系统有封装的定时器创建函数,直接调用即可
/* USER CODE END StartDefaultTask */
}
void Callback01 ( void const * argument )
{
/* USER CODE BEGIN Callback01 */
printf ( " 周期定时器: liangxu shuai\r\n" );
/* USER CODE END Callback01 */
}
void Callback02 ( void const * argument )
{
/* USER CODE BEGIN Callback02 */
printf ( " 单次定时器: laochen shuai\r\n" );
/* USER CODE END Callback02 */
}

十二、中断管理

中断优先级

任何中断的优先级都大于任务!
在我们的操作系统,中断同样是具有优先级的,并且我们也可以设置它的优先级,但是他的优先
级并不是从 015 ,默认情况下它是从 515 0~4 5 个中断优先级不是 FreeRTOS 控制的( 5
取决于 configMAX_SYSCALL_INTERRUPT_PRIORITY )。

相关注意

1在中断中必需使用中断相关的函数;

2. 中断服务函数运行时间越短越好。

实操

实验需求
创建一个队列及一个任务,按下按键 KEY1 触发中断,在中断服务函数里向队列里发送数据,任
务则阻塞接收队列数据。

 

代码实现
stm32f1xx_it.c
# include "cmsis_os.h"       //系统配置的头文件,添加就好
extern osMessageQId myQueue01Handle ;
void HAL_GPIO_EXTI_Callback ( uint16_t GPIO_Pin )
{
uint32_t snd = 1 ;
xQueueSendFromISR ( myQueue01Handle , & snd , NULL );
}
freertos.c
void StartDefaultTask ( void const * argument )
{
/* USER CODE BEGIN StartDefaultTask */
uint32_t rev = 0 ;
/* Infinite loop */
for (;;)
{
if ( xQueueReceive ( myQueue01Handle , & rev , portMAX_DELAY ) == pdTRUE )
printf ( "rev = %d\r\n" , rev );
osDelay ( 1 );
}
/* USER CODE END StartDefaultTask */
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值