一、MS5611模块简介

  MS5611是一款高分辨率高度计传感器,配备 SPI 和 I2C 总线接口。这款气压传感器针对高度计和变高仪进行了优化,高度分辨率可达 10 厘米。MS5611包含一个高线性度压力传感器和一个超低功耗的 24 位 ΔΣ ADC,并内置出厂校准系数。它可提供精确的 24 位数字压力和温度值,并提供多种工作模式,用户可根据需求优化转换速度和功耗。高分辨率温度输出无需额外传感器即可实现高度计/温度计功能。

参数特性

  • 供电电压:DC 3.3V / 5V
  • 通讯方式:I2C / SPI通讯
  • ADC:24位
  • 气压量程:10~1200mbar
  • 分辨率:0.065 / 0.042 / 0.027 / 0.018 / 0.012 mbar
  • 精度:
      Accuracy 25°C, 750 mbar(准确度,理想环境):±1.5mbar。
      Error band -20°C to +85°C, 450~1100 mbar(误差带,最大允许偏离范围):±2.5mbar。
  • 响应时间:0.5 / 1.1 / 2.1 / 4.1 / 8.22 ms
  • 温度量程:-40 ~ 85℃
  • 分辨率:<0.01℃
  • 精度:±0.8℃

应用场景

  • 移动式高度计/气压计系统
  • 自行车码表
  • 变距仪
  • 用于医疗警报的高度感应
  • 室内导航

二、MS5611引脚说明

VCC 电源正
GND 电源负
SCL I2C / SPI时钟
SDA I2C / SPI 串行数据输入
CSB 芯片选择,反补码,接低电平:7位器件地址为1110111,接高电平:7位器件地址为1110110,模块默认低电平
SDO SPI串行数据输出
PS 通讯协议选择,高电平为I2C通讯,低电平为SPI通讯,模块默认高电平

三、MS5611气压与温度读取流程与补偿分析说明

气压与温度读取流程图

第一步:读取校准参数(PROM),这些是工厂标定的补偿系数,决定精度。

  C1:压力灵敏度(SENS)
  C2:压力偏移(OFF)
  C3:温度对SENS影响
  C4:温度对OFF影响
  C5:参考温度
  C6:温度系数

第二步:读取原始ADC数据

  D1:气压原始值(24bit)
  D2:温度原始值(24bit)

第三步:计算温度

  温差 dT = D2 − C5 * 2^8,dT是指当前温度 vs 参考温度 的差值。
  实际温度 TEMP = 2000 + (dT * C6)/ 2^23,单位:0.01℃,如TEMP = 2007 → 20.07°C。

第四步:计算压力补偿参数

  OFF(偏移)= C2 * 2^16 + (C4 * dT)/ 2^7
  SENS(灵敏度)= C1 * 2^15 + (C3 * dT)/ 2^8
  OFF零点偏移和SENS增益(温度会影响),这一步就是把温度误差带入气压模型修正。

第五步:计算最终气压

  P = D1 * SENS - OFF = [(D1 * SENS)/ 2^21 - OFF] / 2^15,输出单位:0.01 mbar,如10009 → 100.09 mbar

上面的流程只是基础版,数据只在20℃以上准确,低温时会出现严重非线性误差,这时就需要手动补偿。补偿流程图如下:

情况1:温度 ≥ 20°C,T2,OFF2,SENS2为0,高温区已经比较线性,不需要补偿。

情况2:温度 < 20°C(低温)
  温度补偿:T2 = dT^2 / 2^31
  压力补偿:OFF2 = 5 * (TEMP − 2000)^2 / 2
       SENS2 = 5 * (TEMP−2000)^2 / 4

情况3:温度 < -15°C(极低温)进一步补偿
  OFF2 = OFF2 + 7 * (TEMP + 1500)^2
  SENS2 = SENS2 + 11 * (TEMP + 1500)^2​ / 2

最终修正补偿:
  TEMP = TEMP - T2
  OFF = OFF - OFF2
  SENS = SENS - SENS2

四、MS5611主要寄存器说明


Reset(0x1E):复位寄存器
Convert D1(0x40~0x48):气压配置寄存器
Convert D2(0x50~0x58):温度配置寄存器
ADC Read(0x00):ADC读取寄存器
PROM Read(0xA0~0xAE):只读校准存储器

其中PROM Read只读校准存储器,MS5611在出厂时进行了校准,校准的6个系数存储 在PROM寄存器,PROM 一共 8个地址(每个16bit),从0xA0到0XAE, 一共8 * 16 = 128位,其中每两个字节为一个地址:
  第0个地址:厂家保留,不用在意
  第1到第6个地址:我们需要读取,后面用于气压计算
  第7个地址:CRC

地址 命令 内容
0 0xA0 厂家保留
1 0xA2 C1压力灵敏度
2 0xA4 C2压力偏移
3 0xA6 C3压力灵敏度温度系数
4 0xA8 C4压力偏移温度系数
5 0xAA C5参考温度
6 0xAC C6温度系数
7 0xAE 4位 CRC 校验,检测C1~C6 是否读取正确

