2

我在互联网上找不到任何关于 STM32 编程的好文档。STM 自己的文档除了寄存器功能之外什么都没有解释。如果有人能解释我的以下问题,我将不胜感激?

  1. 我注意到在 STM 提供的所有示例程序中,main() 的局部变量总是在 main() 函数之外定义(偶尔使用 static 关键字)。有什么理由吗?我应该遵循类似的做法吗?我应该避免在 main 中使用局部变量吗?

  2. 我有一个在时钟中断句柄中更新的全局变量。我在另一个函数中使用相同的变量作为循环条件。我不需要使用某种形式的原子读取操作来访问这个变量吗?我怎么知道时钟中断在函数执行过程中不会改变它的值?每次我需要在函数中使用这个变量时,我是否需要取消时钟中断?(但是,这对我来说似乎非常无效,因为我将它用作循环条件。我相信应该有更好的方法来做到这一点)。

  3. Keil 会自动插入一个以汇编语言编写的启动代码(即 startup_stm32f4xx.s)。此启动代码具有以下导入语句: IMPORT SystemInit IMPORT __main 。在“C”中,这是有道理的。但是,在 C++ 中,main 和 system_init 都有不同的名称(例如 _ int _main__void)。即使不使用“extern“C””,这个启动代码如何仍然可以在 C++ 中工作(我试过了,它成功了)。c++ 链接器 (armcc --cpp) 如何将这些语句与正确的函数相关联?

4

3 回答 3

4

您可以使用局部变量或全局变量,在嵌入式系统中使用局部变量存在堆栈与数据冲突的风险。使用全局变量,您就没有这个问题。但无论您身在何处,嵌入式微控制器、台式机等都是如此。

我会在使用它的前台任务中制作全局副本。

unsigned int myglobal;

void fun ( void )
{
   unsigned int myg;

   myg=myglobal;

然后仅将 myg 用于其余功能。基本上,您正在拍摄快照并使用快照。如果您正在阅读寄存器,您会想要做同样的事情,如果您想根据某样东西的样本做多项事情,请取一个样本并对该样本做出决定,否则该项目可能会在样本之间发生变化。如果您使用一个全局变量与中断处理程序来回通信,那么我将使用两个变量,一个是前台中断,另一个是前台中断。是的,有时您需要仔细管理这样的共享资源,通常这与您需要做不止一件事的时候有关,例如,如果在处理程序可以看到它们更改之前,您有几个项目都需要作为一个组进行更改,那么您需要禁用中断处理程序,直到所有项目都已更改。在这里,嵌入式微控制器没有什么特别之处,这是您在具有完整操作系统的桌面系统上看到的所有基本内容。

如果他们支持 C++,Keil 知道他们在做什么,然后从系统级别他们已经解决了这个问题。我不使用 Keil 我将 gcc 和 llvm 用于像这样的微控制器。

编辑:

这是我正在谈论的一个例子

https://github.com/dwelch67/stm32vld/tree/master/stm32f4d/blinker05

stm32 使用基于定时器的中断,中断处理程序修改与前台任务共享的变量。前台任务获取共享变量的单个快照(每个循环),并且如果需要在循环中多次使用快照,而不是可以更改的共享变量。这是 C 而不是 C++ 我明白这一点,我使用的是 gcc 和 llvm 而不是 Keil。(注意 llvm 已经知道优化紧的 while 循环的问题,非常老的 bug,不知道为什么他们没有兴趣修复它,llvm 适用于这个例子)。

于 2011-11-13T01:42:29.067 回答
4

问题一:局部变量

ST 提供的示例代码不是特别高效或优雅。它完成了工作,但有时他们所做的事情没有充分的理由。

通常,您总是希望变量具有尽可能小的范围。如果您只在一个函数中使用变量,请在该函数中定义它。当且仅当您需要它们在函数完成后保留其值时,才将“静态”关键字添加到局部变量。

在某些嵌入式环境中,例如带有 C18 编译器的 PIC18 架构,局部变量比全局变量更昂贵(更多的程序空间,更慢的执行时间)。在 Cortex M3 上,情况并非如此,因此您应该随意使用局部变量。检查组装清单并亲自查看。

问题2:中断和主循环之间共享变量

人们已经写了整章来解释这组问题的答案。每当你在主循环和中断之间共享一个变量时,你绝对应该使用volatile它的关键字。可以原子地访问 32 位或更少位的变量(除非它们未对齐)。

如果您需要从主循环访问一个更大的变量,或同时访问两个变量,那么您必须在访问变量时禁用时钟中断。如果您的中断不需要精确的时序,这将不是问题。当您重新启用中断时,它会在需要时自动触发。

问题3:C++中的main函数

我不知道。您可以在目标文件上使用arm-none-eabi-nm(或nm工具链中调用的任何内容)来查看 C++ 编译器分配给main(). 我敢打赌,出于这个确切原因,C++ 编译器不会破坏主函数,但我不确定。

于 2011-11-13T19:16:58.210 回答
2

STM 的示例代码不是良好编码实践的典范,它只是为了举例说明他们标准外设库的使用(假设这些是您正在谈论的示例)。在某些情况下,可能会在外部声明变量,main()因为它们是从中断上下文(共享内存)访问的。也有可能这样做只是为了允许在调试器中从任何上下文中观察变量;但这不是复制该技术的理由。我对 STM 示例代码的看法是,即使作为示例代码,它通常也很差,更不用说从软件工程的角度来看了。

在这种情况下,只要不使用多个写入器的读-修改-写语义,您的时钟中断变量是原子的,只要它是 32 位或更少 。无论如何,您都可以安全地拥有一位作家和多个读者。这对于这个特定平台是正确的,但不一定是普遍的;例如,对于 8 位或 16 位系统或多核系统,答案可能会有所不同。volatile在任何情况下都应声明该变量。

我在STM32上用Keil用C++,没有问题。我不确定您为什么认为 C++ 入口点不同,它们不在这里(Keil ARM-MDK v4.22a)。例如,启动代码调用SystemInit()初始化 PLL 和内存时序,然后调用__main()执行全局静态初始化,然后在调用之前调用全局静态对象的 C++ 构造函数main()。如果有疑问,请单步调试调试器中的代码。请务必注意,这__main()不是main()您为应用程序编写的函数,它是 C 和 C++ 具有不同行为的包装器,但最终会调用您的main()函数。

于 2011-11-15T12:15:14.593 回答