FreeRTOS队列(下)

news2024/11/25 11:37:36

文章目录

  • 一、使用队列实现多设备输入
    • 1、增加旋转编码器
    • 2、使用队列集执行任务
    • 3、增加姿态控制(使用MPU6050控制挡球板)
  • 二、队列实验_分发数据给多个任务(赛车游戏)
  • 三、传输大块数据
  • 四、示例:邮箱(Mailbox)
  • 五、队列集
    • 1、创建队列集
    • 2、把队列加入队列集
    • 3、读取队列集

一、使用队列实现多设备输入

1、增加旋转编码器

本节代码为:“14_queue_game_multi_input”。在"13_queue_game"的基础上,增加旋转编码控制功能

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

实现方案:

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

game.c

#include <stdlib.h>
#include <stdio.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"

#define NOINVERT	false
#define INVERT		true

#define sprintf_P  sprintf
#define PSTR(a)  a

#define PLATFORM_WIDTH	12
#define PLATFORM_HEIGHT	4
#define UPT_MOVE_NONE	0
#define UPT_MOVE_RIGHT	1
#define UPT_MOVE_LEFT	2
#define BLOCK_COLS		32
#define BLOCK_ROWS		5
#define BLOCK_COUNT		(BLOCK_COLS * BLOCK_ROWS)

typedef struct{
	float x;
	float y;
	float velX;
	float velY;
}s_ball;

static const byte block[] ={
	0x07,0x07,0x07,
};

static const byte platform[] ={
	0x60,0x70,0x50,0x10,0x30,0xF0,0xF0,0x30,0x10,0x50,0x70,0x60,
};

static const byte ballImg[] ={
	0x03,0x03,
};

static const byte clearImg[] ={
	0,0,0,0,0,0,0,0,0,0,0,0,
};

static bool btnExit(void);
static bool btnRight(void);
static bool btnLeft(void);
void game1_draw(void);

static byte uptMove;
static s_ball ball;
static bool* blocks;
static byte lives, lives_origin;
static uint score;
static byte platformX;

static uint32_t g_xres, g_yres, g_bpp;
static uint8_t *g_framebuffer;

QueueHandle_t xQueuePlatform;//挡球板队列(红外中断写队列 游戏创建队列 挡球板读取队列)
QueueHandle_t xQueueRotary;  //旋转编码器队列(旋转编码器中断写队列 游戏创建队列和任务 任务是读取中断队列 并再次写解析数据队列 挡球板读取队列)

static uint8_t uchQueueRotaryBuf[10 * sizeof(struct rotary_Data)];  //旋转编码器数据存放
static StaticQueue_t pxQueueBuf;  //旋转编码器队列数据结构

/* 挡球板任务 */
static void platform_task(void *params)
{
    byte platformXtmp = platformX;    
    uint8_t dev, data, last_data;
		struct input_Data idata;

    // Draw platform
    draw_bitmap(platformXtmp, g_yres - 8, platform, 12, 8, NOINVERT, 0);
    draw_flushArea(platformXtmp, g_yres - 8, 12, 8);
    
    while (1)
    {
      /* 读队列,读取红外遥控器数据 */
			//if (0 == IRReceiver_Read(&dev, &data))
			xQueueReceive(xQueuePlatform,&idata,portMAX_DELAY);/* 读取队列 */
			
			uptMove = idata.data;//挡球板根据红外遥控器的键值以及旋转编码器旋转方向和速度做出移动

			
			// Hide platform
			draw_bitmap(platformXtmp, g_yres - 8, clearImg, 12, 8, NOINVERT, 0);
			draw_flushArea(platformXtmp, g_yres - 8, 12, 8);
			
			// Move platform
			if(uptMove == UPT_MOVE_RIGHT)
					platformXtmp += 3;
			else if(uptMove == UPT_MOVE_LEFT)
					platformXtmp -= 3;
			uptMove = UPT_MOVE_NONE;
			
			// Make sure platform stays on screen
			if(platformXtmp > 250)
					platformXtmp = 0;
			else if(platformXtmp > g_xres - PLATFORM_WIDTH)
					platformXtmp = g_xres - PLATFORM_WIDTH;
			
			// Draw platform
			draw_bitmap(platformXtmp, g_yres - 8, platform, 12, 8, NOINVERT, 0);
			draw_flushArea(platformXtmp, g_yres - 8, 12, 8);
			
			platformX = platformXtmp;						
    }
}

static void rotary_task(void *params)
{
	struct rotary_Data rdata;
	struct input_Data idata;
	int left;
	int cnt;
	
	while(1)
	{
		/* 读取旋转编码器中断队列 */
		xQueueReceive(xQueueRotary,&rdata,portMAX_DELAY);
		
		/* 处理数据	*/
		/* 速度判断,负数表示向左,正数表示向右	*/
		if(rdata.speed < 0)
		{
			left = 1;
			rdata.speed = 0 - rdata.speed;
		}
		else
			left = 0;
			
//		cnt = rdata.speed / 10;
//		if (!cnt)
//			cnt = 1;
		
		if(rdata.speed>100)//根据速度决定循环次数
			cnt = 5;
		else if(rdata.speed>50)
			cnt = 2;
		else
			cnt = 1;
		
		/* 写挡球板队列 挡球板根据旋转编码器数据做出调整 */
		idata.dev = 1;//设置旋转编码器的输入设备号为1
		idata.data = left?UPT_MOVE_LEFT:UPT_MOVE_RIGHT;
		for(int i=0;i<cnt;i++)
		{
			xQueueSend(xQueuePlatform,&idata,0);
		}
	}
}

void game1_task(void *params)
{		
    uint8_t dev, data, last_data;
    
    g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp);
    draw_init();
    draw_end();
	
	/* 在游戏刚开始处创建队列 */
	xQueuePlatform = xQueueCreate(10,sizeof(struct input_Data));//红外遥控器队列
	xQueueRotary = xQueueCreateStatic(10,sizeof(struct rotary_Data),uchQueueRotaryBuf,&pxQueueBuf);//旋转编码器队列

	/* 创建旋转编码器解析任务 */
   xTaskCreate(rotary_task, "rotary_task", 128, NULL, osPriorityNormal, NULL);
	
	uptMove = UPT_MOVE_NONE;

	ball.x = g_xres / 2;
	ball.y = g_yres - 10;
        
	ball.velX = -0.5;
	ball.velY = -0.6;
//	ball.velX = -1;
//	ball.velY = -1.1;

	blocks = pvPortMalloc(BLOCK_COUNT);
    memset(blocks, 0, BLOCK_COUNT);
	
	lives = lives_origin = 3;
	score = 0;
	platformX = (g_xres / 2) - (PLATFORM_WIDTH / 2);

    xTaskCreate(platform_task, "platform_task", 128, NULL, osPriorityNormal, NULL);

    while (1)
    {
        game1_draw();
        //draw_end();
        vTaskDelay(50);
    }
}

static bool btnExit()
{
	
	vPortFree(blocks);
	if(lives == 255)
	{
		//game1_start();
	}
	else
	{
		//pwrmgr_setState(PWR_ACTIVE_DISPLAY, PWR_STATE_NONE);	
		//animation_start(display_load, ANIM_MOVE_OFF);
		vTaskDelete(NULL);
	}
	return true;
}

static bool btnRight()
{
	uptMove = UPT_MOVE_RIGHT;
	return false;
}

static bool btnLeft()
{
	uptMove = UPT_MOVE_LEFT;
	return false;
}

