繁体   English   中英

如何在STM32F103C8T6上做裸机LED闪烁?

[英]How to do bare-metal LED blink on STM32F103C8T6?

我刚刚开始探索 STM32 MCU。 我想使 BluePill(具有 STM32F103C8T6 MCU)板上的 LED 闪烁。 我怀疑我被什么东西误导了。 根据F1系列的参考手册,有3个主要步骤:

  • 为 PORT 启用时钟(此处为 PORTC)
  • 配置 CNF/MODE 寄存器
  • 根据需要配置 ODR 寄存器,即引脚上的高/低。

我已经按照手册在 KEIL MDK 中编写了代码,但在加载后,代码不运行,我按下了重置按钮,然后 LED 亮起,即使我已将 KEIL 中的设置更改为 RESET & RUN

这是代码和参考手册的部分。

#include<stm32f10x.h>

int main(){
    
    RCC->APB2ENR |= 1<<4; //PORTC is on APB2 bus
    GPIOC->CRH |= (1<<20);
    
    while(1){
        GPIOC->ODR |= 0x00002000;
        for(int i = 0; i < 500000;i++); //dummy delay
        GPIOC->ODR &= ~0x00002000;
        for(int i = 0; i < 500000;i++); // dummy delay

    }

}

参考手册: ODR 寄存器 APB2 总线使能 CRH寄存器

当我使用调试模式时,我注意到一件事,在执行RCC->APB2ENR |= (1<<4)后,时钟没有为 PORTC 启用。 GPIOC调试

LED 不闪烁。 我无法在整个过程中找到错误。

当您编写一个虚拟延迟循环时,智能编译器通常会发现这段代码中没有任何值得发生的事情并优化整个事情。

如果您想知道这是否发生,最好的方法是查看生成的二进制文件的反汇编。

幸运的是,C 提供了volatile关键字来解决这类问题。 它明确告诉编译器不要优化对使用此限定符声明的变量的内存访问。

在这里你可以看到一些示例代码,显示了使用优秀的 Godbolt 工具生成的带有和不带有volatile关键字的汇编代码之间的区别。 如果没有 volatile,for 循环将被优化为空,并且不会发生对i递增或检查。

因此,循环应该写为:

for(volatile int i = 0; i < 500000; i++); //dummy delay

您也可能在其他情况下在嵌入式系统上遇到此类问题,例如当您从多个上下文/线程访问变量时。

实际上 LED 在 while 循环内切换,但仅在调试模式下

是的,因为这是生成的机器代码唯一一次包含这些延迟循环。 在释放模式下,LED 仍然会切换,除非您需要示波器或逻辑分析仪来查看输出引脚的状态以查看它是否在切换 - 您不会仅用眼睛看到它:)

在发布模式下,延迟循环被删除,因为您无法以这种方式实现延迟。 理想情况下,您应该使用计时器,但作为快速破解,这将起作用:

const int N = 500000;
while(1){
    for(int i = 0; i < N;i++) GPIOC->ODR |= 0x00002000;
    for(int i = 0; i < N;i++) GPIOC->ODR &= ~0x00002000;
}

它会起作用,因为GPIOC指向易失性对象,并且编译器无法优化访问。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM