STM32编程技巧和问题



我在网上找不到任何关于STM32编程的好文档。STM自己的文档只解释了寄存器函数。如果有人能解释一下我的以下问题,我将不胜感激。

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

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

  3. Keil自动插入一个用汇编编写的启动代码(即startup_stm32f4xx.s)。这个启动代码有以下import语句:进口SystemInit进口__main在"C"中,这是有道理的。然而,在c++中main和system_init都有不同的名称(例如_int_main__void)。这个启动代码怎么能在c++中仍然工作,即使没有使用"extern"C"(我试过了,它工作了)。c++链接器(armcc—cpp)如何将这些语句与正确的函数关联起来?

您可以使用局部或全局变量,在嵌入式系统中使用局部变量有堆栈与数据冲突的风险。使用全局变量就不会有这个问题。但无论你在哪里,嵌入式微控制器,桌面等,这都是正确的。

我将在使用它的前台任务中复制全局变量。

unsigned int myglobal;
void fun ( void )
{
   unsigned int myg;
   myg=myglobal;

,然后只对函数的其余部分使用myg。基本上你是在拍摄快照并使用快照。如果你在读一个寄存器,你也会想做同样的事情,如果你想基于一个样本做多件事取一个样本,然后根据这个样本做出决定,否则这个项目可能会在样本之间改变。如果你使用一个全局来与中断处理程序来回通信,那么我会使用两个变量一个前台中断,另一个前台中断。是的,有时候你需要小心地管理这样的共享资源,通常它与你需要做不止一件事的时候有关,例如,如果你有几个项目都需要作为一个组来改变,然后处理程序才能看到它们的变化,那么你需要禁用中断处理程序,直到所有的项目都改变了。在这里,嵌入式微控制器没有什么特别之处,这些都是您在具有完整操作系统的桌面系统上看到的基本内容。

Keil知道他们在做什么,如果他们支持c++,那么从系统层面上他们已经解决了这个问题。我不使用Keil,我使用gcc和llvm的微控制器像这个。

编辑:

下面是我正在谈论的一个例子

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

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

问题1:局部变量

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

一般来说,你总是希望你的变量具有尽可能小的作用域。如果只在一个函数中使用变量,则在该函数中定义它。当且仅当您需要局部变量在函数完成后保留其值时,请向局部变量添加"static"关键字。

在一些嵌入式环境中,比如带有C18编译器的PIC18体系结构,局部变量比全局变量开销大得多(更多的程序空间,更慢的执行时间)。在Cortex M3上,情况并非如此,所以您可以随意使用局部变量。查看程序集清单并自己查看。

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

有人写了一整章来解释这组问题的答案。无论何时在主循环和中断之间共享变量,都应该对其使用volatile关键字。32位或更少的变量可以自动访问(除非它们没有对齐)。

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

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

我不确定。你可以在你的目标文件上使用arm-none-eabi-nm(或任何nm在你的工具链中被称为)来查看c++编译器分配给main()的符号名称。我敢打赌,c++编译器不会因为这个确切的原因而破坏main函数,但我不确定。

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

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

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

最新更新