void game1_draw()
{
	bool gameEnded = ((score >= BLOCK_COUNT) || (lives == 255));

	byte platformXtmp = platformX;

    static bool first = 1;

	// Move ball
	// hide ball
	draw_bitmap(ball.x, ball.y, clearImg, 2, 2, NOINVERT, 0);
    draw_flushArea(ball.x, ball.y, 2, 8);

    // Draw platform
    //draw_bitmap(platformX, g_yres - 8, platform, 12, 8, NOINVERT, 0);
    //draw_flushArea(platformX, g_yres - 8, 12, 8);
	
	if(!gameEnded)
	{
		ball.x += ball.velX;
		ball.y += ball.velY;
	}

	bool blockCollide = false;
	const float ballX = ball.x;
	const byte ballY = ball.y;

	// Block collision
	byte idx = 0;
	LOOP(BLOCK_COLS, x)
	{
		LOOP(BLOCK_ROWS, y)
		{
			if(!blocks[idx] && ballX >= x * 4 && ballX < (x * 4) + 4 && ballY >= (y * 4) + 8 && ballY < (y * 4) + 8 + 4)
			{
//				buzzer_buzz(100, TONE_2KHZ, VOL_UI, PRIO_UI, NULL);
				// led_flash(LED_GREEN, 50, 255); // 100ask todo
				blocks[idx] = true;

                // hide block
                draw_bitmap(x * 4, (y * 4) + 8, clearImg, 3, 8, NOINVERT, 0);                
                draw_flushArea(x * 4, (y * 4) + 8, 3, 8);                
				blockCollide = true;
				score++;
			}
			idx++;
		}
	}


	// Side wall collision
	if(ballX > g_xres - 2)
	{
		if(ballX > 240)
			ball.x = 0;		
		else
			ball.x = g_xres - 2;
		ball.velX = -ball.velX;		
	}
	if(ballX < 0)
  {
		ball.x = 0;		
		ball.velX = -ball.velX;	
  }

	// Platform collision
	bool platformCollision = false;
	if(!gameEnded && ballY >= g_yres - PLATFORM_HEIGHT - 2 && ballY < 240 && ballX >= platformX && ballX <= platformX + PLATFORM_WIDTH)
	{
		platformCollision = true;
		// buzzer_buzz(200, TONE_5KHZ, VOL_UI, PRIO_UI, NULL); // 100ask todo
		ball.y = g_yres - PLATFORM_HEIGHT - 2;
		if(ball.velY > 0)
			ball.velY = -ball.velY;
		ball.velX = ((float)rand() / (RAND_MAX / 2)) - 1; // -1.0 to 1.0
	}

	// Top/bottom wall collision
	if(!gameEnded && !platformCollision && (ballY > g_yres - 2 || blockCollide))
	{
		if(ballY > 240)
		{
			// buzzer_buzz(200, TONE_2_5KHZ, VOL_UI, PRIO_UI, NULL); // 100ask todo
			ball.y = 0;
		}
		else if(!blockCollide)
		{
			// buzzer_buzz(200, TONE_2KHZ, VOL_UI, PRIO_UI, NULL); // 100ask todo
			ball.y = g_yres - 1;
			lives--;
		}
		ball.velY *= -1;
	}

	// Draw ball
	draw_bitmap(ball.x, ball.y, ballImg, 2, 2, NOINVERT, 0);
    draw_flushArea(ball.x, ball.y, 2, 8);

    // Draw platform
    //draw_bitmap(platformX, g_yres - 8, platform, 12, 8, NOINVERT, 0);
    //draw_flushArea(platformX, g_yres - 8, 12, 8);

    if (first)
    {
        first = 0;
        
    	// Draw blocks
    	idx = 0;
    	LOOP(BLOCK_COLS, x)
    	{
    		LOOP(BLOCK_ROWS, y)
    		{
    			if(!blocks[idx])
    			{
    				draw_bitmap(x * 4, (y * 4) + 8, block, 3, 8, NOINVERT, 0);
                    draw_flushArea(x * 4, (y * 4) + 8, 3, 8);                
    			}
    			idx++;
    		}
    	}
        
    }

	// Draw score
	char buff[6];
	sprintf_P(buff, PSTR("%u"), score);
	draw_string(buff, false, 0, 0);

    // Draw lives
    if(lives != 255)
    {
        LOOP(lives_origin, i)
        {
            if (i < lives)
                draw_bitmap((g_xres - (3*8)) + (8*i), 1, livesImg, 7, 8, NOINVERT, 0);
            else
                draw_bitmap((g_xres - (3*8)) + (8*i), 1, clearImg, 7, 8, NOINVERT, 0);
            draw_flushArea((g_xres - (3*8)) + (8*i), 1, 7, 8);    
        }
    }   

	// Got all blocks
	if(score >= BLOCK_COUNT)
		draw_string_P(PSTR(STR_WIN), false, 50, 32);

	// No lives left (255 because overflow)
	if(lives == 255)
		draw_string_P(PSTR(STR_GAMEOVER), false, 34, 32);
}

freerots.c

这里与上个实验主要不同的就是游戏任务game_task中增加了旋转编码器队列,而freertos.c中只是进行调用,所以这里的freertos.c与上个实验一样,防止代码重复影响观看,这里就不展开。

2、使用队列集执行任务

修改“Core\Inc\FreeRTOSConfig.h“,增加:

/* USER CODE BEGIN Includes */
#define configUSE_QUEUE_SETS 1
/* Section where include file can be added */
/* USER CODE END Includes */

在STM32CubeMX里把堆调大,比如8000:
14 heap
game.c

#include <stdlib.h>
#include <stdio.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"

#define NOINVERT	false
#define INVERT		true

#define sprintf_P  sprintf
#define PSTR(a)  a

#define PLATFORM_WIDTH	12
#define PLATFORM_HEIGHT	4
#define UPT_MOVE_NONE	0
#define UPT_MOVE_RIGHT	1
#define UPT_MOVE_LEFT	2
#define BLOCK_COLS		32
#define BLOCK_ROWS		5
#define BLOCK_COUNT		(BLOCK_COLS * BLOCK_ROWS)

typedef struct{
	float x;
	float y;
	float velX;
	float velY;
}s_ball;

static const byte block[] ={
	0x07,0x07,0x07,
};

static const byte platform[] ={
	0x60,0x70,0x50,0x10,0x30,0xF0,0xF0,0x30,0x10,0x50,0x70,0x60,
};

static const byte ballImg[] ={
	0x03,0x03,
};

static const byte clearImg[] ={
	0,0,0,0,0,0,0,0,0,0,0,0,
};

static bool btnExit(void);
static bool btnRight(void);
static bool btnLeft(void);
void game1_draw(void);

static byte uptMove;
static s_ball ball;
static bool* blocks;
static byte lives, lives_origin;
static uint score;
static byte platformX;

static uint32_t g_xres, g_yres, g_bpp;
static uint8_t *g_framebuffer;

static QueueSetHandle_t xQueueSetInput;/* 输入设备的队列集 */
static QueueHandle_t xQueuePlatform;
static QueueHandle_t xQueueIR;
static QueueHandle_t xQueueRotary;

/* 挡球板任务 */
static void platform_task(void *params)
{
    byte platformXtmp = platformX;    
    uint8_t dev, data, last_data;
		struct input_Data idata;

    // Draw platform
    draw_bitmap(platformXtmp, g_yres - 8, platform, 12, 8, NOINVERT, 0);
    draw_flushArea(platformXtmp, g_yres - 8, 12, 8);
    
    while (1)
    {
      /* 读队列,读取红外遥控器数据 */
			//if (0 == IRReceiver_Read(&dev, &data))
			xQueueReceive(xQueuePlatform,&idata,portMAX_DELAY);/* 读取队列 */
			
			uptMove = idata.data;//挡球板根据红外遥控器的键值以及旋转编码器旋转方向和速度做出移动

			
			// Hide platform
			draw_bitmap(platformXtmp, g_yres - 8, clearImg, 12, 8, NOINVERT, 0);
			draw_flushArea(platformXtmp, g_yres - 8, 12, 8);
			
			// Move platform
			if(uptMove == UPT_MOVE_RIGHT)
					platformXtmp += 3;
			else if(uptMove == UPT_MOVE_LEFT)
					platformXtmp -= 3;
			uptMove = UPT_MOVE_NONE;
			
			// Make sure platform stays on screen
			if(platformXtmp > 250)
					platformXtmp = 0;
			else if(platformXtmp > g_xres - PLATFORM_WIDTH)
					platformXtmp = g_xres - PLATFORM_WIDTH;
			
			// Draw platform
			draw_bitmap(platformXtmp, g_yres - 8, platform, 12, 8, NOINVERT, 0);
			draw_flushArea(platformXtmp, g_yres - 8, 12, 8);
			
			platformX = platformXtmp;						
    }
}

