Rust嵌入式开发库stm32f4xx-hal的使用:针对STM32F4系列MCU的硬件抽象层(HAL)驱动开发

Rust嵌入式开发库stm32f4xx-hal的使用:针对STM32F4系列MCU的硬件抽象层(HAL)驱动开发

stm32f4xx-hal是一个针对STMicro STM32F4系列微控制器的硬件抽象层库,建立在外设访问API之上。它通过特性门(gate)选择不同的MCU型号,通常由板级支持包(BSP)指定。

支持的MCU型号

型号列表
stm32f401
stm32f405
stm32f407
stm32f410
stm32f411
stm32f412
stm32f413
stm32f415
stm32f417
stm32f423
stm32f427
stm32f429
stm32f437
stm32f439
stm32f446
stm32f469
stm32f479

可选特性

  • rtic1 - 支持RTICv1框架
  • rtic2 - 支持RTICv2框架(与rtic1不兼容,需要nightly编译器)
  • defmt - 为公共枚举和结构体实现defmt::Format
  • can - bxCAN外设支持
  • i2s - I2S外设支持
  • usb_fsusb_hs - USB OTG FS/HS外设支持
  • fsmc_lcd - 通过FMC/FSMC外设的LCD支持
  • sdio-host - SDIO外设支持
  • dsihost - DSI主机支持

项目设置

手动设置

  1. 创建新Rust项目: cargo init
  2. 添加以下依赖到Cargo.toml:
[dependencies]
embedded-hal = "0.2"
nb = "1"
cortex-m = "0.7"
cortex-m-rt = "0.7"
panic-halt = "0.2"

[dependencies.stm32f4xx-hal]
version = "0.22.1"
features = ["stm32f407"]  # 替换为你的微控制器型号
  1. 从stm32f4xx-hal仓库复制.cargo/config.tomlmemory.x到你的项目

快速开始

可以使用cargo generate快速创建空项目:

$ cargo generate --git https://github.com/burrbull/stm32-template/

示例代码

以下是基于delay-syst-blinky.rs的完整示例,展示了如何使用stm32f4xx-hal进行基本的LED闪烁:

#![no_std]
#![no_main]

// 导入必要的库
use panic_halt as _;
use cortex_m_rt::entry;
use stm32f4xx_hal::{
    prelude::*,
    stm32,
    delay::Delay,
};

#[entry]
fn main() -> ! {
    // 获取外设访问权限
    let dp = stm32::Peripherals::take().unwrap();
    let cp = cortex_m::Peripherals::take().unwrap();
    
    // 初始化时钟
    let rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.sysclk(48.mhz()).freeze();
    
    // 初始化GPIO
    let gpioa = dp.GPIOA.split();
    
    // 配置PA5(通常连接板上LED)为推挽输出
    let mut led = gpioa.pa5.into_push_pull_output();
    
    // 初始化延时器
    let mut delay = Delay::new(cp.SYST, clocks);
    
    // 主循环
    loop {
        // 切换LED状态
        led.toggle().unwrap();
        // 延时500ms
        delay.delay_ms(500_u32);
    }
}

完整示例扩展

以下是一个更完整的示例,展示了如何使用stm32f4xx-hal进行串口通信和按键输入检测:

#![no_std]
#![no_main]

use panic_halt as _;
use cortex_m_rt::entry;
use stm32f4xx_hal::{
    prelude::*,
    stm32,
    delay::Delay,
    serial::{Serial, Config},
    gpio::{Input, PullUp},
};

#[entry]
fn main() -> ! {
    // 获取外设访问权限
    let dp = stm32::Peripherals::take().unwrap();
    let cp = cortex_m::Peripherals::take().unwrap();

    // 初始化时钟
    let rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.sysclk(84.mhz()).freeze();

    // 初始化GPIO
    let gpioa = dp.GPIOA.split();
    let gpioc = dp.GPIOC.split();

    // 配置PA5(板上LED)为推挽输出
    let mut led = gpioa.pa5.into_push_pull_output();

    // 配置PC13(通常连接板上按键)为上拉输入
    let button = gpioc.pc13.into_pull_up_input();

    // 配置USART2 (PA2-TX, PA3-RX)
    let tx_pin = gpioa.pa2.into_alternate_af7();
    let rx_pin = gpioa.pa3.into_alternate_af7();
    let serial = Serial::usart2(
        dp.USART2,
        (tx_pin, rx_pin),
        Config::default().baudrate(115200.bps()),
        clocks,
    ).unwrap();

    // 将串口拆分为发送和接收部分
    let (mut tx, mut rx) = serial.split();

    // 初始化延时器
    let mut delay = Delay::new(cp.SYST, clocks);

    // 发送欢迎消息
    let message = b"Hello from STM32F4!\r\n";
    for &byte in message {
        block!(tx.write(byte)).ok();
    }

    // 主循环
    loop {
        // 检查按键状态
        if button.is_low().unwrap() {
            // 按键按下时发送消息
            let pressed_msg = b"Button pressed!\r\n";
            for &byte in pressed_msg {
                block!(tx.write(byte)).ok();
            }
            // 切换LED状态
            led.toggle().unwrap();
            // 延时消抖
            delay.delay_ms(50_u32);
        }

        // 检查串口接收数据
        if let Ok(byte) = rx.read() {
            // 回显接收到的字符
            block!(tx.write(byte)).ok();
            // 如果收到回车符,添加换行符
            if byte == b'\r' {
                block!(tx.write(b'\n')).ok();
            }
        }

        // 短延时减少CPU负载
        delay.delay_ms(10_u32);
    }
}

