stm32c8t6之freertos消息队列

        freertos任务之间的通讯方式有很多,消息队列就是一种,它可以在任务中发送或者读取信息。

        有了解过数据结构的应该能理解消息队列的含义,消息队列本质上来说就是队列。队列就好比你去排队打饭,排队的队伍就是一个队列,队列的原理就是先进先出。你先排队那就你先打完饭,你后排队就等别人打完饭才到你。消息队列也一样,先发送的数据可以先被读取到,后发送的数据后面才会被读取。

创建和删除消息队列

#include "queue.h"

QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength,UBaseType_t uxItemSize,);

        这是消息队列的头文件和创建函数xQueueCreate();我这里采用的是动态的方法进行创建,创建函数的第一个参数是队列储存元素的个数,第二个参数是存储元素的字节个数。

QueueHandle_t queue;

queue = xQueueCreate(4,4);

        首先创建消息队列的句柄,这个是至关重要的,我们发送消息和接收消息都要操作这个句柄。队列创建函数的两个参数我都给了4,代表我要设置队列的大小为4,存储元素的字节也为4。

void vQueueDelete(QueueHandle_t xQueue);

        这是删除消息队列的函数,只要把消息队列的句柄传进去就好了。 

发送消息 

BaseType_t    xQueueSend(
              QueueHandle_t Queue,            //队列句柄
              const void *pvItemToQueue,      //发送数据的指针
              TickType_t xTicksToWait);       //发送数据的时间

        这个是常规的队列发送函数,不能在中断中使用,因为发送函数的第三个参数可以设置发送时间。可以起到一个阻塞的效果,但是在中断中是不可以进行阻塞的,中断有自己的队列发送函数。

BaseType_t    xQueueSendFromISR(
              QueueHandle_t Queue,                          //队列句柄
              const void *pvItemToQueue,                    //发送数据的指针
              BaseType_t *pxHigherPriorityTaskWoken);       //可以给pdTRUE或者pdFLASE

接收消息

BaseType_t    xQueueReceive(
              QueueHandle_t Queue,            //队列句柄
              void *pvBuffer,                 //接收数据的指针
              TickType_t xTicksToWait);       //发送数据的时间

        这是常规的队列接收函数,同样也不能在中断中使用。

BaseType_t    xQueueReceiveFromISR(
              QueueHandle_t Queue,                          //队列句柄
              void *pvBuffer,                               //发送数据的指针
              BaseType_t *pxHigherPriorityTaskWoken);       //可以给pdTRUE或者pdFLASE

        这是中断里使用的接收信息函数。

实战

        接下来做一个消息队列的实验,在stm32c8t6上面设置两个按键,key1和key2。按下key1时发送消息,按下key2时接收消息并打印。

QueueHandle_t queue;				//消息队列句柄

TaskHandle_t startTask_handler;     //总任务的句柄
TaskHandle_t ledTask_handler;		//led的句柄
TaskHandle_t sendTask_handler;		//发送消息的句柄
TaskHandle_t receiveTask_handler;	//接收消息的句柄
TaskHandle_t randTask_handler;		//随机数的句柄


xTaskCreate(startTask,"startTask",512,NULL,1,&startTask_handler);					//创建开始任务

void startTask(void *arg)
{
	BaseType_t xReturn = pdFALSE;														//创建接收值
	
	taskENTER_CRITICAL();    															//临界区
	
	queue = xQueueCreate(4,4);
	
	xReturn = xTaskCreate(ledTask,"ledTask",64,NULL,1,&ledTask_handler);				//创建led任务
	xReturn = xTaskCreate(sendTask,"sendTask",64,NULL,3,&sendTask_handler);				//创建发送任务
	xReturn = xTaskCreate(receiveTask,"receiveTask",64,NULL,2,&receiveTask_handler);	//创建接收任务
	xReturn = xTaskCreate(randTask,"randTask",64,NULL,1,&randTask_handler);				//创建随机数任务
	
	if(xReturn == pdTRUE)
	{
		printf("Task create ok\n");
	}
	else
	{
		printf("Task create error\n");
	}
	
	vTaskDelete(startTask_handler);														//删除开始任务句柄
	
	taskEXIT_CRITICAL();																//临界区
	
}

        这是任务创建的函数,一共创建5个任务,一个开始任务startTsak(void *arg),用来创建其他4个任务的。 

        其他4个任务分别是led任务,随机数任务,发送消息任务和接受消息任务。led任务是用来当心跳包的,可以观察led的状态判断自己的程序有没有卡死之类的。随机数任务是用来赋值给发送函数发送的值的。发送任务就是发送消息的,接受任务就是用来接受消息的。

led任务

void ledTask(void *arg)
{
	while(1)
	{
		GPIO_SetBits(GPIOB,GPIO_Pin_8);
		vTaskDelay(500);
		GPIO_ResetBits(GPIOB,GPIO_Pin_8);
		vTaskDelay(500);
	}
}

        led任务比较简单,就是单纯让led在闪烁。

随机数任务

int send_data1;	

void randTask(void *arg)
{
	while(1)
	{
		send_data1 = rand() % 100 + 1;
	}
	
}

        首先定义一个全局变量send_data1,通过rand()函数将值不断赋值给send_data1,这样就可以确保每次发送的数据都是不一样的。

发送任务

