FreeRTOS_同步互斥与通信_环形buffer、队列_学习笔记
5.5 队列集
要支持多个输入设备时,我们需要实现一个“InputTask”,它读取各个设备的队列,得到数据后再分别转换为游戏的控制键。
InputTask如何及时读取到多个队列的数据?要使用队列集。
队列集的本质也是队列,只不过里面存放的是“队列句柄”。使用过程如下:
创建队列A,它的长度是n1
创建队列B,它的长度是n2
创建队列集S,它的长度是“n1+n2”
把队列A、B加入队列集S
这样,写队列A的时候,会顺便把队列A的句柄写入队列集S
这样,写队列B的时候,会顺便把队列B的句柄写入队列集S
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);
5.5.1 实验
对上一篇笔记的代码进行修改,实现红外遥控和旋转编码器对游戏的共同控制。
在上一篇代码中,直接在中断响应函数IRReceiver_IRQ_Callback中写队列A,
在本例程中,先通过RotaryEncoder_IRQ_Callback(void)写队列B,再通过任务RotaryEncoder_Task读队列B,经过处理后再写队列A
在game1.c中做以下修改:
QueueHandle_t g_xQueuePlatform; /* 挡球板队列 */
QueueHandle_t g_xQueueRotary; /* 旋转编码器队列 */
static uint8_t g_ucQueueRotaryBuf[10*sizeof(struct rotary_data)]; //定义全局变量,保存队列buffer
static StaticQueue_t g_xQueueRotaryStaticStruct; //定义队列
/* 挡球板任务 */
static void platform_task(void *params)
{
...
while (1)
{
//if (0 == IRReceiver_Read(&dev, &data))
xQueueReceive(g_xQueuePlatform, &idata, portMAX_DELAY);
...
}
...
}
static void RotaryEncoderTask(void *params)//旋转编码器的读写队列和解析
{
struct rotary_data rdata;
struct input_data idata;
int left;
int i, cnt;
while (1)
{
/* 读旋转编码器队列 */
xQueueReceive(g_xQueueRotary, &rdata, portMAX_DELAY);
/* 处理数据 */
/* 判断速度: 负数表示向左转动, 正数表示向右转动 */
if (rdata.speed < 0)
{
left = 1;
rdata.speed = 0 - rdata.speed;
}
else
{
left = 0;
}
if (rdata.speed > 100)
cnt = 4;
else if (rdata.speed > 50)
cnt = 2;
else
cnt = 1;
/* 写挡球板队列 */
idata.dev = 1;
idata.val = left ? UPT_MOVE_LEFT : UPT_MOVE_RIGHT;
for (i = 0; i < cnt; i++)
{
xQueueSend(g_xQueuePlatform, &idata, 0);
}
}
}
void game1_task(void *params)
{
...
/* 创建队列 */
g_xQueuePlatform = xQueueCreate(10, sizeof(struct input_data)); //动态创建队列
g_xQueueRotary = xQueueCreateStatic(10, sizeof(struct rotary_data), g_ucQueueRotaryBuf, &g_xQueueRotaryStaticStruct); //静态创建队列
// rotary_data{cnt,speed}和input_data{dev,val}这两个数据结构体在另一个文件中被定义
//g_ucQueueRotaryBuf, &g_xQueueRotaryStaticStruct在全局变量区域被定义了
/*创建旋转编码器的任务,读队列B,解析数据,再写队列A*/
xTaskCreate(RotaryEncoderTask, "RotaryEncoderTask", 128, NULL, osPriorityNormal, NULL);
...
}
在旋转编码器的驱动程序中也需要进行读写队列的修改,driver_rotary_encoder.c如下:
extern QueueHandle_t g_xQueueRotary; /* 旋转编码器队列 */
void RotaryEncoder_IRQ_Callback(void)
{
uint64_t time;
static uint64_t pre_time = 0;
struct rotary_data rdata;
/* 1. 记录中断发生的时刻 */
time = system_get_ns();
/* 上升沿触发: 必定是高电平
* 防抖
*/
mdelay(2);
if (!RotaryEncoder_Get_S1())
return;
/* S1上升沿触发中断
* S2为0表示逆时针转, 为1表示顺时针转
*/
g_speed = (uint64_t)1000000000/(time - pre_time);
if (RotaryEncoder_Get_S2())
{
g_count++;
}
else
{
g_count--;
g_speed = 0 - g_speed;
}
pre_time = time;
/* 写队列 */
rdata.cnt = g_count;
rdata.speed = g_speed;
xQueueSendFromISR(g_xQueueRotary, &rdata, NULL);
}