PIC18堆栈/内存管理的最佳实践?

6
有限的 PIC 微控制器堆栈大小是一个问题,我已经调整了我的代码以适应这个现实。我目前采用了一个简单的范例,将密切相关的函数分组到一个模块中,并在模块中声明所有变量为全局静态变量(以减少存储在自动 psect 中的变量数量, 并且只有 ISR 中的可变性问题是相关的,我已经考虑到了)。我这样做并不是因为这是良好的做法,而是因为现实中你必须将整个项目中存在的所有本地函数变量分配给有限的空间。在 8/16 位芯片的嵌入式世界中,如果采用这种方法是否合适,只要我确保采取必要的预防措施呢?我还会进行其他操作,例如为 Ethernet 分配 >256 字节的 RAM(我知道标准 MTU 应该是 1500,但我们有一种特殊情况和非常有限的 RAM),并通过指针访问该内存,以避免使用内存银行的语义。我做错了吗?我的应用程序运行良好,但我完全愿意接受改进建议。
7个回答

4
我知道这个问题是4年前提出的,但它仍未得到恰当的答复。我认为OP所问的是他们解决HiTech PICC18 C编译器限制的方法是否有效和/或最佳实践。正如稍后的评论中提到的那样,限制(一个相当糟糕且不被Hitech广告的限制)是“Hi-Tech编译器仅允许使用256字节的自动变量”。实际上,限制比这更糟糕,因为局部变量和参数的总计只有256字节。当超过此限制时,链接器警告也非常晦涩。只要函数在调用树的不同分支上,编译器就可以重叠变量以重用空间。这意味着你可以有效地拥有超过256字节。但请注意,中断处理程序(如果使用优先级方案则有多个)具有其自己的调用树,共享256字节的本地/参数块。
局部变量 减少局部变量所需空间的两种解决方案是:将局部变量变成全局变量或静态变量。使它们静态保持作用域不变,并且只要该函数不是从中断调用的就是安全的(无论如何,编译器都不允许重入)。这可能是首选的选择。缺点是编译器无法重用这些变量的位置以减少总体内存消耗。将变量移动到全局范围允许重用,但重用管理必须由程序员管理。可能最好的平衡是使简单变量静态,但使像字符串缓冲区这样的大块内存全局,并仔细重用它们。
初始化要小心。
foo()
{
 int myvar = 5;
}

必须更改为

foo()
{
 static int myvar;
 myvar = 5;
}
参数 如果您在参数中传递大量数据,您很快就会遇到同样的256字节限制。在这里,您最好的选择可能是传递到全局分配的结构体/选项的指针。或者,您可以设置由顶层呼叫者设置并由调用程序在树下读取的全局设置变量。这真的取决于软件设计哪种方法更好。
我也曾遇到与OP相同的问题,我认为从使用Hitech编译器转移是长期最佳选择。编译器编写者采取的将所有本地/参数分配在一个块中的优化决策只适用于非常小的RAM尺寸PICS。对于大型PICS,您将在达到设备的RAM尺寸之前耗尽本地/参数。然后,您必须开始修改代码以适应编译器,这是反常的。
总之…… 是的,您的方法是有效的。但是请考虑使本地静态化,如果适用的话,因为一般来说,减小范围会使您的代码更安全。

将本地变量转换为函数作用域静态变量时需要注意的一点是要小心检查初始化。 - Felix

2
而C18编译器使用一些FSR(指针)来管理数据栈,Microchip的新XC8编译器似乎使用了一个已编译的栈,因此您应该在编译时确切地知道栈占用了多少空间。您还将确切地知道每个栈变量存储在哪里。我在XC8用户指南中阅读了所有关于这个特性的内容,听起来很棒。假设您正在使用XC8,那么这个问题就不成问题了。

1

我在处理内存有限的芯片编译器/链接器方面的经验是,只要不使用递归函数并告知编译器,那么编译器非常能够确定所需的最小堆栈空间。

我甚至见过一些编译器为每个自动存储变量分配全局固定地址(完全没有堆栈),其中多个变量被分配到重叠的内存中,只要它们的生命周期不重叠。

在进行(速度或空间)优化时的一般建议是:进行测量以证明您的优化确实具有积极影响。


0

这可能听起来显而易见,但在8位处理器上尽量不要使用16位变量。 16位变量在更大的架构中是可以使用和需要的,但在受限制的(8位)架构中,16位算术是在短时间内耗尽RAM和ROM存储器的快速方式。

如果您尝试递增16位变量,则编译器将包含一个16位增量库,在大多数情况下会消耗大量空间。

此外,尽量不要进行除法或乘法运算,因为对于某些控制器它们是软件实现的。

个人而言,我通常使用char,并且在需要除法运算时,使用右旋转'n'次以2的n次方进行除法运算。

希望这有所帮助!


1
下降投票,因为这只是提供一般建议,没有解决OP的主要问题。 - Felix

0

由于你的内存快要用完了,你必须计算每个字节的RAM。使用本地变量(自动变量)可以在需要时重复使用内存(函数中的本地内存)。当您将变量移动到全局静态地址空间时,您为每个变量分配了唯一的空间。这是地址空间的浪费。

Microchip编译器允许不同的变量共享相同的地址。我手头没有文档,但可以通过pragma实现。

但是你需要的是对RAM需求的分析。当您看到堆栈无法容纳所有变量但自动变量可以减少全局内存使用时,应考虑使用启动代码和链接脚本来增加堆栈大小。


我们正在使用 Hi-Tech PICC18 Pro 编译器。它与 Microchip C18 的区别很多,但其中重要的是,Hi-Tech 编译器只允许最多 256 字节的自动变量,并且该编译器没有任何链接脚本可以供我编辑(据我所知)。 - Nate
OP在谈论堆栈/局部空间,而不是RAM的总体内存。 - Felix

0

最佳实践是选择符合要求的硬件。

有些微控制器的成本只比其他微控制器高几美元,但可以节省数百或数千美元的开发成本。如果这是一项业余开发,则您的努力可能不算什么。但在现实世界中,您经常会发现硬件仅考虑硬件成本而设计。

特别是PIC18并不是紧凑代码的最佳示例,这也可能会导致闪存内存的问题。


这不是我第一次听到这个观点。我认同这个观点,我会提出使用AVR微控制器和可能的IAR开发工具的理由来进行未来项目。但是我无法控制资金,而且当前项目已经编写了大量针对当前芯片的固件,所以在这个阶段更换微控制器不是一个选项。 - Nate
由非技术经理运营的商业企业正是推动开发人员使用最小芯片的企业! - Radu
@Nate 一个具有RAM限制的AVR芯片会遇到与PIC相同的问题。虽然个人微处理器偏好很好(而AVR是不错的芯片),但这并不是最终回答你问题的答案。 - boz

0
有点晚了,但如果您使用的是C18编译器的话,您应该仔细查看一下C18编译器用户指南。
通过静态分配本地变量(覆盖自动关键字),您可以显著减少堆栈的使用。更好的是,您可以使用叠加存储标识符,它允许在同一地址放置具有不同非重叠生命周期的变量,从而最小化RAM的使用。(C18编译器必须在非扩展模式下运行)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接