其中,CRC校验如下

五、STM32F103驱动MS5611读取气压与温度数据

准备工作

STM32F103C8T6最小系统板,MS5611传感器模块,OLED模块,导线若干等。

接线说明

STM32F103C8T6 MS5611
3.3V VCC
GND GND
PA0 SCL
PA1 SDA
PB8 OLED -> SCL
PB9 OLED -> SDA

代码示例

MS5611.c

#include "ms5611.h"

uint16_t Cal_C1_6[8];
uint32_t D1 = 0, D2 = 0, dT = 0;

void MS5611_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(PORT_MS5611, MS5611_SCL, (BitAction)BitValue);
	Delay_us(10);
}

void MS5611_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(PORT_MS5611, MS5611_SDA, (BitAction)BitValue);
	Delay_us(10);
}

uint8_t MS5611_R_SDA(void)
{
	uint8_t BitValue;
	BitValue = GPIO_ReadInputDataBit(PORT_MS5611, MS5611_SDA);
	Delay_us(10);
	return BitValue;
}
	
void MS5611_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_MS5611, ENABLE);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Pin = MS5611_SCL | MS5611_SDA;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(PORT_MS5611, &GPIO_InitStructure);
	
	GPIO_SetBits(PORT_MS5611, MS5611_SCL|MS5611_SDA);
}

void MyI2C_Start(void)
{
	MS5611_W_SDA(1);
	MS5611_W_SCL(1);
	MS5611_W_SDA(0);
	MS5611_W_SCL(0);
}

void MyI2C_Stop(void)
{
	MS5611_W_SDA(0);
	MS5611_W_SCL(1);
	MS5611_W_SDA(1);
}

void MyI2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for (i = 0; i < 8; i ++)
	{
		MS5611_W_SDA(Byte & (0x80 >> i));
		MS5611_W_SCL(1);
		MS5611_W_SCL(0);
	}
}

uint8_t MyI2C_ReceiveByte(void)
{
	uint8_t i, Byte = 0x00;
	MS5611_W_SDA(1);
	for (i = 0; i < 8; i ++)
	{
		MS5611_W_SCL(1);
		if (MS5611_R_SDA() == 1){Byte |= (0x80 >> i);}
		MS5611_W_SCL(0);
	}
	return Byte;
}

void MyI2C_SendAck(uint8_t AckBit)
{
	MS5611_W_SDA(AckBit);
	MS5611_W_SCL(1);
	MS5611_W_SCL(0);
}

uint8_t MyI2C_ReceiveAck(void)
{
	uint8_t AckBit;
	MS5611_W_SDA(1);
	MS5611_W_SCL(1);
	AckBit = MS5611_R_SDA();
	MS5611_W_SCL(0);
	return AckBit;
}

uint8_t MS5611_Reset(void)
{
	MyI2C_Start();
	MyI2C_SendByte(MS5611_ADDR|0);
	if(MyI2C_ReceiveAck() == 1) return 1;
	MyI2C_SendByte(MS5611_RESET);
	if(MyI2C_ReceiveAck() == 1) return 2;
	MyI2C_Stop();
	return 0;
}

void MS5611_Read_PROM(void)
{
	uint8_t data_H=0,data_L=0;
	uint8_t i = 0;
	
	for( i = 0; i < 8; i++ )
	{
		MyI2C_Start();//起始信号
		MyI2C_SendByte(MS5611_ADDR|0); //器件地址+写
		MyI2C_ReceiveAck();
		MyI2C_SendByte( MS5611_PROM + i * 2 ); //寄存器地址
		MyI2C_ReceiveAck();
		MyI2C_Stop();
		
		Delay_us(200);
		
		MyI2C_Start();//起始信号
		MyI2C_SendByte(MS5611_ADDR|1); //器件地址+读
		MyI2C_ReceiveAck();
		
		data_H = MyI2C_ReceiveByte();//读取的数据高8位
		MyI2C_SendByte(0);
		data_L = MyI2C_ReceiveByte();//读取的数据低8位
		MyI2C_SendByte(1);
		
		MyI2C_Stop();
		//保存出厂校准数据
		Cal_C1_6[i] = (data_H<<8) | data_L;
	}
}

