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_TaskvTaskDelete(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);elseprintf("没有收到数据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");elseprintf("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_TaskvTaskDelete(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_TaskvTaskDelete(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 RunningLowPriority_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_TaskvTaskDelete(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"); }elseprintf ( "事件 错误\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_TaskvTaskDelete(AppTaskCreate_Handle);taskEXIT_CRITICAL();
}//接收任务
static void Receive1_Task(void* parameter)
{ BaseType_t xReturn = pdTRUE;uint32_t r_num;xReturn=xTaskNotifyWait(0x0, //进入函数的时候不清除任务bitULONG_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 通信方式相关推荐
- 9.FreeRTOS学习笔记-任务通知
基本概念 每个任务都有一个 32 位的通知值 任务通知可以替代二值信号量.计数信号量.事件组,也可以替代长度为 1 的队列(可以保存一个 32位整数或指针值) 通知比通过信号量等 ICP 通信方式解除 ...
- STM32F4+FreeRTOS+FreeRTosTcpIp移植教程
花了几天时间完成了FreeRTOS自带的TCP/IP协议栈在stm32F407上的移植,在此记录并分享,第一次写这个,写的不好的地方见谅. 硬件是stm32F407最小系统(内带phy控制器),所以还 ...
- FreeRTOS学习-队列管理
1. 简介 在FreeRTOS中,提供了多种任务间通讯的机制,包括消息队列.信号量和互斥锁.事件组.任务通知,他们的总体特征如下图所示: 从图中可以看出,消息队列.信号量和互斥锁.事件组都是间接的任务 ...
- FreeRTOS消息队列
全文字数9920,预计阅读时长12分钟 问题解答 曾经有人问我,FreeRTOS那么多API,到底怎么记住呢? 我想说,其实API不难记,就是有点难找,因为FreeRTOS的API很多都是带参宏,所以 ...
- ESP32使用freeRTOS的消息队列
零. 声明 本专栏文章我们会以连载的方式持续更新,本专栏计划更新内容如下: 第一篇:ESP-IDF基本介绍,主要会涉及模组,芯片,开发板的介绍,环境搭建,程序编译下载,启动流程等一些基本的操作,让你对 ...
- 1、野火freertos学习笔记
野火freertos学习笔记 1.任务 1.1 栈 1.2 任务的切换 taskYIELD(); 1.3 临界段 2.空闲任务 3.任务优先级 4.任务延时的表现 5.时间片 5.1抢占式.协做式 6 ...
- FreeRTOS:一、入门知识
文章目录 前言 二.FreeRTOS简介 三.FreeRTOS源码结构 1.关于各个c文件的主要用途: 2.四种内存分配方式比较: 3.优先级: 4.任务状态: 5.通信方式: 6.临界区 7.调度锁 ...
- FreeRTOS 常用函数详解
提示:此内容涉及部分汇编+数据结构+计算机操作系统 目录 FreeRTOS调度机制 优先级与状态 FreeRTOS调度链表 相关函数 一.xTaskCreate 创建任务相关 三. QueueDefi ...
- FreeRTOS任务通知 基于STM32
文章目录 一.任务通知简介 二.任务通知的运作机制 三.任务通知的函数接口讲解 1. xTaskGenericNotify() 2.xTaskNotifyGive() 3.vTaskNotifyGiv ...
最新文章
- Web测试需要了解的知识
- 中国增速第一!《全球数字经济白皮书》发布
- iphone7像素_iPhone 7能否再战三年?这几点因素你得考虑到!
- No bean named 'dataSource' is defined
- ps里面怎么插入流程图_photoshop cs6绘画带箭头简单流程图的操作步骤介绍
- python中变量作用域
- torch各个版本镜像_如何解决在cuda上安装torch后torch.cuda.is_available()返回False
- linux的文件权限前面的东西,linux 文件权限解析
- 每天Leetcode 刷题 初级算法篇-数学问题-3的幂
- 3. JavaScript Date 对象
- 智能汽车“增量部件”争夺战(一):以华为海思为样榜,比亚迪蔚来们的漫漫造芯路
- VSCode中插件Code Spell Checker
- html页面调节图片大小,怎么用css设置图片大小?
- 【概率论】【笔记】【@汤家凤】【数一】【第五章】
- 弦截法(Secant Method)迭代求根的python程序
- gitlab上创建新的分支并发布代码
- 微软官方出了一款吊打WPS的PPT插件
- 理解线性变换和基(坐标)变换
- 第5天-[21天学Python]-Python中自定义函数及调用的方法
- 软件测试需求管理系统,软件测试管理及工具应用