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)//栈溢出时卡死到钩子函数中
	{}
}
相关推荐
脚步的影子21 分钟前
Redis 入门 - C#|.NET Core客户端库六种选择
redis·c#·.netcore
阿川!1 小时前
嵌入式软件--51单片机 DAY 4
单片机·嵌入式硬件·51单片机
cyt涛2 小时前
Redis访问工具
数据库·redis·spring cache·注解·客户端·lettuce·redistemplae
AllWe$2 小时前
九、Redis 的实际使用与Redis的设计
数据库·redis·缓存
好家伙VCC3 小时前
嵌入式 面试单片机面试题目分析和答案 IIC总线 IIC协议 SPI总线 I2C协议讲解 SPI和IIC协议对比比较SPI的工作模式比较 IIC基本概念讲解
单片机·嵌入式硬件
秋风&萧瑟3 小时前
【STM32】CAN总线基础入门
stm32·单片机·嵌入式硬件
机智的小神仙儿4 小时前
我是Redis,请看我和mysql是如何交互的吧~
redis·mysql·缓存·交互
LCMICRO-133108477464 小时前
长芯微LS5541/LS5542国产数模转换芯片DAC完全替代P2P替代AD5541/AD5542
单片机·嵌入式硬件·fpga开发·硬件工程·dsp开发