FreeRTOS记录(六、FreeRTOS消息队列—Enocean模块串口通讯、RAM空间不足问题分析)_freertos每个现成占用ram(2)

img
img

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

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

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

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

*/
void LPUART1_IRQHandler(void)
{
u8 res;
/* USER CODE BEGIN LPUART1_IRQn 0 */
if(__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_RXNE) == SET){
res = hlpuart1.Instance->RDR;

xQueueSendFromISR(EnoceanQueueHandle,&res,NULL);

}
/* USER CODE END LPUART1_IRQn 0 */
HAL_UART_IRQHandler(&hlpuart1);
/* USER CODE BEGIN LPUART1_IRQn 1 */

/* USER CODE END LPUART1_IRQn 1 */
}


### 2.1 操作寄存器接收串口数据


上面代码中可以看到,使用的是M0 的内核,操作 ISR 和 RDR 寄存器:


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


如果是M3、M4 的内核,操作 SR 和 DR 寄存器:


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


### 2.1 中断中入队


在串口中断中,使用了`xQueueSendFromISR(EnoceanQueueHandle,&res,NULL);`向消息队列中发送数据;


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


**为什么使用`xQueueSendFromISR`而不用`osMessagePut`。**


在CubeMX中,封装好的消息发送函数为`osMessagePut`,和其他一样,封装好的会自动判断是否在中断中发送,自动引用`xQueueSendFromISR`或者`xQueueSend`函数,源码如下:



/**
* @brief Put a Message to a Queue.
* @param queue_id message queue ID obtained with \ref osMessageCreate.
* @param info message information.
* @param millisec timeout value or 0 in case of no time-out.
* @retval status code that indicates the execution status of the function.
* @note MUST REMAIN UNCHANGED: \b osMessagePut shall be consistent in every CMSIS-RTOS.
*/
osStatus osMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec)
{
portBASE_TYPE taskWoken = pdFALSE;
TickType_t ticks;

ticks = millisec / portTICK_PERIOD_MS;
if (ticks == 0) {
ticks = 1;
}

if (inHandlerMode()) {
if (xQueueSendFromISR(queue_id, &info, &taskWoken) != pdTRUE) {
return osErrorOS;
}
portEND_SWITCHING_ISR(taskWoken);
}
else {
if (xQueueSend(queue_id, &info, ticks) != pdTRUE) {
return osErrorOS;
}
}

return osOK;
}


 但是注意!!!`osMessagePut` 的第二个参数为`uint32_t` 类型,所以只有当定义的消息`Item Size` 为 `uint32_t`  时候才能使用,否则消息会出错!


## 3、消息队列函数形参分析


### 3.1 关于 void \*p


从源码可知,消息队列接收中定义了一个`osEvent event;`



osEvent osMessageGet (osMessageQId queue_id, uint32_t millisec)
{
portBASE_TYPE taskWoken;
TickType_t ticks;
osEvent event;


我们在上一篇文章


[FreeRTOS记录(五、FreeRTOS任务通知)](https://bbs.csdn.net/topics/618679757)


中的二、任务通知使用 章节的 3、接收通知 小节 用到过`osEvent`类型,给出了结构体的定义。


其中结构体中关于 value 是一个联合体,比如如果是uint32\_t 类型的数据,直接使用 value.v 读取,但如果是一个地址,而且可能是不同的数据类型,就得使用到 void \*p :


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


同样的,我们在`xQueueReceive`和`xQueueGenericSend`里面,第二个形参也使用了`void *`类型:



BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )


BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition )


这里要说明的是,**void \*可以指向任何类型的数据!**


来看看我们经常使用的`memset`原型,是不是能更好的理解:



void *memset(void *s, int ch, size_t n);//将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s


所以消息队列这里使用表示他可以发送和接收任意类型的数据,也就是消息类型,从简单的数据,到结构体,二维数组等消息都是可以传递的。


### 3.2 关于 void \* const pvBuffer 和 const void \* const pvItemToQueue


