嵌入式Rust中运行时将SPI引脚重配置为GPIO的实现方法
STM32H7 HAL中SPI MOSI引脚动态切换为GPIO输出的实现方案
问题背景
基于STM32H7xx HAL开发电路板时,存在SPI3外设控制多个组件的场景,但其中一个设备需要复用SPI3的MOSI引脚作为普通GPIO驱动。需要完成psu_13v8_ctrl函数中引脚重配置的TODO部分。
核心思路
要实现引脚的动态切换,必须将SPI实例拆分为总线控制部分和独立引脚,这样才能释放MOSI引脚进行GPIO配置;操作完成后再将引脚恢复为SPI复用功能,重新初始化SPI外设。
修改后的完整代码
use cortex_m::prelude::_embedded_hal_blocking_spi_Write; use stm32h7xx_hal::{ spi::{Enabled, Spi, SpiBus}, gpio::{Output, Pin, PushPull, Alternate, Speed}, pac::SPI3, embedded_hal::spi::Mode, }; // 根据实际硬件定义SPI3 MOSI引脚的复用类型 // 示例:PD6引脚,复用功能AF6对应SPI3 MOSI(需和硬件手册匹配) type Spi3MosiPin = Pin<'D', 6, Alternate<6, PushPull>>; pub struct ShiftRegSelector { up_ciod_sh_reg_sel0: Pin<'D', 10, Output<PushPull>>, up_ciod_sh_reg_sel1: Pin<'D', 11, Output<PushPull>>, up_ciod_sh_reg_n_latch: Pin<'E', 10, Output<PushPull>>, up_ciod_sh_reg_n_rst: Pin<'E', 15, Output<PushPull>>, spi_bus: SpiBus<SPI3, Enabled>, mosi: Spi3MosiPin, spi_mode: Mode, spi_baud_rate: u32, } impl ShiftRegSelector { pub fn new( up_ciod_sh_reg_sel0: Pin<'D', 10, Output<PushPull>>, up_ciod_sh_reg_sel1: Pin<'D', 11, Output<PushPull>>, up_ciod_sh_reg_n_latch: Pin<'E', 10, Output<PushPull>>, up_ciod_sh_reg_n_rst: Pin<'E', 15, Output<PushPull>>, spi: Spi<SPI3, Enabled>, ) -> Self { // 拆分SPI为总线控制单元和独立引脚 let (spi_bus, pins) = spi.split(); // 保存原始SPI配置参数,用于后续恢复 let spi_mode = spi.mode(); let spi_baud_rate = spi.baud_rate(); Self { up_ciod_sh_reg_sel0, up_ciod_sh_reg_sel1, up_ciod_sh_reg_n_latch, up_ciod_sh_reg_n_rst, spi_bus, mosi: pins.mosi, spi_mode, spi_baud_rate, } } pub fn psu_13v8_ctrl(&mut self, enable: bool) { // 1. 禁用SPI外设,释放MOSI引脚 let (spi_bus_disabled, mosi_pin) = self.spi_bus.disable(self.mosi); self.spi_bus = spi_bus_disabled; // 2. 将MOSI引脚重配置为GPIO推挽输出 let mut mosi_gpio = mosi_pin.into_push_pull_output(Speed::High); self.reset_mux(); // 3. 根据需求驱动GPIO电平 if enable { mosi_gpio.set_high(); } else { mosi_gpio.set_low(); } self.latch_mux(); self.reset_mux(); // 4. 将GPIO恢复为SPI复用功能(注意复用功能号要匹配硬件) let mosi_spi = mosi_gpio.into_alternate::<6, PushPull>(); // 5. 重新启用SPI外设,恢复原始配置 self.spi_bus = self.spi_bus.enable(mosi_spi, self.spi_mode, self.spi_baud_rate); self.mosi = self.spi_bus.pins().mosi; } fn latch_mux(&mut self) { self.up_ciod_sh_reg_sel0.set_high(); self.up_ciod_sh_reg_sel1.set_high(); self.up_ciod_sh_reg_n_latch.set_low(); } fn reset_mux(&mut self) { self.up_ciod_sh_reg_n_rst.set_high(); self.up_ciod_sh_reg_n_latch.set_high(); } pub fn something(&mut self) { // 临时组合SPI总线和引脚,执行写操作 let mut spi = Spi::from_bus(self.spi_bus, self.mosi); spi.write(&[0x11u8, 0x22, 0x33]).unwrap(); // 拆分回总线和引脚,保存回结构体 let (bus, pins) = spi.split(); self.spi_bus = bus; self.mosi = pins.mosi; } }
关键细节说明
- SPI拆分与重组:STM32H7 HAL的
Spi实例可以通过split()拆分为SpiBus(总线控制)和独立引脚,操作完成后用from_bus()重新组合,这是实现引脚动态切换的核心。 - 引脚复用配置:转换GPIO到SPI复用功能时,必须指定正确的Alternate功能号(示例中为AF6),需严格匹配硬件手册中SPI3 MOSI引脚的复用定义。
- 状态保存:必须保存SPI的原始模式(CPOL/CPHA)和波特率,否则恢复后的SPI无法正常工作。
- 引脚速度:配置GPIO输出时指定的速度(示例中为
Speed::High)需根据硬件驱动能力调整。
内容的提问来源于stack exchange,提问作者Dushara
相关产品推荐
相关产品推荐

