用 C 语言 (STM32F4) 估计剩余的可用 RAM

Posted

技术标签:

【中文标题】用 C 语言 (STM32F4) 估计剩余的可用 RAM【英文标题】:Estimating available RAM left with safety margin in C (STM32F4) 【发布时间】:2015-09-03 18:37:18 【问题描述】:

我目前正在使用 STM32CubeMx 和 Keil uVision 为 STM32F407 开发应用程序。我知道嵌入式系统中的动态内存分配大多不鼓励,但在互联网上我可以找到一些支持它的论点。

由于我的发明家灵魂,我想尝试去做,但要安全地去做。假设我正在为传入的 UART 消息创建一个动态分配的 fifo,其中包含由 msg 本身及其长度组成的结构。但是我不想这样做消耗所有堆大小,因此我想检查我还剩下多少:Me new(?)想法是尝试临时分配一些大块内存(比如 100 个字符)- 如果成功,我接受传入的 msg,如果不是 - 这意味着我的堆用完了并忽略 msg(或接受它并将最旧的出队)。检查后我当然释放了临时内存。

我想到了几个问题:

    首先,这有意义吗?根据您的经验,您认为它是否有用且安全? 我找不到关于在 ES 中究竟共享 RAM 的确切信息(我知道堆、堆栈和易失性变量),所以我的问题是:为 1. 提供这个答案不是“地狱不回家”,什么临时内存检查器的大小你会为提到的控制器选择吗? 关于 micro 本身 - 它有 192kB RAM,但是在 Drivers\CMSIS\Device\ST\STM32F4xx\Source\Templates\arm\startup_stm32f407xx.s 文件中仅分配了 512B+1024B堆和堆栈 - 是不是很少,留下百日咳,剩余 190kB 用于 volatile vars?将堆大小增加到 50kB 是否明智?如果是,我是直接在此文件中执行此操作,还是在其他地方执行此操作更好?

可能对你们中的一些人来说,“安全动态记忆”和“嵌入”在一篇文章中既令人震惊又令人眼花缭乱,但请记住,这是在试验和探索新的视野:) 谢谢和问候。

【问题讨论】:

如果你想保护 RAM,你首先可能想摆脱 ST“标准”库。这不仅会释放一些 RAM,而且会保护相当多的代码。 我自己在STM32F4上使用动态内存分配。但是,我确实使用了一个池,所以我有 RT/中断安全行为。但是,我会适当地处理内存不足的情况。 @Olaf:我不同意你对 ST 的 StdPeriph 库的立场。我没有任何问题。另一方面,他们的 Cube lib 是一堆热气腾腾的马粪。 @jalooc:如果您打算尝试了解嵌入式上的动态内存分配,您不妨从一些现有资源开始:barrgroup.com/Embedded-Systems/How-To/… 【参考方案1】:

好的,我已经通过动态堆可用空间检查测试了我的想法,并且效果很好(尽管我没有进行长期测试),但是@Clifford 的回答和this article 说服我放弃了动态分配的想法。最终,我实现了我自己的静态堆,其中包含页面(2d 数组)、占用页面指示器(0-1 页面大小数组)和结构的 fifo,包括指向我的静态堆上的 msg 的指针(实际上只是索引数组)和消息长度(以确定它占用多少连续页面)。我收到的 95% 的 msg 应该只占用一页,5% - 2 或 3 页,所以碎片仍然是可能的,但至少我严格控制它,它只影响分配给这个模块的内存部分代码(换句话说:碎片不会泄漏到代码的其他部分)。到目前为止,它的工作没有任何问题,并且肯定更快,因为查找时间是 O(n*m),n - 页数,m - 可能的最长页,但考虑到概率法则,它下降到在)。此外,n 总是比内存中所有分配单元的数量小很多,因此查找起来也更少。

【讨论】:

很好地报告了您的解决方案。您自己实现的优势是您可以实现自己的工具来进行性能分析和调整。【参考方案2】:

Keil uVision 仅描述 IDE。如果您使用的是 KEil MDK-ARM,这意味着 ARM 的 RealView 编译器,那么您可以使用 __heapstats() function 获取准确的堆信息。

__heapstats() 有点奇怪,它不是简单地返回一个值,而是将堆信息输出到一个格式化的输出流,该输出流由传递给它的函数指针和文件描述符提供便利。输出函数必须有类似fprintf() 的接口。您当然可以使用fprintf(),但这需要您正确使用retargetted the stdio

例如:

typedef int (*__heapprt)(void *, char const *, ...);
__heapstats( (__heapprt)fprintf, stdout ) ; 

例如输出:

4180 bytes in 1 free blocks (avge size 4180)
1 blocks 2^11+1 to 2^12

不幸的是,它并不能真正实现您所需要的,因为它会输出文本。但是,您可以实现自己的函数来捕获内存中的数据并解析结果。您可能只需要捕获第一个十进制数字字符并丢弃其他任何内容,除了可用内存量和最大可分配块当然不一定是同一件事。碎片由数量或空闲块及其平均大小表示。您或许可以保证至少能够分配一个平均大小的块。

嵌入式系统中动态分配的问题与处理内存耗尽有关,而在实时系统中,则与使用默认 malloc/free 实现的分配和释放时间的不确定性有关。在您的情况下,您最好使用固定块分配器。您可以通过创建内存块的静态数组(或通过在启动时从堆中动态分配它们)并将指向每个块的指针放置在队列或链表或堆栈结构上来实现这样的分配器。要分配您只需从队列/列表/堆栈中删除一个指针,然后释放您将指针放回。当可用块结构为空时,内存耗尽。它完全是确定性的,因为它是您的实现,因此可以轻松监控性能和容量。

关于问题 3。您需要调整堆和系统堆栈大小以适合您的应用程序。我使用的大多数工具都有一个链接器脚本,它会自动分配所有可用内存,而不是静态分配、分配给堆栈或为其他目的保留给堆。但是 MDK-ARM 在默认链接描述文件中并没有这样做,而是分配一个固定大小的堆。

您可以使用链接器映射文件摘要来确定有多少空间未使用并手动扩展堆。当静态分配的数据量可能增加时,我通常会留下少量未使用的空间来进行维护。然而,在某些时候;您最终会耗尽内存,并且来自链接器的神秘错误消息可能不会明显表明您的堆太大。可以覆盖默认链接器脚本并提供您自己的,然后毫无疑问可以自动调整堆大小 - 尽管我从未尝试过。

【讨论】:

我仍然坚持基于池的分配器是动态的。我看到拆分/合并分配器和池式分配器之间的区别更多。除此之外,我同意。 @Olaf :是的,它们是动态的,因为内存块在运行时被分配了一个用途。我不认为我有其他建议。这里的重点不是避免动态内存分配,而是避免默认堆实现的非确定性行为。 我可以忍受 :-)(简单地说:我同意)

以上是关于用 C 语言 (STM32F4) 估计剩余的可用 RAM的主要内容,如果未能解决你的问题,请参考以下文章

STM32F4 阿波罗 库函数与C语言知识

STM32F4 UART1 DMA发送和接收不定长度数据

stm32f4为啥没人用

c_cpp STM32F4标准库GPIO设置

基于stm32f4的ucGUI通过外部flash存储汉字库显示任意英文字符和汉字组合(控件可用)

STM32F4 HAL库开发 -- 工程模板解读