STM32---FreeRTOS消息队列

一、简介

1、队列简介:

队列 :是任务到任务,任务到中断、中断到任务数据交流的一种机制(消息传递)。

FreeRTOS基于队列,实现了多种功能,其中包括队列集、互斥信号量、计数型信号量、二值信号量、递归互斥信号量,因此很有必要深入了解FreeRTOS的队列。

(中断一关闭,就不会出现任务切换,以防多个任务同时操作队列)

2、FreeRTOS队列特点:

1.数据入队出队方式:先进先出

2.数据传递方式:实际值

3.多任务访问

  1. 出队、入队堵塞

问题:当多个任务写入消息给一个"满队列"时,这些任务都会进入阻塞状态,也就是说有多个任务 在等待同一 个队列的空间。那当队列中有空间时,哪个任务会进入就绪态?

答: 1、优先级最高的任务 2、如果大家的优先级相同,那等待时间最久的任务会进入就绪态

注:我始终认为自己不是一个很聪明的人,所以这些理论知识,我都是浅尝辄止,量力而行。

3、往队列写入消息API函数 :

4、从队列读取消息API函数:

二、实验

1、实验步骤

2、代码:

main.c

cs 复制代码
#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "task.h"
#include "freertos_demo.h"
#include "Delay.h"
#include "sys.h"
#include "usart.h"
#include "LED.h"
#include "Key.h"

 
 int main(void)
 {	
	 
	 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组 4 
	 uart_init(115200);	 
	 delay_init();
	 Key_Init();
	 LED_Init();
	 
	    // 创建任务
   FrrrRTOS_Demo();
		 	  
}

freertos_demo.c

cs 复制代码
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "LED.h"
#include "Key.h"
#include "usart.h"
#include "delay.h"

/******************************************************************任务配置****************************************************/
//任务优先级
#define START_TASK_PRIO					1
//任务堆栈大小	
#define START_TASK_STACK_SIZE 	128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);


//任务优先级
#define TASK1_PRIO							2
//任务堆栈大小	
#define TASK1_STACK_SIZE 				128  
//任务句柄
TaskHandle_t Task1_Handler;
//任务函数
void task1(void *pvParameters);
 
//任务优先级
#define TASK2_PRIO							3
//任务堆栈大小	
#define TASK2_STACK_SIZE 				128  
//任务句柄
TaskHandle_t Task2_Handler;
//任务函数
void task2(void *pvParameters);

//任务优先级
#define TASK3_PRIO							4
//任务堆栈大小	
#define TASK3_STACK_SIZE 				128  
//任务句柄
TaskHandle_t Task3_Handler;
//任务函数
void task3(void *pvParameters);
 


char  task_buffer[500]; 							//用于存储系统中任务信息表格



/******************************************************************任务函数****************************************************/
QueueHandle_t		key_queue; 						//小数据句柄
QueueHandle_t		big_data_queue; 			//大数据	句柄
char buff[100] = {"苍天已死,黄天当立;岁在甲子,天下大吉"};
void FrrrRTOS_Demo(void)
{
		
		key_queue = xQueueCreate(2, sizeof(uint8_t));
		if(key_queue != NULL)
		{
			printf("\r\nkey_queue队列创建成功!!!\r\n");
		}else{ printf("key_queue队列创建失败!!!\r\n");	}
		
		big_data_queue = xQueueCreate(1, sizeof(char *));
		if(big_data_queue != NULL)
		{
			printf("big_data_queue队列创建成功!!!\r\n");
		}else{ printf("big_data_queue队列创建失败!!!\r\n");	}
			 //创建开始任务
		xTaskCreate((TaskFunction_t )start_task,            			//任务函数
                ( char*         )"start_task",          			//任务名称
                (uint16_t       )START_TASK_STACK_SIZE, 			//任务堆栈大小
                (void*          )NULL,                  			//传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       			//任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   			//任务句柄 
	  // 启动任务调度
		vTaskStartScheduler();
	 
}


 void start_task(void *pvParameters)
{
	 taskENTER_CRITICAL();           //进入临界区
    //创建1任务
    xTaskCreate((TaskFunction_t )task1,     	
                (const char*    )"task1",   	
                (uint16_t       )TASK1_STACK_SIZE, 
                (void*          )NULL,				
                (UBaseType_t    )TASK1_PRIO,	
                (TaskHandle_t*  )&Task1_Handler); 
    //创建2任务
    xTaskCreate((TaskFunction_t )task2,     
                (const char*    )"task2",   
                (uint16_t       )TASK2_STACK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )TASK2_PRIO,
                (TaskHandle_t*  )&Task2_Handler);    
    //创建3任务
    xTaskCreate((TaskFunction_t )task3,     
                (const char*    )"task3",   
                (uint16_t       )TASK3_STACK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )TASK3_PRIO,
                (TaskHandle_t*  )&Task3_Handler);  								
  
		
    vTaskDelete(NULL); 							//删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
}


