STM32第二十一课:FreeRTOS事件组&软件定时器

目录


一、事件组

本质上是任务同步,但比二值信号量优秀的是可以一对多。

我的理解:事件组就是标志位的集合,将多个标志位放到一个集合里。

事件组中的事件位数(或标志数)的多少取决于 configUSE_16_BIT_TICKS or configTICK_TYPE_WIDTH_IN_BITS 是否 用于控制 TickType_t 的类型:

如果 configUSE_16_BIT_TICKS 设置为 1,则事件组内实现的位数(或标志数)为 8; 如果 configUSE_16_BIT_TICKS 设置为 0,则为 24。

如果 configTICK_TYPE_WIDTH_IN_BITS 设置为 TICK_TYPE_WIDTH_16_BITS,则事件组内实现的位数(或标志数)为 8; 如果 configTICK_TYPE_WIDTH_IN_BITS 设置为 TICK_TYPE_WIDTH_32_BITS,则为 24 ;如果 configTICK_TYPE_WIDTH_IN_BITS 设置为 TICK_TYPE_WIDTH_64_BITS,则为 56。

STM32中通常设置24个时间位即可。

1.事件组创建

先添加头文件

c 复制代码
#include "event_groups.h"

创建一个时间组句柄

c 复制代码
/********************事件组句柄***********************/
EventGroupHandle_t Event;

最后用上面创建的句柄进行承接创建。

c 复制代码
Event = xEventGroupCreate();

此时,我们就获得了一个具有24事件位的事件组。

创建完成时,事件组的每位都为0。

2.事件组置位

当发生特殊事件(数据传输完成,解析完成等。。),此时我们就可以使用事件组。让时间组的一位进行置位。

事件组置位函数有两个:

使用环境 置位函数
中断函数中 EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet )
中断函数中 xEventGroupSetBitsFromISR( xEventGroup, uxBitsToSet, pxHigherPriorityTaskWoken )

参数1:想要置位的事件组是哪个。

参数2:想要置的是哪一位,比如想要置第2位就填0x04(100)

虽然函数不同,但参数基本是一样的。只不过在中断中多一个参数3,参数3填 pdTRUE会开启高优先级事件触发提醒。一般我们不需要,直接填NULL即可。

3.事件组等待

EventBits_t xEventGroupWaitBits(

复制代码
    const EventGroupHandle_t xEventGroup, /* 事件标志组句柄 */
    const EventBits_t uxBitsToWaitFor, /* 等待被设置的事件标志位 */
    const BaseType_t xClearOnExit, /* 选择是否清零被置位的事件标志位 */
    const BaseType_t xWaitForAllBits, /* 选择是否等待所有标
    TickType_t xTicksToWait ); /* 设置等待时间 */
    )

若设置超时时间:在超时时间中会阻塞等待,超时时会返回当前事件组的值,但不会清0标志位(无论是否设置清0标志位)

设置清0标志位,成功获取时会清0标志位。

若设置超时时间为:portMAX_DELAY,此时该函数就会死等,成功时返回。通常都是portMAX_DELAY

参数4,有pdTRUE和pdFALSE两种状态。

如果这个参数设置为 pdTRUE,要等待第 2 个参数 uxBitsToWaitFor 所指定的标志位全部被置 1,函数才可以返回。当然,超出了在参数xTicksToWait 设置的溢出时间也是会返回的。

如果这个参数设置为 pdFALSE,第 2 个参数uxBitsToWaitFor 所指定的任何标志位被置 1,函数都会返回,超出溢出时间也会返回,

c 复制代码
Ret = xEventGroupWaitBits(Event,0x07,pdTRUE,pdFALSE,portMAX_DELAY);

二、软件定时器

软件定时器(或者只是"定时器" )能够让函数在 未来的设定时间执行。 由定时器执行的函数称为定时器的回调函数 。 从定时器启动到其回调函数执行之间的时间被称为定时器的周期。 简而言之, 当定时器的周期到期时,定时器的回调函数会被执行。

其实软件定时器就是设置了一个周期,周期结束了就会调用回调函数。

注意:回调函数中不要有阻塞

1.软件定时器创建

添加头文件

c 复制代码
#include "times.h"

创建句柄

c 复制代码
TimerHandle_t Timer1;//软件定时器

定时器任务创建

c 复制代码
	Timer1 = xTimerCreate("Timer1",1000,pdTRUE,(void *)1,vTimerCallback);

参数1:pcTimerName:定时器名字