static void ProcessIRData(void)
{
	struct ir_Data idata;
	static struct input_Data input;
	xQueueReceive(xQueueIR,&idata,0);
	
	if(idata.data == IR_KEY_LEFT)
	{
		input.dev = idata.dev;
		input.data= UPT_MOVE_LEFT;
	}
	else if(idata.data == IR_KEY_RIGHT)
	{
		input.dev = idata.dev;
		input.data= UPT_MOVE_RIGHT;
	}
	else if(idata.data == IR_KEY_REPEAT)
	{
		/* 重复按下时保持不变 */
	}
	else
	{
		input.dev = idata.dev;
		input.data= UPT_MOVE_NONE;		
	}
	
	/* 写挡球板队列 */
	xQueueSend(xQueuePlatform,&input,0);
}
static void ProcessRotaryData(void)
{
	struct rotary_Data rdata;
	struct input_Data idata;
	
	int left;
	int i,cnt;
	
	/* 读取旋转编码器中断队列 */
	xQueueReceive(xQueueRotary, &rdata, 0);
	
	/* 处理数据	*/
	/* 速度判断,负数表示向左,正数表示向右	*/
	if(rdata.speed < 0)
	{
		left = 1;
		rdata.speed = 0 - rdata.speed;
	}
	else
		left = 0;
		
//		cnt = rdata.speed / 10;
//		if (!cnt)
//			cnt = 1;
	
	if(rdata.speed>100)//根据速度决定循环次数
		cnt = 4;
	else if(rdata.speed>50)
		cnt = 2;
	else
		cnt = 1;
	
	/* 写挡球板队列 挡球板根据旋转编码器数据做出调整 */
	idata.dev = 1;//设置旋转编码器的输入设备号为1
	idata.data = left ? UPT_MOVE_LEFT : UPT_MOVE_RIGHT;
	for(int i=0;i<cnt;i++)
	{
		xQueueSend(xQueuePlatform, &idata, 0);
	}
}

static void Inputtask(void *params)
{
	QueueSetMemberHandle_t xQueueHandle;
	
	while(1)
	{
		/* 读队列集 得到有数据的队列句柄*/
		xQueueHandle = xQueueSelectFromSet(xQueueSetInput, portMAX_DELAY);
		
		if(xQueueHandle)
		{
			/* 读取队列  得到数据 处理数据*/
			if(xQueueHandle == xQueueIR)
			{
				ProcessIRData();
			}
			else if(xQueueHandle == xQueueRotary)
			{
				ProcessRotaryData();
			}
			
			/* 写挡球板队列 */
		}
	}
}

void game1_task(void *params)
{		
    uint8_t dev, data, last_data;
    
    g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp);
    draw_init();
    draw_end();
	
	/* 在游戏刚开始处创建队列 */
	xQueuePlatform = xQueueCreate(10,sizeof(struct input_Data));
	
	/* 创建队列集 */
	xQueueSetInput =  xQueueCreateSet(IR_QUEUE_LEN + ROTARY_QUEUE_LEN);
	
	/* 将创建的队列加入队列集 */
	xQueueIR = GetQueueIR();
	xQueueRotary = GetQueueRotary();
	
	xQueueAddToSet(xQueueIR,xQueueSetInput);
	xQueueAddToSet(xQueueRotary, xQueueSetInput);
	
	/* 创建输入任务 */
  xTaskCreate(Inputtask, "Inputtask", 128, NULL, osPriorityNormal, NULL);	
	
	uptMove = UPT_MOVE_NONE;

	ball.x = g_xres / 2;
	ball.y = g_yres - 10;
        
	ball.velX = -0.5;
	ball.velY = -0.6;
//	ball.velX = -1;
//	ball.velY = -1.1;

	blocks = pvPortMalloc(BLOCK_COUNT);
    memset(blocks, 0, BLOCK_COUNT);
	
	lives = lives_origin = 3;
	score = 0;
	platformX = (g_xres / 2) - (PLATFORM_WIDTH / 2);

    xTaskCreate(platform_task, "platform_task", 128, NULL, osPriorityNormal, NULL);

    while (1)
    {
        game1_draw();
        //draw_end();
        vTaskDelay(50);
    }
}

static bool btnExit()
{
	
	vPortFree(blocks);
	if(lives == 255)
	{
		//game1_start();
	}
	else
	{
		//pwrmgr_setState(PWR_ACTIVE_DISPLAY, PWR_STATE_NONE);	
		//animation_start(display_load, ANIM_MOVE_OFF);
		vTaskDelete(NULL);
	}
	return true;
}

static bool btnRight()
{
	uptMove = UPT_MOVE_RIGHT;
	return false;
}

static bool btnLeft()
{
	uptMove = UPT_MOVE_LEFT;
	return false;
}

void game1_draw()
{
	bool gameEnded = ((score >= BLOCK_COUNT) || (lives == 255));

	byte platformXtmp = platformX;

    static bool first = 1;

	// Move ball
	// hide ball
	draw_bitmap(ball.x, ball.y, clearImg, 2, 2, NOINVERT, 0);
    draw_flushArea(ball.x, ball.y, 2, 8);

    // Draw platform
    //draw_bitmap(platformX, g_yres - 8, platform, 12, 8, NOINVERT, 0);
    //draw_flushArea(platformX, g_yres - 8, 12, 8);
	
	if(!gameEnded)
	{
		ball.x += ball.velX;
		ball.y += ball.velY;
	}

	bool blockCollide = false;
	const float ballX = ball.x;
	const byte ballY = ball.y;

	// Block collision
	byte idx = 0;
	LOOP(BLOCK_COLS, x)
	{
		LOOP(BLOCK_ROWS, y)
		{
			if(!blocks[idx] && ballX >= x * 4 && ballX < (x * 4) + 4 && ballY >= (y * 4) + 8 && ballY < (y * 4) + 8 + 4)
			{
//				buzzer_buzz(100, TONE_2KHZ, VOL_UI, PRIO_UI, NULL);
				// led_flash(LED_GREEN, 50, 255); // 100ask todo
				blocks[idx] = true;

                // hide block
                draw_bitmap(x * 4, (y * 4) + 8, clearImg, 3, 8, NOINVERT, 0);                
                draw_flushArea(x * 4, (y * 4) + 8, 3, 8);                
				blockCollide = true;
				score++;
			}
			idx++;
		}
	}


	// Side wall collision
	if(ballX > g_xres - 2)
	{
		if(ballX > 240)
			ball.x = 0;		
		else
			ball.x = g_xres - 2;
		ball.velX = -ball.velX;		
	}
	if(ballX < 0)
  {
		ball.x = 0;		
		ball.velX = -ball.velX;	
  }

	// Platform collision
	bool platformCollision = false;
	if(!gameEnded && ballY >= g_yres - PLATFORM_HEIGHT - 2 && ballY < 240 && ballX >= platformX && ballX <= platformX + PLATFORM_WIDTH)
	{
		platformCollision = true;
		// buzzer_buzz(200, TONE_5KHZ, VOL_UI, PRIO_UI, NULL); // 100ask todo
		ball.y = g_yres - PLATFORM_HEIGHT - 2;
		if(ball.velY > 0)
			ball.velY = -ball.velY;
		ball.velX = ((float)rand() / (RAND_MAX / 2)) - 1; // -1.0 to 1.0
	}

	// Top/bottom wall collision
	if(!gameEnded && !platformCollision && (ballY > g_yres - 2 || blockCollide))
	{
		if(ballY > 240)
		{
			// buzzer_buzz(200, TONE_2_5KHZ, VOL_UI, PRIO_UI, NULL); // 100ask todo
			ball.y = 0;
		}
		else if(!blockCollide)
		{
			// buzzer_buzz(200, TONE_2KHZ, VOL_UI, PRIO_UI, NULL); // 100ask todo
			ball.y = g_yres - 1;
			lives--;
		}
		ball.velY *= -1;
	}

	// Draw ball
	draw_bitmap(ball.x, ball.y, ballImg, 2, 2, NOINVERT, 0);
    draw_flushArea(ball.x, ball.y, 2, 8);

    // Draw platform
    //draw_bitmap(platformX, g_yres - 8, platform, 12, 8, NOINVERT, 0);
    //draw_flushArea(platformX, g_yres - 8, 12, 8);

    if (first)
    {
        first = 0;
        
    	// Draw blocks
    	idx = 0;
    	LOOP(BLOCK_COLS, x)
    	{
    		LOOP(BLOCK_ROWS, y)
    		{
    			if(!blocks[idx])
    			{
    				draw_bitmap(x * 4, (y * 4) + 8, block, 3, 8, NOINVERT, 0);
                    draw_flushArea(x * 4, (y * 4) + 8, 3, 8);                
    			}
    			idx++;
    		}
    	}
        
    }

	// Draw score
	char buff[6];
	sprintf_P(buff, PSTR("%u"), score);
	draw_string(buff, false, 0, 0);

    // Draw lives
    if(lives != 255)
    {
        LOOP(lives_origin, i)
        {
            if (i < lives)
                draw_bitmap((g_xres - (3*8)) + (8*i), 1, livesImg, 7, 8, NOINVERT, 0);
            else
                draw_bitmap((g_xres - (3*8)) + (8*i), 1, clearImg, 7, 8, NOINVERT, 0);
            draw_flushArea((g_xres - (3*8)) + (8*i), 1, 7, 8);    
        }
    }   

	// Got all blocks
	if(score >= BLOCK_COUNT)
		draw_string_P(PSTR(STR_WIN), false, 50, 32);

	// No lives left (255 because overflow)
	if(lives == 255)
		draw_string_P(PSTR(STR_GAMEOVER), false, 34, 32);
}

