如何在 STM32F103C8T6 上实现裸机 LED 闪烁?
How to do bare-metal LED blink on STM32F103C8T6?
我刚刚开始探索 STM32 MCU。我想让 BluePill(具有 STM32F103C8T6 MCU)板上的 LED 闪烁。我怀疑我被某些东西误导了。根据F1系列的参考手册,主要有3个步骤:
- 为端口启用时钟(此处为 PORTC)
- 配置 CNF/MODE 寄存器
- 根据需要配置 ODR 寄存器,即在引脚上 HIGH/LOW。
我已经按照手册在KEIL MDK中编写了代码,但是加载后,代码没有运行,我按下复位按钮然后LED亮起,甚至尽管我已经在 KEIL.
中将设置更改为 RESET & 运行
这是参考手册的代码和部分。
#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
}
}
参考手册:
当我使用调试模式时,我注意到一件事,即在执行 RCC->APB2ENR |= (1<<4)
后没有为 PORTC 启用时钟。
LED 不闪烁。整个过程我找不到错误。
Actually the LED toggles inside the while loop ,but only in debug mode
是的,因为这是唯一一次生成的机器代码包含那些延迟循环。在释放模式下,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
指向一个可变对象,编译器无法优化访问。
当您编写一个虚拟延迟循环时,聪明的编译器通常会发现这段代码中没有任何值得发生的事情并优化整个事情。
如果你想知道发生过这种情况,最好的方法是查看生成的二进制文件的反汇编。
幸运的是,C 提供了 volatile
关键字来解决这类问题。它明确告诉编译器不要优化对使用此限定符声明的变量的内存访问。
Here 您可以看到一些示例代码,这些示例代码显示了使用优秀的 godbolt 工具生成的带有和不带有 volatile
关键字的汇编代码之间的区别。如果没有 volatile,for 循环将优化为无,并且不会发生 i
的递增或检查。
因此循环应该写成:
for(volatile int i = 0; i < 500000; i++); //dummy delay
您可能 运行 在嵌入式系统上遇到此类问题,在其他情况下,例如当您从多个 contexts/threads.
访问一个变量时
我刚刚开始探索 STM32 MCU。我想让 BluePill(具有 STM32F103C8T6 MCU)板上的 LED 闪烁。我怀疑我被某些东西误导了。根据F1系列的参考手册,主要有3个步骤:
- 为端口启用时钟(此处为 PORTC)
- 配置 CNF/MODE 寄存器
- 根据需要配置 ODR 寄存器,即在引脚上 HIGH/LOW。
我已经按照手册在KEIL MDK中编写了代码,但是加载后,代码没有运行,我按下复位按钮然后LED亮起,甚至尽管我已经在 KEIL.
中将设置更改为 RESET & 运行这是参考手册的代码和部分。
#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
}
}
参考手册:
当我使用调试模式时,我注意到一件事,即在执行 RCC->APB2ENR |= (1<<4)
后没有为 PORTC 启用时钟。
LED 不闪烁。整个过程我找不到错误。
Actually the LED toggles inside the while loop ,but only in debug mode
是的,因为这是唯一一次生成的机器代码包含那些延迟循环。在释放模式下,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
指向一个可变对象,编译器无法优化访问。
当您编写一个虚拟延迟循环时,聪明的编译器通常会发现这段代码中没有任何值得发生的事情并优化整个事情。
如果你想知道发生过这种情况,最好的方法是查看生成的二进制文件的反汇编。
幸运的是,C 提供了 volatile
关键字来解决这类问题。它明确告诉编译器不要优化对使用此限定符声明的变量的内存访问。
Here 您可以看到一些示例代码,这些示例代码显示了使用优秀的 godbolt 工具生成的带有和不带有 volatile
关键字的汇编代码之间的区别。如果没有 volatile,for 循环将优化为无,并且不会发生 i
的递增或检查。
因此循环应该写成:
for(volatile int i = 0; i < 500000; i++); //dummy delay
您可能 运行 在嵌入式系统上遇到此类问题,在其他情况下,例如当您从多个 contexts/threads.
访问一个变量时