//1 任务函数
void task1(void *pvParameters)
{
	uint8_t 	 key = 0;
	BaseType_t err;
	char *buf;
	buf = &buff[0];
	while(1)
	{
		key = Key_GetNum();
		if(key == 1 || key == 2)
		{
			err = xQueueSend( key_queue, &key, portMAX_DELAY );
			if(err != pdTRUE)
			{
				printf("key_queue队列发送失败\r\n");
			}
		}else if(key == 3)
		{
			err = xQueueSend( big_data_queue, &buf, portMAX_DELAY );
			if(err != pdTRUE)
			{
				printf("key_queue队列发送失败\r\n");
			}
		}
		vTaskDelay(50);

	}
}


// 任务2 小数据出队函数
void task2(void *pvParameters)
{
	uint8_t    key = 0;
	BaseType_t err = 0;
		
    // 任务主循环
    while (1)
    {
			err = xQueueReceive( key_queue,&key,portMAX_DELAY );
			if(err != pdTRUE)
			{
				printf("key_queue队列读取失败\r\n");		
			}else{printf("key = %d\r\n",key);};
    }
}

//不调用系统延时函数,因为xQueueReceive()函数如果读取完队列里面的数据,就会由就绪态转变为阻塞态;

// 任务3 大数据出队函数
void task3(void *pvParameters)
{	
	char *    buf;
	BaseType_t err = 0;
		
    // 任务主循环
    while (1)
    {
			err = xQueueReceive( big_data_queue, &buf, portMAX_DELAY);
			if(err != pdTRUE)
			{
				printf("big_data_queue队列读取失败\r\n");		
			}else{printf("key = %s\r\n",buf);};
    }
}

key.c

cs 复制代码
#include "stm32f10x.h"                  // Device header
#include "FreeRTOS.h"
#include "task.h"
#include "usart.h"
#include "Delay.h"



/**
  * 函    数:按键初始化
  * 参    数:无
  * 返 回 值:无
	* 按键:PB4/PB12/PB14
  */
void Key_Init(void)
{
	
	GPIO_InitTypeDef GPIO_InitStructure;
	
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启GPIOB的时钟
	
	/*GPIO初始化*/
	GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_4 | GPIO_Pin_12 | GPIO_Pin_14;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);					
	
}


/**
  * 函    数:按键获取键码
  * 参    数:无
  * 返 回 值:按下按键的键码值,范围:0~3,返回0代表没有按键按下
  * 注意事项:此函数是阻塞式操作,当按键按住不放时,函数会卡住,直到按键松手
  */