void sendTask(void *arg)
{
	
	
	BaseType_t xReturn = pdFALSE;
	
	while(1)
	{
		if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) == RESET)
		{
			xReturn = xQueueSend(queue,&send_data1,0);
			if(xReturn == pdFALSE)
			{
				printf("data send error\n");
			}
			else
			{
				printf("data send ok\n");
			}
		}
		
		vTaskDelay(200);
	}
}

        首先定义一个xReturn 变量来接收xQueueSend()函数的值,来判断它是否操作成功。因为我自己没有封装按键key的函数,所以直接索性判断PA0是否被按下了。xQueueSend()函数的第一个参数就是消息队列的句柄;第二个参数是生成的随机数,这里要取地址;最后一个参数是设置发送的时间,我给的是0,就是一调用就直接发送。

        最后一个小问题,当执行完流程后最好延时一下,不延时的话可能会导致按下一次按键,就会发送好几次数据,我这里延时了200ms。 

接收任务

void receiveTask(void *arg)
{
	int rec_data;
	
	BaseType_t xReturn = pdFALSE;
	
	while(1)
	{
		if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1) == RESET)
		{
			xReturn = xQueueReceive(queue,&rec_data,portMAX_DELAY);
			if(xReturn == pdFALSE)
			{
				printf("receive ok\n");
			}
			else
			{
				printf("receive data :%d\n",rec_data);
			}
		}
		vTaskDelay(200);
	}
}

        先定义一个变量rec_data来接收数据,当PA1被按下时,也就是按键被按下时,就开始接收。xQueueReceive()函数的第一个参数是队列句柄;第二个参数是用来接收数据,要取地址;最后一个参数是接收的时间,我这里给的是portMAX_DELAY,这个是freertos的一个宏定义,表示一直等待接收。流程的最后也是一样给延个时,防止它一次性读取很多数据。

源代码

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"
#include "queue.h"
#include "led.h"
#include "stdlib.h"


QueueHandle_t queue;				//消息队列句柄

TaskHandle_t startTask_handler;     //总任务的句柄
TaskHandle_t ledTask_handler;		//led的句柄
TaskHandle_t sendTask_handler;		//发送消息的句柄
TaskHandle_t receiveTask_handler;	//接收消息的句柄
TaskHandle_t randTask_handler;		//随机数的句柄

int send_data1;						

void ledTask(void *arg)
{
	while(1)
	{
		GPIO_SetBits(GPIOB,GPIO_Pin_8);
		vTaskDelay(500);
		GPIO_ResetBits(GPIOB,GPIO_Pin_8);
		vTaskDelay(500);
	}
}

void receiveTask(void *arg)
{
	int rec_data;
	
	BaseType_t xReturn = pdFALSE;
	
	while(1)
	{
		if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1) == RESET)
		{
			xReturn = xQueueReceive(queue,&rec_data,portMAX_DELAY);
			if(xReturn == pdFALSE)
			{
				printf("receive ok\n");
			}
			else
			{
				printf("receive data :%d\n",rec_data);
			}
		}
		vTaskDelay(200);
	}
}

void sendTask(void *arg)
{
	
	
	BaseType_t xReturn = pdFALSE;
	
	while(1)
	{
		if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) == RESET)
		{
			xReturn = xQueueSend(queue,&send_data1,0);
			if(xReturn == pdFALSE)
			{
				printf("data send error\n");
			}
			else
			{
				printf("data send ok\n");
			}
		}
		
		vTaskDelay(200);
	}
}

void randTask(void *arg)
{
	while(1)
	{
		send_data1 = rand() % 100 + 1;
	}
	
}

void startTask(void *arg)
{
	BaseType_t xReturn = pdFALSE;														//创建接收值
	
	taskENTER_CRITICAL();    															//临界区
	
	queue = xQueueCreate(4,4);
	
	xReturn = xTaskCreate(ledTask,"ledTask",64,NULL,1,&ledTask_handler);				//创建led任务
	xReturn = xTaskCreate(sendTask,"sendTask",64,NULL,3,&sendTask_handler);				//创建发送任务
	xReturn = xTaskCreate(receiveTask,"receiveTask",64,NULL,2,&receiveTask_handler);	//创建接收任务
	xReturn = xTaskCreate(randTask,"randTask",64,NULL,1,&randTask_handler);				//创建随机数任务
	
	if(xReturn == pdTRUE)
	{
		printf("Task create ok\n");
	}
	else
	{
		printf("Task create error\n");
	}
	
	vTaskDelete(startTask_handler);														//删除开始任务句柄
	
	taskEXIT_CRITICAL();																//临界区
	
}


int main(void)
{
	LED_Init();
	usrt1_init(9600);
	xTaskCreate(startTask,"startTask",512,NULL,1,&startTask_handler);					//创建开始任务
	vTaskStartScheduler();
}

实验现象

stm32c8t6之freertos消息队列_第1张图片        我这里用串口来进行操作,先是进行复位,然后串口助手第一行打印了Task create ok这句话,说明所有任务都创建成功了。

        我按了一下key1,串口打印data send ok,说明发送成功;再按一下key2,串口打印了recrive data :12,说明消息队列也接收到了消息。

        随后我发送了3次数据,此时队列里有3个元素,随后我又读取了两次数据,这时队列了就剩一个数据了。所以,这就导致了我接下来再发送5次数据时,后面两次都失败了。原因是在发送第4次数据时,队列已经满了,就发送进不去了,因为我设置的队列最大值为4,所以就会打印error。 

        顺便再提一嘴,如果各位在操作时,发现发送数据和接收数据同时进行,或者在进行发送和接受数据操作时,突然打印不出数据了。那么问题肯定是出在发送和接收数据的延时上或者任务的优先级上,我也遇到过这些问题。

        以上就是队列的操作。 

你可能感兴趣的:(FreeRtos,stm32,嵌入式硬件,单片机)