freerots.c

这里与上个实验主要不同的就是游戏任务game_task中增加了旋转编码器队列,而freertos.c中只是进行调用,所以这里的freertos.c与上个实验一样,防止代码重复影响观看,这里就不展开。

3、增加姿态控制(使用MPU6050控制挡球板)

本节源码:在"15_queueset_game"的基础上,改出"16_queueset_game_mpu6050",支持6轴传感器,使用姿态控制玩游戏。

关于MPU6050的了解可以参考:https://zhuanlan.zhihu.com/p/30621372

game.c

#include <stdlib.h>
#include <stdio.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"


#define NOINVERT	false
#define INVERT		true

#define sprintf_P  sprintf
#define PSTR(a)  a

#define PLATFORM_WIDTH	12
#define PLATFORM_HEIGHT	4
#define UPT_MOVE_NONE	0
#define UPT_MOVE_RIGHT	1
#define UPT_MOVE_LEFT	2
#define BLOCK_COLS		32
#define BLOCK_ROWS		5
#define BLOCK_COUNT		(BLOCK_COLS * BLOCK_ROWS)

typedef struct{
	float x;
	float y;
	float velX;
	float velY;
}s_ball;

static const byte block[] ={
	0x07,0x07,0x07,
};

static const byte platform[] ={
	0x60,0x70,0x50,0x10,0x30,0xF0,0xF0,0x30,0x10,0x50,0x70,0x60,
};

static const byte ballImg[] ={
	0x03,0x03,
};

static const byte clearImg[] ={
	0,0,0,0,0,0,0,0,0,0,0,0,
};

static bool btnExit(void);
static bool btnRight(void);
static bool btnLeft(void);
void game1_draw(void);

static byte uptMove;
static s_ball ball;
static bool* blocks;
static byte lives, lives_origin;
static uint score;
static byte platformX;

static uint32_t g_xres, g_yres, g_bpp;
static uint8_t *g_framebuffer;

static QueueSetHandle_t xQueueSetInput;/* 输入设备的队列集 */
static QueueHandle_t xQueuePlatform;
static QueueHandle_t xQueueIR;
static QueueHandle_t xQueueRotary;
static QueueHandle_t xQueueMPU6050;//MPU6050队列


/* 挡球板任务 */
static void platform_task(void *params)
{
    byte platformXtmp = platformX;    
    uint8_t dev, data, last_data;
		struct input_Data idata;

    // Draw platform
    draw_bitmap(platformXtmp, g_yres - 8, platform, 12, 8, NOINVERT, 0);
    draw_flushArea(platformXtmp, g_yres - 8, 12, 8);
    
    while (1)
    {
      /* 读队列,读取红外遥控器数据 */
			//if (0 == IRReceiver_Read(&dev, &data))
			xQueueReceive(xQueuePlatform,&idata,portMAX_DELAY);/* 读取队列 */
			
			uptMove = idata.data;//挡球板根据红外遥控器的键值以及旋转编码器旋转方向和速度做出移动

			
			// Hide platform
			draw_bitmap(platformXtmp, g_yres - 8, clearImg, 12, 8, NOINVERT, 0);
			draw_flushArea(platformXtmp, g_yres - 8, 12, 8);
			
			// Move platform
			if(uptMove == UPT_MOVE_RIGHT)
					platformXtmp += 3;
			else if(uptMove == UPT_MOVE_LEFT)
					platformXtmp -= 3;
			uptMove = UPT_MOVE_NONE;
			
			// Make sure platform stays on screen
			if(platformXtmp > 250)
					platformXtmp = 0;
			else if(platformXtmp > g_xres - PLATFORM_WIDTH)
					platformXtmp = g_xres - PLATFORM_WIDTH;
			
			// Draw platform
			draw_bitmap(platformXtmp, g_yres - 8, platform, 12, 8, NOINVERT, 0);
			draw_flushArea(platformXtmp, g_yres - 8, 12, 8);
			
			platformX = platformXtmp;						
    }
}

static void ProcessIRData(void)
{
	struct ir_Data idata;
	static struct input_Data input;
	xQueueReceive(xQueueIR,&idata,0);
	
	if(idata.data == IR_KEY_LEFT)
	{
		input.dev = idata.dev;
		input.data= UPT_MOVE_LEFT;
	}
	else if(idata.data == IR_KEY_RIGHT)
	{
		input.dev = idata.dev;
		input.data= UPT_MOVE_RIGHT;
	}
	else if(idata.data == IR_KEY_REPEAT)
	{
		/* 重复按下时保持不变 */
	}
	else
	{
		input.dev = idata.dev;
		input.data= UPT_MOVE_NONE;		
	}
	
	/* 写挡球板队列 */
	xQueueSend(xQueuePlatform,&input,0);
}
static void ProcessRotaryData(void)
{
	struct rotary_Data rdata;
	struct input_Data idata;
	
	int left;
	int i,cnt;
	
	/* 读取旋转编码器中断队列 */
	xQueueReceive(xQueueRotary, &rdata, 0);
	
	/* 处理数据	*/
	/* 速度判断,负数表示向左,正数表示向右	*/
	if(rdata.speed < 0)
	{
		left = 1;
		rdata.speed = 0 - rdata.speed;
	}
	else
		left = 0;
		
//		cnt = rdata.speed / 10;
//		if (!cnt)
//			cnt = 1;
	
	if(rdata.speed>100)//根据速度决定循环次数
		cnt = 4;
	else if(rdata.speed>50)
		cnt = 2;
	else
		cnt = 1;
	
	/* 写挡球板队列 挡球板根据旋转编码器数据做出调整 */
	idata.dev = 1;//设置旋转编码器的输入设备号为1
	idata.data = left ? UPT_MOVE_LEFT : UPT_MOVE_RIGHT;
	for(int i=0;i<cnt;i++)
	{
		xQueueSend(xQueuePlatform, &idata, 0);
	}
}

static void ProcessMPU6050Data(void)
{
	struct mpu6050_data mdata;//mpu6050 x方向的数据
	struct input_Data idata;
	
	/* 读取MPU6050中断队列 获得数据 */
	xQueueReceive(xQueueMPU6050, &mdata, 0);
	
	/* 处理数据	*/
	/* 方向判断,大于90表示向左,小于90表示向右	*/
	if(mdata.angle_x > 90)
	{
		idata.data = UPT_MOVE_LEFT;
	}
	else if(mdata.angle_x < 90)
	{
		idata.data = UPT_MOVE_RIGHT;
	}
	else
	{
		idata.data = UPT_MOVE_NONE;
	}
		
//		cnt = rdata.speed / 10;
//		if (!cnt)
//			cnt = 1;
	
	/* 写挡球板队列 挡球板根据MPU6050数据做出调整 */
	idata.dev = 2;//设置MPU6050的输入设备号为2
	xQueueSend(xQueuePlatform, &idata, 0);//读取完数据将数据放松给挡球板队列

}

