代码编织梦想

队列

参考《FreeRTOS入门与工程实践(基于DshanMCU-103)》里《第11章  队列(queue)》

资料来自韦东山freertos 教程,写博客的目的是为了做一个记录,不对请喷。

FreeRTOS入门与工程实践 --由浅入深带你学习FreeRTOS(FreeRTOS教程 基于STM32,以实际项目为导向)_哔哩哔哩_bilibili

1. 数据传输的方法

1.1 任务之间如何传输数据

多种方法比较:

数据个数

互斥措施

阻塞-唤醒

使用场景

全局变量

1

一读一写

环形缓冲区

多个

一读一写

队列

多个

多读多写

1.2 队列的本质

队列中,数据的读写本质就是环形缓冲区,在这个基础上增加了互斥措施、阻塞-唤醒机制。

如果这个队列不传输数据,只调整"数据个数",它就是信号量(semaphore)。

如果信号量中,限定"数据个数"最大值为1,它就是互斥量(mutex)。

画图演示队列的"阻塞-唤醒"机制。

使用队列的方法可以,做到互斥和同步的效果,提高程序的执行效率,避免浪费CPU资源。

2. 队列实验_多设备玩游戏(挡球板游戏)

实验目的:使用红外遥控器、旋转编码器玩游戏。

实现方案:

  • 游戏任务:读取队列A获得控制信息,用来控制游戏
  • 红外遥控器驱动:在中断函数里解析出按键后,写队列A
  • 旋转编码器:
    • 它的中断函数里解析出旋转编码器的状态,写队列B
    • 它的任务函数里,读取队列B,构造好数据后写队列A

3. 队列集实验(挡球板游戏)

3.1 改进程序框架

上面程序框架,导致硬件驱动程序和应用业务处理逻辑没有分离。如果需要增加新的设备对挡球板的控制,我们还需要添加新的任务,这会导致系统资源紧张。

我们需要做到像于Rotary 旋转编码器数据处理一样的程序框架,应用处理和驱动分离,需要有一个任务来统一处理传感器的数据,然后上传给App使用。我们可以利用队列集来完成这个框架。

4. 队列实验_分发数据给多个任务(赛车游戏)

红外遥控器的中断函数解析出按键值后,写入3个队列:3个赛车任务读取其中一个队列得到按键数据。

代码思路创建三个队列,赛车任务不断地去读取队列执行业务逻辑。

第一版代码,最傻实现(学生时期的我就是这样写代码的)

思路过程:

赛车任务里面需要不断地去查询自己的队列,通过任务的名字去判断我们需要读取的队列。这样做的坏处是如果需要加赛车很麻烦。

任务函数

static void CarTask(void *params)
{
    char *task_1 = "car1";
    char *task_2 = "car2";
    char *task_3 = "car3";

    char *task_name;

    uint8_t re1;
    uint8_t re2;
    uint8_t re3;

    struct car *pcar = params;
    ir_data_t idata;
    #if 0
    /* 创建自己的队列 */
    QueueHandle_t xQueueIR = xQueueCreate(10, sizeof(ir_data_t));

    /* 注册队列 */
    RegisterQueueHandle(xQueueIR);
    #endif
    /* 显示汽车 */
    ShowCar(pcar);

    task_name = pcTaskGetName(NULL);
    re1 = strcmp(task_name, task_1);
    re2 = strcmp(task_name, task_2);
    re3 = strcmp(task_name, task_3);

    while (1)
    {
        #if 0
        /* 读取按键值:读队列 */
        xQueueReceive(xQueueIR, &idata, portMAX_DELAY);
        #endif

        if (re1 == 0)
        {
            // printf("%s\r\n",task_name);

            /* 读取按键值:读队列 */
            xQueueReceive(g_queue_car1_handler, &idata, portMAX_DELAY);
        }
        if (re2 == 0)
        {
            /* 读取按键值:读队列 */
            xQueueReceive(g_queue_car2_handler, &idata, portMAX_DELAY);
            // printf("%s\r\n",task_name);
        }
        if (re3 == 0)
        {
            // printf("%s\r\n",task_name);
            /* 读取按键值:读队列 */
            xQueueReceive(g_queue_car2_handler, &idata, portMAX_DELAY);
        }

        /* 控制汽车往右移动 */
        if (idata.val == pcar->control_key)
        {
            if (pcar->x < g_xres - CAR_LENGTH)
            {
                /* 隐藏汽车 */
                HideCar(pcar);

                /* 调整位置 */
                pcar->x += 20;
                if (pcar->x > g_xres - CAR_LENGTH)
                {
                    pcar->x = g_xres - CAR_LENGTH;
                }

                /* 重新显示汽车 */
                ShowCar(pcar);
            }
        }
    }
}

中断函数

static DispatchKey(ir_data_t *idata)
{
	extern QueueHandle_t g_queue_car1_handler;
	extern QueueHandle_t g_queue_car2_handler;
	extern QueueHandle_t g_queue_car3_handler;
	xQueueSendFromISR(g_queue_car1_handler, idata, NULL);
	xQueueSendFromISR(g_queue_car2_handler, idata, NULL);
	xQueueSendFromISR(g_queue_car3_handler, idata, NULL);
}

第二版代码

加入一个注册中心的概念(实际产品项目中也经常用到这种思想),创建一个内存池里面保存队列的句柄,把想要使用的队列放入这个内存池中。

