FreeRTOS_同步互斥与通信_队列集_学习笔记

发布于:2024-05-21 ⋅ 阅读:(177) ⋅ 点赞:(0)

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);
}

网站公告

今日签到

点亮在社区的每一天
去签到