static void Inputtask(void *params)
{
	QueueSetMemberHandle_t xQueueHandle;
	
	while(1)
	{
		/* 读队列集 得到有数据的队列句柄*/
		xQueueHandle = xQueueSelectFromSet(xQueueSetInput, portMAX_DELAY);
		
		if(xQueueHandle)
		{
			/* 读取队列  得到数据 处理数据*/
			if(xQueueHandle == xQueueIR)
			{
				ProcessIRData();
			}
			else if(xQueueHandle == xQueueRotary)
			{
				ProcessRotaryData();
			}
			else if(xQueueHandle == xQueueMPU6050)
			{
				ProcessMPU6050Data();
			}
			
			/* 写挡球板队列 */
		}
	}
}

void game1_task(void *params)
{		
    uint8_t dev, data, last_data;
    
    g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp);
    draw_init();
    draw_end();
	
	/* 在游戏刚开始处创建队列 */
	xQueuePlatform = xQueueCreate(10,sizeof(struct input_Data));
	
	/* 创建队列集 */
	xQueueSetInput =  xQueueCreateSet(IR_QUEUE_LEN + ROTARY_QUEUE_LEN + MPU6050_QUEUE_LEN);
	
	/* 将创建的队列加入队列集 */
	xQueueIR = GetQueueIR();
	xQueueRotary = GetQueueRotary();
	xQueueMPU6050 = GetQueueMPU6050();//获得mpu6050的句柄
	
	xQueueAddToSet(xQueueIR,xQueueSetInput);
	xQueueAddToSet(xQueueRotary, xQueueSetInput);
	xQueueAddToSet(xQueueMPU6050,xQueueSetInput);//加入队列集
	
	/* 这里让队列先加入队列集再创建任务是因为mpu6050每隔50ms就会运行一次 如果让其执行完再加入队列集会导致队列满了 */
    xTaskCreate(MPU6050_Task, "MPU6050Task", 128, NULL, osPriorityNormal, NULL);
    /* 创建输入任务 */
    xTaskCreate(Inputtask, "Inputtask", 128, NULL, osPriorityNormal, NULL);	
	
	uptMove = UPT_MOVE_NONE;

	ball.x = g_xres / 2;
	ball.y = g_yres - 10;
        
	ball.velX = -0.5;
	ball.velY = -0.6;
//	ball.velX = -1;
//	ball.velY = -1.1;

	blocks = pvPortMalloc(BLOCK_COUNT);
    memset(blocks, 0, BLOCK_COUNT);
	
	lives = lives_origin = 3;
	score = 0;
	platformX = (g_xres / 2) - (PLATFORM_WIDTH / 2);

	xTaskCreate(platform_task, "platform_task", 128, NULL, osPriorityNormal, NULL);
    while (1)
    {
        game1_draw();
        //draw_end();
        vTaskDelay(50);
    }
}

static bool btnExit()
{
	
	vPortFree(blocks);
	if(lives == 255)
	{
		//game1_start();
	}
	else
	{
		//pwrmgr_setState(PWR_ACTIVE_DISPLAY, PWR_STATE_NONE);	
		//animation_start(display_load, ANIM_MOVE_OFF);
		vTaskDelete(NULL);
	}
	return true;
}

static bool btnRight()
{
	uptMove = UPT_MOVE_RIGHT;
	return false;
}

static bool btnLeft()
{
	uptMove = UPT_MOVE_LEFT;
	return false;
}

void game1_draw()
{
	bool gameEnded = ((score >= BLOCK_COUNT) || (lives == 255));

	byte platformXtmp = platformX;

    static bool first = 1;

	// Move ball
	// hide ball
	draw_bitmap(ball.x, ball.y, clearImg, 2, 2, NOINVERT, 0);
    draw_flushArea(ball.x, ball.y, 2, 8);

    // Draw platform
    //draw_bitmap(platformX, g_yres - 8, platform, 12, 8, NOINVERT, 0);
    //draw_flushArea(platformX, g_yres - 8, 12, 8);
	
	if(!gameEnded)
	{
		ball.x += ball.velX;
		ball.y += ball.velY;
	}

	bool blockCollide = false;
	const float ballX = ball.x;
	const byte ballY = ball.y;

	// Block collision
	byte idx = 0;
	LOOP(BLOCK_COLS, x)
	{
		LOOP(BLOCK_ROWS, y)
		{
			if(!blocks[idx] && ballX >= x * 4 && ballX < (x * 4) + 4 && ballY >= (y * 4) + 8 && ballY < (y * 4) + 8 + 4)
			{
//				buzzer_buzz(100, TONE_2KHZ, VOL_UI, PRIO_UI, NULL);
				// led_flash(LED_GREEN, 50, 255); // 100ask todo
				blocks[idx] = true;

                // hide block
                draw_bitmap(x * 4, (y * 4) + 8, clearImg, 3, 8, NOINVERT, 0);                
                draw_flushArea(x * 4, (y * 4) + 8, 3, 8);                
				blockCollide = true;
				score++;
			}
			idx++;
		}
	}


	// Side wall collision
	if(ballX > g_xres - 2)
	{
		if(ballX > 240)
			ball.x = 0;		
		else
			ball.x = g_xres - 2;
		ball.velX = -ball.velX;		
	}
	if(ballX < 0)
  {
		ball.x = 0;		
		ball.velX = -ball.velX;	
  }

	// Platform collision
	bool platformCollision = false;
	if(!gameEnded && ballY >= g_yres - PLATFORM_HEIGHT - 2 && ballY < 240 && ballX >= platformX && ballX <= platformX + PLATFORM_WIDTH)
	{
		platformCollision = true;
		// buzzer_buzz(200, TONE_5KHZ, VOL_UI, PRIO_UI, NULL); // 100ask todo
		ball.y = g_yres - PLATFORM_HEIGHT - 2;
		if(ball.velY > 0)
			ball.velY = -ball.velY;
		ball.velX = ((float)rand() / (RAND_MAX / 2)) - 1; // -1.0 to 1.0
	}

	// Top/bottom wall collision
	if(!gameEnded && !platformCollision && (ballY > g_yres - 2 || blockCollide))
	{
		if(ballY > 240)
		{
			// buzzer_buzz(200, TONE_2_5KHZ, VOL_UI, PRIO_UI, NULL); // 100ask todo
			ball.y = 0;
		}
		else if(!blockCollide)
		{
			// buzzer_buzz(200, TONE_2KHZ, VOL_UI, PRIO_UI, NULL); // 100ask todo
			ball.y = g_yres - 1;
			lives--;
		}
		ball.velY *= -1;
	}

	// Draw ball
	draw_bitmap(ball.x, ball.y, ballImg, 2, 2, NOINVERT, 0);
    draw_flushArea(ball.x, ball.y, 2, 8);

    // Draw platform
    //draw_bitmap(platformX, g_yres - 8, platform, 12, 8, NOINVERT, 0);
    //draw_flushArea(platformX, g_yres - 8, 12, 8);

    if (first)
    {
        first = 0;
        
    	// Draw blocks
    	idx = 0;
    	LOOP(BLOCK_COLS, x)
    	{
    		LOOP(BLOCK_ROWS, y)
    		{
    			if(!blocks[idx])
    			{
    				draw_bitmap(x * 4, (y * 4) + 8, block, 3, 8, NOINVERT, 0);
                    draw_flushArea(x * 4, (y * 4) + 8, 3, 8);                
    			}
    			idx++;
    		}
    	}
        
    }

	// Draw score
	char buff[6];
	sprintf_P(buff, PSTR("%u"), score);
	draw_string(buff, false, 0, 0);

    // Draw lives
    if(lives != 255)
    {
        LOOP(lives_origin, i)
        {
            if (i < lives)
                draw_bitmap((g_xres - (3*8)) + (8*i), 1, livesImg, 7, 8, NOINVERT, 0);
            else
                draw_bitmap((g_xres - (3*8)) + (8*i), 1, clearImg, 7, 8, NOINVERT, 0);
            draw_flushArea((g_xres - (3*8)) + (8*i), 1, 7, 8);    
        }
    }   

	// Got all blocks
	if(score >= BLOCK_COUNT)
		draw_string_P(PSTR(STR_WIN), false, 50, 32);

	// No lives left (255 because overflow)
	if(lives == 255)
		draw_string_P(PSTR(STR_GAMEOVER), false, 34, 32);
}

