本文主要是介绍【FreeRTOS】队列实验-分发数据给多个任务(赛车游戏),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
- 0 前言
- 1 队列实验_分发数据给多个任务(赛车游戏)
- 2 赛车游戏
- 2.1 game.c
- 2.2 注册队列
- 2.3显示汽车
- 2.4隐藏汽车
- 2.5 CarTask
- 2.6 car_game
- 2.7 MX_FREERTOS_Init
- 3 总结
0 前言
学习视频:
【FreeRTOS入门与工程实践 --由浅入深带你学习FreeRTOS(FreeRTOS教程 基于STM32,以实际项目为导向)】 【精准空降到 01:25】 https://www.bilibili.com/video/BV1Jw411i7Fz/?p=38&share_source=copy_web&vd_source=8af85e60c2df9af1f0fd23935753a933&t=85
参考《FreeRTOS入门与工程实践(基于DshanMCU-103).pdf》
1 队列实验_分发数据给多个任务(赛车游戏)
本节源码:在"16_queueset_game_mpu6050"的基础上,改出"17_queue_car_dispatch"
红外遥控器的中断函数解析出按键值后,写入3个队列:3个赛车任务读取其中一个队列得到按键数据。
任务:
写一个简陋版赛车游戏,在红外的中断函数里,我们解析按键值,以前是写一个队列,现在是把键值写三个队列,一共有三个任务,分别控制三辆车。
2 赛车游戏
2.1 game.c
编写game2.c,先测试图像是否能正常显示
/** 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"#include "game2.h"#define NOINVERT false
#define INVERT true#define CAR_COUNT 3
#define CAR_WIDTH 12
#define CAR_LENGTH 15
#define ROAD_SPEED 6static uint32_t g_xres, g_yres, g_bpp;
static uint8_t *g_framebuffer;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);
}
#endifstatic 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);
}void car_game(void)
{int x;int i, j;g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp);draw_init();draw_end();/* 画出路标 */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);}}draw_bitmap(0, 0, carImg, 15, 16, NOINVERT, 0);draw_flushArea(0, 0, 15, 16);
}
然后,将这个car_game放到初始化代码MX_FREERTOS_Init里
观察现象:
现在,我们就绘制出来了第一辆赛车和三条分割线!
显示三辆汽车
void car_game(void)
{int x;int i, j;g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp);draw_init();draw_end();/* 画出路标 */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个汽车任务 */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);}
}
同一个键值,写三个队列,这三个任务都可以分别读各自的队列,得到同一份数据
由驱动程序分发给不同的队列
2.2 注册队列
void RegisterQueueHandle(QueueHandle_t queueHandle)
{if (g_queue_cnt < 10){g_xQueues[g_queue_cnt] = queueHandle;g_queue_cnt++;}
}static void DispatchKey(struct ir_data *pidata)
{
#if 0 extern QueueHandle_t g_xQueueCar1;extern QueueHandle_t g_xQueueCar2;extern QueueHandle_t g_xQueueCar3;xQueueSendFromISR(g_xQueueCar1, pidata, NULL);xQueueSendFromISR(g_xQueueCar2, pidata, NULL);xQueueSendFromISR(g_xQueueCar3, pidata, NULL);
#elseint i;for (i = 0; i < g_queue_cnt; i++){xQueueSendFromISR(g_xQueues[i], pidata, NULL);}
#endif
}
这个注册队列,就是把底层驱动程序的某一个数组里,以后等硬件捕获到数据之后,它会自动分发数据
在中断函数里,检测到重复码之后,就会调用这个分发函数,把一个数据,写入多个队列
void IRReceiver_IRQ_Callback(void)
{uint64_t time;static uint64_t pre_time = 0;struct ir_data data;/* 1. 记录中断发生的时刻 */ time = system_get_ns();/* 一次按键的最长数据 = 引导码 + 32个数据"1" = 9+4.5+2.25*32 = 85.5ms* 如果当前中断的时刻, 举例上次中断的时刻超过这个时间, 以前的数据就抛弃*/if (time - pre_time > 100000000) {g_IRReceiverIRQ_Cnt = 0;}pre_time = time;g_IRReceiverIRQ_Timers[g_IRReceiverIRQ_Cnt] = time;/* 2. 累计中断次数 */g_IRReceiverIRQ_Cnt++;/* 3. 次数达标后, 解析数据, 放入buffer */if (g_IRReceiverIRQ_Cnt == 4){/* 是否重复码 */if (isRepeatedKey()){/* device: 0, val: 0, 表示重复码 *///PutKeyToBuf(0);//PutKeyToBuf(0);/* 写队列 */data.dev = 0;data.val = 0; //如果有重复码,就上报上一次的按键值DispatchKey(&data);g_IRReceiverIRQ_Cnt = 0;}}if (g_IRReceiverIRQ_Cnt == 68){IRReceiver_IRQTimes_Parse();g_IRReceiverIRQ_Cnt = 0;}
}
或者是解析到实际的键值之后,也写多个队列
static int IRReceiver_IRQTimes_Parse(void)
{uint64_t time;int i;int m, n;unsigned char datas[4];unsigned char data = 0;int bits = 0;int byte = 0;struct ir_data idata; //修改成 ir_data/* 1. 判断前导码 : 9ms的低脉冲, 4.5ms高脉冲 */time = g_IRReceiverIRQ_Timers[1] - g_IRReceiverIRQ_Timers[0];if (time < 8000000 || time > 10000000){return -1;}time = g_IRReceiverIRQ_Timers[2] - g_IRReceiverIRQ_Timers[1];if (time < 3500000 || time > 55000000){return -1;}/* 2. 解析数据 */for (i = 0; i < 32; i++){m = 3 + i*2;n = m+1;time = g_IRReceiverIRQ_Timers[n] - g_IRReceiverIRQ_Timers[m];data <<= 1;bits++;if (time > 1000000){/* 得到了数据1 */data |= 1;}if (bits == 8){datas[byte] = data;byte++;data = 0;bits = 0;}}/* 判断数据正误 */datas[1] = ~datas[1];datas[3] = ~datas[3];if ((datas[0] != datas[1]) || (datas[2] != datas[3])){g_IRReceiverIRQ_Cnt = 0;return -1;}//PutKeyToBuf(datas[0]); //写环形缓冲区//PutKeyToBuf(datas[2]);/* 写队列 */idata.dev = datas[0];idata.val = datas[2];DispatchKey(&idata);xQueueSendToBackFromISR(g_xQueueIR, &idata, NULL);return 0;
}
2.3显示汽车
这是显示一辆汽车的代码:
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);
}
2.4隐藏汽车
这是隐藏一辆汽车的代码:
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);
}
2.5 CarTask
编写CarTask任务函数
static void CarTask(void *params)
{struct car *pcar = params;struct ir_data idata;/* 创建自己的队列 */QueueHandle_t xQueueIR = xQueueCreate(10, sizeof(struct ir_data));/* 注册队列 */RegisterQueueHandle(xQueueIR);/* 显示汽车 */ShowCar(pcar);while (1){/* 读取按键值:读队列 */xQueueReceive(xQueueIR, &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);}}}
}
2.6 car_game
在car_game里实现所有的操作
void car_game(void)
{int x;int i, j;g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp);draw_init();draw_end();/* 画出路标 */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 0for (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);}
#endifxTaskCreate(CarTask, "car1", 128, &g_cars[0], osPriorityNormal, NULL);xTaskCreate(CarTask, "car2", 128, &g_cars[1], osPriorityNormal, NULL);xTaskCreate(CarTask, "car3", 128, &g_cars[2], osPriorityNormal, NULL);
}
最后修改初始化的部分
2.7 MX_FREERTOS_Init
这是初始化部分,我们在初始化中,调用car_game();这一个函数即可!
void MX_FREERTOS_Init(void) {/* USER CODE BEGIN Init */LCD_Init();LCD_Clear();IRReceiver_Init();RotaryEncoder_Init();LCD_PrintString(0, 0, "Starting");/* USER CODE END Init *//* USER CODE BEGIN RTOS_MUTEX *//* add mutexes, ... *//* USER CODE END RTOS_MUTEX *//* USER CODE BEGIN RTOS_SEMAPHORES *//* add semaphores, ... *//* USER CODE END RTOS_SEMAPHORES *//* USER CODE BEGIN RTOS_TIMERS *//* start timers, add new ones, ... *//* USER CODE END RTOS_TIMERS *//* USER CODE BEGIN RTOS_QUEUES *//* add queues, ... *//* USER CODE END RTOS_QUEUES *//* Create the thread(s) *//* creation of defaultTask */defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);/* USER CODE BEGIN RTOS_THREADS *//* add threads, ... *//* 创建任务 */extern void PlayMusic(void *params);xTaskCreate(PlayMusic, "MusicTask", 128, NULL, osPriorityNormal, NULL);car_game(); //使用这个函数来创建任务/* USER CODE END RTOS_THREADS *//* USER CODE BEGIN RTOS_EVENTS *//* add events, ... *//* USER CODE END RTOS_EVENTS */}
3 总结
我们在驱动程序里面,想使用同一个输入数据,控制多个任务!
我们可以在这个驱动程序里面,写多个队列,写哪些队列呢?这个决定权可以交给应用程序,应用程序调用一个注册函数,把它的句柄告诉驱动程序,驱动程序会把它记录下来,这样就可以实现分发数据给多个任务~
这篇关于【FreeRTOS】队列实验-分发数据给多个任务(赛车游戏)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!