基于TI MSPM0G3507的MS5611气压传感器I2C驱动移植与高度测量实战

基于TI MSPM0G3507的MS5611气压传感器I2C驱动移植与高度测量实战 基于TI MSPM0G3507的MS5611气压传感器I2C驱动移植与高度测量实战最近在做一个无人机项目需要测量飞行高度选来选去用了MS5611这款高精度气压传感器。手头正好有TI的MSPM0G3507开发板立创开发板但网上找的例程大多是STM32的直接拿来用不了。折腾了两天终于把驱动成功移植过来了过程踩了不少坑也积累了一些经验。今天我就把从硬件连接到代码调试的完整过程手把手分享给大家如果你也在用MSPM0系列芯片做气压测量这篇教程应该能帮你省下不少时间。咱们的目标很明确让MSPM0G3507通过I2C总线读取GY-63模块MS5611传感器的气压和温度数据并换算成可用的数值。我会先讲讲传感器和开发板怎么接线然后深入分析MS5611的通信协议最后把完整的、可以直接用的C代码贴出来并解释每一部分是干什么的。1. 硬件准备与连接工欲善其事必先利其器。首先得把硬件搞清楚、连正确。1.1 认识我们的“主角”MS5611-01BA03模块我用的模块是市面上常见的GY-63核心芯片是MS5611-01BA03。先看看它的关键参数这对后续编程和理解数据范围很重要参数数值说明工作电压1.8V ~ 3.6V重要必须用3.3V供电与MSPM0G3507的IO电平匹配。通信接口I2C (或 SPI)本教程使用I2C模式。气压测量范围10 ~ 1200 mbar相当于海平面以下到约9000米高度。气压精度±1.5 mbar在常温下高度分辨率可达十几厘米。温度测量范围-40℃ ~ 85℃温度精度±0.8℃引脚数量5 Pin (常用)VCC, GND, SCL, SDA, PS。模块上有一个关键的PS引脚它决定了传感器的工作模式PS接高电平默认模块工作在I2C模式。我们的原理图上PS通过上拉电阻接了VCC所以默认就是I2C模式直接用就行。PS接低电平模块工作在SPI模式。在I2C模式下器件地址由CSB引脚决定在GY-63模块上这个引脚可能被标记为SDO或SA0CSB引脚接高电平时器件地址为0xEE(写) /0xEF(读)。CSB引脚接低电平时器件地址为0xEC(写) /0xED(读)。我的模块上CSB也是通过电阻上拉到VCC的所以地址就是0xEE。这个地址在代码里会用到。1.2 连接MSPM0G3507开发板接线非常简单只需要4根线。MSPM0G3507开发板上的I2C接口引脚是固定的我们需要在SysConfig工具中配置。这里假设我们使用I2C0。MSPM0G3507引脚GY-63模块引脚连接线说明3.3VVCC电源正极GNDGND电源地PA10(配置为I2C0 SCL)SCLI2C时钟线PA9(配置为I2C0 SDA)SDAI2C数据线注意I2C总线需要上拉电阻通常模块上已经集成了4.7kΩ的上拉电阻。如果没有你需要在SCL和SDA线上各接一个4.7kΩ电阻到3.3V。2. 软件配置使用SysConfig工具TI的MSPM0系列推荐使用图形化的SysConfig工具来配置引脚和外设这比直接写寄存器方便多了。打开工程在你的CCS或IAR工程中找到并双击empty.syscfg文件。添加GPIO配置在打开的界面中点击ADD按钮选择GPIO。我们需要添加两个GPIO配置分别对应SCL (PA10) 和 SDA (PA9)。在配置中将引脚功能选择为I2C0 SCL和I2C0 SDA。模式选择为Open Drain开漏这是I2C标准要求的。保存并生成代码配置完成后点击保存。系统可能会提示你文件将被覆盖一定要选择Yes to All。处理编译警告保存后点击编译可能会因为一些依赖关系报错先不用管它。关键步骤是让SysConfig生成配置文件。包含头文件SysConfig工具会自动生成一个ti_msp_dl_config.h文件里面定义了GPIO_SDA_PIN、GPIO_SCL_PIN等宏。我们的工程已经通过board.h包含了这个文件所以在代码里直接#include board.h就能使用这些宏定义了。这个过程相当于用图形界面“画”出了硬件连接图并自动生成了对应的C语言宏定义省去了我们手动查数据手册、定义引脚号的麻烦。3. MS5611驱动代码深度解析与移植这是最核心的部分。网上很多例程的I2C底层驱动IIC_StartIIC_Stop等是依赖特定硬件延时或寄存器操作的我们需要将其适配到MSPM0的GPIO操作上。3.1 头文件 (bsp_ms5611.h) 的移植头文件主要完成宏定义和函数声明。最关键的是将通用的SDA、SCL操作映射到MSPM0的GPIO驱动函数DL库。#ifndef _BSP_MS5611_H_ #define _BSP_MS5611_H_ #include board.h // 包含SysConfig生成的引脚定义 // 将SDA引脚设置为输出模式主机驱动数据线时 #define SDA_OUT() { \ DL_GPIO_initDigitalOutput(GPIO_SDA_IOMUX); \ DL_GPIO_setPins(GPIO_PORT, GPIO_SDA_PIN); \ DL_GPIO_enableOutput(GPIO_PORT, GPIO_SDA_PIN); \ } // 将SDA引脚设置为输入模式主机读取数据线时 #define SDA_IN() { DL_GPIO_initDigitalInput(GPIO_SDA_IOMUX); } // 读取SDA引脚的电平状态 #define SDA_GET() ( ( ( DL_GPIO_readPins(GPIO_PORT,GPIO_SDA_PIN) GPIO_SDA_PIN ) 0 ) ? 1 : 0 ) // 控制SDA和SCL引脚输出高(1)或低(0)电平 #define SDA(x) ( (x) ? (DL_GPIO_setPins(GPIO_PORT,GPIO_SDA_PIN)) : (DL_GPIO_clearPins(GPIO_PORT,GPIO_SDA_PIN)) ) #define SCL(x) ( (x) ? (DL_GPIO_setPins(GPIO_PORT,GPIO_SCL_PIN)) : (DL_GPIO_clearPins(GPIO_PORT,GPIO_SCL_PIN)) ) // 函数声明 char MS5611_Reset(void); void MS5611_Read_PROM(void); float Get_TEMP(void); float Get_pressure(void); #endif代码解读DL_GPIO_开头的函数是TI MSPM0 DriverLib库提供的用于操作GPIO比直接操作寄存器更简单安全。GPIO_SDA_IOMUXGPIO_PORTGPIO_SDA_PIN这些宏都在ti_msp_dl_config.h中由SysConfig定义好了我们直接使用即可。SDA(x)和SCL(x)宏用三元运算符实现了一个简洁的引脚电平设置。3.2 核心驱动函数 (bsp_ms5611.c) 的实现驱动代码较长我们分段讲解关键函数。完整的代码我会在最后提供。首先是I2C底层时序模拟。MS5611的I2C通信速率不高我们可以用GPIO模拟Bit-banging的方式这样更灵活也便于理解协议。// I2C起始信号SCL高电平时SDA产生一个下降沿 void IIC_Start(void) { SDA_OUT(); SDA(1); delay_us(5); SCL(1); delay_us(5); SDA(0); delay_us(5); // 产生下降沿 SCL(0); delay_us(5); // 钳住总线准备发送数据 } // I2C停止信号SCL高电平时SDA产生一个上升沿 void IIC_Stop(void) { SDA_OUT(); SCL(0); SDA(0); SCL(1); delay_us(5); SDA(1); delay_us(5); // 产生上升沿 }delay_us(5)是微秒级延时你需要根据你的主频实现一个简单的延时函数。对于MSPM0G3507可以用SysTick或者简单的循环实现。其次是读取传感器出厂校准值。这是MS5611高精度的关键传感器在出厂时会在PROM中存储6个校准系数C1-C6和一个CRC校验值。每次上电后我们必须先读取这些系数。uint16_t Cal_C1_6[8]; // 存储C1-C6及CRC void MS5611_Read_PROM(void) { uint8_t data_H0, data_L0; uint8_t i 0; // PROM的地址从0xA0开始每个系数占2个字节共8个地址C1-C6CRC厂商信息 for( i 0; i 8; i ) { IIC_Start(); Send_Byte(0xee | 0); // 发送器件地址写命令 (0xEE) I2C_WaitAck(); Send_Byte( 0xA0 i * 2 ); // 发送PROM读取命令 I2C_WaitAck(); IIC_Stop(); delay_us(200); // 重启I2C改为读模式 IIC_Start(); Send_Byte(0xee | 1); // 发送器件地址读命令 (0xEF) I2C_WaitAck(); data_H Read_Byte(); // 读高8位 IIC_Send_Ack(0); // 发送ACK data_L Read_Byte(); // 读低8位 IIC_Send_Ack(1); // 发送NACK结束读取 IIC_Stop(); Cal_C1_6[i] (data_H 8) | data_L; // 合并为16位数据 } }最后是读取原始数据并进行换算。MS5611测量时需要分别启动气压和温度的AD转换然后读取原始的24位数字量D1和D2。// 读取原始数据D1气压或D2温度 uint32_t MS5611_Read_D1_D2(uint8_t regaddr) // regaddr: 0x48(气压) 或 0x58(温度) { uint32_t dat 0; uint8_t buff[3] {0}; // 1. 发送转换命令 IIC_Start(); Send_Byte(0xee | 0); I2C_WaitAck(); Send_Byte(regaddr); // 启动转换 I2C_WaitAck(); IIC_Stop(); delay_ms(10); // 等待转换完成OSR4096时最大需要9ms // 2. 发送ADC读命令 IIC_Start(); Send_Byte(0xee | 0); I2C_WaitAck(); Send_Byte(0X00); // ADC读命令 I2C_WaitAck(); IIC_Stop(); delay_ms(10); // 3. 读取24位转换结果 IIC_Start(); Send_Byte(0xee | 1); I2C_WaitAck(); buff[0] Read_Byte(); // 高8位 IIC_Send_Ack(0); buff[1] Read_Byte(); // 中8位 IIC_Send_Ack(0); buff[2] Read_Byte(); // 低8位 IIC_Send_Ack(1); // 最后一个字节后发NACK IIC_Stop(); dat (((buff[0]16) | ( buff[1]8)) | buff[2]); return dat; }拿到原始数据D1、D2和校准系数C1-C6后就需要套用数据手册中的公式进行换算。这个过程有点复杂但代码已经封装好了。float Get_pressure(void) { long long SENS 0; long long P 0; long long OFF 0; uint32_t dT 0; uint32_t D1 0, D2 0; // 1. 读取原始数据 D1 MS5611_Read_D1_D2(0x48); // 读取气压原始值 delay_ms(10); D2 MS5611_Read_D1_D2(0x58); // 读取温度原始值 delay_ms(10); // 2. 计算温度偏移量dT和实际温度未使用但dT需要 dT D2 - ((uint32_t)Cal_C1_6[5] * 256); // TEMP 2000 (dT * Cal_C1_6[6]) / 8388608; // 实际温度计算单位0.01°C // 3. 计算气压补偿值OFF和SENS OFF (long long)Cal_C1_6[2] * 65536 (long long)Cal_C1_6[4] * dT / 128; SENS (long long)Cal_C1_6[1] * 32768 (long long)Cal_C1_6[3] * dT / 256; // 4. 计算最终气压值P P (D1 * SENS / 2097152 - OFF) / 32768; // 返回单位为百帕斯卡(HPa)的气压值 return (P / 100.0); }提示公式中的常数如256、65536、8388608等都是2的幂次方是数据手册中定义的固定系数。使用long long类型是为了防止中间计算结果溢出。4. 实战应用与验证驱动写好了最后就是在主函数里调用它了。流程非常清晰初始化 - 复位传感器 - 读校准值 - 循环读取数据。#include board.h #include stdio.h // 用于printf打印 #include bsp_ms5611.h int main(void) { // 开发板初始化时钟、串口等 board_init(); // 1. 复位MS5611传感器 MS5611_Reset(); delay_ms(300); // 等待传感器复位稳定 // 2. 读取至关重要的出厂校准系数 MS5611_Read_PROM(); printf(MS5611 Sensor Start!\r\n); while(1) { // 3. 获取温度值内部已包含读取D1、D2的步骤 float temperature Get_TEMP(); // 例如返回25.30 // 4. 获取气压值单位HPa百帕 float pressure Get_pressure(); // 例如返回1013.25 // 将浮点数放大100倍方便用整数打印出小数点后两位 uint32_t temp_int temperature * 100; uint32_t pres_int pressure * 100; printf(温度 %d.%02d ℃\r\n, temp_int/100, temp_int%100); printf(气压 %d.%02d HPa\r\n, pres_int/100, pres_int%100); printf(\n); delay_ms(1000); // 每秒读取一次 } }将代码编译下载到MSPM0G3507开发板通过串口助手如Putty、SecureCRT查看打印信息。如果一切顺利你就能看到类似下面的输出MS5611 Sensor Start! 温度 25.30 ℃ 气压 1013.25 HPa这表示气压传感器驱动已经成功移植并工作正常了。你可以用这个气压值结合标准大气压公式进一步推算出当前的海拔高度。这个移植好的工程我调试了很久I2C的时序和换算公式是重点尤其是等待ADC转换完成的时间delay_ms(10)不能省否则读到的数据是错的。希望这份详细的教程和代码能帮你快速上手。