1、简介及注意事项
气压计MS5611-01BA03 采用24位的气压和温度AD转换值,SPI、IIC接口协议读取,采用256、512、1024、2048和4096的过采样率提高采样精度。256的过采样率最低转换时间为0.5ms。
该气压计仅仅只有5个基本指令:复位、读ProM校准值、D1启动温度转换、D2启动气压转换、读取ADC转换值结果。
1. 关于初始化
-
在上电之后,需要执行复位指令,确保校准值Prom都载入到寄存器中。
-
Prom寄存器值,读取一次即可。Prom值中从0xA0-0xAE,最后一位始终为0,所以共八个指令,第一个是厂商信息,2-7是六个系数信息,8是CRC校验信息。
-
SPI模式可以采用0和3模式,即:SPI_CPOL_Low和SPI_CPHA_1Edge、SPI_CPOL_High和SPI_CPHA_2Edge。
-
在启动AD转换之后,需要等待相应的时间去读取,否则读取的时候可能为0;连续读取两次数据也为0;
2. 关于数据计算
在读完数据计算温度和气压的时候,需要注意变量的位数,比如OFF和SENS是64位的变量,如果定义成32位的就会出现数据丢失,计算错误的情况。 -
在计算温度时,
T=2000+DT*((float)PROM[5]/8388608); 一定要注意其中的float强制转换,否则会出现数据跳变,有些人强制转换成整形,也是不对的。 -
在计算气压时,
OFF=((int64_t)PROM[1]<<16)+(((int64_t)PROM[3]*DT)>>7);
SENS=((int64_t)PROM[0]<<15)+(((int64_t)PROM[2]*DT)>>8);
P=((int64_t)((CovData[0]*SENS)>>21)-OFF)>>15;
要转化成更高的位数,否则也会出现数据丢失的情况。
3. 关于协议时序图 -
气压、温度转换数据读取
在发送完0x48(OSR 4096)之后,需要等待8.22ms,之后再去读取数据,读取数据的时候,也需要发送相应的数据,才能传回数据。 -
Prom数据读取
PORM数据只需要读取一次即可,这些数据都是厂家出厂时就校准好的,之后也不会更改。以下是楼主读到的参数,每个气压计参数都不一样,如果不一样也无需要考虑这个。
![](https://img-blog.csdnimg.cn/2020011511274758.png?x-ossprocess=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM0NDMwMzcx#pic_center=90x90)
2、数据读取
刚发出这篇博文之后,阅读数立即飙到1000多,我当时就想这个气压计就这么多人开始搞么,当时手头货还没到,到了立即开始调试,虽然途中也遇到了坑,但总归数据是出来了。
以下是温度、气压(毫巴)、高度。
接下来话不多说,直接上代码,使用定时器中断控制转换时间来读取数据。
//首先是初始化代码
u16 PROM[6]; //存储6个校准系数
u32 CovData[2]; //存储两个24位ADC值,温度和气压
void Spi3IOInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// Enable GPIOC clocks
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
// Connect SPI2 pins to AF5
GPIO_PinAFConfig(MS_SPI_PORT, GPIO_PinSource10, GPIO_AF_SPI3); // SCK
GPIO_PinAFConfig(MS_SPI_PORT, GPIO_PinSource11, GPIO_AF_SPI3); // MISO
GPIO_PinAFConfig(MS_SPI_PORT, GPIO_PinSource12, GPIO_AF_SPI3); // MOSI
// SPI SCK、MOSI pin configuration
GPIO_InitStructure.GPIO_Pin = MS_SPI_SCK_PIN|MS_SPI_MOSI_PIN|MS_SPI_MISO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//GPIO_PuPd_DOWN;//GPIO_PuPd_UP;//GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(MS_SPI_PORT, &GPIO_InitStructure);
//SPI2 NSS pin in output pushpull mode
GPIO_InitStructure.GPIO_Pin = MS_SPI_CS_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
MS_SPI_CS_HIGH(); // cs default state is high
MS_SPI_CS_LOW();
MS_SPI_CS_HIGH();
}
void SPI3Init(void)
{
SPI_InitTypeDef SPI_InitStructure;
SPI_I2S_DeInit(SPI3);
//Enable the SPI periph
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);
Spi3IOInit();
// SPI configuration
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; // SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI3, &SPI_InitStructure);
// Enable the SPI3
SPI_Cmd(SPI3, ENABLE);
}
void Timer2Init(u32 Frequency) //使用定时器中断,定时读取ADC值
{
u32 Period = SystemCoreClock/168/Frequency;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitTypeDefStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_DeInit(TIM2);
TIM_TimeBaseInitTypeDefStructure.TIM_Period=(Period-1);
TIM_TimeBaseInitTypeDefStructure.TIM_Prescaler=(42-1);
TIM_TimeBaseInitTypeDefStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitTypeDefStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitTypeDefStructure);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //外部中断8
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01; //抢占优先级1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
TIM_Cmd(TIM2,ENABLE);
}
void ReadMS5611PROMData()
{
PROM[0] = SpiReadPromCmd(0xA2);
PROM[1] = SpiReadPromCmd(0xA4);
PROM[2] = SpiReadPromCmd(0xA6);
PROM[3] = SpiReadPromCmd(0xA8);
PROM[4] = SpiReadPromCmd(0xAA);
PROM[5] = SpiReadPromCmd(0xAC);
}
void MS5611Init() //先复位,再读取系数
{
SPI3Init();
SpiWriteCmd(0x1E);
Delay_ms(3);
ReadMS5611PROMData();
Timer2Init(800);
}
u8 ReadFlag=0;
void TIM2_IRQHandler() //定时读取ADC值,读完之后,Readflag=2.去处理数据。
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
{
if(ReadFlag==0)
{
CovData[0]=SpiReadCovCmd(0x00);
SpiWriteCmd(0x50);
ReadFlag=1;
}
else if(ReadFlag==1)
{
CovData[1]=SpiReadCovCmd(0x00);
SpiWriteCmd(0x40);
ReadFlag=2;
}
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
void SpiWriteCmd(u8 cmd)
{
MS_SPI_CS_LOW();
while((SPI3->SR &0x0002)==0);
SPI3->DR = (cmd);
while((SPI3->SR &0x0001)==0);
SPI_I2S_ReceiveData(SPI3);
MS_SPI_CS_HIGH();
}
u32 SpiReadPromCmd(u8 cmd)
{
u32 returnvalue;
MS_SPI_CS_LOW();
while((SPI3->SR &0x0002)==0);
SPI3->DR = cmd;
while((SPI3->SR &0x0001)==0);
(unsigned char)(SPI_I2S_ReceiveData(SPI3)); // Return the Byte read from the SPI bus
while((SPI3->SR &0x0002)==0);
SPI3->DR = 0x00;
while((SPI3->SR &0x0001)==0);
returnvalue = (unsigned char)(SPI_I2S_ReceiveData(SPI3)); // Return the Byte read from the SPI bus
returnvalue=returnvalue<<8;
while((SPI3->SR &0x0002)==0);
SPI3->DR = 0x00;
while((SPI3->SR &0x0001)==0);
returnvalue|=(unsigned char)(SPI_I2S_ReceiveData(SPI3)); // Return the Byte read from the SPI bus
MS_SPI_CS_HIGH();
return(returnvalue);
}
u32 SpiReadCovCmd(u8 cmd)
{
u32 returnvalue;
MS_SPI_CS_LOW();
while((SPI3->SR &0x0002)==0);
SPI3->DR = cmd;
while((SPI3->SR &0x0001)==0);
(unsigned char)(SPI_I2S_ReceiveData(SPI3)); // Return the Byte read from the SPI bus
while((SPI3->SR &0x0002)==0);
SPI3->DR = 0x01;
while((SPI3->SR &0x0001)==0);
returnvalue = (unsigned char)(SPI_I2S_ReceiveData(SPI3)); // Return the Byte read from the SPI bus
returnvalue=returnvalue<<8;
while((SPI3->SR &0x0002)==0);
SPI3->DR = 0x01;
while((SPI3->SR &0x0001)==0);
returnvalue|=(unsigned char)(SPI_I2S_ReceiveData(SPI3)); // Return the Byte read from the SPI bus
returnvalue=returnvalue<<8;
while((SPI3->SR &0x0002)==0);
SPI3->DR = 0x01;
while((SPI3->SR &0x0001)==0);
returnvalue|=(unsigned char)(SPI_I2S_ReceiveData(SPI3)); // Return the Byte read from the SPI bus
MS_SPI_CS_HIGH();
return(returnvalue);
}
void Delay_ms(u32 nTime) //滴答定时器做延时,
{
TimeDelay=nTime;
while(TimeDelay!=0x00);
}
接下来是最重要的数据计算了,很多同学都读出数据了,但是就是在计算上出问题,数据一直都出不来。其中很重要的原因就是变量位数不对,很少人知道32位的单片机还可以定义64位的变量,而且还可以处理,以下就是变量的位数定义。
void ReadMS5611CovData()
{
unsigned char StrBuff[50];
int32_t DT,T,P; //根据数据手册,定义相应位数的变量存储数据。
int64_t OFF,SENS;
float H;
float T2,Aux,OFF2,SENS2;
if(ReadFlag==2)
{
ReadFlag=0;
DT=CovData[1]-((u32)PROM[4]<<8);
T=2000+DT*((float)PROM[5]/8388608); //一定要用float强制转换,否则数据会跳变出错。
OFF=((int64_t)PROM[1]<<16)+(((int64_t)PROM[3]*DT)>>7); //强制转换到更高的位数上,否则出现数据丢失的情况。
SENS=((int64_t)PROM[0]<<15)+(((int64_t)PROM[2]*DT>>8);
if(T<2000)
{
T2 = (float)(DT*DT)/0x80000000;
Aux = (T-2000)*(T-2000);
OFF2 = 2.5*Aux;
SENS2 = 1.25*Aux;
if(T < -1500)
{
Aux = (T+1500)*(T+1500);
OFF2 = OFF2 + 7*Aux;
SENS2 = SENS + 5.5*Aux;
}
}
else
{
T2 = 0;
OFF2 = 0;
SENS2 = 0;
}
T = T - T2;
OFF = OFF - OFF2;
SENS = SENS - SENS2;
P=((int64_t)((CovData[0]*SENS)>>21)-OFF)>>15; //先左移21位-OFF之后,再强制转换,否则出现2倍的关系
H=(float)(44330.0f*(1.0f - pow((float)P/101325.0f, 0.190295f)));
//sprintf((char *)StrBuff,"%d***%d***%.2f C***%lld***%lld***%.2f mbar\r\n",CovData[0],CovData[1],T/100.0,OFF,SENS,P/100.0);
sprintf((char *)StrBuff,"%.2f ℃ %.2f mbar %.2f m\r\n",T/100.0,P/100.0,H);
SendData(StrBuff,strlen((char *)StrBuff));
}
}
楼主分享一个完整的工程文件,用STM32F405使用SPI协议读取MS5611气压计的数据,温度、气压计数据均正常,并计算出海拔高度。
下载链接: keil 工程文件
如有雷同,纯属我抄你,有问题可以直接联系邮箱,在个人资料里面。