uint8_t Key_GetNum(void)
{
	uint8_t KeyNum = 0;																				//定义变量,默认键码值为0
	
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_4) == 0)			  //读PB4输入寄存器的状态,如果为0,则代表按键1按下
	{
		KeyNum= GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_4);
		printf("KeyNum = %d\r\n",KeyNum);
		delay_xms(20);																					//延时消抖
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_4) == 0);	//等待按键松手
		delay_xms(20);																					//延时消抖
		KeyNum = 1;																							//置键码为1
	}
	
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12) == 0)			
	{
		KeyNum= GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12);
		printf("KeyNum = %d\r\n",KeyNum);
		delay_xms(20);											
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12) == 0);	
		delay_xms(20);									
		KeyNum = 2;											
	}
	
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)			
	{
		KeyNum= GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14);
		printf("KeyNum = %d\r\n",KeyNum);
		delay_xms(20);											
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0);	
		delay_xms(20);									
		KeyNum = 3;											
	}
	
	
	return KeyNum;																						//返回键码值,如果没有按键按下,所有if都不成立,则键码为默认值0
}

3、实验结果解析

开始运行:

按下按键1(PB4):

按下按键1,就会往队列key_queue里面写入key值(1),然后任务切换到task2将队列key_queue里面的数据读取出来;;

按下按键2(PB12):

按下按键2,就会往队列key_queue里面写入key值(2),然后任务切换到task2将队列key_queue里面的数据读取出来;

按下按键3(PB14) :

按下按键2,就会往队列big_data_queue里面写入key值(3),然后任务切换到task3将队列big_data_queue里面的数据读取出来;

三、重点

使用队列相关函数时需要将下面宏置1(默认是1):

#define configSUPPORT_DYNAMIC_ALLOCATION 1

队列创建函数:

xQueueCreate( uxQueueLength, uxItemSize ) ; //uxQueueLength:队列长度;uxItemSize 队列参数的大小

队列写入消息函数:

xQueueSend( xQueue, pvItemToQueue, xTicksToWait ); //xQueue:待写入的队列;pvItemToQueue:待写入的消息;xTicksToWait:阻塞超时时间

队列读取消息函数:

xQueueReceive( QueueHandle_t xQueue,void * const pvBuffer,TickType_t xTicksToWait ) ; //xQueue:待读取的队列;pvBuffer:信息读取缓冲区;xTicksToWait:阻塞超时时间

问题:任务2(task2)和任务3(task3)没有系统延时函数(xTaskDelay()),按优先级来说应该一直执行任务3(task3),复位后却先执行了任务1(task1)?

答:因为xQueueReceive()和xQueueSend()函数,如果读取完或写入完队列里面的数据,自动会使任务由就绪态转变为阻塞态,知道队列里面有数据可以写入或者读出;

相关推荐
沉在嵌入式的鱼1 小时前
linux串口对0X0D、0X0A等特殊字符的处理
linux·stm32·单片机·特殊字符·串口配置
学习路上_write1 小时前
AD5293驱动学习
c语言·单片机·嵌入式硬件·学习
影阴2 小时前
存储器和寄存器
stm32·单片机·嵌入式硬件
吃西瓜的年年2 小时前
3. C语言核心语法2
c语言·嵌入式硬件·改行学it
李洛克073 小时前
RDMA CM UDP 通信完整指南
单片机·网络协议·udp
思茂信息3 小时前
CST电动车EMC仿真——电机控制器MCU滤波仿真
javascript·单片机·嵌入式硬件·cst·电磁仿真
小曹要微笑3 小时前
I2C总线技术解析(纯文字版)
单片机·嵌入式硬件·esp32·iic
我送炭你添花3 小时前
可编程逻辑器件(PLD)的发展历程、原理、开发与应用详解
嵌入式硬件·fpga开发
袖手蹲4 小时前
Arduino UNO Q 从 Arduino Cloud 远程控制闪烁 LED
人工智能·单片机·嵌入式硬件·电脑
平凡灵感码头4 小时前
第一次做蓝牙产品,从零开发 嵌入式开发日志(2)AC63NSDK 完整合并版目录说明
stm32·单片机·嵌入式硬件