中断函数

static QueueHandle_t g_xQueues[10];   //队列内存池
static int g_queue_cnt = 0;

void RegisterQueueHandle(QueueHandle_t queueHandler){
	if(g_queue_cnt != 10){
		g_xQueues[g_queue_cnt++] = queueHandler;
	}
}

static DispatchKey(ir_data_t *idata)
{
#if 0
	extern QueueHandle_t g_queue_car1_handler;
	extern QueueHandle_t g_queue_car2_handler;
	extern QueueHandle_t g_queue_car3_handler;
	xQueueSendFromISR(g_queue_car1_handler, idata, NULL);
	xQueueSendFromISR(g_queue_car2_handler, idata, NULL);
	xQueueSendFromISR(g_queue_car3_handler, idata, NULL);
#endif
	for (uint8_t i = 0; i < g_queue_cnt; i++)
	{	
		if(g_queue_cnt == 10){
		  break;
		}else{
			xQueueSendFromISR(g_xQueues[i],idata,NULL);
		}
	}
	
}

任务函数

static void CarTask(void *params)
{
#if 0
	char *task_1 = "car1";
	char *task_2 = "car2";
	char *task_3 = "car3";

	char *task_name;

	uint8_t re1;
	uint8_t re2;
	uint8_t re3;
#endif
	struct car *pcar = params;

	ir_data_t idata;

	/* 创建自己的队列 */
	QueueHandle_t xQueueIR = xQueueCreate(10, sizeof(ir_data_t));

	/* 注册队列 */
	RegisterQueueHandle(xQueueIR);

	/* 显示汽车 */
	ShowCar(pcar);
#if 0
	task_name = pcTaskGetName(NULL);
	re1 = strcmp(task_name, task_1);
	re2 = strcmp(task_name, task_2);
	re3 = strcmp(task_name, task_3);
#endif
	while (1)
	{
		/* 读取按键值:读队列 */
		xQueueReceive(xQueueIR, &idata, portMAX_DELAY);

#if 0
		if (re1 == 0)
		{
			// printf("%s\r\n",task_name);

			/* 读取按键值:读队列 */
			xQueueReceive(g_queue_car1_handler, &idata, portMAX_DELAY);
		}
		if (re2 == 0)
		{
			/* 读取按键值:读队列 */
			xQueueReceive(g_queue_car2_handler, &idata, portMAX_DELAY);
			// printf("%s\r\n",task_name);
		}
		if (re3 == 0)
		{
			// printf("%s\r\n",task_name);
			/* 读取按键值:读队列 */
			xQueueReceive(g_queue_car2_handler, &idata, portMAX_DELAY);
		}
#endif
		/* 控制汽车往右移动 */
		if (idata.val == pcar->control_key)
		{
			if (pcar->x < g_xres - CAR_LENGTH)
			{
				/* 隐藏汽车 */
				HideCar(pcar);

				/* 调整位置 */
				pcar->x += 20;
				if (pcar->x > g_xres - CAR_LENGTH)
				{
					pcar->x = g_xres - CAR_LENGTH;
				}

				/* 重新显示汽车 */
				ShowCar(pcar);
			}
		}
	}
}

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_54376810/article/details/136631186

韦东山学习笔记_hello&code的博客-爱代码爱编程

1:地址空间的分配2:开发板上一般都用SDRAM做内存 ,flash(nor、nand)来当做ROM。其中nand flash没有地址线,一次至少要读一页(512B). 其他两个有地址线3:nandflash不用来运行代码,只用来存储代码,NORflash,SDRAM可以直接运行代码) 4:s3c2440总共有8个内存banks  6个内存bank可以当作

韦东山linux嵌入式学习——硬件复习_荡失路的细路的博客-爱代码爱编程

Linux嵌入式学习——点个灯(二) JZ2440LED原理图GPIOS3C2440 本文是基于韦东山视频的学习笔记 汇总点这 JZ2440 要实际来点灯了,用的是JZ2440的开发板,因为听说这款开

基于stm32瑞士军刀-爱代码爱编程

声明:参考韦东山老师网页知识点以及自己理解整理笔记。 一、数据传输方法对比(以下列举方法逐步改善) 1、全局变量 在FreeRTOS中,使用全局变量 进行数据传输可能被打断,导致传输错误,上节具体讲过原因。 2、

韦东山硬件编程学习随笔_韦东山单片机的通用模块-爱代码爱编程

CPU,MPU,MCU CPU和MPU差不多,MPU功能更单一 芯片=CPU+RAM+FLASH MCU(也叫单片机,微控制器)=芯片+其他模块(比如GPIO) stm32里,cortex_m3就是cpu, ARM架构的cpu,cpu内部的寄存器和外部的外设寄存器,是在一个地址空间内,而x86则是分开的。cpu内部的寄存器cpu可以直接访问,

queueformcu 开源项目安装与使用指南-爱代码爱编程

QueueForMcu 开源项目安装与使用指南 QueueForMcu基于单片机实现的队列功能模块,主要用于8位、16位、32位非运行RTOS的单片机应用,兼容大多数单片机平台。项目地址:https://gitcode.com/gh_mirrors/qu/QueueForMcu 1. 项目目录结构及介绍 QueueForMcu项目遵循简洁的目录组