freertos.c

#include "driver_led.h"
#include "driver_lcd.h"
#include "driver_mpu6050.h"
#include "driver_timer.h"
#include "driver_ds18b20.h"
#include "driver_dht11.h"
#include "driver_active_buzzer.h"
#include "driver_passive_buzzer.h"
#include "driver_color_led.h"
#include "driver_ir_receiver.h"
#include "driver_ir_sender.h"
#include "driver_light_sensor.h"
#include "driver_ir_obstacle.h"
#include "driver_ultrasonic_sr04.h"
#include "driver_spiflash_w25q64.h"
#include "driver_rotary_encoder.h"
#include "driver_motor.h"
#include "driver_key.h"
#include "driver_uart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

static StackType_t g_pucStackOfLightTask[128];
static StaticTask_t g_TCBofLightTask;
static TaskHandle_t xLightTaskHandle;

static StackType_t g_pucStackOfColorTask[128];
static StaticTask_t g_TCBofColorTask;
static TaskHandle_t xColorTaskHandle;
void game1_task(void *params);

osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};

void StartDefaultTask(void *argument);

void MX_FREERTOS_Init(void); 
void MX_FREERTOS_Init(void) 
{
  LCD_Init();
  LCD_Clear();
  
  MPU6050_Init();
  IRReceiver_Init();
  RotaryEncoder_Init();
  LCD_PrintString(0, 0, "Starting");
    
  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);


  extern void PlayMusic(void *params);	
  extern void MPU6050_Task(void *params);

  xTaskCreate(PlayMusic, "MusicTask", 128, NULL, osPriorityNormal, NULL);
  xTaskCreate(game1_task, "GameTask", 128, NULL, osPriorityNormal, NULL);
  xTaskCreate(MPU6050_Task, "MPU6050Task", 128, NULL, osPriorityNormal, NULL);
    
void StartDefaultTask(void *argument)
{
    uint8_t dev, data;
    int len;
	int bRunning;
	
	TaskHandle_t xSoundTaskHandle = NULL;
	BaseType_t ret;
	
	vTaskDelete(NULL);
	
	LCD_Init();
	LCD_Clear();

	MPU6050_Init();
	RotaryEncoder_Init();
  IRReceiver_Init();

	LCD_PrintString(0, 0, "Waiting control");
	
	while(1)
	{
		MPU6050_Test();
	}

    while (1)
    {
		if (0 == IRReceiver_Read(&dev, &data))
		{		
			if (data == 0xa8) /* play */
			{
			  extern void PlayMusic(void *params);
			  if (xSoundTaskHandle == NULL)
			  {
					LCD_ClearLine(0, 0);
					LCD_PrintString(0, 0, "Create Task");
					ret = xTaskCreate(PlayMusic, "SoundTask", 128, NULL, osPriorityNormal+1, &xSoundTaskHandle);
					bRunning = 1;
			  }
			  else
			  {
				  if (bRunning)
				  {
					  LCD_ClearLine(0, 0);
					  LCD_PrintString(0, 0, "Suspend Task");
					  vTaskSuspend(xSoundTaskHandle);
					  PassiveBuzzer_Control(0); 
					  bRunning = 0;
				  }
				  else
				  {
					  LCD_ClearLine(0, 0);
					  LCD_PrintString(0, 0, "Resume Task");
					  vTaskResume(xSoundTaskHandle);
					  bRunning = 1;
				  }
			  }
			}
			
			else if (data == 0xa2) /* power */
			{
				if (xSoundTaskHandle != NULL)
				{
					LCD_ClearLine(0, 0);
					LCD_PrintString(0, 0, "Delete Task");
					vTaskDelete(xSoundTaskHandle);
					PassiveBuzzer_Control(0);
					xSoundTaskHandle = NULL;
				}
			}
		}
    }
}

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

本节源码:在"16_queueset_game_mpu6050"的基础上,改出"17_queue_car_dispatch"

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

game2.c

#include <stdlib.h>
#include <stdio.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"

#define CAR_COUNT	3
#define CAR_WIDTH	12
#define CAR_LENGTH	15
#define ROAD_SPEED	6

#define NOINVERT	false
#define INVERT		true

static uint32_t g_xres, g_yres, g_bpp;
static uint8_t *g_framebuffer;

struct car
{
	int x;
	int y;
	int control_key;
};

struct car 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 roadMarking[]={
	0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
};

static const byte clearImg[30]={0};

#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);//x坐标 y坐标 位图 位图宽 位图高 位图是否翻转 最后一般为0(一行15个字节即位图宽为15 高度为2即表示16个像素 则位图高为16)
	draw_flushArea(0, 0, 15, 16);
	
	draw_bitmap(0, 16, roadMarking, 8, 1, NOINVERT, 0);
	draw_flushArea(0, 16, 8, 1);
	
	while(1);
}
#endif

static void ShowCar(struct car *car)
{
	draw_bitmap(car->x, car->y, carImg, 15, 16, NOINVERT, 0);
	draw_flushArea(car->x, car->y, 15, 16);	
}

static void HideCar(struct car *car)
{
	draw_bitmap(car->x, car->y, clearImg, 15, 16, NOINVERT, 0);
	draw_flushArea(car->x, car->y, 15, 16);		
}

static void car_task(void *params)
{
	struct car *car = params;
	struct ir_Data idata;
	
	/* 创建自己的队列 */
	QueueHandle_t xQueueIR = xQueueCreate(10,sizeof(struct ir_Data));
	
	/* 注册队列 */
	RegisterQueueHandle(xQueueIR);
	
	/* 初始化小车 */
	ShowCar(car);
	
	while(1)
	{
		/* 读取按键值  */
		xQueueReceive(xQueueIR,&idata,portMAX_DELAY);
		
		/* 控制汽车往右移动 */
		if(idata.data == car->control_key)
		{
			if(car->x < g_xres - CAR_LENGTH)
			{
				/* 隐藏汽车 */
				HideCar(car);
				
				/* 调整位置 */
				car->x += 20;//每次按下右移20个单位
				if(car->x > g_xres - CAR_LENGTH)//超过屏幕分辨率(128)
					car->x = g_xres - CAR_LENGTH;//到达最大位置处
				
				/* 重新显示汽车 */
				ShowCar(car);
			}
		}
	}
}

void car_game(void)
{
	int i,j;
	int x;
	
	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);		
		}
	}

#if 0
	/* 显示三辆小车 */
	for(i=0;i<3;i++)
	{
		draw_bitmap(cars[i].x, cars[i].y, carImg, 15, 16, NOINVERT, 0);
		draw_flushArea(cars[i].x, cars[i].y, 15, 16);	
	}
#endif

	xTaskCreate(car_task, "car1task", 128, &cars[0], osPriorityNormal, NULL);	
	xTaskCreate(car_task, "car2task", 128, &cars[1], osPriorityNormal, NULL);	
	xTaskCreate(car_task, "car3task", 128, &cars[2], osPriorityNormal, NULL);	
}

三、传输大块数据

FreeRTOS的队列使用拷贝传输,也就是要传输uint32_t时,把4字节的数据拷贝进队列; 要传输一个8字节的结构体时,把8字节的数据拷贝进队列。

如果要传输1000字节的结构体呢?写队列时拷贝1000字节,读队列时再拷贝1000字节? 不建议这么做,影响效率!

这时候,我们要传输的是这个巨大结构体的地址:把它的地址写入队列,对方从队列得到这个地址,使用地址去访问那1000字节的数据。

使用地址来间接传输数据时,这些数据放在RAM里,对于这块RAM,要保证这几点:

  • RAM的所有者、操作者,必须清晰明了

​ 这块内存,就被称为"共享内存"。要确保不能同时修改RAM。比如,在写队列之前只有由发送者修改这块RAM,在读队列之后只能由接收者访问这块RAM。

  • RAM要保持可用

​ 这块RAM应该是全局变量,或者是动态分配的内存。对于动然分配的内存,要确保它不能提前释放:要等到接收者用完后再释放。另外,不能是局部变量。

FreeRTOS_10_queue_bigtransfer程序会创建一个队列,然后创建1个发送任务、1个接收任务:

  • 创建的队列:长度为1,用来传输"char *"指针
  • 发送任务优先级为1,在字符数组中写好数据后,把它的地址写入队列
  • 接收任务优先级为2,读队列得到"char *"值,把它打印出来

