FreeRTOS记录(七、FreeRTOS信号量、事件标志组、邮箱和消息队列、任务通知的关系

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

如果你需要这些资料,可以戳这里获取


关于递归互斥量, 递归互斥信号量解决死锁问题 :



MutexLock mutex;
void add()
{
mutex.lock();
// do something
mutex.unlock();
}
void count_all()
{
mutex.lock();
// do something
add();
mutex.unlock();
}


#### 1.1.5 事件标志组 Events


事件是一种实现任务间通信的机制,主要用于实现多任务间的同步,但事件通信只能是事件类型的通信,无数据传输。与信号量不同的是,它可以实现一对多,多对多的同步。 即一个任务可以等待多个事件的发生:可以是任意一个事件发生时唤醒任务进行事件处理;也可以是几个事件都发生后才唤醒任务进行事件处理。同样,也可以是多个任务同步多个事件。


在程序中使用`configUSE_16_BIT_TICKS`这个宏定义来确定用户可以使用的事件标志位是8个还是24个:


![在这里插入图片描述](https://img-blog.csdnimg.cn/eab793c16d0843bd8c359abe63f353d8.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_15,color_FFFFFF,t_70,g_se,x_16)


![在这里插入图片描述](https://img-blog.csdnimg.cn/1f3e51d0b1294c8098d896fffd7ee515.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


事件标志组 就是 裸机中的标志位,裸机中需要对每一个需要判断的事件自行定义一个标志位,然后需要用户自行管理,而事件标志组,把这个功能集中起来方便管理,用户可以使用24个(configUSE\_16\_BIT\_TICKS == 1)事件管理位。


在CubeMX中,好像对事件标志组的支持不太完善:


![在这里插入图片描述](https://img-blog.csdnimg.cn/bd37b70f0374420bbb3f5e365ec4aa33.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


我两台电脑上的CubeMX版本有一个有 Events选项,另一个没有。这里不用在意,我们在生成的工程代码中,可以看到有对应的事件组标志的文件:


![在这里插入图片描述](https://img-blog.csdnimg.cn/0179cc66c11b4e15a58d3f63223ee482.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_10,color_FFFFFF,t_70,g_se,x_16)


在文件中,我们能够了解到和事件组有关的API函数:


注意中断中设置事件组`xEventGroupSetBitsFromISR`的注释,后面会有应用Demo



/*事件标志组句柄*/
typedef void * EventGroupHandle_t;
/*创建事件标志组,动态*/
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
EventGroupHandle_t xEventGroupCreate( void ) PRIVILEGED_FUNCTION;
#endif
/*创建事件标志组,静态*/
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ) PRIVILEGED_FUNCTION;
#endif
/*等待事件标志位,可以在阻塞状态下等待一个或者多个事件位*/
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, //事件组名称
const EventBits_t uxBitsToWaitFor, //等待的位
const BaseType_t xClearOnExit, //读取之后是否清除,pdFALSE不清除,pdTRUE 清除
const BaseType_t xWaitForAllBits, //等待的位是与还是或,pdTRUE,则要等待全部的位都置1
TickType_t xTicksToWait ) //阻塞时间
/*清除标志位*/
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear )
/*中断中清除标志位*/
BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet )
/*设置标志位*/
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet )
/*
中断中设置标志位
标记退出此函数以后是否进行任务切换,这个变量的值函数会自 动设置的,用户不用进行设置,
用户只需要提供一个变量来保存 这个值就行了。
当此值为 pdTRUE 的时候在退出中断服务函数之 前一定要进行一次任务切换。
返回值:
pdPASS : 事件位置 1 成功。
pdFALSE: 事件位置 1 失败。

*/
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
BaseType_t *pxHigherPriorityTaskWoken )
/*
多个事件的同步,在文件中官方给出了示例,需要了解请参考
任务A接收事件,将事件所需的一些处理委托给任务B、任务C、任务D三个任务,如果任务A在其他三个任务没有完成当前事件的处理时无法接收下一个事件,此时四个任务就需要彼此同步。每个任务执行到同步点后将在此等待其他任务完成处理并到达相应的同步点后才能继续执行,如此处的任务A只能在其他任务都达到同步点后才能接收另一个事件
必须为每个参与同步的任务分配唯一的事件位。
每个任务在到达同步点时设置自己的事件位。
设置自己的事件位后,事件组上的每个任务都会阻塞,以等待代表其他同步任务的事件位被设置。
*/
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
const EventBits_t uxBitsToWaitFor,
TickType_t xTicksToWait )

