QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet,
TickType_t const xTicksToWait );
队列集
要支持多个输入设备时,我们需要实现一个“InputTask”,它读取各个设备的队列,得 到数据后再分别转换为游戏的控制键。
InputTask 如何及时读取到多个队列的数据?要使用队列集。
队列集的本质也是队列,只不过里面存放的是“队列句柄”。
使用过程如下:
a. 创建队列A,它的长度是n1
b. 创建队列B,它的长度是n2
c. 创建队列集S,它的长度是“n1+n2”
d. 把队列A、B加入队列集S
e. 这样,写队列A的时候,会顺便把队列A的句柄写入队列集S
f. 这样,写队列B的时候,会顺便把队列B的句柄写入队列集S
g. InputTask 先读取队列集 S,它的返回值是一个队列句柄,这样就可以知道哪个队列有 有数据了;然后InputTask再读取这个队列句柄得到数据。创建队列集
创建队列集
函数原型如下:
QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength )
把队列加入队列集
BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore,
QueueSetHandle_t xQueueSet );
读取队列集
QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet,
TickType_t const xTicksToWait );
示例:
如何创建队列集和添加队列
static QueueHandle_t xQueueHandle1 = NULL; /*队列1句柄*/
static QueueHandle_t xQueueHandle2 = NULL; /*队列2句柄*/
static QueueSetHandle_t xQueueSet = NULL; /*队列集句柄*/
/*实验说明:
*将两个队列添加到队列集中,任务一二使用队列发送信息
*任务三使用队列集进行监测信息*/
/*队列集实验流程*/
/* 1. 创建两个队列 */
/* 2. 创建一个个队列集 */
/* 3. 把队列添加到队列集中 */
/* 4. 创建三个任务 */
/*创建队列以及队列集合*/
void creat_queue(void)
{
/*创建第一个队列*/
xQueueHandle1=xQueueCreate(2,sizeof(int));/* 创建队列1 */
(xQueueHandle1 == NULL)?
printf("error create queue1\r\n"):/*创建失败*/
printf(" create queue1 ok! \r\n");/*创建成功*/
/*创建第二个队列*/
xQueueHandle2=xQueueCreate(2,sizeof(int));/* 创建队列2 */
(xQueueHandle2 == NULL)?
printf("error create queue2\r\n"):/*创建失败*/
printf(" create queue2 ok! \r\n");/*创建成功*/
/*创建队列集*/
xQueueSet= xQueueCreateSet(4); /* 创建队列集 参数:队列总长度 当前为:2+2 */
(xQueueSet == NULL)?/*创建失败*/
printf("error create queue set \r\n"):
printf(" create queue set ok! \r\n");
/* 3. 把2个queue添加进queue set */
xQueueAddToSet(xQueueHandle1, xQueueSet);
xQueueAddToSet(xQueueHandle2, xQueueSet);
}
读取队列集
static int value3;
QueueSetMemberHandle_t handle;
/* 读取队列集 选择 队列数据 */
handle = xQueueSelectFromSet(xQueueSet, portMAX_DELAY);/*死等*/
/* 读取队列*/
xQueueReceive(handle, &value3, 0);
/* 将读取到的数据打印出来*/
printf("get data : %d\r\n", value3);
如果两个队列的内容不同怎么办?可以学习韦东山
static void InputTask(void *params)
{
QueueSetMemberHandle_t xQueueHandle;
while (1)
{
/* 读队列集, 得到有数据的队列句柄 */
xQueueHandle = xQueueSelectFromSet(g_xQueueSetInput, portMAX_DELAY);
if (xQueueHandle)
{
/* 读队列句柄得到数据,处理数据 */
if (xQueueHandle == g_xQueueIR)
{
ProcessIRData();
}
else if (xQueueHandle == g_xQueueRotary)
{
ProcessRotaryData();
}
else if (xQueueHandle == g_xQueueMPU6050)
{
ProcessMPU6050Data();
}
}
}
}
信号量
简单介绍
信号量,本质是用队列实现的。但是,我们刚学的队列是用来传递数据,但是信号量顾名思义是传递信号的。
使用信号量时,先创建、然后去添加资源、获得资源。使用句柄来表示一个信号量。
信号量的计数值都有限制:限定了最大值。如果最大值被限定为1,那么它就是二进制 信号量;如果最大值不是1,它就是计数型信号量。
二进制信号量/计数型信号量
创建
二进制信号量
/* 创建一个二进制信号量,返回它的句柄。
* 此函数内部会分配信号量结构体
* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateBinary( void );
/* 创建一个二进制信号量,返回它的句柄。
* 此函数无需动态分配内存,所以需要先有一个StaticSemaphore_t结构体,并传入它的
指针
* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuf
fer );
计数型信号
/* 创建一个计数型信号量,返回它的句柄。
* 此函数内部会分配信号量结构体
* uxMaxCount: 最大计数值
* uxInitialCount: 初始计数值
* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t
uxInitialCount);
/* 创建一个计数型信号量,返回它的句柄。
* 此函数无需动态分配内存,所以需要先有一个StaticSemaphore_t结构体,并传入它的
指针
* uxMaxCount: 最大计数值
* uxInitialCount: 初始计数值
* pxSemaphoreBuffer: StaticSemaphore_t结构体指针
* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount,
UBaseType_t uxInitialCount,
StaticSemaphore_t *pxSemaphore
Buffer );
删除
对于动态创建的信号量,不再需要它们时,可以删除它们以回收内存。 vSemaphoreDelete可以用来删除二进制信号量、计数型信号量,函数原型如下:
/*
* xSemaphore: 信号量句柄,你要删除哪个信号量
*/
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
give/take函数
二进制信号量、计数型信号量的give、take操作函数是一样的。这些函数也分为2个版 本:给任务使用,给ISR使用。列表如下:
示例:
SemaphoreHandle_t BinarySemaphore; //定义
BinarySemaphore=xSemaphoreCreateBinary();//创建动态二进制信号量
xSemaphoreGive(BinarySemaphore); //give
/*某个任务take信号量*/
void OLED_Test(void)
{
int cnt = 0;
BaseType_t err = pdPASS;
OLED_Init();
// 清屏
OLED_Clear();
while (1)
{
err=xSemaphoreTake(BinarySemaphore,portMAX_DELAY);
// if(err == pdPASS)
// {
//
// }
// 在第0列第2页打印一个字符串"Hello World!"
OLED_PrintString(0, 0, "Hello World!");
OLED_PrintSignedVal(0, 2, cnt++);
}
}
/*某个中断give信号量*/
//回调函数
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *nhrtc) //句柄需要取新名避免与hrtc冲突
{
xSemaphoreGiveFromISR(BinarySemaphore,NULL);
}
互斥信号量
互斥量是一种特殊的二进制信号量。
创建
使用互斥量时,先创建、然后去获得、释放它。使用句柄来表示一个互斥量。 创建互斥量的函数有2种:动态分配内存,静态分配内存,函数原型如下:
* 创建一个互斥量,返回它的句柄。
* 此函数内部会分配互斥量结构体
* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateMutex( void );
/* 创建一个互斥量,返回它的句柄。
* 此函数无需动态分配内存,所以需要先有一个StaticSemaphore_t结构体,并传入它的
指针
* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer );
要想使用互斥量,需要在配置文件FreeRTOSConfig.h中定义:
##define configUSE_MUTEXES 1
其他函数
要注意的是,互斥量不能在ISR中使用。 各类操作函数,比如删除、give/take,跟一般是信号量是一样的。
注意
互斥量可以解决优先级翻转的问题(这个可以自己去了解)
问题:低优先级任务获得了锁,但是它优先级太低而无法运行。
互斥型号量怎么解决:低优先级任务获得了锁,但是它优先级太低而无法运行。
示例
/* 互斥量句柄 */
SemaphoreHandle_t xMutex;
/* 创建互斥量 */
xMutex = xSemaphoreCreateMutex( );
atic void vSenderTask( void *pvParameters )
{
const TickType_t xTicksToWait = pdMS_TO_TICKS( 10UL );
int cnt = 0;
int task = (int)pvParameters;
int i;
char c;
/* 无限循环 */
for( ;; )
{
/* 获得互斥量: 上锁 */
xSemaphoreTake(xMutex, portMAX_DELAY);
printf("Task %d use UART count: %d, ", task, cnt++);
c = (task == 1 ) ? 'a' : 'A';
for (i = 0; i < 26; i++)
printf("%c", c + i);
printf("\r\n");
/* 释放互斥量: 开锁 */
xSemaphoreGive(xMutex);
vTaskDelay(xTicksToWait);
}
递归锁
递归锁解决什么问题?
比如,自我死锁
怎么解决这类问题?
/* 创建一个递归锁,返回它的句柄。
* 此函数内部会分配互斥量结构体
* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void );
/* 释放 */
BaseType_t xSemaphoreGiveRecursive( SemaphoreHandle_t xSemaphore );
/* 获得 */
BaseType_t xSemaphoreTakeRecursive(
SemaphoreHandle_t xSemaphore,
Ti
不建议使用
所以没有例子
事件组
事件组是啥?我就不说了,我只说怎么用.
创建
使用事件组之前,要先创建,得到一个句柄;使用事件组时,要使用句柄来表明使用哪 个事件组。 有两种创建方法:动态分配内存、静态分配内存。函数原型如下:
/* 创建一个事件组,返回它的句柄。
* 此函数内部会分配事件组结构体
* 返回值: 返回句柄,非NULL表示成功
*/
EventGroupHandle_t xEventGroupCreate( void );
/* 创建一个事件组,返回它的句柄。
* 此函数无需动态分配内存,所以需要先有一个StaticEventGroup_t结构体,并传入它的
指针
* 返回值: 返回句柄,非NULL表示成功
*/
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer
);
删除
对于动态创建的事件组,不再需要它们时,可以删除它们以回收内存。 vEventGroupDelete可以用来删除事件组,函数原型如下:
/*
* xEventGroup: 事件组句柄,你要删除哪个事件组
*/
void vEventGroupDelete( EventGroupHandle_t xEventGroup )
设置事件
可以设置事件组的某个位、某些位,使用的函数有2个:
⚫ 在任务中使用xEventGroupSetBits()
⚫ 在ISR中使用xEventGroupSetBitsFromISR()
有一个或多个任务在等待事件,如果这些事件符合这些任务的期望,那么任务还会被唤 醒。 函数原型如下:
/* 设置事件组中的位
* xEventGroup: 哪个事件组
* uxBitsToSet: 设置哪些位?
* 如果uxBitsToSet的bitX, bitY为1, 那么事件组中的bitX, bitY被设置为
1
* 可以用来设置多个位,比如 0x15 就表示设置bit4, bit2, bit0
* 返回值: 返回原来的事件值(没什么意义, 因为很可能已经被其他任务修改了)
*/
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet );
/* 设置事件组中的位
* xEventGroup: 哪个事件组
* uxBitsToSet: 设置哪些位?
* 如果uxBitsToSet的bitX, bitY为1, 那么事件组中的bitX, bitY被设置为
1
* 可以用来设置多个位,比如 0x15 就表示设置bit4, bit2, bit0
* pxHigherPriorityTaskWoken: 有没有导致更高优先级的任务进入就绪态? pdTRUE-有, p
dFALSE-没有
* 返回值: pdPASS-成功, pdFALSE-失败
*/
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
BaseType_t * pxHigherPriorityTaskWoken );
值得注意的是,ISR中的函数,比如队列函数xQueueSendToBackFromISR、信号量函 数xSemaphoreGiveFromISR,它们会唤醒某个任务,最多只会唤醒1个任务。
但是设置事件组时,有可能导致多个任务被唤醒,这会带来很大的不确定性。所以 xEventGroupSetBitsFromISR函数不是直接去设置事件组,而是给一个FreeRTOS后台任务 (daemon task)发送队列数据,由这个任务来设置事件组。
如果后台任务的优先级比当前被中断的任务优先级高,xEventGroupSetBitsFromISR 会设置*pxHigherPriorityTaskWoken为pdTRUE。
如果daemon task成功地把队列数据发送给了后台任务,那么 xEventGroupSetBitsFromISR的返回值就是pdPASS。
等待事件
使用xEventGroupWaitBits来等待事件,可以等待某一位、某些位中的任意一个, 也可以等待多位;等到期望的事件后,还可以清除某些位。
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait );
示例:
这3个任务的代码和执行流程如下:
- A:"炒菜任务"优先级最高,先执行。它要等待的2个事件未发生:洗菜、生火,进入阻塞状态
- B:"生火任务"接着执行,它要等待的1个事件未发生:洗菜,进入阻塞状态
- C:"洗菜任务"接着执行,它洗好菜,发出事件:洗菜,然后调用F等待"炒菜"事件
- D:"生火任务"等待的事件满足了,从B处继续执行,开始生火、发出"生火"事件
- E:"炒菜任务"等待的事件满足了,从A出继续执行,开始炒菜、发出"炒菜"事件
- F:"洗菜任务"等待的事件满足了,退出F、继续执行C
要注意的是,代码B处等待到"洗菜任务"后并不清除该事件,如果清除的话会导致"炒菜任务"无法执行。
同步点
EventBits_t xEventGroupSync(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
const EventBits_t uxBitsToWaitFor,
TickType_t xTicksToWait );
示例
/* 表示我做好了, 还要等别人都做好 */
xEventGroupSync(xEventGroup, COOKING, ALL, portMAX_DELAY);