SPI是飞控中IMU的首选通信总线——高速、全双工、适合高频数据采集。FMT基于RT-Thread的SPI框架实现了总线管理、设备挂载、互斥锁保护、自动重配置等机制支持多个传感器共享同一条SPI总线。本文从SPI协议出发讲解FMT的SPI框架设计和驱动开发流程。1. SPI协议基础1.1 物理层SPI使用四根线SCLK时钟由主机控制MOSI主机出从机入MISO主机入从机出CS片选每个从机一根┌──────┐ MCU ─────┤ SCLK ├───── Sensor1 MCU ─────┤ MOSI ├───── Sensor1 MCU ─────┤ MISO ├───── Sensor1 MCU ─────┤ CS1 ├───── Sensor1 MCU ─────┤ CS2 ├───── Sensor2 └──────┘1.2 四种模式模式CPOLCPHA含义Mode 000空闲低电平第一个边沿采样Mode 101空闲低电平第二个边沿采样Mode 210空闲高电平第一个边沿采样Mode 311空闲高电平第二个边沿采样// spi.h#defineRT_SPI_CPHA(10)// 时钟相位#defineRT_SPI_CPOL(11)// 时钟极性#defineRT_SPI_MODE_0(0|0)// CPOL0, CPHA0#defineRT_SPI_MODE_1(0|RT_SPI_CPHA)// CPOL0, CPHA1#defineRT_SPI_MODE_2(RT_SPI_CPOL|0)// CPOL1, CPHA0#defineRT_SPI_MODE_3(RT_SPI_CPOL|RT_SPI_CPHA)// CPOL1, CPHA11.3 FMT中的SPI传感器注意以下芯片支持的模式指芯片数据手册标注的SPI兼容模式FMT实际配置指FMT驱动代码中实际设置的模式。FMT统一将所有SPI传感器配置为Mode 3时钟7MHz除TF卡使用Mode 0/10MHz外。传感器芯片芯片支持的模式FMT实际配置IMUBMI088Mode 0/3Mode 3, 7MHzIMUICM42688PMode 0/3Mode 3, 7MHzIMUICM20689Mode 0/3Mode 3IMUICM20600Mode 0/3Mode 3气压计MS5611Mode 0/3Mode 3, 7MHz气压计SPL06Mode 0/3Mode 3, 7MHz气压计BMP581Mode 0/3Mode 3, 7MHz磁力计BMM150Mode 0/3Mode 3, 7MHz磁力计RM3100Mode 0/3Mode 3, 1MHzFlashGD25QXXMode 0/3Mode 3, 7MHzFRAMRAMTRONMode 0/3Mode 3, 7MHzTF卡SPI TF CardMode 0/3Mode 0, 10MHz2. 框架架构┌─────────────────────────────────────────────┐ │ 传感器驱动层 │ │ BMI088 / ICM42688P / MS5611 / GD25QXX │ ├─────────────────────────────────────────────┤ │ SPI便捷函数 │ │ spi_read_reg8 / spi_write_reg8 │ │ spi_read_multi_reg8 / spi_read_bank_reg8 │ ├─────────────────────────────────────────────┤ │ SPI核心层 (spi_core) │ │ rt_spi_transfer / rt_spi_send_then_recv │ │ rt_spi_take_bus / rt_spi_release_bus │ │ 互斥锁 自动重配置 │ ├─────────────────────────────────────────────┤ │ SPI总线驱动 (BSP) │ │ configure / xfer 操作函数表 │ └─────────────────────────────────────────────┘3. 总线结构3.1 总线与设备// spi.hstructrt_spi_bus{structrt_deviceparent;// 设备基类conststructrt_spi_ops*ops;// 操作函数表structrt_mutexlock;// 互斥锁多设备共享保护structrt_spi_device*owner;// 当前总线所有者};structrt_spi_device{structrt_deviceparent;// 设备基类structrt_spi_bus*bus;// 所属总线structrt_spi_configurationconfig;// 设备配置};structrt_spi_configuration{rt_uint8_tmode;// SPI模式rt_uint8_tdata_width;// 数据宽度rt_uint16_treserved;rt_uint32_tmax_hz;// 最大时钟频率};3.2 操作函数表// spi.hstructrt_spi_ops{rt_err_t(*configure)(structrt_spi_device*dev,structrt_spi_configuration*config);rt_uint32_t(*xfer)(structrt_spi_device*dev,structrt_spi_message*message);};BSP层只需实现configure和xfer两个函数即可完成SPI总线驱动。4. 总线注册与设备挂载4.1 注册SPI总线在BSP初始化中注册SPI总线// drv_spi.c - SIEON S1staticstructrt_spi_opsstm32_spi_ops{configure,transfer};rt_err_tdrv_spi_init(void){// 注册SPI总线staticstructstm32_spi_busstm32_spi1;RT_TRY(stm32_spi_register(SPI1,stm32_spi1,spi1));staticstructstm32_spi_busstm32_spi2;RT_TRY(stm32_spi_register(SPI2,stm32_spi2,spi2));staticstructstm32_spi_busstm32_spi4;RT_TRY(stm32_spi_register(SPI4,stm32_spi4,spi4));staticstructstm32_spi_busstm32_spi5;RT_TRY(stm32_spi_register(SPI5,stm32_spi5,spi5));// ... 挂载SPI设备 ...returnRT_EOK;}stm32_spi_register内部调用rt_spi_bus_register完成总线注册、互斥锁初始化和GPIO配置。4.2 挂载SPI设备每个SPI从机需要挂载到总线上通过CS引脚区分。FMT使用stm32_spi_cs结构体封装CS的GPIO端口和引脚// drv_spi.c - SIEON S1 BSP层SPI设备挂载structstm32_spi_cs{GPIO_TypeDef*GPIOx;uint16_tGPIO_Pin;};// SPI1总线上挂载1个设备气压计staticstructrt_spi_devicert_spi1_device_1;staticstructstm32_spi_csstm32_spi1_cs_1;// PB1rt_spi_bus_attach_device(rt_spi1_device_1,spi1_dev1,spi1,(void*)stm32_spi1_cs_1);// SPI4总线上挂载4个设备IMU 磁力计staticstructrt_spi_devicert_spi4_device_1,rt_spi4_device_2,rt_spi4_device_3,rt_spi4_device_4;staticstructstm32_spi_csstm32_spi4_cs_1;// PC13 - BMI088 Gyrostaticstructstm32_spi_csstm32_spi4_cs_2;// PI8 - BMI088 Accelstaticstructstm32_spi_csstm32_spi4_cs_3;// PE3 - ICM42688Pstaticstructstm32_spi_csstm32_spi4_cs_4;// PI4 - BMM150rt_spi_bus_attach_device(rt_spi4_device_1,spi4_dev1,spi4,(void*)stm32_spi4_cs_1);rt_spi_bus_attach_device(rt_spi4_device_2,spi4_dev2,spi4,(void*)stm32_spi4_cs_2);rt_spi_bus_attach_device(rt_spi4_device_3,spi4_dev3,spi4,(void*)stm32_spi4_cs_3);rt_spi_bus_attach_device(rt_spi4_device_4,spi4_dev4,spi4,(void*)stm32_spi4_cs_4);// SPI5总线上挂载1个设备Flashstaticstructrt_spi_devicert_spi5_device_1;staticstructstm32_spi_csstm32_spi5_cs_1;// PF10rt_spi_bus_attach_device(rt_spi5_device_1,spi5_dev1,spi5,(void*)stm32_spi5_cs_1);BSP层的transfer函数通过user_data获取CS引脚执行GPIO操作// drv_spi.c - transfer函数中的CS控制structstm32_spi_cs*stm32_spi_csdevice-parent.user_data;if(message-cs_take){LL_GPIO_ResetOutputPin(stm32_spi_cs-GPIOx,stm32_spi_cs-GPIO_Pin);// 拉低CS}// ... SPI数据传输 ...if(message-cs_release){LL_GPIO_SetOutputPin(stm32_spi_cs-GPIOx,stm32_spi_cs-GPIO_Pin);// 拉高CS}4.3 配置SPI设备驱动初始化时配置SPI参数。注意FMT中所有SPI传感器统一使用Mode 3// icm42688p.crt_err_tdrv_icm42688_init(constchar*spi_dev_name,constchar*gyro_dev_name,constchar*accel_dev_name,uint32_tdev_id){spi_devrt_device_find(spi_dev_name);RT_ASSERT(spi_dev!NULL);/* 配置SPI参数 */structrt_spi_configurationcfg;cfg.data_width8;cfg.modeRT_SPI_MODE_3|RT_SPI_MSB;// FMT统一使用Mode 3cfg.max_hz7000000;// 7MHzstructrt_spi_device*spi_device_t(structrt_spi_device*)spi_dev;// 直接写入设备配置不等待下次传输时才生效spi_device_t-config.data_widthcfg.data_width;spi_device_t-config.modecfg.modeRT_SPI_MODE_MASK;spi_device_t-config.max_hzcfg.max_hz;RT_TRY(rt_spi_configure(spi_device_t,cfg));/* 底层初始化寄存器配置 */RT_TRY(low_level_init());/* 注册陀螺仪和加速度计HAL设备 */RT_TRY(hal_gyro_register(gyro_dev,gyro_dev_name,RT_DEVICE_FLAG_RDWR,(void*)dev_id));RT_TRY(hal_accel_register(accel_dev,accel_dev_name,RT_DEVICE_FLAG_RDWR,(void*)dev_id));returnRT_EOK;}5. 互斥锁与自动重配置5.1 多设备共享保护当多个设备共享同一条SPI总线时需要互斥访问// spi_core.c - rt_spi_transfer()rt_size_trt_spi_transfer(structrt_spi_device*device,constvoid*send_buf,void*recv_buf,rt_size_tlength){// 1. 获取总线锁resultrt_mutex_take((device-bus-lock),RT_WAITING_FOREVER);if(resultRT_EOK){// 2. 检查是否需要重配置if(device-bus-owner!device){resultdevice-bus-ops-configure(device,device-config);if(resultRT_EOK){device-bus-ownerdevice;// 更新总线所有者}}// 3. 执行传输message.send_bufsend_buf;message.recv_bufrecv_buf;message.lengthlength;message.cs_take1;message.cs_release1;resultdevice-bus-ops-xfer(device,message);// 4. 释放总线锁rt_mutex_release((device-bus-lock));}returnresult;}5.2 自动重配置为什么需要自动重配置不同传感器可能使用不同的SPI模式或时钟频率。当总线从设备A切换到设备B时需要重新配置总线参数。设备A (Mode 0, 10MHz) 使用总线 │ ▼ 设备B (Mode 3, 1MHz) 请求总线 │ ├── 检测到 owner ! deviceB ├── 重新调用 configure(deviceB, ...) ├── owner deviceB ▼ 设备B 正常传输owner字段记录当前总线配置对应的设备避免不必要的重配置。6. SPI消息结构// spi.hstructrt_spi_message{constvoid*send_buf;// 发送缓冲区void*recv_buf;// 接收缓冲区rt_size_tlength;// 数据长度structrt_spi_message*next;// 下一条消息链表unsignedcs_take:1;// 是否拉低CS开始传输unsignedcs_release:1;// 是否拉高CS结束传输};链表消息多条消息可以串联实现一次CS周期内的复杂传输。7. 便捷读写函数7.1 寄存器读写SPI传感器通常使用寄存器地址数据的通信格式地址最高位表示读/写方向// spi.h#defineSPI_DIR_READ0x80// 读方向位#defineSPI_DIR_WRITE0x00// 写方向位// 写寄存器rt_inlinert_err_tspi_write_reg8(rt_device_tspi_device,uint8_treg,uint8_tval){uint8_tbuffer[2];buffer[0]SPI_DIR_WRITE|reg;// 寄存器地址写方向buffer[1]val;// 数据rt_size_tw_bytert_spi_transfer((structrt_spi_device*)spi_device,buffer,NULL,2);return(w_byte2)?RT_EOK:RT_ERROR;}// 读寄存器rt_inlinert_err_tspi_read_reg8(rt_device_tspi_device,uint8_treg,uint8_t*buffer){uint8_treg_addr;reg_addrSPI_DIR_READ|reg;// 寄存器地址读方向returnrt_spi_send_then_recv((structrt_spi_device*)spi_device,reg_addr,1,buffer,1);}// 读多个连续寄存器rt_inlinert_err_tspi_read_multi_reg8(rt_device_tspi_device,uint8_treg,uint8_t*buffer,uint8_tlen){uint8_treg_addr;reg_addrSPI_DIR_READ|reg;returnrt_spi_send_then_recv((structrt_spi_device*)spi_device,reg_addr,1,buffer,len);}7.2 Bank寄存器读写ICM42688P等传感器使用Bank选择寄存器需要先切换Bank再读写// spi.hrt_inlinert_err_tspi_read_bank_reg8(rt_device_tspi_device,uint8_tbank_reg,uint8_tbank,uint8_treg,uint8_t*buffer){spi_write_reg8(spi_device,bank_reg,bank);// 切换Bankreturnspi_read_reg8(spi_device,reg,buffer);// 读取寄存器}8. 驱动开发流程以ICM42688P为例完整的SPI驱动开发流程8.1 查找设备spi_devrt_device_find(spi4_dev3);8.2 配置SPI参数structrt_spi_configurationcfg;cfg.data_width8;cfg.modeRT_SPI_MODE_3|RT_SPI_MSB;// FMT统一使用Mode 3cfg.max_hz7000000;// 7MHzrt_spi_configure((structrt_spi_device*)spi_dev,cfg);8.3 读取设备IDuint8_tdev_id;RT_TRY(spi_read_reg8(spi_dev,REG_WHO_AM_I,dev_id));if(dev_id!0x47){DRV_DBG(ICM42688P wrong device id:0x%x\n,dev_id);returnRT_ENOSYS;}8.4 配置寄存器// 切换到Bank0spi_write_reg8(spi_dev,REG_REG_BANK_SEL,0);// 配置陀螺仪量程和采样率spi_write_reg8(spi_dev,REG_PWR_MGMT0,0x0F);// 使能陀螺仪加速度计spi_write_reg8(spi_dev,REG_GYRO_CONFIG0,...);spi_write_reg8(spi_dev,REG_ACCEL_CONFIG0,...);8.5 读取数据uint8_traw[6];floatgyro[3];// 读取陀螺仪6字节原始数据RT_TRY(spi_read_multi_reg8(spi_dev,REG_GYRO_DATA_X1,raw,6));// 使用int16_t_from_bytes辅助函数拼接为16位有符号整数// 定义在 conversion.h: int16_t int16_t_from_bytes(uint8_t bytes[]);gyro[0]int16_t_from_bytes(raw[0]);gyro[1]int16_t_from_bytes(raw[2]);gyro[2]int16_t_from_bytes(raw[4]);8.6 写后读校验FMT的IMU驱动使用写后读校验确保寄存器配置正确。不同传感器的实现略有差异ICM42688P版本读一次校验// icm42688p.cstaticrt_err_t__write_checked_reg(rt_device_tspi_device,rt_uint8_treg,rt_uint8_tval){rt_uint8_tr_val;RT_TRY(spi_write_reg8(spi_device,reg,val));RT_TRY(spi_read_reg8(spi_device,reg,r_val));return(r_valval)?RT_EOK:RT_ERROR;}BMI088版本读两次校验因为BMI088加速度计部分的SPI读操作会先返回一个dummy byte// bmi088.cstaticrt_err_t__write_checked_reg(rt_device_tspi_device,rt_uint8_treg,rt_uint8_tval){rt_uint8_tr_val;RT_TRY(spi_write_reg8(spi_device,reg,val));/* BMI088加速度计读操作会先发送一个dummy字节 之后才是实际的寄存器内容 */RT_TRY(spi_read_reg8(spi_device,reg,r_val));RT_TRY(spi_read_reg8(spi_device,reg,r_val));return(r_valval)?RT_EOK:RT_ERROR;}9. SIEON S1的SPI总线布局SPI1 ─── SPL06 (气压计) └── spi1_dev1 (PB1) SPI2 ─── EXT_CS1 (外部扩展) ├── spi2_dev1 (PG10) ├── EXT_CS2 (外部扩展) ├── spi2_dev2 (PG11) └── EXT_CS3 (外部扩展) └── spi2_dev3 (PG12) SPI4 ─── BMI088 Gyro (IMU) ├── spi4_dev1 (PC13) ├── BMI088 Accel (IMU) ├── spi4_dev2 (PI8) ├── ICM42688P (冗余IMU) ├── spi4_dev3 (PE3) └── BMM150 (磁力计) └── spi4_dev4 (PI4) SPI5 ─── GD25QXX (Flash) └── spi5_dev1 (PF10)SPI4总线上挂载了4个设备2个IMU芯片共3个传感器 1个磁力计通过互斥锁和自动重配置机制实现安全共享。SPI2提供3个外部扩展CS引脚可用于外接SPI传感器。10. 与I2C对比特性SPII2C速度高MHz级低KHz级线数4根SCLK/MOSI/MISO/CS2根SCL/SDA全双工是否多设备CS片选地址寻址适用场景IMU、Flash、高频传感器低速外设、导出接口在SIEON S1中板载IMUBMI088/ICM42688P、磁力计BMM150、气压计SPL06和FlashGD25QXX全部使用SPI。I2C总线主要用于外部扩展接口如注释中提到的QMC5883L磁力计i2c1_dev2。总结FMT SPI总线框架的核心设计要点设计原因实现总线/设备分离多个传感器共享SPI总线rt_spi_busrt_spi_device互斥锁保护防止并发访问冲突rt_mutex lock自动重配置不同设备使用不同模式/频率owner字段检测操作函数表BSP层只需实现底层驱动configurexfer便捷读写函数简化寄存器操作spi_read_reg8/write_reg8Bank寄存器支持ICM系列多Bank访问spi_read_bank_reg8
FMT开源飞控开发(十一):SPI总线框架与多设备共享
发布时间:2026/6/30 1:02:32
SPI是飞控中IMU的首选通信总线——高速、全双工、适合高频数据采集。FMT基于RT-Thread的SPI框架实现了总线管理、设备挂载、互斥锁保护、自动重配置等机制支持多个传感器共享同一条SPI总线。本文从SPI协议出发讲解FMT的SPI框架设计和驱动开发流程。1. SPI协议基础1.1 物理层SPI使用四根线SCLK时钟由主机控制MOSI主机出从机入MISO主机入从机出CS片选每个从机一根┌──────┐ MCU ─────┤ SCLK ├───── Sensor1 MCU ─────┤ MOSI ├───── Sensor1 MCU ─────┤ MISO ├───── Sensor1 MCU ─────┤ CS1 ├───── Sensor1 MCU ─────┤ CS2 ├───── Sensor2 └──────┘1.2 四种模式模式CPOLCPHA含义Mode 000空闲低电平第一个边沿采样Mode 101空闲低电平第二个边沿采样Mode 210空闲高电平第一个边沿采样Mode 311空闲高电平第二个边沿采样// spi.h#defineRT_SPI_CPHA(10)// 时钟相位#defineRT_SPI_CPOL(11)// 时钟极性#defineRT_SPI_MODE_0(0|0)// CPOL0, CPHA0#defineRT_SPI_MODE_1(0|RT_SPI_CPHA)// CPOL0, CPHA1#defineRT_SPI_MODE_2(RT_SPI_CPOL|0)// CPOL1, CPHA0#defineRT_SPI_MODE_3(RT_SPI_CPOL|RT_SPI_CPHA)// CPOL1, CPHA11.3 FMT中的SPI传感器注意以下芯片支持的模式指芯片数据手册标注的SPI兼容模式FMT实际配置指FMT驱动代码中实际设置的模式。FMT统一将所有SPI传感器配置为Mode 3时钟7MHz除TF卡使用Mode 0/10MHz外。传感器芯片芯片支持的模式FMT实际配置IMUBMI088Mode 0/3Mode 3, 7MHzIMUICM42688PMode 0/3Mode 3, 7MHzIMUICM20689Mode 0/3Mode 3IMUICM20600Mode 0/3Mode 3气压计MS5611Mode 0/3Mode 3, 7MHz气压计SPL06Mode 0/3Mode 3, 7MHz气压计BMP581Mode 0/3Mode 3, 7MHz磁力计BMM150Mode 0/3Mode 3, 7MHz磁力计RM3100Mode 0/3Mode 3, 1MHzFlashGD25QXXMode 0/3Mode 3, 7MHzFRAMRAMTRONMode 0/3Mode 3, 7MHzTF卡SPI TF CardMode 0/3Mode 0, 10MHz2. 框架架构┌─────────────────────────────────────────────┐ │ 传感器驱动层 │ │ BMI088 / ICM42688P / MS5611 / GD25QXX │ ├─────────────────────────────────────────────┤ │ SPI便捷函数 │ │ spi_read_reg8 / spi_write_reg8 │ │ spi_read_multi_reg8 / spi_read_bank_reg8 │ ├─────────────────────────────────────────────┤ │ SPI核心层 (spi_core) │ │ rt_spi_transfer / rt_spi_send_then_recv │ │ rt_spi_take_bus / rt_spi_release_bus │ │ 互斥锁 自动重配置 │ ├─────────────────────────────────────────────┤ │ SPI总线驱动 (BSP) │ │ configure / xfer 操作函数表 │ └─────────────────────────────────────────────┘3. 总线结构3.1 总线与设备// spi.hstructrt_spi_bus{structrt_deviceparent;// 设备基类conststructrt_spi_ops*ops;// 操作函数表structrt_mutexlock;// 互斥锁多设备共享保护structrt_spi_device*owner;// 当前总线所有者};structrt_spi_device{structrt_deviceparent;// 设备基类structrt_spi_bus*bus;// 所属总线structrt_spi_configurationconfig;// 设备配置};structrt_spi_configuration{rt_uint8_tmode;// SPI模式rt_uint8_tdata_width;// 数据宽度rt_uint16_treserved;rt_uint32_tmax_hz;// 最大时钟频率};3.2 操作函数表// spi.hstructrt_spi_ops{rt_err_t(*configure)(structrt_spi_device*dev,structrt_spi_configuration*config);rt_uint32_t(*xfer)(structrt_spi_device*dev,structrt_spi_message*message);};BSP层只需实现configure和xfer两个函数即可完成SPI总线驱动。4. 总线注册与设备挂载4.1 注册SPI总线在BSP初始化中注册SPI总线// drv_spi.c - SIEON S1staticstructrt_spi_opsstm32_spi_ops{configure,transfer};rt_err_tdrv_spi_init(void){// 注册SPI总线staticstructstm32_spi_busstm32_spi1;RT_TRY(stm32_spi_register(SPI1,stm32_spi1,spi1));staticstructstm32_spi_busstm32_spi2;RT_TRY(stm32_spi_register(SPI2,stm32_spi2,spi2));staticstructstm32_spi_busstm32_spi4;RT_TRY(stm32_spi_register(SPI4,stm32_spi4,spi4));staticstructstm32_spi_busstm32_spi5;RT_TRY(stm32_spi_register(SPI5,stm32_spi5,spi5));// ... 挂载SPI设备 ...returnRT_EOK;}stm32_spi_register内部调用rt_spi_bus_register完成总线注册、互斥锁初始化和GPIO配置。4.2 挂载SPI设备每个SPI从机需要挂载到总线上通过CS引脚区分。FMT使用stm32_spi_cs结构体封装CS的GPIO端口和引脚// drv_spi.c - SIEON S1 BSP层SPI设备挂载structstm32_spi_cs{GPIO_TypeDef*GPIOx;uint16_tGPIO_Pin;};// SPI1总线上挂载1个设备气压计staticstructrt_spi_devicert_spi1_device_1;staticstructstm32_spi_csstm32_spi1_cs_1;// PB1rt_spi_bus_attach_device(rt_spi1_device_1,spi1_dev1,spi1,(void*)stm32_spi1_cs_1);// SPI4总线上挂载4个设备IMU 磁力计staticstructrt_spi_devicert_spi4_device_1,rt_spi4_device_2,rt_spi4_device_3,rt_spi4_device_4;staticstructstm32_spi_csstm32_spi4_cs_1;// PC13 - BMI088 Gyrostaticstructstm32_spi_csstm32_spi4_cs_2;// PI8 - BMI088 Accelstaticstructstm32_spi_csstm32_spi4_cs_3;// PE3 - ICM42688Pstaticstructstm32_spi_csstm32_spi4_cs_4;// PI4 - BMM150rt_spi_bus_attach_device(rt_spi4_device_1,spi4_dev1,spi4,(void*)stm32_spi4_cs_1);rt_spi_bus_attach_device(rt_spi4_device_2,spi4_dev2,spi4,(void*)stm32_spi4_cs_2);rt_spi_bus_attach_device(rt_spi4_device_3,spi4_dev3,spi4,(void*)stm32_spi4_cs_3);rt_spi_bus_attach_device(rt_spi4_device_4,spi4_dev4,spi4,(void*)stm32_spi4_cs_4);// SPI5总线上挂载1个设备Flashstaticstructrt_spi_devicert_spi5_device_1;staticstructstm32_spi_csstm32_spi5_cs_1;// PF10rt_spi_bus_attach_device(rt_spi5_device_1,spi5_dev1,spi5,(void*)stm32_spi5_cs_1);BSP层的transfer函数通过user_data获取CS引脚执行GPIO操作// drv_spi.c - transfer函数中的CS控制structstm32_spi_cs*stm32_spi_csdevice-parent.user_data;if(message-cs_take){LL_GPIO_ResetOutputPin(stm32_spi_cs-GPIOx,stm32_spi_cs-GPIO_Pin);// 拉低CS}// ... SPI数据传输 ...if(message-cs_release){LL_GPIO_SetOutputPin(stm32_spi_cs-GPIOx,stm32_spi_cs-GPIO_Pin);// 拉高CS}4.3 配置SPI设备驱动初始化时配置SPI参数。注意FMT中所有SPI传感器统一使用Mode 3// icm42688p.crt_err_tdrv_icm42688_init(constchar*spi_dev_name,constchar*gyro_dev_name,constchar*accel_dev_name,uint32_tdev_id){spi_devrt_device_find(spi_dev_name);RT_ASSERT(spi_dev!NULL);/* 配置SPI参数 */structrt_spi_configurationcfg;cfg.data_width8;cfg.modeRT_SPI_MODE_3|RT_SPI_MSB;// FMT统一使用Mode 3cfg.max_hz7000000;// 7MHzstructrt_spi_device*spi_device_t(structrt_spi_device*)spi_dev;// 直接写入设备配置不等待下次传输时才生效spi_device_t-config.data_widthcfg.data_width;spi_device_t-config.modecfg.modeRT_SPI_MODE_MASK;spi_device_t-config.max_hzcfg.max_hz;RT_TRY(rt_spi_configure(spi_device_t,cfg));/* 底层初始化寄存器配置 */RT_TRY(low_level_init());/* 注册陀螺仪和加速度计HAL设备 */RT_TRY(hal_gyro_register(gyro_dev,gyro_dev_name,RT_DEVICE_FLAG_RDWR,(void*)dev_id));RT_TRY(hal_accel_register(accel_dev,accel_dev_name,RT_DEVICE_FLAG_RDWR,(void*)dev_id));returnRT_EOK;}5. 互斥锁与自动重配置5.1 多设备共享保护当多个设备共享同一条SPI总线时需要互斥访问// spi_core.c - rt_spi_transfer()rt_size_trt_spi_transfer(structrt_spi_device*device,constvoid*send_buf,void*recv_buf,rt_size_tlength){// 1. 获取总线锁resultrt_mutex_take((device-bus-lock),RT_WAITING_FOREVER);if(resultRT_EOK){// 2. 检查是否需要重配置if(device-bus-owner!device){resultdevice-bus-ops-configure(device,device-config);if(resultRT_EOK){device-bus-ownerdevice;// 更新总线所有者}}// 3. 执行传输message.send_bufsend_buf;message.recv_bufrecv_buf;message.lengthlength;message.cs_take1;message.cs_release1;resultdevice-bus-ops-xfer(device,message);// 4. 释放总线锁rt_mutex_release((device-bus-lock));}returnresult;}5.2 自动重配置为什么需要自动重配置不同传感器可能使用不同的SPI模式或时钟频率。当总线从设备A切换到设备B时需要重新配置总线参数。设备A (Mode 0, 10MHz) 使用总线 │ ▼ 设备B (Mode 3, 1MHz) 请求总线 │ ├── 检测到 owner ! deviceB ├── 重新调用 configure(deviceB, ...) ├── owner deviceB ▼ 设备B 正常传输owner字段记录当前总线配置对应的设备避免不必要的重配置。6. SPI消息结构// spi.hstructrt_spi_message{constvoid*send_buf;// 发送缓冲区void*recv_buf;// 接收缓冲区rt_size_tlength;// 数据长度structrt_spi_message*next;// 下一条消息链表unsignedcs_take:1;// 是否拉低CS开始传输unsignedcs_release:1;// 是否拉高CS结束传输};链表消息多条消息可以串联实现一次CS周期内的复杂传输。7. 便捷读写函数7.1 寄存器读写SPI传感器通常使用寄存器地址数据的通信格式地址最高位表示读/写方向// spi.h#defineSPI_DIR_READ0x80// 读方向位#defineSPI_DIR_WRITE0x00// 写方向位// 写寄存器rt_inlinert_err_tspi_write_reg8(rt_device_tspi_device,uint8_treg,uint8_tval){uint8_tbuffer[2];buffer[0]SPI_DIR_WRITE|reg;// 寄存器地址写方向buffer[1]val;// 数据rt_size_tw_bytert_spi_transfer((structrt_spi_device*)spi_device,buffer,NULL,2);return(w_byte2)?RT_EOK:RT_ERROR;}// 读寄存器rt_inlinert_err_tspi_read_reg8(rt_device_tspi_device,uint8_treg,uint8_t*buffer){uint8_treg_addr;reg_addrSPI_DIR_READ|reg;// 寄存器地址读方向returnrt_spi_send_then_recv((structrt_spi_device*)spi_device,reg_addr,1,buffer,1);}// 读多个连续寄存器rt_inlinert_err_tspi_read_multi_reg8(rt_device_tspi_device,uint8_treg,uint8_t*buffer,uint8_tlen){uint8_treg_addr;reg_addrSPI_DIR_READ|reg;returnrt_spi_send_then_recv((structrt_spi_device*)spi_device,reg_addr,1,buffer,len);}7.2 Bank寄存器读写ICM42688P等传感器使用Bank选择寄存器需要先切换Bank再读写// spi.hrt_inlinert_err_tspi_read_bank_reg8(rt_device_tspi_device,uint8_tbank_reg,uint8_tbank,uint8_treg,uint8_t*buffer){spi_write_reg8(spi_device,bank_reg,bank);// 切换Bankreturnspi_read_reg8(spi_device,reg,buffer);// 读取寄存器}8. 驱动开发流程以ICM42688P为例完整的SPI驱动开发流程8.1 查找设备spi_devrt_device_find(spi4_dev3);8.2 配置SPI参数structrt_spi_configurationcfg;cfg.data_width8;cfg.modeRT_SPI_MODE_3|RT_SPI_MSB;// FMT统一使用Mode 3cfg.max_hz7000000;// 7MHzrt_spi_configure((structrt_spi_device*)spi_dev,cfg);8.3 读取设备IDuint8_tdev_id;RT_TRY(spi_read_reg8(spi_dev,REG_WHO_AM_I,dev_id));if(dev_id!0x47){DRV_DBG(ICM42688P wrong device id:0x%x\n,dev_id);returnRT_ENOSYS;}8.4 配置寄存器// 切换到Bank0spi_write_reg8(spi_dev,REG_REG_BANK_SEL,0);// 配置陀螺仪量程和采样率spi_write_reg8(spi_dev,REG_PWR_MGMT0,0x0F);// 使能陀螺仪加速度计spi_write_reg8(spi_dev,REG_GYRO_CONFIG0,...);spi_write_reg8(spi_dev,REG_ACCEL_CONFIG0,...);8.5 读取数据uint8_traw[6];floatgyro[3];// 读取陀螺仪6字节原始数据RT_TRY(spi_read_multi_reg8(spi_dev,REG_GYRO_DATA_X1,raw,6));// 使用int16_t_from_bytes辅助函数拼接为16位有符号整数// 定义在 conversion.h: int16_t int16_t_from_bytes(uint8_t bytes[]);gyro[0]int16_t_from_bytes(raw[0]);gyro[1]int16_t_from_bytes(raw[2]);gyro[2]int16_t_from_bytes(raw[4]);8.6 写后读校验FMT的IMU驱动使用写后读校验确保寄存器配置正确。不同传感器的实现略有差异ICM42688P版本读一次校验// icm42688p.cstaticrt_err_t__write_checked_reg(rt_device_tspi_device,rt_uint8_treg,rt_uint8_tval){rt_uint8_tr_val;RT_TRY(spi_write_reg8(spi_device,reg,val));RT_TRY(spi_read_reg8(spi_device,reg,r_val));return(r_valval)?RT_EOK:RT_ERROR;}BMI088版本读两次校验因为BMI088加速度计部分的SPI读操作会先返回一个dummy byte// bmi088.cstaticrt_err_t__write_checked_reg(rt_device_tspi_device,rt_uint8_treg,rt_uint8_tval){rt_uint8_tr_val;RT_TRY(spi_write_reg8(spi_device,reg,val));/* BMI088加速度计读操作会先发送一个dummy字节 之后才是实际的寄存器内容 */RT_TRY(spi_read_reg8(spi_device,reg,r_val));RT_TRY(spi_read_reg8(spi_device,reg,r_val));return(r_valval)?RT_EOK:RT_ERROR;}9. SIEON S1的SPI总线布局SPI1 ─── SPL06 (气压计) └── spi1_dev1 (PB1) SPI2 ─── EXT_CS1 (外部扩展) ├── spi2_dev1 (PG10) ├── EXT_CS2 (外部扩展) ├── spi2_dev2 (PG11) └── EXT_CS3 (外部扩展) └── spi2_dev3 (PG12) SPI4 ─── BMI088 Gyro (IMU) ├── spi4_dev1 (PC13) ├── BMI088 Accel (IMU) ├── spi4_dev2 (PI8) ├── ICM42688P (冗余IMU) ├── spi4_dev3 (PE3) └── BMM150 (磁力计) └── spi4_dev4 (PI4) SPI5 ─── GD25QXX (Flash) └── spi5_dev1 (PF10)SPI4总线上挂载了4个设备2个IMU芯片共3个传感器 1个磁力计通过互斥锁和自动重配置机制实现安全共享。SPI2提供3个外部扩展CS引脚可用于外接SPI传感器。10. 与I2C对比特性SPII2C速度高MHz级低KHz级线数4根SCLK/MOSI/MISO/CS2根SCL/SDA全双工是否多设备CS片选地址寻址适用场景IMU、Flash、高频传感器低速外设、导出接口在SIEON S1中板载IMUBMI088/ICM42688P、磁力计BMM150、气压计SPL06和FlashGD25QXX全部使用SPI。I2C总线主要用于外部扩展接口如注释中提到的QMC5883L磁力计i2c1_dev2。总结FMT SPI总线框架的核心设计要点设计原因实现总线/设备分离多个传感器共享SPI总线rt_spi_busrt_spi_device互斥锁保护防止并发访问冲突rt_mutex lock自动重配置不同设备使用不同模式/频率owner字段检测操作函数表BSP层只需实现底层驱动configurexfer便捷读写函数简化寄存器操作spi_read_reg8/write_reg8Bank寄存器支持ICM系列多Bank访问spi_read_bank_reg8