这个程序故意设置接收任务的优先级更高,在它访问数组的过程中,接收任务无法执行、无法写这个数组。

main函数中创建了队列、创建了发送任务、接收任务,代码如下:

/* 定义一个字符数组 */ 
static char pcBuffer[100];

/* vSenderTask被用来创建2个任务,用于写队列 
* vReceiverTask被用来创建1个任务,用于读队列 
*/ 
static void vSenderTask( void *pvParameters ); 
static void vReceiverTask( void *pvParameters );

/*-----------------------------------------------------------*/
/* 队列句柄, 创建队列时会设置这个变量 */ 
QueueHandle_t xQueue; 
 
int main( void ) 
{ 
 prvSetupHardware(); 
  
 /* 创建队列: 长度为1,数据大小为4字节(存放一个char指针) */ 
 xQueue = xQueueCreate( 1, sizeof(char *) ); 
 
 if( xQueue != NULL ) 
 { 
  /* 创建1个任务用于写队列 
   * 任务函数会连续执行,构造buffer数据,把buffer地址写入队列 
   * 优先级为1 
   */ 
  xTaskCreate( vSenderTask, "Sender", 1000, NULL, 1, NULL ); 
 
  /* 创建1个任务用于读队列 
   * 优先级为2, 高于上面的两个任务 
   * 这意味着读队列得到buffer地址后,本任务使用buffer时不会被打断 
   */ 
  xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 2, NULL ); 
 
  /* 启动调度器 */ 
  vTaskStartScheduler(); 
 } 
 else 
 { 
  /* 无法创建队列 */ 
 } 
 
 /* 如果程序运行到了这里就表示出错了, 一般是内存不足 */ 
 return 0; 
} 

发送任务的函数中,现在全局大数组pcBuffer中构造数据,然后把它的地址写入队列,代码如下:

static void vSenderTask( void *pvParameters ) 
{ 
 BaseType_t xStatus; 
 static int cnt = 0;
 char *buffer; 
 
 /* 无限循环 */ 
 for( ;; ) 
 { 
  sprintf(pcBuffer, "www.100ask.net Msg %d\r\n", cnt++); 
  buffer = pcBuffer; // buffer变量等于数组的地址, 下面要把这个地址写入队列 
   
  /* 写队列 
   * xQueue: 写哪个队列 
   * pvParameters: 写什么数据? 传入数据的地址, 会从这个地址把数据复制进队列 
   * 0: 如果队列满的话, 即刻返回 
   */ 
  xStatus = xQueueSendToBack( xQueue, &buffer, 0 ); /* 只需要写入4字节, 无需写入整个buffer */ 
 
  if( xStatus != pdPASS ) 
  { 
   printf( "Could not send to the queue.\r\n" ); 
  } 
 } 
} 

接收任务的函数中,读取队列、得到buffer的地址、打印,代码如下:

static void vReceiverTask( void *pvParameters ) 
{ 
 /* 读取队列时, 用这个变量来存放数据 */ 
 char *buffer; 
 const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );  
 BaseType_t xStatus; 
 
 /* 无限循环 */ 
 for( ;; ) 
 { 
   /* 读队列 
    * xQueue: 读哪个队列 
    * &xReceivedStructure: 读到的数据复制到这个地址 
    * xTicksToWait: 没有数据就阻塞一会 
    */ 
    xStatus = xQueueReceive( xQueue, &buffer, xTicksToWait); /* 得到buffer地址,只是4字节 */ 
 
    if( xStatus == pdPASS ) 
    {
        /* 读到了数据 */ 
        printf("Get: %s", buffer); 
    } 
    else 
    { 
        /* 没读到数据 */ 
        printf( "Could not receive from the queue.\r\n" ); 
    } 
  } 
} 

运行结果如下图所示:

1721094970978

四、示例:邮箱(Mailbox)

本节代码为:FreeRTOS_11_queue_mailbox。

FreeRTOS的邮箱概念跟别的RTOS不一样,这里的邮箱称为"橱窗"也许更恰当:

  • 它是一个队列,队列长度只有1

  • 写邮箱:新数据覆盖旧数据,在任务中使用xQueueOverwrite(),在中断中 使用xQueueOverwriteFromISR()

    ​ 既然是覆盖,那么无论邮箱中是否有数据,这些函数总能成功写入数据。

  • 读邮箱:读数据时,数据不会被移除;在任务中使用xQueuePeek(),在中 断中使用xQueuePeekFromISR()

​ 这意味着,第一次调用时会因为无数据而阻塞,一旦曾经写入数据,以后读邮箱 时总能成功。

main函数中创建了队列(队列长度为1)、创建了发送任务、接收任务:

  • 发送任务的优先级为2,它先执行
  • 接收任务的优先级为1

代码如下:

/* 队列句柄, 创建队列时会设置这个变量 */ 
QueueHandle_t xQueue; 
int main( void ) 
{ 
    prvSetupHardware(); 
    /* 创建队列: 长度为1,数据大小为4字节(存放一个char指针) */ 
    xQueue = xQueueCreate( 1, sizeof(uint32_t) ); 
    if( xQueue != NULL ) 
    { 
        /* 创建1个任务用于写队列 
        * 任务函数会连续执行,构造buffer数据,把buffer地址写入队列 
        * 优先级为2 
        */ 
        xTaskCreate( vSenderTask, "Sender", 1000, NULL, 2, NULL ); 
        /* 创建1个任务用于读队列 
        * 优先级为1
        百问网 
        */ 
        xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 1, NULL ); 
        /* 启动调度器 */ 
        vTaskStartScheduler(); 
    } 
    else 
    { 
        /* 无法创建队列 */ 
    } 
    /* 如果程序运行到了这里就表示出错了, 一般是内存不足 */ 
    return 0; 
}

发送任务、接收任务的代码和执行流程如下:

  • A:发送任务先执行,马上阻塞
  • BC:接收任务执行,这是邮箱无数据,打印"Could not …"。在发送任务阻塞过程中,接收任务多次执行、多次打印。
  • D:发送任务从阻塞状态退出,立刻执行、写队列
  • E:发送任务再次阻塞
  • FG、HI、……:接收任务不断"偷看"邮箱,得到同一个数据,打印出多个"Get: 0"
  • J:发送任务从阻塞状态退出,立刻执行、覆盖队列,写入1
  • K:发送任务再次阻塞
  • LM、……:接收任务不断"偷看"邮箱,得到同一个数据,打印出多个"Get: 1"

1721095814749

运行结果如下图所示:

1721095874267

五、队列集

假设有2个输入设备:红外遥控器、旋转编码器,它们的驱动程序应该专注于“产生硬件数据”,不应该跟“业务有任何联系”。比如:红外遥控器驱动程序里,它只应该把键值记录下来、写入某个队列,它不应该把键值转换为游戏的控制键。在红外遥控器的驱动程序里,不应该有游戏相关的代码,这样,切换使用场景时,这个驱动程序还可以继续使用。

把红外遥控器的按键转换为游戏的控制键,应该在游戏的任务里实现。

要支持多个输入设备时,我们需要实现一个“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再读取这个队列句柄得到数据。

1、创建队列集

函数原型如下:

QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength ) ;
参数说明
uxQueueLength队列集长度,最多能存放多少个数据(队列句柄)
返回值非0:成功,返回句柄,以后使用句柄来操作队列;NULL:失败,因为内存不足

2、把队列加入队列集

函数原型如下:

BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, 
							QueueSetHandle_t xQueueSet ); 
参数说明
xQueueOrSemaphore队列句柄,这个队列要加入队列集
xQueueSet队列集句柄
返回值pdTRUE:成功;pdFALSE:失败

3、读取队列集

函数原型如下:

QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, 
											TickType_t const xTicksToWait ); 
参数说明
xQueueSet队列集句柄
xTicksToWait如果队列集空则无法读出数据,可以让任务进入阻塞状态,xTicksToWait 表示阻塞的最大时间(Tick Count)。如果被设为0,无法读出数据时函数会立刻返回;如果被设为portMAX_DELAY,则会一直阻塞直到有数据可写
返回值NULL:失败;队列句柄:成功

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2045620.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