uint32_t MS5611_Read_D1_D2(uint8_t regaddr)        
{
	uint32_t dat = 0;
	uint8_t buff[3] ={0};
	MyI2C_Start();//起始信号
	MyI2C_SendByte(MS5611_ADDR|0); //器件地址+写
	if( MyI2C_ReceiveAck() == 1 )printf("D1 NACK -1\r\n");
	MyI2C_SendByte(regaddr); //OSR = 4096
	if( MyI2C_ReceiveAck() == 1 )printf("D1 NACK -2\r\n");
	MyI2C_Stop();
	
	Delay_ms(10);
	
	MyI2C_Start();//起始信号
	MyI2C_SendByte(MS5611_ADDR|0); //器件地址+写
	if( MyI2C_ReceiveAck() == 1 )printf("D1 NACK -3\r\n");
	MyI2C_SendByte(MS5611_ADC); 
	if( MyI2C_ReceiveAck() == 1 )printf("D1 NACK -4\r\n");
	MyI2C_Stop();
	
	Delay_ms(10);
	
	MyI2C_Start();//起始信号
	MyI2C_SendByte(MS5611_ADDR|1); //器件地址+读
	if( MyI2C_ReceiveAck() == 1 )printf("D1 NACK -5\r\n");

	buff[0] = MyI2C_ReceiveByte();
	MyI2C_SendByte(0);
	buff[1] = MyI2C_ReceiveByte();
	MyI2C_SendByte(0);
	buff[2] = MyI2C_ReceiveByte();
	MyI2C_SendByte(1);
	MyI2C_Stop();
	
	dat = (((buff[0]<<16) | ( buff[1]<<8)) | buff[2]);
	return dat;
}

float Get_TEMP(void)
{
    int64_t TEMP;
    int64_t T2 = 0;

    D2 = MS5611_Read_D1_D2(MS5611_CONVERT_D2);

    dT = (int64_t)D2 - ((int64_t)Cal_C1_6[5] << 8);

    TEMP = 2000 + ((dT * (int64_t)Cal_C1_6[6]) >> 23);

    if(TEMP < 2000)
    {
        T2 = (dT * dT) >> 31;
        TEMP -= T2;
    }

    return TEMP / 100.0f;
}

float Get_pressure(void)
{
    int64_t TEMP;
    int64_t OFF, SENS;
    int64_t OFF2 = 0, SENS2 = 0;
    int64_t T2 = 0;
    int64_t P;

    D1 = MS5611_Read_D1_D2(MS5611_CONVERT_D1);
	Delay_ms(10);
    D2 = MS5611_Read_D1_D2(MS5611_CONVERT_D2);
	Delay_ms(10);

    dT = (int64_t)D2 - ((int64_t)Cal_C1_6[5] << 8);

    TEMP = 2000 + ((dT * (int64_t)Cal_C1_6[6]) >> 23);

    OFF = ((int64_t)Cal_C1_6[2] << 16) +
          ((int64_t)Cal_C1_6[4] * dT >> 7);

    SENS = ((int64_t)Cal_C1_6[1] << 15) +
           ((int64_t)Cal_C1_6[3] * dT >> 8);

    if(TEMP < 2000)
    {
        T2 = (dT * dT) >> 31;

        OFF2 = (5 * (TEMP - 2000) * (TEMP - 2000)) >> 1;
        SENS2 = (5 * (TEMP - 2000) * (TEMP - 2000)) >> 2;

        if(TEMP < -1500)
        {
            OFF2 += 7 * (TEMP + 1500) * (TEMP + 1500);
            SENS2 += (11 * (TEMP + 1500) * (TEMP + 1500)) >> 1;
        }

        TEMP -= T2;
        OFF -= OFF2;
        SENS -= SENS2;
    }

    P = (((D1 * SENS) >> 21) - OFF) >> 15;

    return P / 100.0f - 10.0f;
}

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "Serial.h"
#include "ms5611.h"
#include "oled.h"

float temp, pressure;

int main(void)
{
	Serial_Init();
	MS5611_Init();
	MS5611_Reset();
	Delay_ms(300);

    MS5611_Read_PROM();
	
	OLED_Init();
	
	OLED_ShowString(0,0,"MS5611",16,1);
	OLED_ShowChinese(0,18,0,16,1);
	OLED_ShowChinese(18,18,1,16,1);
	OLED_ShowString(34,18,": ",16,1);  
	OLED_ShowChinese(70,18,2,16,1);
	OLED_ShowChinese(0,36,3,16,1);
	OLED_ShowChinese(18,36,4,16,1);
	OLED_ShowString(34,36,": ",16,1); 
	OLED_ShowString(100,36,"HPa",16,1); 
	OLED_Refresh();
	
	printf("MS5611\r\n");
	
	while (1) 
	{       
		temp = Get_TEMP();
		pressure = Get_pressure();
		
		OLED_ShowNum(46,18,temp,2,16,1);
		OLED_ShowNum(46,36,pressure,4,16,1);
		OLED_ShowString(80,36,".",16,1); 
		OLED_ShowNum(86,36,(uint32_t)(pressure*10)/10,1,16,1);
		OLED_Refresh();
		
		//输出温度
        printf("温度 = %.0f℃\r\n",temp );
        //输出气压
        printf("气压 = %.2fHPa\r\n",pressure ); 
        printf("\n");
		  
		Delay_ms(1000);
	} 
}

效果展示

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