STM32L152无HAL裸机Rust实现LED闪烁故障求助
问题描述
我是11年级学生,课堂上学过汇编编程。出于兴趣尝试用Rust替代汇编编写STM32L152无HAL裸机程序,已实现LED点亮但无法完成闪烁功能。汇编版本程序可正常运行,Rust代码中首次write_volatile能点亮LED,但之后程序似乎陷入死循环。我仅使用unsafe和write_volatile实现,希望能得到改进或解决该问题的建议。
Rust代码
#![no_std] #![no_main] use core::arch::asm; use core::panic::PanicInfo; use core::ptr::write_volatile; #[no_mangle] #[link_section = ".text._start"] pub extern "C" fn _start() -> ! { let led_base: u32 = 0x40020000; let led_state = led_base | 0x14; unsafe { asm!( "ldr r1, =0x40023800", "ldr r0, =0x00008007", "str r0, [r1, #0x1C]", // Enable GPIO: A, B, C "ldr r1, =0x40020000", "ldr r0, =0xA8000400", "str r0, [r1, #0x00]", // Set LED as output /*"ldr r0, =0x20", "ldr r1, =0x40020000", "str r0, [r1, #0x14]"*/ // Turn LED on ); loop { write_volatile(led_state as *mut u32, 1 << 5); // Turn LED on for _ in 0..65000 { // Wait asm!("nop"); } write_volatile(led_state as *mut u32, 0x00); // Turn LED off for _ in 0..65000 { // Wait asm!("nop"); } } } } #[panic_handler] fn panic(_info: &PanicInfo) -> ! { loop {} }
汇编参考代码
.word 0x20000400 //Anfang Stackpionter -> SP .word 0x080000ed //reset_handler: Programmadresse nach reset .space 0xe4 //Reservierter Platz für Interrupt-Vektoren //###### Registerdefinitionen: Namen, Basisadresse/Offset ###### .equ RCC,0x40023800 //Basisadresse: System-Clock .equ AHBENR,0x1C //Offset: .equ GPIOA,0x40020000 //Basisadresse GPIOA .equ MODER,0x00 //Offset: MODER "Mode Register" .equ ODR,0x14 //Offset: ODR "Output Data Register" .global _start //für Linker wichtig: Definition, an welcher Adresse das Programm anfängt _start: ldr r1, =RCC //GPIOA,B,C aktivieren (7:...111) ldr r0, =0x00008007 // str r0, [r1,AHBENR] ldr r1, =0x40020000 ldr r0, =0xA8000400 str r0, [r1, #0x00] // Set LED as output main: bl led_on bl wait bl led_off bl wait b main //Endlosschleife Hauptprogramm- Sprung zu main wait: mov r3, #65000 mov r4, #0 for_0: add r4, r4, #1 cmp r4, r3 bne for_0 bx r14 led_on: ldr r0, =0x20 ldr r1, =0x40020000 str r0, [r1, #0x14] bx r14 led_off: ldr r0, =0x00 ldr r1, =0x40020000 str r0, [r1, #0x14] bx r14
解决方案及改进建议
问题核心在于Rust编译器的优化行为:你写的延迟循环中的asm!("nop")没有标记为volatile,编译器会认为这段代码没有副作用,直接将其优化掉,导致LED状态切换几乎没有延迟,看起来像是程序陷入死循环;或者编译器对循环的优化导致执行流程异常。
针对这个问题,有几种可靠的修改方式:
方式1:给asm!添加volatile属性
修改延迟循环中的asm!指令,明确告诉编译器该指令不能被优化:
for _ in 0..65000 { // Wait asm!("nop", volatile); }
方式2:使用自旋等待专用函数
Rust标准库提供了core::sync::atomic::spin_loop_hint(),专门用于自旋等待场景,编译器不会优化掉这个函数的调用,同时它比单纯的nop更高效(会提示CPU进入低功耗自旋状态):
首先引入该函数:
use core::sync::atomic::spin_loop_hint;
然后修改延迟循环:
for _ in 0..65000 { // Wait spin_loop_hint(); }
方式3:封装延迟函数并禁止内联
将延迟逻辑封装成独立函数,并添加#[inline(never)]属性防止编译器内联优化,同时配合volatile的asm:
#[inline(never)] unsafe fn wait() { for _ in 0..65000 { asm!("nop", volatile); } }
然后在主循环中调用:
loop { write_volatile(led_state as *mut u32, 1 << 5); // Turn LED on wait(); write_volatile(led_state as *mut u32, 0x00); // Turn LED off wait(); }
另外,验证了你的寄存器地址、位操作逻辑和汇编版本完全一致,这部分没有问题,修改延迟逻辑后即可实现正常闪烁。
内容的提问来源于stack exchange,提问作者HttpRafa
相关产品推荐
相关产品推荐

