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

参数特性
应用场景

| VCC | 电源正 |
|---|---|
| GND | 电源负 |
| SCL | I2C / SPI时钟 |
| SDA | I2C / SPI 串行数据输入 |
| CSB | 芯片选择,反补码,接低电平:7位器件地址为1110111,接高电平:7位器件地址为1110110,模块默认低电平 |
| SDO | SPI串行数据输出 |
| PS | 通讯协议选择,高电平为I2C通讯,低电平为SPI通讯,模块默认高电平 |
气压与温度读取流程图
第一步:读取校准参数(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

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校验如下

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 -1rn");
MyI2C_SendByte(regaddr); //OSR = 4096
if( MyI2C_ReceiveAck() == 1 )printf("D1 NACK -2rn");
MyI2C_Stop();
Delay_ms(10);
MyI2C_Start();//起始信号
MyI2C_SendByte(MS5611_ADDR|0); //器件地址+写
if( MyI2C_ReceiveAck() == 1 )printf("D1 NACK -3rn");
MyI2C_SendByte(MS5611_ADC);
if( MyI2C_ReceiveAck() == 1 )printf("D1 NACK -4rn");
MyI2C_Stop();
Delay_ms(10);
MyI2C_Start();//起始信号
MyI2C_SendByte(MS5611_ADDR|1); //器件地址+读
if( MyI2C_ReceiveAck() == 1 )printf("D1 NACK -5rn");
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("MS5611rn");
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℃rn",temp );
//输出气压
printf("气压 = %.2fHParn",pressure );
printf("n");
Delay_ms(1000);
}
}

全部0条评论
快来发表一下你的评论吧 !