【问题标题】:Best Practices for PIC18 Stack/Memory Management?PIC18 堆栈/内存管理的最佳实践?
【发布时间】:2011-05-04 20:50:52
【问题描述】:

预算 PIC 的有限堆栈大小是一个问题区域,我已调整我的代码以适应这一现实。我目前采用粗略的范式,将密切相关的函数分组到一个模块中,并将模块中的所有变量声明为全局静态(以减少存储在 auto psect 中的变量数量,可变性问题仅与 ISR 相关,我对此进行了说明.) 我不这样做是因为这是一种很好的做法,但实际情况是您有有限的空间来分配整个项目中存在的所有本地函数变量。在 8/16 位芯片的嵌入式世界中,如果我确定采取必要的预防措施,这是一种合适的方法吗?我还做一些事情,比如为以太网分配 > 256 字节的 RAM(我知道它应该是 1500 作为标准 MTU,但我们有一个自定义情况和非常有限的 RAM)缓冲区,并且必须通过指针访问该内存,这样我就可以避免语义的内存银行。我做错了吗?我的应用程序有效,但我 100% 愿意接受改进建议。 [c]

【问题讨论】:

    标签: c memory stack


    【解决方案1】:

    我知道这是 4 年前提出的,但仍未得到正确回答。我相信 OP 所要求的是他们解决 HiTech PICC18 C 编译器有效和/或最佳实践的限制的方法。正如稍后的评论中提到的那样,限制(一个相当糟糕的限制,Hitech 没有很好地宣传)是“Hi-Tech 编译器只允许最多 256 个字节的自动变量”。实际上,限制比这更糟糕,因为局部变量和参数总共有 256 个字节。超出此值时的链接器警告也很神秘。如果函数位于调用树的不同分支上,那么编译器可以重叠变量以重用空间。这意味着您可以有效地拥有超过 256 个字节。但请注意,中断处理程序(或使用优先级方案的处理程序)有自己的调用树,共享 256 字节的本地/参数块。

    当地人 减少 locals 所需空间的两种解决方案是:使 locals 全局或使它们静态。使它们静态保持范围相同,并且如果不从中断调用函数是安全的(无论如何编译器都不允许租用)。这可能是首选选项。缺点是编译器不能重用这些变量的位置来减少整体内存消耗。将变量移动到全局范围允许重用,但重用管理必须由程序员管理。可能最好的平衡是将简单的变量设为静态,但将大块内存(如字符串缓冲区)设为全局并小心重用它们。

    小心初始化。

    foo()
    {
     int myvar = 5;
    }
    

    必须改成

    foo()
    {
     static int myvar;
     myvar = 5;
    }
    

    参数 如果您在参数中沿调用树传递大量数据,您将很快遇到相同的 256 字节限制。您最好的选择可能是将指针传递给全局分配的“选项”结构。或者,您可以拥有由顶级调用者设置并由树下的被调用者读取的全局设置变量。这真的取决于软件的设计哪种方法更好。

    我一直在努力解决与 OP 相同的问题,我认为从长远来看,最好的选择是放弃使用 Hitech 编译器。编译器编写者在一个块中分配所有局部变量/参数的优化决策仅适用于非常小的 ram 大小的 PICS。对于大型 PICS,您将在达到设备的 ram 大小之前用完本地/参数。然后你必须开始修改你的代码以适应不正常的编译器。

    总之...是的,您的方法是有效的。但是,如果合适的话,请考虑简单地将本地变量设为静态,因为通常,缩小范围会使您的代码更安全。

    【讨论】:

    • 将本地转换为函数范围静态时要注意的一点是要小心检查初始化。
    【解决方案2】:

    虽然 C18 编译器使用一些 FSR(指针)来管理数据堆栈,但听起来 Microchip 的新 XC8 编译器使用已编译堆栈,因此您应该确切知道堆栈在编译时占用了多少空间.您还将确切知道每个堆栈变量的存储位置。我在XC8 user's guide 中读到了这一切,听起来很棒。假设您使用的是 XC8,那么该功能应该会让这个问题变得毫无意义。

    【讨论】:

      【解决方案3】:

      我对内存有限芯片的编译器/链接器的经验是,只要您不使用递归函数并通知编译器,编译器就能够确定最小的堆栈空间量需要。
      我什至见过编译器为每个变量自动存储一个全局固定地址(根本没有堆栈),其中几个变量被分配到重叠的内存,只要它们的生命周期不重叠。

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

      【讨论】:

        【解决方案4】:

        由于您几乎没有内存,您必须计算 RAM 的每个字节。使用局部变量(auto)允许在需要的地方重用内存(函数中的局部变量)。当你将变量移动到全局静态地址空间时,你给每个变量一个唯一的空间。这是浪费地址空间。

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

        但您需要的是对 RAM 要求的分析。当您看到堆栈不能保存所有变量但自动变量会减少全局内存使用时,您应该考虑使用启动代码和链接描述文件来增加堆栈大小。

        【讨论】:

        • 我们使用的是 Hi-Tech PICC18 Pro 编译器。它和 Microchip C18 之间的区别很多,但重要的是 Hi-Tech 编译器只允许最多 256 字节的自动变量,而且这个编译器没有任何我可以编辑的链接器脚本(据我所知)。
        • OP 说的是堆栈/本地空间,而不是一般的内存。
        【解决方案5】:

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

        周围的微控制器成本仅高出几美元,却节省了数百或数千美元的开发成本。如果这是一个爱好发展,你的努力可能不算数。但在现实世界中,您经常会发现仅考虑硬件成本而设计的硬件。

        特别是 PIC18 不是紧凑代码的最佳示例,闪存也可能存在问题。

        【讨论】:

        • 这不是我第一次听到这个观点。这很好,我将提出一个案例,将 AVR uCs 用于未来的项目和可能的 IAR 开发工具。我不控制资金,而且这个项目已经为当前芯片编写了大量固件,此时更改 uC 不是一种选择。
        • @harper 由非技术经理经营的商业企业正是推动开发人员尽可能使用最小芯片的企业!
        • @Nate 具有 RAM 限制的 AVR 会遇到与 PIC 相同的问题。虽然个人 uP 偏好很好(而 AVR 是很好的筹码),但它最终并不能回答您的问题。
        【解决方案6】:

        这听起来很明显,但尽量不要在 8 位前级上使用 16 位变量。 16 位变量很好,在更大的架构中需要,但在有限的(8 位)架构中,16 位算法是一种快速耗尽 RAM 和 ROM 存储器的方法。

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

        另外,尽量不要除法或乘法,因为有些控制器是软件实现的。

        就我个人而言,我一直使用char,当需要除法操作时,使用旋转右“n”次除以 2 n 次。

        希望这会有所帮助!

        【讨论】:

        • 投反对票,因为这只是提供一般性建议,并没有解决 OP 的主要问题。
        【解决方案7】:

        有点晚了,但您还应该仔细查看 C18 编译器用户指南(如果您使用的是此编译器)。

        您可以通过静态分配局部变量(覆盖 auto 关键字)来显着减少堆栈。更好的是,您可以使用覆盖存储标识符,它允许将不同的非重叠生命周期变量放置在同一地址,从而最大限度地减少 RAM。 (C18 编译器必须在非扩展模式下运行)。

        【讨论】:

          猜你喜欢
          • 2011-06-25
          • 2012-03-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-07-19
          • 2012-03-20
          • 1970-01-01
          • 2012-03-07
          相关资源
          最近更新 更多