许可证

0-clause BSD license


1 回复

stm32f4xx-hal 使用指南:STM32F4系列MCU的硬件抽象层开发

简介

stm32f4xx-hal 是一个针对STM32F4系列微控制器的硬件抽象层(HAL)库,为Rust嵌入式开发提供了类型安全的API接口。它基于embedded-hal标准,支持STM32F401、F405、F407、F411、F412、F413、F415、F417、F423、F427、F429、F437、F439、F446、F469和F479等型号。

主要特性

  • 类型安全的GPIO配置
  • 外设初始化与配置
  • 中断处理支持
  • DMA支持
  • 定时器、PWM、ADC等功能
  • 支持RTIC框架

使用方法

1. 添加依赖

Cargo.toml中添加:

[dependencies]
stm32f4xx-hal = { version = "0.15.0", features = ["stm32f407", "rt"] }
cortex-m = "0.7.6"
cortex-m-rt = "0.7.3"

注意:features中的stm32f407需要替换为你实际使用的MCU型号。

2. 基本GPIO控制示例

#![no_std]
#![no_main]

use panic_halt as _;
use cortex_m_rt::entry;
use stm32f4xx_hal::{
    prelude::*,
    gpio::GpioExt,
    pac,
};

#[entry]
fn main() -> ! {
    // 获取外设访问权限
    let dp = pac::Peripherals::take().unwrap();
    
    // 配置GPIO
    let gpioc = dp.GPIOC.split();
    let mut led = gpioc.pc13.into_push_pull_output();
    
    loop {
        // 翻转LED状态
        led.toggle();
        
        // 简单延时
        cortex_m::asm::delay(8_000_000);
    }
}

3. 串口通信示例

use stm32f4xx_hal::{
    prelude::*,
    serial::{Serial, config::Config},
    pac,
};

#[entry]
fn main() -> ! {
    let dp = pac::Peripherals::take().unwrap();
    
    // 配置时钟
    let rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.sysclk(84.mhz()).freeze();
    
    // 配置GPIO
    let gpioa = dp.GPIOA.split();
    
    // 配置USART2
    let tx_pin = gpioa.pa2.into_alternate();
    let rx_pin = gpioa.pa3.into_alternate();
    
    let serial = Serial::usart2(
        dp.USART2,
        (tx_pin, rx_pin),
        Config::default().baudrate极速快3(115200.bps()),
        &clocks,
    ).unwrap();
    
    let (mut tx, mut _rx) = serial.split();
    
    // 发送字符串
    writeln!(tx, "Hello from STM32F4!\r").unwrap();
    
    loop {}
}

4. 定时器与PWM示例

use stm32f极速快34xx-hal::{
    prelude::*,
    timer::Timer,
    gpio::GpioExt,
    pac,
};

#[entry]
fn main() -> ! {
    let dp = pac::Peripherals::take().unwrap();
    
    // 配置时钟
    let rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.sysclk(84.mhz()).freeze();
    
    // 配置GPIO
    let gpioa = dp.GPIOA.split();
    let pwm_pin = gpioa.pa8.into_alternate();
    
    // 配置定时器1为PWM模式
    let mut pwm = Timer::tim1(dp.TIM1, 1.khz(), &clocks)
        .pwm(pwm_pin, 100.microseconds())
        .unwrap();
    
    let max_duty = pwm.get_max_duty();
    pwm.enable();
    
    // 呼吸灯效果
    let mut duty = 0;
    let mut step = 1;
    
    loop {
        pwm.set_duty(duty);
        cortex_m::asm::delay(10_000);
        
        duty += step;
        if duty == 0 || duty == max_duty {
            step = -step;
        }
    }
}

高级功能

1. 中断处理

use stm32f4xx-hal::{
    interrupt,
    gpio::{Input, PullUp, ExtiPin},
    pac,
};

#[interrupt]
fn EXTI0() {
    // 处理外部中断0
    unsafe {
        (*pac::EXTI::ptr()).pr.modify(|_, w| w.pr0().set_bit());
    }
    
    cortex_m::asm::bkpt();  // 调试断点
}

fn main() -> ! {
    let dp = pac::Peripherals::take().unwrap();
    
    // 配置GPIO和外部中断
    let gpioa = dp.GPIOA.split();
    let mut button = gpioa.pa0.into_pull_up_input();
    button.make_interrupt_source(&mut dp.SYSCFG);
    button.trigger_on_edge(&mut dp.EXTI, Edge::FALLING);
    button.enable_interrupt(&mut dp.EXTI);
    
    // 启用中断
    unsafe {
        cortex_m::peripheral::NVIC::unmask(pac::Interrupt::EXTI0);
    }
    
    loop {}
}

