目录
- 一、事件组
- 1.事件组创建
- 2.事件组置位
- 3.事件组等待
- 二、软件定时器
- 1.软件定时器创建
- 2.软件定时器执行
- 3.例程代码
一、事件组
本质上是任务同步,但比二值信号量优秀的是可以一对多。
我的理解:事件组就是标志位的集合,将多个标志位放到一个集合里。
事件组中的事件位数(或标志数)的多少取决于 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.事件组创建
先添加头文件
#include "event_groups.h"
创建一个时间组句柄
/********************事件组句柄***********************/
EventGroupHandle_t Event;
最后用上面创建的句柄进行承接创建。
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,函数都会返回,超出溢出时间也会返回,
Ret = xEventGroupWaitBits(Event,0x07,pdTRUE,pdFALSE,portMAX_DELAY);
二、软件定时器
软件定时器(或者只是“定时器” )能够让函数在 未来的设定时间执行。 由定时器执行的函数称为定时器的回调函数。 从定时器启动到其回调函数执行之间的时间被称为定时器的周期。 简而言之, 当定时器的周期到期时,定时器的回调函数会被执行。
其实软件定时器就是设置了一个周期,周期结束了就会调用回调函数。
注意:回调函数中不要有阻塞。
1.软件定时器创建
添加头文件
#include "times.h"
创建句柄
TimerHandle_t Timer1;//软件定时器
定时器任务创建
Timer1 = xTimerCreate("Timer1",1000,pdTRUE,(void *)1,vTimerCallback);
参数1:pcTimerName:定时器名字
参数2:xTimerPeriodInTicks:定时周期
参数3:uxAutoReload:
pdTRUE:周期模式
pdFALSE:单次模式
参数4:pvTimerID:定时器ID,用于多个定时器共用同一个回调函数,判断那个定时器超时。
2.软件定时器执行
调用函数:
void vTimerCallback(TimerHandle_t xTimer)
{
printf("定时器ID:%d\r\n",(u8)pvTimerGetTimerID(xTimer));
Led_Waterfall();
}
定时器开始执行
xTimerStart(Timer1,1);
参数1:表示要启动的定时器的句柄。
参数2:表示在启动定时器之前等待的节拍数。如果希望在启动定时器之前有一个延迟,可以设置一个等待时间。
3.例程代码
main.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)//栈溢出时卡死到钩子函数中
{}
}