FreeRTOS 通信方式
Posted 为了维护世界和平_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FreeRTOS 通信方式相关的知识,希望对你有一定的参考价值。
一、消息队列
消息队列是一种常用于任务间通信的数据结构, 队列可以在任务与任务间、中断和任务间传递信息。读写队列均支持超时机制。
1、创建队列
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,//队列长度
UBaseType_t uxItemSize );//队列中消息单元的大小,以字节为单位
2、删除队列
vQueueDelete()
3、队列发送
BaseType_t xQueueSend(QueueHandle_t xQueue,//队列句柄
const void * pvItemToQueue,//指针,指向要发送到队列尾部的队列消息
TickType_t xTicksToWait);//等待时间
4、在中断服务程序中使用的发送函数
BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,
const void *pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken//为一个可选参数, 可以设置为 NULL。
);
5、向队列首发送消息
BaseType_t xQueueSendToFront( QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait );
6、用于在中断服务程序中向消息队列队首发送一个消息
BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue,
const void *pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken);
7、接收队列
从一个队列中接收消息并把消息从队列中删除
BaseType_t xQueueReceive(QueueHandle_t xQueue,
void *pvBuffer,
TickType_t xTicksToWait);
8、在中断中接收
xQueueReceiveFromISR()
9、 消息队列应用实例
QueueHandle_t Test_Queue =NULL;
#define QUEUE_LEN 4 /*消息队列长度*/
#define QUEUE_SIZE 4 /*每个消息大小 */
/
static void AppTaskCreate(void)
BaseType_t xReturn = pdPASS;
taskENTER_CRITICAL(); //进入临界区
/* 创建Test_Queue */
Test_Queue = xQueueCreate((UBaseType_t ) QUEUE_LEN,(UBaseType_t ) QUEUE_SIZE);
创建任务一:Send_Task
创建任务二:Receive_Task
vTaskDelete(AppTaskCreate_Handle);
taskEXIT_CRITICAL();
/
//任务一:发送
static void Send_Task(void* parameter)
BaseType_t xReturn = pdPASS;
uint32_t send_data1 = 1;
uint32_t send_data2 = 2;
while (1)
xReturn = xQueueSend( Test_Queue,&send_data1,0 );
if(pdPASS == xReturn)
printf("send_data1 发送成功!\\n\\n");
vTaskDelay(20);
//任务二:接收
static void Receive_Task(void* parameter)
BaseType_t xReturn = pdTRUE;
uint32_t r_queue;
while (1)
xReturn = xQueueReceive( Test_Queue, &r_queue,portMAX_DELAY);
if(pdTRUE == xReturn)
printf("收到数据%d\\n\\n",r_queue);
else
printf("没有收到数据0x%lx\\n",xReturn);
二、信号量
实现任务之间同步或临界资源的互斥访问
1、创建二值信号量
xSemaphoreCreateBinary()
2、创建计数信号量
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount,//计数信号量的最大值
UBaseType_t uxInitialCount//创建计数信号量的初始值
);
3、信号量删除函数
vSemaphoreDelete()
4、信号量释放函数
释放的信号量对象必须是已经被创建的,可以用于二值信号量、计数信号量、互斥量的释放,但不能释放由函数xSemaphoreCreateRecursiveMutex()创建的递归互斥量。此外该函数不能在中断中使用
xSemaphoreGive( xSemaphore )
5、中断中释放信号量
用于释放一个信号量,带中断保护。它不能释放互斥量,这是因为互斥量
不可以在中断中使用。
xSemaphoreGiveFromISR()
6、信号量获取
获取一个信号量,可以是二值信号量、计数信号量、互斥量
xSemaphoreTake( xSemaphore, xBlockTime )
参数: xSemaphore:信号量句柄;xBlockTime:等待信号量可用的最大超时时间。
7、不带阻塞机制获取信号量的函数
不能用于互斥量
xSemaphoreTakeFromISR()
8、信号量应用实例
创建两个任务,一个是获取信号量任务,一个是释放互斥量任务
获取信号量任务是一直在等待信号量,其等待时间是 portMAX_DELAY,等到获取到信号量之后,任务开始执行任务代码。
SemaphoreHandle_t BinarySem_Handle =NULL;
static void Receive_Task(void* parameter)
BaseType_t xReturn = pdPASS;
while (1)
//获取二值信号量,没有获取则一直等待
xReturn = xSemaphoreTake(BinarySem_Handle,portMAX_DELAY);
if(pdTRUE == xReturn)
printf("BinarySem_Handle get successful |!\\n\\n");
static void Send_Task(void* parameter)
BaseType_t xReturn = pdPASS;
while (1)
if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
xReturn = xSemaphoreGive( BinarySem_Handle );//给出信号量
if( xReturn == pdTRUE )
printf("BinarySem_Handle 释放成功\\r\\n");
else
printf("BinarySem_Handle 释放失败\\r\\n");
vTaskDelay(20);
static void AppTaskCreate(void)
BaseType_t xReturn = pdPASS;
taskENTER_CRITICAL();
/* 创建二值信号量*/
BinarySem_Handle = xSemaphoreCreateBinary();
if(NULL != BinarySem_Handle)
printf("BinarySem_Handle create successful!\\r\\n");
创建任务一:Receive_Task
创建任务二:Send_Task
vTaskDelete(AppTaskCreate_Handle);
taskEXIT_CRITICAL();
三、互斥量
它支持互斥量所有权、递归访问以及防止优先级翻转的特性,用于实现对临界资源的独占式处理,是用于保护资源的互锁。不能用于中断函数中
与信号量区别
二值信号量:用于实现同步(任务之间或者任务与中断之间)
应用场景
可能会引起优先级翻转的情况
递归互斥量更适用于:
任务可能会多次获取互斥量的情况下。这样可以避免同一任务多次递归持有而造成死锁的问题。
1、互斥量创建 ,只能被同一个任务获取一次
xSemaphoreCreateMutex()
2、递归互斥量创建, 它可以被同一个任务获取很多次,获取多少次就需要释放多少次
xSemaphoreCreateRecursiveMutex()
3、互斥量删除
vSemaphoreDelete()
4、互斥量获取
xSemaphoreTake()
5、递归互斥量获取函
xSemaphoreTakeRecursive( xMutex, xBlockTime )
6、互斥量释放
xSemaphoreGive()
7、递归互斥量释放
xSemaphoreGiveRecursive()
8、互斥量应用实例
实验验证了在低优先级任务运行的时候,中优先级任务无法抢占低优先级的任务。
SemaphoreHandle_t MuxSem_Handle =NULL;
static void AppTaskCreate(void)
BaseType_t xReturn = pdPASS;/
taskENTER_CRITICAL();
/* MuxSem */
MuxSem_Handle = xSemaphoreCreateMutex();
if(NULL != MuxSem_Handle)
printf("MuxSem_Handle 创建成功¦!\\r\\n");
xReturn = xSemaphoreGive( MuxSem_Handle );//给出信号量
创建任务一:LowPriority_Task
创建任务二:MidPriority_Task
创建任务三:HighPriority_Task
vTaskDelete(AppTaskCreate_Handle);
taskEXIT_CRITICAL();
static void LowPriority_Task(void* parameter)
static uint32_t i;
BaseType_t xReturn = pdPASS;
while (1)
//获取信号量,没有则一直等待
printf("LowPriority_Task获取互斥量\\n");
xReturn = xSemaphoreTake(MuxSem_Handle,
portMAX_DELAY);
if(pdTRUE == xReturn)
printf("LowPriority_Task Running\\n\\n");
for(i=0;i<2000000;i++)//占用低级任务互斥量
taskYIELD();//调度
printf("LowPriority_Task释放信号量!\\r\\n");
xReturn = xSemaphoreGive( MuxSem_Handle );//释放信号量
vTaskDelay(1000);
static void MidPriority_Task(void* parameter)
while (1)
printf("MidPriority_Task Running\\n");
vTaskDelay(1000);
static void HighPriority_Task(void* parameter)
BaseType_t xReturn = pdTRUE;
while (1)
xReturn = xSemaphoreTake(MuxSem_Handle,
portMAX_DELAY);
printf("HighPriority_Task 获取互斥量!\\r\\n");
if(pdTRUE == xReturn)
printf("HighPriority_Task Running\\n");
printf("HighPriority_Task 释放互斥量!\\r\\n");
xReturn = xSemaphoreGive( MuxSem_Handle );//给出互斥量
vTaskDelay(1000);
输出打印
HighPriority_Task 获取互斥量
HighPriority_Task Running
HighPriority_Task 释放互斥量!
MidPriority_Task Running
LowPriority_Task 获取互斥量
LowPriority_Task Running
LowPriority_Task 释放互斥量!
HighPriority_Task 获取互斥量
四、事件
可以用事件来做标志位,判断某些事件是否发生了。
1、事件创建函数 xEventGroupCreate()
2、事件删除函数 vEventGroupDelete()
3、事件组置位函数 xEventGroupSetBits()(任务)
4、事件组置位函数 xEventGroupSetBitsFromISR()(中断)
5、等待事件函数 xEventGroupWaitBits()
6、xEventGroupClearBits()与 xEventGroupClearBitsFromISR()
等待事件函数
EventBits_t xEventGroupWaitBits(const EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,//按位或的值
const BaseType_t xClearOnExit,//pdRTUE 系统将清除由形参 uxBitsToWaitFor 指定的事件标志位
const BaseType_t xWaitForAllBits,//pdFALSE:uxBitsToWaitFor 指定的位有其中任意一个置位;pdTRUE uxBitsToWaitFor 指定的位都置位的时候
TickType_t xTicksToWait );//等待时间
设置函数
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet );//事件的标志位
7、 事件应用实例
创建两个任务,一个是设置事件任务,一个是等待事件任务
设置事件任务 通过检测按键的按下情况设置不同的事件标志位,
等待事件任务 则获取这两个事件标志位,并且判断两个事件是否都发生,若是则输出相应信息
static EventGroupHandle_t Event_Handle =NULL;
#define KEY1_EVENT (0x01 << 0)
#define KEY2_EVENT (0x01 << 1)
static void AppTaskCreate(void)
BaseType_t xReturn = pdPASS;
taskENTER_CRITICAL();
/* 创建事件 Event_Handle */
Event_Handle = xEventGroupCreate();
if(NULL != Event_Handle)
printf("Event_Handle创建成功\\r\\n");
创建任务一:LED_Task
创建任务二:KEY_Task
vTaskDelete(AppTaskCreate_Handle);
taskEXIT_CRITICAL();
static void LED_Task(void* parameter)
EventBits_t r_event;
while (1)
r_event = xEventGroupWaitBits(Event_Handle,
KEY1_EVENT|KEY2_EVENT,//事件
pdTRUE,pdTRUE, portMAX_DELAY);
if((r_event & (KEY1_EVENT|KEY2_EVENT)) == (KEY1_EVENT|KEY2_EVENT))
printf ( "收到事件\\n");
else
printf ( "事件 错误\\n");
static void KEY_Task(void* parameter)
while (1)
if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
xEventGroupSetBits(Event_Handle,KEY1_EVENT); //触发事件一
if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON )
xEventGroupSetBits(Event_Handle,KEY2_EVENT); 触发事件二
vTaskDelay(20);
五、通知
每个任务都有一个 32 位的通知值,在大多数情况下,任务通知可以替代二值信号量、计数信号量、事件组,也可以替代长度为 1 的队列;任务通知的使用无需创建队列;
发送任务通知函数
xTaskGenericNotify()
获取任务通知
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, //任务句柄
uint32_t ulValue, //值
eNotifyAction eAction );
eNotifyAction 的取值
* eNoAction = 0//通知任务而不更新其通知值
* eSetBits//设置任务通知值中的值
* eIncrement//增加任务的通道值
* eSetvaluewithoverwrite//覆盖当前通知
* eSetValueWithoutoverwrite//不覆盖当前通知
等待任务通知
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,//
uint32_t ulBitsToClearOnExit,
uint32_t *pulNotificationValue,//值
TickType_t xTicksToWait );//事件
1、 通知应用实例(代替消息队列)
static void AppTaskCreate(void)
BaseType_t xReturn = pdPASS;
taskENTER_CRITICAL();
创建任务一: Receive1_Task
创建任务二 :Send_Task
vTaskDelete(AppTaskCreate_Handle);
taskEXIT_CRITICAL();
//接收任务
static void Receive1_Task(void* parameter)
BaseType_t xReturn = pdTRUE;
uint32_t r_num;
xReturn=xTaskNotifyWait(0x0, //进入函数的时候不清除任务bit
ULONG_MAX, //退出函数的时候清除所有bit
(uint32_t *)&r_char, //任务通知值
portMAX_DELAY); //阻塞事件
if( pdTRUE == xReturn )
printf("Receive1_Task 任务通知消息 %d \\n",r_num);
//发送任务
static void Send_Task(void* parameter)
BaseType_t xReturn = pdPASS;
uint32_t send1 = 1;
while (1)
if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
xReturn = xTaskNotify( Receive1_Task_Handle, /*任务句柄*/
(uint32_t)&test_str1, /*任务内容 */
eSetValueWithOverwrite );/*覆盖当前通知*/
if( xReturn == pdPASS )
printf("Receive1_Task_Handle ÈÎÎñ֪ͨÏûÏ¢·¢Ëͳɹ¦!\\r\\n");
vTaskDelay(20);
2、通知应用实例(代替信号量)
static void Receive1_Task(void* parameter)
while (1)
//获取任务通知,没有则一直等待
ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
printf("Receive1_Task !\\n\\n");
static void Send_Task(void* parameter)
BaseType_t xReturn = pdPASS;
while (1)
if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
xReturn = xTaskNotifyGive(Receive1_Task_Handle);//任务句柄
if( xReturn == pdTRUE )
printf("Receive1_Task_Handle ÈÎÎñ֪ͨ·¢Ëͳɹ¦!\\r\\n");
vTaskDelay(20);
以上是关于FreeRTOS 通信方式的主要内容,如果未能解决你的问题,请参考以下文章