远程桌面你的凭据不工作解决方法

如果遇到“你的凭据不工作”的提示&#xff0c;请首先确认您输入的凭据是否正确。如果确认无误但仍然出现Windows远程桌面凭据不工作的提示&#xff0c;这可能是由于Windows安全设置的问题所导致的。要解决远程桌面连接提示你的凭据不工作这一问题&#xff0c;您可以尝试以下解…

qemu的VNC协议(RFB协议)

1、握手 1.1、服务器收到客户端TCP连接上后&#xff0c;服务器发送RFB的版本&#xff1a; 52 46 42 20 30 30 33 2e 30 30 38 0a RFB 003.008\n 1.2、客户端收到后回复&#xff1a; 52 46 42 20 30 30 33 2e 30 30 38 0a RFB 003.008\n 2、安全认证 2.1、服务器发送支…

MySQL数据库之part2

一、索引原理与慢查询优化 一&#xff09;介绍 1、为何需要有索引 一般的应用系统&#xff0c;读写比例是10:1左右&#xff0c;而且插入操作和一般的更新操作很少出现性能问题。在生产环境中&#xff0c;我们遇到最多的、也是最容易出问题的还是一些负责的查询操作。因此查询语…

密码学基础---椭圆曲线一文打尽

1.ECC简介及密钥生成 当前公认安全有效的三大类公钥密钥体制分别为基于大数因子分解难题(RSA)、离散对数难题(DSA)和椭圆曲线离散对数&#xff08;ECC&#xff09;难题的密码体制。 最初RSA由于其容易理解被广泛运用&#xff0c;但随着计算机性能的提升&#xff0c;要保证RS…

JavaScript学习笔记(十三):网络请求JS AJAX

1、AJAX - XMLHttpRequest 对象 1.1 XMLHttpRequest 对象是 AJAX 的基石。 创建 XMLHttpRequest 对象定义回调函数打开 XMLHttpRequest 对象向服务器发送请求 1.2 XMLHttpRequest 对象 所有现代浏览器都支持 XMLHttpRequest 对象。 XMLHttpRequest 对象可用于在后台与 Web…

【mysql】MySQL的数据库简单搭建

文章目录 前言基础知识数据库数据表数据表结构记录&#xff08;数据&#xff09; 数据类型数值类型字符串类型日期类型二进制类型其他数据类型 约束where子句其他条件 order by 语句 &#xff08;排序&#xff09;group by语句&#xff08;分组&#xff09; 数据库操作新建数据…

漏洞挖掘 | EDU拿敏感信息的骚思路

1. 寻找资产 在进行edu漏洞挖掘的时候&#xff0c;我们常常遇到统一认证平台&#xff0c;账号是学号&#xff0c;密码是身份证后6位&#xff08;甚至是更复杂的密码&#xff09;&#xff0c;同时找到这两者的几率很小&#xff0c;所以我们把关注点放在微信小程序中&#xff0c…

15 个高难度 Java 面试题及答案

一、企业聘用指南 聘用合适的 Java 开发人员对您的业务至关重要。聘用不当可能会导致代码效率低下、错过最后期限以及团队活力不佳。这就是为什么在聘用候选人之前必须彻底审查每位候选人的原因。这个过程的一部分是在面试 Java 开发人员候选人时提出正确的问题。 通过我们列…

OK-COIN : 总统大选成为比特币牛市的导火索

每一届总统大选&#xff0c;都将带动比特币进入牛市行情&#xff0c;还有三个多月时间&#xff0c;现在比特币经过底部针震荡整理后&#xff0c;形成了坚实的“双针探底”形态&#xff0c;确认比特币底部形成&#xff0c;随后迎来了非常强势的单边趋势性行情机会 &#xff0c;相…

IDEA关键词全局检索-之jar包

正常没有下载到源码的jar包&#xff0c;是无法检索到.class编译文件中内容的 repository本地仓库中&#xff0c;也是没有源码的 《检索步骤》 1、首先&#xff0c;下载源码 - 可以配置maven自动下载所有jar包的源码&#xff08;再同样配置File-NewProjectsSetup-Settingsfor…

CAN总线学习笔记

1 CAN总线简介 CAN&#xff08;Controller Area Network&#xff09;控制器局域网。 2 电平表示 CAN 总线用两根数据线传输数据&#xff0c;使用差分信号。 2.1 显性电平–低电平–逻辑零 当单片机发送逻辑0信号时&#xff0c;CAN转换芯片输出端会输出一个3.5V信号和一个…

【大模型理论篇】GPT系列预训练模型原理讲解

1. 背景简述 GPT的全称是Generative Pre-Trained Transformer&#xff0c;以Transformer为基础模型&#xff08;可以看Transformer的原理解析&#xff09;&#xff0c;先后迭代了GPT-1【1】&#xff0c;GPT-2【2】&#xff0c;GPT-3【3】、GPT3.5(InstructGPT)【10】、GPT4。…

【直播预告】智能机器人赛道技术培训定档8.20

在不远的将来&#xff0c;机器人可能会成为我们日常生活中不可或缺的伙伴&#xff0c;它们在工业生产线上精准操作&#xff0c;在家庭中提供温馨陪伴&#xff0c;甚至在探索未知领域中担当先锋。而现在&#xff0c;正是我们拥抱这一未来&#xff0c;深入了解并掌握智能机器人技…

【一个月备战国赛】按模型算法分类的国内外优秀论文分享

时间一天一天过去&#xff0c;离国赛又更近一步&#xff0c;昨天分享了matlab的算法程序包&#xff0c;但是可能很多小伙伴拿到算法之后&#xff0c;不知道该如何去使用&#xff0c;如何与题目相结合去完成论文的写作&#xff0c;那么这里就需要家人们提高自己文章的阅读量&…

【python与java的区别-序列(字符串、列表、元组)02】

序列都可以进行的操作包括索引&#xff0c;切片&#xff0c;加&#xff0c;乘&#xff0c;检查成员。 一、字符串 1、定义 Java中的字符是单引号&#xff0c;字符串是双引号&#xff1b;Python则是单双都可以&#xff0c;python也可以使用三引号创建多行字符串 shello fre…

kubernetes之HPA和VPA

目录 HPA 服务发布 创建HPA 增加负载 停止产生负载 VPA HPA HPA&#xff08;Horizontal Pod Autoscaling&#xff0c;Pod水平自动伸缩&#xff09;是Kubernetes中的一个核心功能&#xff0c;它允许用户根据当前Pod的资源使用率&#xff08;如CPU、内存等&#xff09;或其…

想知道排名好的自闭症学校有哪些?这里为你解答

在当今社会&#xff0c;自闭症儿童的教育和康复问题备受关注&#xff0c;网络上关于自闭症学校排名的文章层出不穷。然而&#xff0c;家长们在为自己的孩子挑选合适的学校时&#xff0c;切不可盲目依赖这些排名&#xff0c;一定要结合线下实地考察。 那么&#xff0c;一…

ONES 王颖奇:关于 ONES V6 发布的解读

经过近一年的产品研发&#xff0c;ONES 正式发布 V6 版。 结合 ONES 的产品成熟度阶段和近两年的市场变化&#xff0c;ONES V6 为以下几个场景做了能力深耕。 1. 系统兼容和部署方面&#xff0c;软件供应链国产化政策引导趋势明显&#xff0c;在金融等领域要求全栈信创支持。 …

【数据结构-哈希前缀】力扣1590. 使数组和能被 P 整除

给你一个正整数数组 nums&#xff0c;请你移除 最短 子数组&#xff08;可以为 空&#xff09;&#xff0c;使得剩余元素的 和 能被 p 整除。 不允许 将整个数组都移除。 请你返回你需要移除的最短子数组的长度&#xff0c;如果无法满足题目要求&#xff0c;返回 -1 。 子数组…

模型类DTO、PO、VO

项目中有三类模型类:DTO数据传输对象、PO持久化对象&#xff0c;DTO用于接口层向业务层之间传输数据&#xff0c;PO用于业务层与持久层之间传输数据&#xff1b;有些项目还会设置V0对象&#xff0c;V0对象用在前端与接口层之间传输数据&#xff0c;当前端有多个平台且接口存在差…