参数2:xTimerPeriodInTicks:定时周期

参数3:uxAutoReload:

pdTRUE:周期模式

pdFALSE:单次模式

参数4:pvTimerID:定时器ID,用于多个定时器共用同一个回调函数,判断那个定时器超时。

2.软件定时器执行

调用函数:

c 复制代码
void vTimerCallback(TimerHandle_t xTimer)
{
	printf("定时器ID:%d\r\n",(u8)pvTimerGetTimerID(xTimer));
	Led_Waterfall();
}

定时器开始执行

c 复制代码
	xTimerStart(Timer1,1);

参数1:表示要启动的定时器的句柄。

参数2:表示在启动定时器之前等待的节拍数。如果希望在启动定时器之前有一个延迟,可以设置一个等待时间。

3.例程代码

main.c

c 复制代码
#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
#include "delay.h"
#include "string.h"
#include "pwm.h"
#include "adc.h"
#include "su03t.h"
#include "dht11.h"
#include "kqm.h"
#include "usart.h"
#include "key.h"
//使用FreeRtos相关头文件之前,一定要先包含这个#include "FreeRtos.h"
#include "FreeRtos.h"
#include "task.h"
#include "semphr.h"
#include "event_groups.h"
#include "queue.h"
#include "time.h"
EventGroupHandle_t Events;
char D_time[20];
TaskHandle_t liushui_Task;
TimerHandle_t Timer1;//软件定时器

void liu_Task(void *p)
{
	Led_Waterfall();
}
void vTimerCallback(TimerHandle_t xTimer)
{
	printf("定时器ID:%d\r\n",(u8)pvTimerGetTimerID(xTimer));
	Led_Waterfall();
}
int main()
{
	Led_Init();
	Usart1_Config();//初始化串口1
	BaseType_t Ret = pdPASS;
//	Ret = xTaskCreate(liu_Task, //创建任务的任务函数名
//                    "liu_Task",//任务名字
//                    100,//任务栈深度。32位单片机*4
//                    NULL,//创建任务时传递参数,没有就给NULL
//                    1,//任务优先级
//										&liushui_Task);//任务的句柄,用于后边删除,挂起任务
//	if(Ret == pdPASS){
//		printf("流水灯任务创建完成\r\n");
//	}
	Timer1 = xTimerCreate("Timer1",1000,pdTRUE,(void *)1,vTimerCallback);
	xTimerStart(Timer1,1);
	printf("开始调度!\r\n");
	vTaskStartScheduler();
	while(1)
	{
		
	}
}

void vApplicationStackOverflowHook( TaskHandle_t xTask,char *pcTaskName )
{
	printf("任务:%s->栈溢出\r\n",pcTaskName);
	printf("任务剩余空间:%d\r\n",(int)uxTaskGetStackHighWaterMark(xTask));
	while(1)//栈溢出时卡死到钩子函数中
	{}
}
相关推荐
影阴5 分钟前
stm32实现CAN通讯测试
stm32·单片机·嵌入式硬件·hal
Silicore_Emma5 分钟前
芯谷科技—D2010:高效电机控制与保护的卓越之选
单片机·电机控制·工业自动化·电动工具调速·智能家电设备·绍兴芯谷·d2010
cui_win6 分钟前
Redis 生产环境命令管控规范
数据库·redis·缓存
xiaohai@Linux26 分钟前
基于 TCP 的IOT物联网云端服务端和设备客户端通信架构设计与实现
嵌入式硬件·物联网·网络协议·tcp/ip
创界工坊工作室27 分钟前
DPJ-137 基于单片机的公交车自动报站系统设计(源代码+proteus仿真)
stm32·单片机·嵌入式硬件·51单片机·proteus
TDengine (老段)36 分钟前
开放生态破局工业大数据困局:TDengine 的迭代升级与全链路数据自由流动
大数据·数据库·物联网·时序数据库·tdengine·涛思数据
疯狂的豆包1 小时前
ESP32与MAX98357:打造智能收音机的奇妙之旅
单片机
小坏讲微服务1 小时前
Spring Boot4.0 集成 Redis 实现看门狗 Lua 脚本分布式锁完整使用
java·spring boot·redis·分布式·后端·lua
啃硬骨头1 小时前
Aurix TC387 Ethernet代码解析之六_MAC的LwIP初始化3
单片机·嵌入式硬件
为什么要做囚徒1 小时前
并发系列(一):深入理解信号量(含 Redis 分布式信号量)
redis·分布式·多线程·并发编程·信号量