对于第一个`void * const pvBuffer`:


const 后面紧跟的是 pvBuffer , pvBuffer const 类型的,不可变(这里是指的某个地址不可变)但是 \*pvBuffer 可变(该地址的数据是可变的)  
 消息队列接收的时候使用这个定义的形参;


对应的举个例子,如果是`const void *pvBuffer`:\*pvBuffer 是const ,const 后面紧跟的是void,所以 \*pvBuffer 可能是任意类型,但是这个数据不可变。


对于第二个`const void * const pvItemToQueue`:


pvItemToQueue 和 \*pvItemToQueue 都是不可变的(这个地址不可变,这个地址上的数据不可变)  
 消息队列发送的时候使用这个定义实参;


## 4、数据接收处理


### 4.1 在任务中接收


在任务中接收消息,因为要保存到数组里面,使用了`xQueueReceive`函数:




uint8 USART_Enocean_BUF[100];
uint8 Enocean_Data = 0; //数据长度记录

/* USER CODE END Header_StartenoecanTask */
void StartenoecanTask(void const * argument)
{
/* USER CODE BEGIN StartenoecanTask */
/* Infinite loop */
for(;😉
{
if(xQueueReceive(EnoceanQueueHandle,&USART_Enocean_BUF[Enocean_Data++],portMAX_DELAY) == pdPASS){
while(xQueueReceive(EnoceanQueueHandle,&USART_Enocean_BUF[Enocean_Data++],15));
HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出
memset(USART_Enocean_BUF,0,sizeof(USART_Enocean_BUF));
Enocean_Data=0;
}
// osDelay(1);
}


在CubeMX中,封装好的消息发送函数为`osMessageGet` ,和其他一样,封装好的会自动判断是否在中断中,自动引用`xQueueReceiveFromISR`或者`xQueueReceive`函数,源码如下:



/**
* @brief Get a Message or Wait for a Message from a Queue.
* @param queue_id message queue ID obtained with \ref osMessageCreate.
* @param millisec timeout value or 0 in case of no time-out.
* @retval event information that includes status code.
* @note MUST REMAIN UNCHANGED: \b osMessageGet shall be consistent in every CMSIS-RTOS.
*/
osEvent osMessageGet (osMessageQId queue_id, uint32_t millisec)
{
portBASE_TYPE taskWoken;
TickType_t ticks;
osEvent event;

event.def.message_id = queue_id;
event.value.v = 0;

if (queue_id == NULL) {
event.status = osErrorParameter;
return event;
}

taskWoken = pdFALSE;

ticks = 0;
if (millisec == osWaitForever) {
ticks = portMAX_DELAY;
}
else if (millisec != 0) {
ticks = millisec / portTICK_PERIOD_MS;
if (ticks == 0) {
ticks = 1;
}
}

if (inHandlerMode()) {
if (xQueueReceiveFromISR(queue_id, &event.value.v, &taskWoken) == pdTRUE) {
/* We have mail */
event.status = osEventMessage;
}
else {
event.status = osOK;
}
portEND_SWITCHING_ISR(taskWoken);
}
else {
if (xQueueReceive(queue_id, &event.value.v, ticks) == pdTRUE) {
/* We have mail */
event.status = osEventMessage;
}
else {
event.status = (ticks == 0) ? osOK : osEventTimeout;
}
}

return event;
}


需要把数据保存至我们自己定义的数组中,所以使用了`xQueueReceive`函数。


然后等待15ms,确保收到的是一帧完整的数据。接收完一帧数据,通过串口1打印出来,然后清空数据。


### 4.2 数据解析


根据上面的代码,通过串口助手测试看看效果,发现有下面的问题,最后会多出来一位, 但是每次都是一帧数据正常发送(很简单的问题,仔细看一下代码就知道问题所在了):


![在这里插入图片描述](https://img-blog.csdnimg.cn/3e73f21fdb2546f38b7a96e310c2ae5c.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/6451763ee1024901bdee5d85cdb8975b.png)  
 然后数据解析函数直接用以前的驱动包,需要稍微修改一下函数:


![在这里插入图片描述](https://img-blog.csdnimg.cn/ce078a9d522543d28770466bf6e658b7.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/8f63380bdc0e4da2b8fcd1483822af14.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_12,color_FFFFFF,t_70,g_se,x_16)


至此,使用消息队列 串口接收 不定长度的数据测试完成,结果也比较理想。


另外提一下,Enocean除了接收,发送可以直接用以前写好的函数,直接在需要的任务中调用:


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


### 4.3 缓存大小问题(未解决)


在测试中,本来为了解决RAM使用空间,想把`USART_Enocean_BUF[100]` 数组大小设置为50,因为最大的一帧数据也只有在读ID的时候44个字节,每次读取完成都会把数组清0。所以觉得数组 大小 50 足够使用了,但是实际上测试下来发现,50,甚至是80 90的数组大小都不够使用,接收任务会出问题,具体原因一下子还不明白。


这个缓存问题,按理来说,80、90字节 应该也够的,我试着把消息队列的大小也定义成缓存一样大大小,还是不行,这个问题后面如果发现再来补充!(未解决)


## 5 、RAM空间不足问题


内存空间不足 小结写在前面:


* FreeRTOS定义的 TOTAL\_HEAP\_SIZE,直接在内存占用这么大的空间,编译过后的.bss段直接增加对应大小;  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/248e7d2a240943aaa461e5f70492c1c0.png)
* 系统的Minimum Heap Size 和 Minimum Stack Size ,(也可以认为)直接需要占用这么大的空间;  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/9dd74237ae31424785c49d4f2866d87f.png)
* 如果 FreeRTOS 使能了定时器,定时器启动以后会有一个任务:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/6386c9467e9941b4ac2aba20f7035a95.png)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/73fc79df1859441694d750e975ac7379.png)  
  虽然前面申请了 TOTAL\_HEAP\_SIZE 空间,图中这个任务`Tmr Svc`会占用TOTAL\_HEAP\_SIZE 空间内容

  注意!!除了任务所占用的申请过的空间不用计算内存,开启软件定时器之后还会额外占用.bss段  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/796ce02852d74abbb9c5672ff2b3f1a7.png)
