基于STM32F103驱动MS5611读取气压与温度数据

电子说

1.4w人已加入

描述

一、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电源负
SCLI2C / SPI时钟
SDAI2C / SPI 串行数据输入
CSB芯片选择,反补码,接低电平:7位器件地址为1110111,接高电平:7位器件地址为1110110,模块默认低电平
SDOSPI串行数据输出
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

地址命令内容
00xA0厂家保留
10xA2C1压力灵敏度
20xA4C2压力偏移
30xA6C3压力灵敏度温度系数
40xA8C4压力偏移温度系数
50xAAC5参考温度
60xACC6温度系数
70xAE4位 CRC 校验,检测C1~C6 是否读取正确

其中,CRC校验如下

传感器

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

准备工作

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

接线说明

STM32F103C8T6MS5611
3.3VVCC
GNDGND
PA0SCL
PA1SDA
PB8OLED -> SCL
PB9OLED -> 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);
	} 
}

效果展示

传感器

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分