You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 05:45:10