1. 上一篇文章讲解了FreeRTOS如何创建任务和运行任务,这一篇主要讲FreeRTOS如何处理共享资源,也就是任务之间同步和通信的机制。 2. 什么是共享资源?共享资源就是你在不同任务之间都会调用同一个资源,类似裸机程序中的全局变量,在裸机程序中,我们可以在任意时刻访问这个变量,但是当我们在RTOS中却不能,原因在于实时操作系统动态的根据调度器切换任务,变量在什么时候被改变我们是不知道的,假如多个任务同时使用一个变量的话,这就会导致程序错乱。
举例说明:
int buf[2]={0};
void task1_func(void)
{
...
int sum = 0;
buf[0] = 1;
buf[1] = 2;
while(1)
{
buf[0]++;
sum= buf[0]+buf[1];
printf("task 1 buf[0] : %d \n",buf[0]);
printf("task 1 buf[1] : %d \n",buf[1]);
printf("task 1 sum : %d \n",sum);
...
}
}
void task2_func(void)
{
...
buf[0] = 1;
buf[1] = 2;
while(1)
{
buf[0]++;
buf[1]++;
printf("task 2 buf[0] : %d \n",buf[0]);
printf("task 2 buf[1] : %d \n",buf[1]);
printf("task 2 sum : %d \n",sum);
...
}
}
我们来分析一下sum值会是多少? 按照裸机代码分析:sum = 2+2 = 4
但是实际情况有可能会不同:
假设当task1函数执行完buf[0]++后,切换到任务task2,task2也执行了buf[0]++,此时buf[0]=3,在切换回task1,此时在执行sum=buf[0]+buf[1]=3+2=5,这和我们想要的结果完全不同,当然只是我举的例子实际情况有可能和我说的不同,但我只想想说明这个问题,让人明白RTOS中不能使用这样的全局变量的原因。
3. 那么freeRTOS如何解决这个问题那?它定义了几种特殊的数据类型来处理任务之间的消息同步和消息共享机制。有事件标志组,消息队列,信号量(计数信号量,二值信号量,互斥信号量)等。还有一种比较特殊的方式就是控制调度锁。 我主要想将的是两个一个任务机制,另外一个就是消息队列,因为诸如信号量这些都是基于消息队列完成的。 3.1 任务调度锁应用 调度锁:RTOS提供的调度器开关函数,如果某个任务调用了调度锁开关函数,处于调度锁开和调度锁关之间的代码在执行期间是不会被高优先级的任务抢占的,即任务调度被禁止。 对应函数:
void vTaskSuspendAll( void )//字面理解就是延迟挂起所有任务
BaseType_t xTaskResumeAll( void )// 恢复所有任务
/*注意:这两个函数必须成对出现!*/
举例:
task1_func()
{
if(flag==1){
vTaskSuspendAll();//开启调度锁
led_blink();
vTaskSuspendAll();//关闭调度锁
}
}
3.2 消息队列应用 消息队列我们可以理解成裸机编程中的数组,只不过在操作系统中这个数组必须按照操作系统的要求来操作。
编辑
添加图片注释,不超过 140 字(可选)
任务1 将数据发送给消息队列,任务2冲消息队列中接收数据。FreeRTOS的消息采用的是先入先出(FIFO)原则。 特殊情况:
任务1向消息队列发送数据时,任务2取出数据,但是当发送数据的速度比取出的快就会导致消息队列放满的青睐。这个时候FreeRTOS在发送函数中提供超时等待,用户可以设置这个时间,当超出这个时间就溢出不处理。
还有就是取出的比放入的快,导致消息队列空,有可以设置超时等待,当超过时间之后也溢出不处理。
消息队列API:
/*创建队列*/
/**
* uxQueueLength 消息队列支持的消息个数
* uxItemSize 每一个消息的多少个字节
*/
xQueueCreate( uxQueueLength, uxItemSize )
/*发送消息*/
/**
* xQueue 消息队列句柄,靠这个参数找到想要操作的消息队列
* pvItemToQueue 要传递消息的地址
* xTickToWait 当消息队列满脸之后,等待消息队列的最大时间 ,一般设置成最大portMAX_DELAY
*/
xQueueSend( xQueue, pvItemToQueue, xTicksToWait)
/*发送消息*/
/**
* xQueue 消息队列句柄,靠这个参数找到想要操作的消息队列
* pvItemToQueue 要传递消息的地址
* xTickToWait 当消息队列满脸之后,等待消息队列的最大时间,一般设置成最大portMAX_DELAY
*/
xQueueReceive( xQueue, pvBuffer, xTicksToWait )
举例说明:
#define TASK1_TASK_PRIO 2 //优先级
#define TASK1_STK_SIZE 256 //堆栈大小
TaskHandle_t Task1Task_Handler; //任务句柄
void task1_task(void *pvParameters);//任务函数
#define TASK2_TASK_PRIO 3
#define TASK2_STK_SIZE 256
TaskHandle_t Task2Task_Handler;
void task2_task(void *pvParameters);
#define MSG_Q_NUM 1
QueueHandle_t Message_Queue;// 定义消息队列
int main(void)
{
... //初始化
Message_Queue=xQueueCreate(MSG_Q_NUM,sizeof(u8)); //创建消息队列
/*创建任务*/
xTaskCreate((TaskFunction_t )task2_task,
(const char* )"task2_task",
(uint16_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_TASK_PRIO,
(TaskHandle_t* )&Task2Task_Handler);
xTaskCreate((TaskFunction_t )task2_task,
(const char* )"task2_task",
(uint16_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_TASK_PRIO,
(TaskHandle_t* )&Task2Task_Handler);
vTaskStartScheduler(); //开始调度
}
//任务1 函数
void task1_task(void *pvParameters)
{
u8 key=10;
BaseType_t err;
while(1)
{
if((Key_Queue!=NULL))
{
err=xQueueSend(Message_Queue,&key,10);//发送队列
if(err==errQUEUE_FULL)
{
printf("error\r\n");
}
}
vTaskDelay(10);
}
}
void task2_task(void *pvParameters)
{
u8 key;
while(1)
{
if(Key_Queue!=NULL)
{
if(xQueueReceive(Key_Queue,&key,portMAX_DELAY))//接收队列
{
printf("key = %d \n",key);
}
}
vTaskDelay(10);
}
}