2. DMA传输

use stm32f4xx-hal::{
    dma::{self, StreamsTuple, StreamX, Transfer},
    pac,
};

fn main() -> ! {
    let dp = pac::Peripherals::take().unwrap();
    
    // 配置DMA
    let streams = StreamsTuple::new(dp.DMA2);
    let mut dma = StreamX极速快3::new(streams.0);
    
    // 创建源和目标缓冲区
    let src: &'static mut [u8; 128] = cortex_m::singleton!(: [u8; 128] = [0xAA; 128]).unwrap();
    let dst: &'static mut [u8; 128] = cortex_m::singleton!(: [u8; 128] = [0; 128]).unwrap();
    
    // 创建DMA传输
    let transfer = Transfer::init(
        dma,
        src,
        dst,
        None,
        dma::config::DmaConfig::default()
            .memory_increment(true)
            .peripheral_increment(false),
    );
    
    // 启动传输
    let transfer = transfer.start();
    
    // 等待传输完成
    while !transfer.get_transfer_complete_flag() {}
    
    // 释放资源
    let (_dma, _src, dst) = transfer.wait();
    
    loop {
        // 使用传输后的数据
        if dst[0] == 0xAA {
            cortex_m::asm::bkpt();
        }
    }
}

完整ADC示例

#![no_std]
#![no_main]

use panic_halt as _;
use cortex_m_rt::entry;
use stm32f4xx_hal::{
    prelude::*,
    pac,
    adc::{
        Adc,
        config::AdcConfig,
    },
    delay::Delay,
};

#[entry]
fn main() -> ! {
    // 获取外设访问权限
    let dp = pac::Peripherals::take().unwrap();
    let cp = cortex_m::Peripheral::take().unwrap();
    
    // 配置时钟
    let rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.sysclk(84.mhz()).freeze();
    
    // 配置GPIO
    let gpioa = dp.GPIOA.split();
    
    // 配置ADC引脚
    let adc_pin = gpioa.pa0.into_analog();
    
    // 创建延时器
    let mut delay = Delay::new(cp.SYST, clocks);
    
    // 配置ADC
    let mut adc = Adc::adc1(dp.ADC1, true, AdcConfig::default());
    
    loop {
        // 读取ADC值
        let sample: u16 = adc.read(&adc_pin).unwrap();
        
        // 简单延时
        delay.delay_ms(1000u32);
    }
}

完整I2C示例

#![no_std]
#![no_main]

use panic_halt as _;
use cortex_m_rt::entry;
use stm32f4xx_hal::{
    prelude::*,
    pac,
    i2c::I2c,
};

#[entry]
fn main() -> ! {
    // 获取外设访问权限
    let dp = pac::Peripherals::take().unwrap();
    
    // 配置时钟
    let rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.sysclk(84.mhz()).freeze();
    
    // 配置GPIO
    let gpiob = dp.GPIOB.split();
    
    // 配置I2C引脚
    let scl = gpiob.pb6.into_alternate().set_open_drain();
    let sda = gpiob.pb7.into_alternate().set_open_drain();
    
    // 配置I2C
    let mut i2c = I2c::i2c1(
        dp.I2C1,
        (scl, sda),
        100.khz(),
        clocks,
    );
    
    // I2C设备地址
    const DEVICE_ADDR: u8 = 0x68;
    // 寄存器地址
    const REGISTER_ADDR: u8 = 0x00;
    // 数据缓冲区
    let mut data = [0u8; 1];
    
    loop {
        // 读取寄存器
        i2c.write_read(DEVICE_ADDR, &[REGISTER_ADDR], &mut data).unwrap();
        
        // 写入寄存器
        i2c.write(DEVICE_ADDR, &[REGISTER_ADDR, data[0]]).unwrap();
    }
}

开发建议

  1. 选择合适的MCU型号:确保在Cargo.toml中正确指定你的STM32F4型号
  2. 利用Rust的类型系统:HAL库的设计充分利用了Rust的类型安全特性
  3. 参考官方示例:stm32f4xx-hal仓库中有丰富的示例代码
  4. 结合RTIC框架:对于实时性要求高的应用,考虑使用RTIC框架
  5. 调试工具:建议使用probe-rs和cargo-embed进行调试

常见问题

  1. 时钟配置错误:确保正确配置系统时钟和外设时钟
  2. GPIO模式不正确:根据用途选择正确的GPIO模式(输入、输出、复用功能等)
  3. 中断未触发:检查中断优先级、使能状态和触发条件
  4. DMA传输失败:确认源和目标地址、传输方向和缓冲区大小设置正确

stm32f4xx-hal库为STM32F4系列MCU提供了强大的Rust支持,结合Rust的安全特性和嵌入式开发的灵活性,是开发可靠嵌入式系统的优秀选择。

回到顶部