事件组(本质,车辆协同,改进姿态控制)
事件组本质
事件组的概念
事件组可以简单地认为就是一个整数:⚫ 的每一位表示一个事件⚫ 每一位事件的含义由程序员决定,比如: Bit0 表示用来串口是否就绪, Bit1表示按键是否被按下⚫ 这些位,值为 1 表示事件发生了,值为 0 表示事件没发生⚫ 一个或多个任务、 ISR 都可以去写这些位;一个或多个任务、 ISR 都可以去读这些位⚫ 可以等待某一位、某些位中的任意一个,也可以等待多位高八位用来表示是与关系还是或关系低八位用来表示是哪个任务的开启如果有若干个等待事件(A,B任务)那么如果触发写事件,那么先会遍历若干事件(A,B)再进行执行若干事件(A,B)
事件组的操作
事件组和队列、信号量等不太一样,主要集中在 2 个地方:
⚫
唤醒谁?
◼
队列、信号量:事件发生时,只会唤醒一个任务
◼
事件组:事件发生时,会唤醒所有符号条件的任务,简单地说它有
"
广播
"
的
作用
⚫
是否清除事件?
◼
队列、信号量:是消耗型的资源,队列的数据被读走就没了;信号量被获取
后就减少了
◼
事件组:被唤醒的任务有两个选择,可以让事件保留不动,也可以清除事件
以上图为列,事件组的常规操作如下:
⚫
先创建事件组
192
⚫
任务
C
、
D
等待事件:
◼
等待什么事件?可以等待某一位、某些位中的任意一个,也可以等待多位。
简单地说就是
"
或
"
、
"
与
"
的关系。
◼
得到事件时,要不要清除?可选择清除、不清除。
⚫
任务
A
、
B
产生事件:设置事件组里的某一位、某些位
事件组函数
创建
函数原型:
EventGroupHandle_t xEventGroupCreate( void );
参数:
- 无参数:此函数不需要传入任何参数。
返回值:
- EventGroupHandle_t:返回创建的事件组句柄。如果创建成功,返回一个有效的句柄。如果创建失败(例如,内存不足),则返回
NULL
。
说明:
- 事件组是一个位掩码,允许多个任务通过设置或清除特定的位来进行同步。任务可以等待某些特定位被设置,或者可以设置特定的位来通知其他任务。
使用场景:
- 任务间同步:例如,一个任务可以设置某个事件标志,另一个任务等待该标志。
- 任务间通信:通过设置和等待不同的事件位来实现不同任务间的状态传递。
删除
函数原型:
void vEventGroupDelete( EventGroupHandle_t xEventGroup )
参数:
- xEventGroup:事件组的句柄,即通过
xEventGroupCreate()
创建的事件组。
返回值:
- 无返回值:该函数没有返回值。如果事件组成功删除,内存会被释放;如果没有问题,函数执行后不会有任何反馈。
说明:
vEventGroupDelete()
用于显式删除一个事件组。在 FreeRTOS 中,事件组是通过内存分配的,因此需要通过vEventGroupDelete()
来释放占用的内存。若事件组不再需要,调用该函数进行删除是一个良好的做法。- 在调用
vEventGroupDelete()
之前,确保没有任务在使用该事件组。特别是,调用该函数时,不应有任务正在等待该事件组的事件位,否则会导致潜在的同步问题。
设置事件
函数原型:
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet );
参数:
- xEventGroup: 事件组的句柄(由
xEventGroupCreate()
返回),指定要操作的事件组。 - uxBitsToSet: 要设置的事件位。这个参数是一个位掩码,可以指定一个或多个事件位,设置这些事件位时,位上对应的值将变为
1
。你可以通过按位或操作来设置多个位。
返回值:
- 返回当前事件组中的所有位的状态(即事件组中每个位的当前值)。这个返回值是一个
EventBits_t
类型,它包含了事件组中所有位的状态,允许调用者检查其他任务可能已经设置的位。
说明:
- 设置事件位:通过调用
xEventGroupSetBits()
,可以设置指定事件组中的某些位。设置事件位后,其他任务(通过xEventGroupWaitBits()
)如果等待这些位,就会被唤醒。 - 位掩码:
uxBitsToSet
是一个位掩码,使用按位或操作来设置一个或多个事件位。例如,如果要设置第 0 位和第 2 位,可以将掩码设置为0x05
(即00000101
)。 - 不会清除位:
xEventGroupSetBits()
只会设置事件位,它不会清除已经设置的位。如果需要清除事件位,可以使用xEventGroupClearBits()
。
等待事件
参数说明:
-
xEventGroup:要操作的事件组句柄。该句柄是通过
xEventGroupCreate()
创建的事件组。 -
uxBitsToWaitFor:指定任务等待的事件位。这是一个位掩码,任务将等待至少这些位被设置。位的设置可以是 0 或 1,可以通过
xEventGroupSetBits()
设置这些位。 -
xClearOnExit:布尔值(
pdTRUE
或pdFALSE
)。如果为pdTRUE
,在任务成功退出时,事件组中的相关事件位会被清除。否则,位将保持设置状态,等待其他任务或操作再次使用。 -
xWaitForAllBits:布尔值(
pdTRUE
或pdFALSE
)。如果为pdTRUE
,任务会等待 所有 指定的事件位都被设置;如果为pdFALSE
,任务只需要等待其中的任何一个位被设置即可。 -
xTicksToWait:任务等待的时间(以滴答为单位)。如果为
portMAX_DELAY
,任务会无限等待直到事件位被设置。如果在给定的时间内事件位未被设置,任务将超时并返回。
返回值:
- 返回值:成功时,函数返回当前事件组的所有事件位状态。返回值是一个事件位掩码,可以通过与操作 (
&
) 来检查哪个事件位被设置。 - 如果在超时之前没有设置期望的事件位,返回值将不包含
uxBitsToWaitFor
中的位。
说明:
xEventGroupWaitBits()
是一个阻塞函数,通常用于任务之间的同步。它会挂起当前任务,直到一个或多个指定的事件位被设置,或者直到超时为止。
事件组车辆协同
代码
/*
* Project: N|Watch
* Author: Zak Kemble, contact@zakkemble.co.uk
* Copyright: (C) 2013 by Zak Kemble
* License: GNU GPL v3 (see License.txt)
* Web: http://blog.zakkemble.co.uk/diy-digital-wristwatch/
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "cmsis_os.h"
#include "FreeRTOS.h" // ARM.FreeRTOS::RTOS:Core
#include "task.h" // ARM.FreeRTOS::RTOS:Core
#include "event_groups.h" // ARM.FreeRTOS::RTOS:Event Groups
#include "semphr.h" // ARM.FreeRTOS::RTOS:Core
#include "draw.h"
#include "resources.h"
#include "driver_lcd.h"
#include "driver_ir_receiver.h"
#include "driver_rotary_encoder.h"
#include "driver_mpu6050.h"
#define NOINVERT false
#define INVERT true
#define CAR_COUNT 3
#define CAR_WIDTH 12
#define CAR_LENGTH 15
#define ROAD_SPEED 6
static SemaphoreHandle_t g_xSemTicks;
static uint32_t g_xres, g_yres, g_bpp;
static uint8_t *g_framebuffer;
static EventGroupHandle_t g_xEventCar;
struct car {
int x;
int y;
int control_key;
};
struct car g_cars[3] = {
{0, 0, IR_KEY_1},
{0, 17, IR_KEY_2},
{0, 34, IR_KEY_3},
};
static const byte carImg[] ={
0x40,0xF8,0xEC,0x2C,0x2C,0x38,0xF0,0x10,0xD0,0x30,0xE8,0x4C,0x4C,0x9C,0xF0,
0x02,0x1F,0x37,0x34,0x34,0x1C,0x0F,0x08,0x0B,0x0C,0x17,0x32,0x32,0x39,0x0F,
};
static const byte clearImg[30] ={0};
static const byte roadMarking[] ={
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
};
#if 0
void car_test(void)
{
g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp);
draw_init();
draw_end();
draw_bitmap(0, 0, carImg, 15, 16, NOINVERT, 0);
draw_flushArea(0, 0, 15, 16);
draw_bitmap(0, 16, roadMarking, 8, 1, NOINVERT, 0);
draw_flushArea(0, 16, 8, 1);
while (1);
}
#endif
static void ShowCar(struct car *pcar)
{
draw_bitmap(pcar->x, pcar->y, carImg, 15, 16, NOINVERT, 0);
draw_flushArea(pcar->x, pcar->y, 15, 16);
}
static void HideCar(struct car *pcar)
{
draw_bitmap(pcar->x, pcar->y, clearImg, 15, 16, NOINVERT, 0);
draw_flushArea(pcar->x, pcar->y, 15, 16);
}
static void Car1Task(void *params)
{
struct car *pcar = params;
struct ir_data idata;
/* 创建自己的队列 */
QueueHandle_t xQueueIR = xQueueCreate(10, sizeof(struct ir_data));
/* 注册队列 */
RegisterQueueHandle(xQueueIR);
/* 显示汽车 */
ShowCar(pcar);
/* 获得信号量 */
//xSemaphoreTake(g_xSemTicks, portMAX_DELAY);
while (1)
{
/* 读取按键值:读队列 */
//xQueueReceive(xQueueIR, &idata, portMAX_DELAY);
/* 控制汽车往右移动 */
//if (idata.val == pcar->control_key)
{
if (pcar->x < g_xres - CAR_LENGTH)
{
/* 隐藏汽车 */
HideCar(pcar);
/* 调整位置 */
pcar->x += 1;
if (pcar->x > g_xres - CAR_LENGTH)
{
pcar->x = g_xres - CAR_LENGTH;
}
/* 重新显示汽车 */
ShowCar(pcar);
vTaskDelay(50);
if (pcar->x == g_xres - CAR_LENGTH)
{
/* 释放信号量 */
//xSemaphoreGive(g_xSemTicks);
xEventGroupSetBits(g_xEventCar,(1<<0));
vTaskDelete(NULL);
}
}
}
}
}
static void Car2Task(void *params)
{
struct car *pcar = params;
struct ir_data idata;
vTaskDelay(1000);
/* 创建自己的队列 */
QueueHandle_t xQueueIR = xQueueCreate(10, sizeof(struct ir_data));
/* 注册队列 */
RegisterQueueHandle(xQueueIR);
/* 显示汽车 */
ShowCar(pcar);
/* 获得信号量 */
//xSemaphoreTake(g_xSemTicks, portMAX_DELAY);
xEventGroupWaitBits(g_xEventCar,(1<<0),pdTRUE,pdFALSE,portMAX_DELAY);
while (1)
{
/* 读取按键值:读队列 */
//xQueueReceive(xQueueIR, &idata, portMAX_DELAY);
/* 控制汽车往右移动 */
//if (idata.val == pcar->control_key)
{
if (pcar->x < g_xres - CAR_LENGTH)
{
/* 隐藏汽车 */
HideCar(pcar);
/* 调整位置 */
pcar->x += 1;
if (pcar->x > g_xres - CAR_LENGTH)
{
pcar->x = g_xres - CAR_LENGTH;
}
/* 重新显示汽车 */
ShowCar(pcar);
//vTaskDelay(50);
mdelay(50);
if (pcar->x == g_xres - CAR_LENGTH)
{
/* 释放信号量 */
//xSemaphoreGive(g_xSemTicks);
//vTaskDelete(NULL);
}
}
}
}
}
static void Car3Task(void *params)
{
struct car *pcar = params;
struct ir_data idata;
/* 创建自己的队列 */
QueueHandle_t xQueueIR = xQueueCreate(10, sizeof(struct ir_data));
/* 注册队列 */
RegisterQueueHandle(xQueueIR);
/* 显示汽车 */
ShowCar(pcar);
vTaskDelay(2000);
/* 获得信号量 */
//xSemaphoreTake(g_xSemTicks, portMAX_DELAY);
xEventGroupWaitBits(g_xEventCar,(1<<0),pdTRUE,pdFALSE,portMAX_DELAY);
while (1)
{
/* 读取按键值:读队列 */
//xQueueReceive(xQueueIR, &idata, portMAX_DELAY);
/* 控制汽车往右移动 */
//if (idata.val == pcar->control_key)
{
if (pcar->x < g_xres - CAR_LENGTH)
{
/* 隐藏汽车 */
HideCar(pcar);
/* 调整位置 */
pcar->x += 1;
if (pcar->x > g_xres - CAR_LENGTH)
{
pcar->x = g_xres - CAR_LENGTH;
}
/* 重新显示汽车 */
ShowCar(pcar);
//vTaskDelay(50);
mdelay(50);
if (pcar->x == g_xres - CAR_LENGTH)
{
/* 释放信号量 */
//xSemaphoreGive(g_xSemTicks);
vTaskDelete(NULL);
}
}
}
}
}
void car_game(void)
{
int x;
int i, j;
g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp);
draw_init();
draw_end();
//g_xSemTicks = xSemaphoreCreateCounting(1, 1);
//g_xSemTicks = xSemaphoreCreateMutex();
EventGroupHandle_t xEventGroupCreate();
/* 画出路标 */
for (i = 0; i < 3; i++)
{
for (j = 0; j < 8; j++)
{
draw_bitmap(16*j, 16+17*i, roadMarking, 8, 1, NOINVERT, 0);
draw_flushArea(16*j, 16+17*i, 8, 1);
}
}
/* 创建3个汽车任务 */
#if 0
for (i = 0; i < 3; i++)
{
draw_bitmap(g_cars[i].x, g_cars[i].y, carImg, 15, 16, NOINVERT, 0);
draw_flushArea(g_cars[i].x, g_cars[i].y, 15, 16);
}
#endif
xTaskCreate(Car1Task, "car1", 128, &g_cars[0], osPriorityNormal, NULL);
xTaskCreate(Car2Task, "car2", 128, &g_cars[1], osPriorityNormal+2, NULL);
xTaskCreate(Car3Task, "car3", 128, &g_cars[2], osPriorityNormal+3, NULL);
}
事件组改进姿态控制
代码
/**********************************************************************
* 函数名称: MPU6050_Callback
* 功能描述: MPU6050中断回调函数,它会写事件组
* 输入参数: 无
* 输出参数: 无
* 返 回 值: 无
* 修改日期 版本号 修改人 修改内容
* -----------------------------------------------
* 2023/09/07 V1.0 韦东山 创建
***********************************************************************/
//void EXTI9_5_IRQHandler()
void MPU6050_Callback(void)
{
/* 设置事件组: bit0 */
xEventGroupSetBitsFromISR(g_xEventMPU6050, (1<<0), NULL);
}