* `Include definitions`里面使能了需要的定义,需要占用一点.bss段  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2330111735fc4593846e8845e73db24d.png)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/ef58ddd744ad4321972565ce21384d4a.png)
* 消息队列肯定也是要占用额外的.bss段的(这里已经使用上了,我就不重新去改掉,后面遇到再来维护)


### 5.1问题的出现


这次测试使用的是STM32L051C8,8KB的RAM,64KB的Flash,Flash还是够用的,但是8KB的RAM使用起来就有点捉襟见肘,在中途编译的时候就已经发现:


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


具体如何计算我有一篇博文单独介绍 内存问题:[STM32的内存管理相关(内存架构,内存管理,map文件分析)](https://bbs.csdn.net/topics/618679757),包括本文后面的一些分析内存部分,也需要参考上面这篇博文的内容。


果然,在接下来RAM空间不够了:


![在这里插入图片描述](https://img-blog.csdnimg.cn/a7aeae0e4c6b4ff8bfe32fe3aeed0809.png)  
 原因是由于我发现 FreeRTOS 内存可用字节数不够了:


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


**收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。**
![img](https://img-blog.csdnimg.cn/img_convert/cb4bbb5424dc2c726dc5c79d8f1e8c11.png)
![img](https://img-blog.csdnimg.cn/img_convert/43759b91265678b47c15f0a7a5775c5c.png)

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

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

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人**

**都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

70,g_se,x_16)


**收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。**
[外链图片转存中...(img-htdU8e3k-1715874662499)]
[外链图片转存中...(img-DYBs0JD6-1715874662499)]

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

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

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人**

**都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值