#define xEventGroupGetBits( xEventGroup ) xEventGroupClearBits( xEventGroup, 0 )

EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup )

void vEventGroupDelete( EventGroupHandle_t xEventGroup )


### 1.2 Demo测试


#### 1.2.1 二值信号量


从二值信号量开始测试,逻辑: 在按键按下的时候释放信号量,在`THread`任务中等待信号量(阻塞方式),等到了就读取一次温湿度打印出来:


按键函数中,使用`osSemaphoreRelease`释放信号量:


![在这里插入图片描述](https://img-blog.csdnimg.cn/f85d3ad778174e7596c9494dc720d6ee.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


在温湿度读取任务中,使用`osSemaphoreWait`一直等待信号量,收到信号量就执行一次温湿度读取,这里我完全是和任务通知使用的同样的方式:


![在这里插入图片描述](https://img-blog.csdnimg.cn/a79338c0b1184b6f83887da3498de3ce.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


测试结果:


![在这里插入图片描述](https://img-blog.csdnimg.cn/e0f4ad5404de43d3b5aebe2d34760c76.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_16,color_FFFFFF,t_70,g_se,x_16)


可以看到,在使用一个任务叫醒另外一个任务的时候,任务通知和二值信号量是一样的,我们在前面说过任务通知更加高效,占用RAM空间更小,所以这种情况,任务通知是有优势的。


#### 1.2.2 计数信号量


计数信号量的测试和以上一样,通过按键释放计数信号量,但是在测试过程中,其实发现计数信号量的使用不应该是这样,计数信号量典型的应用场合就是停车位模型,总共有多少个车位,就是多少个信号量,入口进入一辆车信号量-1,出口离开一辆车信号量+1,这里我依然把这么使用的程序和结果放出来,做一个参考。


测试时候发现(有待分析):信号量在创建的时候就已经存在了,如果某一个任务等待信号量使用阻塞方式`osWaitForever`,那么在程序运行开始的时候,就会直接接受到该信号量,如果是计数信号量,那么创建的时候有多少个数值,那么等待信号量的任务就会运行多少次


在KeyTask中:


![在这里插入图片描述](https://img-blog.csdnimg.cn/9446edc228784fc8bf83de09ae748e71.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


读取温湿度任务:


![在这里插入图片描述](https://img-blog.csdnimg.cn/96e615ab999141448590fe1986e0ff11.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


测试结果:


![在这里插入图片描述](https://img-blog.csdnimg.cn/2fa1af609a3b49cbb09076ec1720d052.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_15,color_FFFFFF,t_70,g_se,x_16)


#### 1.2.3 互斥信号量(优先级继承)


互斥信号量我们做一个 优先级继承的测试。


新建两个任务作为测试任务:


![在这里插入图片描述](https://img-blog.csdnimg.cn/68d074f091724a16b90235ae7eb4d49f.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)  
 在KeyTask中:


![在这里插入图片描述](https://img-blog.csdnimg.cn/9d5746b0422e4ab29fd9a86cf310dbd7.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


在两个测试任务中:


![在这里插入图片描述](https://img-blog.csdnimg.cn/7d857e573dc74d3b86123fa8d30bf7fa.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


测试结果:


![在这里插入图片描述](https://img-blog.csdnimg.cn/806308e26931435f97cf5a09b871cf24.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_15,color_FFFFFF,t_70,g_se,x_16)


接下来,我们加入互斥信号量,在按键中需要获得互斥量,然后释放,然后再使用优先级低的测试任务获得互斥量,然后释放。理论上来说,因为有高优先级的任务(按键任务)一直需要等待互斥量,所以在进行测试任务1 和测试任务2调度的时候,会先运行 获得互斥量的原本优先级低的测试任务,具体过程如下。


在KeyTask中:


![在这里插入图片描述](https://img-blog.csdnimg.cn/9e1368f942174d74867877ad48a150fc.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


在低优先级的测试任务中:


![在这里插入图片描述](https://img-blog.csdnimg.cn/acb279bccc744a5fb67531505480448f.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


测试结果如下:


![在这里插入图片描述](https://img-blog.csdnimg.cn/ef543c1cea0b4aa584e47f6494dd17f9.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_15,color_FFFFFF,t_70,g_se,x_16)


从上图结果和前面结果可以看出,testtask01的优先级继承了 高优先级的按键任务的优先级。


#### 1.2.4 事件标志组


我们测试使用几个事件标志组来触发温湿度读取的任务:


(记录使用的两台电脑用的平台不一样,一个是STM32F103,一个是STM32L051,所以细心的朋友能够看出有些区别,但是整体上我还是尽量做到一致)


1、定时器定时10S以后发送一个标志位;  
 2、按钮按下发送一个标志位;  
 3、收到一个无线报文发送一个标志位;


先定义一下事件组的位:


![在这里插入图片描述](https://img-blog.csdnimg.cn/4619284dcac249d7a86896bcac59ae90.png)


创建事件标志组:


![在这里插入图片描述](https://img-blog.csdnimg.cn/ec70470c42594431a447309bba871377.png)


在KeyTask中 (任务中设置标志位):


![在这里插入图片描述](https://img-blog.csdnimg.cn/4c3c6cc3f0bf491aa4c071abbc36485c.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


在无线报文接收任务中(任务中设置标志位):


![在这里插入图片描述](https://img-blog.csdnimg.cn/50203266b2654517ba045cdc9074ba54.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


在定时器中断函数中 (中断中设置标志位):


![在这里插入图片描述](https://img-blog.csdnimg.cn/929ac79de6c84ef2a07750a0efd7c8e3.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


但是注意,需要使用`xEventGroupSetBitsFromISR`函数需要如下的宏定义:


![在这里插入图片描述](https://img-blog.csdnimg.cn/90f67888cb644ab8a19bd724a9a8bf9c.png)


上面的3个宏定义可以自己在config里面添加,也可以在CubeMX里面设置,下面给出在CubeMX中对应的设置:


![在这里插入图片描述](https://img-blog.csdnimg.cn/9fb4ee8773a34275857aef68a784699f.png)


![在这里插入图片描述](https://img-blog.csdnimg.cn/2372ec3973d94d7391f1519149b24d1d.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/0ce9f2fcf1014f54ae7cfff20f4e96d6.png)


读取温湿度任务:


![在这里插入图片描述](https://img-blog.csdnimg.cn/730fe05b952947448290474bff2d5fb6.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


测试结果:


![在这里插入图片描述](https://img-blog.csdnimg.cn/890fcb51c6324a79bf2a0af57f115d01.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_14,color_FFFFFF,t_70,g_se,x_16)


我们再修改一下接收事件任务中`xEventGroupWaitBits`的参数,使得需要等到所有的事件标志位置位才能执行:


![在这里插入图片描述](https://img-blog.csdnimg.cn/8a13b1acd7f347cea0a3738d018f58dc.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


在温湿度读取任务最后加上一个判断语句:


![在这里插入图片描述](https://img-blog.csdnimg.cn/55136654de944011aa20e871b60fdb16.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_17,color_FFFFFF,t_70,g_se,x_16)


测试结果:


![在这里插入图片描述](https://img-blog.csdnimg.cn/1e899585a6734f92ac11b66eef817ec6.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_16,color_FFFFFF,t_70,g_se,x_16)


## 二、消息队列与邮箱


任务与任务之间的消息传递,可以使用消息队列,邮箱。任务通知也是可以的(下一节)。


消息队列的使用在我上一篇博文中有过使用示例:[FreeRTOS记录(六、FreeRTOS消息队列—Enocean模块串口通讯、RAM空间不足问题分析)](https://bbs.csdn.net/topics/618679757)


### 2.1 邮箱


邮箱可以指定发送对象,也可以全部task访问,队列则是全部task都可以访问。


邮箱只能发送一条消息,所以可以认为邮箱就是长度为1的消息队列。消息邮箱一次只能发一条会覆盖原来消息。


邮箱队列控制块结构体如下:



typedef struct os_mailQ_cb {
const osMailQDef_t *queue_def;
QueueHandle_t handle;
osPoolId pool;
} os_mailQ_cb_t;


在CubeMX中好像不能直接生成邮箱的程序


在`cmsis_os.c`中找到关于邮箱的函数:  
 创建`osMailCreate`,发送`osMailPut`,接收`osMailGet`,分别如下:



/**
* @brief Create and Initialize mail queue
* @param queue_def reference to the mail queue definition obtain with \ref osMailQ
* @param thread_id thread ID (obtained by \ref osThreadCreate or \ref osThreadGetId) or NULL.
* @retval mail queue ID for reference by other functions or NULL in case of error.
* @note MUST REMAIN UNCHANGED: \b osMailCreate shall be consistent in every CMSIS-RTOS.
*/
osMailQId osMailCreate (const osMailQDef_t *queue_def, osThreadId thread_id)
{
#if (configSUPPORT_DYNAMIC_ALLOCATION == 1)
(void) thread_id;

osPoolDef_t pool_def = {queue_def->queue_sz, queue_def->item_sz, NULL};

/* Create a mail queue control block */

*(queue_def->cb) = pvPortMalloc(sizeof(struct os_mailQ_cb));

if (*(queue_def->cb) == NULL) {
return NULL;
}
(*(queue_def->cb))->queue_def = queue_def;

/* Create a queue in FreeRTOS */
(*(queue_def->cb))->handle = xQueueCreate(queue_def->queue_sz, sizeof(void *));

if ((*(queue_def->cb))->handle == NULL) {
vPortFree(*(queue_def->cb));
return NULL;
}

/* Create a mail pool */
(*(queue_def->cb))->pool = osPoolCreate(&pool_def);
if ((*(queue_def->cb))->pool == NULL) {
//TODO: Delete queue. How to do it in FreeRTOS?
vPortFree(*(queue_def->cb));
return NULL;
}

return *(queue_def->cb);
#else
return NULL;
#endif
}


osStatus osMailPut (osMailQId queue_id, void *mail){…}

osEvent osMailGet (osMailQId queue_id, uint32_t millisec){…}


### 2.2 邮箱Demo


我们在程序中定义一个邮箱消息,然后通过按钮发送,还是通过温湿度读取任务接收,因为不能自动创建,我们需要自己创建,从上面内容我们知道邮箱和消息队列类似,所以我们根据消息队列的定义,来试着定义一个邮箱。


**消息队列的定义**



/* Create the queue(s) */
/* definition and creation of myQueue01 */
osMessageQDef(myQueue01, 16, uint8_t);
myQueue01Handle = osMessageCreate(osMessageQ(myQueue01), NULL);


![在这里插入图片描述](https://img-blog.csdnimg.cn/31ce73ec3f57416d9bcb023b2d2909c2.png)


**邮箱的定义**


结合消息队列的定义,再根据下图我们可以试着定义一个邮箱


![在这里插入图片描述](https://img-blog.csdnimg.cn/f94829074a734c43bc5dfb93de6f08e8.png)  
 定义如下:


![在这里插入图片描述](https://img-blog.csdnimg.cn/c704d6715c4b4ec0a3cdafac18cb1a9e.png)


#### 2.2.1 基本测试


在KeyTask中:


![在这里插入图片描述](https://img-blog.csdnimg.cn/0a359023adfc4e8988196b2ff92758e9.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


在温湿度读取任务中:


![在这里插入图片描述](https://img-blog.csdnimg.cn/ffc12badaf444fabb9cd12fec9d7fddf.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


测试结果:


![在这里插入图片描述](https://img-blog.csdnimg.cn/c7aeced5b0fa49ffb8c82b01f9caa2f3.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_13,color_FFFFFF,t_70,g_se,x_16)


#### 2.2.1 问题测试


说明,上面的例子定义邮箱的消息类型为`uint8_t`,而我发送的邮箱消息为`0x1234`,但是还是能够正常的接收和发送,后来我把定义改成 `osMailQDef(myMail01,1,uint8_t);` 长度改为1,还是能够正常接收发送`0x1234`,说明我对里对邮箱的使用和理解还是不到位,对于`uint8_t`类型能发0x1234 暂时没有深入研究,为了多了解一点邮箱中这个长度的意思,我继续做了测试


**测试一:**


在KeyTask中:


![在这里插入图片描述](https://img-blog.csdnimg.cn/479f711e2fe14f4b923ead4faf0a6221.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


在温湿度读取任务中(这里是为了验证邮箱,所以打印出邮箱内容):


![在这里插入图片描述](https://img-blog.csdnimg.cn/cbbd25cc9c414bd79aecf50c317e4794.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_17,color_FFFFFF,t_70,g_se,x_16)


测试一结果:


![在这里插入图片描述](https://img-blog.csdnimg.cn/c7b15f19f14f4d5d81149a78333c4540.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


**测试二:**


测试二的KeyTask中的代码和温湿度读取任务中的代码不变,只变了一个地方`osMailQDef(myMail01,1,uint8_t);`Mail的长度变为1。


![在这里插入图片描述](https://img-blog.csdnimg.cn/1ca087de02ea4a629db757faf898f4e1.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


测试二结果:


![在这里插入图片描述](https://img-blog.csdnimg.cn/05b0d05675fa4ca3aaced57cfa19337f.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_16,color_FFFFFF,t_70,g_se,x_16)


**测试三:**


在KeyTask中 for 循环前后加入临界区保护:


![在这里插入图片描述](https://img-blog.csdnimg.cn/fee3f508c8e94e1e99b605ac78280c6d.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


测试三结果:


![在这里插入图片描述](https://img-blog.csdnimg.cn/53d802b789d54bd8ab72dc22dd16e082.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_18,color_FFFFFF,t_70,g_se,x_16)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/f4b79eb1e58a4947965753aa372a5a46.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


经过这几个测试,我们对Cubem中邮箱定义的这个长度有了清楚的认识,  
 同时也知道了邮箱发送的时候会产生任务调度,这个在邮箱发送函数  
 `BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition )`  
 源码中也能看出来到确实会发生调度。


当然,除了长度这个参数,第三个类型这个参数才是邮箱和消息队列更加核心和有意思的地方,除了基本的数据类型,我们还可以自定义一个结构体,我们把类型定义为我们定义的结构体,比如:



/* add queues, … */
typedef struct
{
uint8_t value1;
uint32_t value2;
uint16_t value3;
uint8 *addr1;
}My_Mail;

osMailQDef(myMail01,16,My_Mail);
myMail01Handle = osMailCreate(osMailQ(myMail01),NULL);


后续如果遇到使用再来更新。



![img](https://img-blog.csdnimg.cn/img_convert/80b1e786e817a605e141076eedc04590.png)
![img](https://img-blog.csdnimg.cn/img_convert/f7fd103254041af1d48e887a5b94ce40.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**

**需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

   uint8\_t  value1;
    uint32\_t value2;
    uint16\_t value3;
    uint8    \*addr1;
  }My_Mail;
  
  osMailQDef(myMail01,16,My_Mail);
  myMail01Handle = osMailCreate(osMailQ(myMail01),NULL);
 

后续如果遇到使用再来更新。

[外链图片转存中…(img-lF9OTKdH-1715874299104)]
[外链图片转存中…(img-T0DyhmHX-1715874299105)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

如果你需要这些资料,可以戳这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值