MS5607气压计调试过程记录
采用 SPI通讯方式驱动MS5607,主控用的是STM32F777
程序是利用STM32CUBEMX生成的 驱动 根据芯片手册和一些参考资料自己写的,数据暂时看是没问题的。
没有测试环境 没有系统测试过,数值验证没问题。
1,上电初始化MS5607:主要是复位芯片 然后读出prom校准数据(C1-C6)
向spi总线发送0x1e,复位芯片
void MX56XX_Ba_Reset(void)
{
uint8_t _cmd=0x1e;
MS5607_SPI_CS(0);
MS56XX_ReadWriteByte(_cmd);
MS5607_SPI_CS(1);
delay_xms(3);
}
然后读取prom校准数据,这个数据是厂商写好的 ,只需要开机上电读取一次就行。
prom read地址从0xA0到0xAE 。
读prom
void MX56XX_Read_Prom_Data(void)
{
uint8_t _cmd;
uint8_t _ctr;
uint8_t _buff[2];
_cmd = 0xa0; //prom首地址
for(_ctr = 0;_ctr<8;_ctr++)
{
MS5607_SPI_CS(0);
MS56XX_ReadWriteByte(_cmd);
_buff[0] = MS56XX_ReadWriteByte(0XFF);
_buff[1] = MS56XX_ReadWriteByte(0XFF);
Cal_C[_ctr]=(_buff[0]<<8)+_buff[1];
_cmd += 0x02;
MS5607_SPI_CS(1);
}
}
这里有个地方需要注意 ,下图是prom的内存映射。
之前读取的数据一直不对,以为数据C1的地址是0xa0,后来看了手册后才知道还有制造商的信息。
从prom的内存映射可以看出,prom的首地址 存放的是制造商的数据和设备信息 ,地址 1-6是校准数据c1-c6,地址7是4bit的crc校验信息。
所以 这就是循环读取8次的原因,然后Cal_C[1]~Cal_C[6] 就对应了的C1~C6的校准数据。Cal_C[7]是CRC校验数据,可以进行验证通信使用。
还有就是C1~C6的数据是16位的 ,我用的SPI读取是读一个字节,所以 连续读取两次在组成16位的数据在用就可以了。
2 以上初始化完成以后,接下来就是循环读取D1,D2的值 然后用公式计算 数字温度和压强。
读地址是ADC Rread 0x00。
D1 的分辨率 OSR=4096 地址 0x48
D2的分辨率 OSR=4096 地址 0x58
计算公式和变量信息。
读D1
uint8_t _presbuf[3];
uint32_t MS56XX_Do_Conversion_Pres(void) //数据读取
{
uint8_t _cmd=0X48;
uint32_t _D1;
// uint8_t _buf[4];
MS5607_SPI_CS(0);
MS56XX_ReadWriteByte(_cmd); //0x48 -->D1 OSR=4096
MS5607_SPI_CS(1);
MS56XX_Read_Buf(0x00,_presbuf,3);
_D1=(_presbuf[0]<<16)+ (_presbuf[1]<<8) +(_presbuf[2]);//读取adc值
//_D1=_buf[0]<<24 | buf[1]<<16 |buf[2]<<8 | buf[3];//读取adc值
return _D1;
}
读D2
uint8_t _tempbuf[3];
uint32_t MS56XX_Do_Conversion_Temp(void) //数据读取
{
uint8_t _cmd=0X58;
uint32_t _D2;
MS5607_SPI_CS(0);
MS56XX_ReadWriteByte(_cmd); //0x58 -->D2 OSR=4096
MS5607_SPI_CS(1);
MS56XX_Read_Buf(0x00,_tempbuf,3);
_D2=(_tempbuf[0]<<16)+ (_tempbuf[1]<<8) +(_tempbuf[2]);//读取adc值
return _D2;
}
//读取并计算数字温度
char MS56XX_GetTemperature (void) //计算温度
{
D2_Temp = MS56XX_Do_Conversion_Temp ( ); //循环读取 D1 D2
if(D2_Temp == 0)
return 0;
delay_ms ( 10 );
if(D2_Temp > ( ( uint32_t ) Cal_C[5] * (0x00000001 << 8) ))
dT = D2_Temp - ( ( uint32_t ) Cal_C[5] * (0x00000001 << 8) ); //公式 dT = D2 - TREF = D2 - C5 * 2^8
else
{
dT = ( ( uint32_t ) Cal_C[5] * (0x00000001 << 8) ) - D2_Temp;
dT *= -1;
}
Temperature = 2000 + ( dT * Cal_C[6] ) / (0x00000001 << 23); //算出温度值的100倍,2001表示20.01° 公式TEMP =20°C + dT * TEMPSENS =2000 + dT * C6 / 2^23
if(Temperature<-4000) Temperature=-4000;
if(Temperature>8500) Temperature=8500;
return 1;
}
读取并计算数字气压
char MS56XX_GetPressure (void) //计算温度补偿压力
{
D1_Pres = MS56XX_Do_Conversion_Pres ( ); //循环读取 D1 D2
if(D1_Pres == 0)
return 0;
delay_xms ( 10 );
OFF = ( ( int64_t )Cal_C[2] * (0x00000001 << 17) ) + ( ( int64_t ) Cal_C[4] * dT ) / 64.0; //公式 OFF = OFFT1 + TCO *dT = C2 *2^17 +(C4 *dT )/ 2^6
SENS = ( ( int64_t ) Cal_C[1] * (0x00000001 << 16) ) + ( ( int64_t ) Cal_C[3] * dT ) / 128.0; //公式SENS = SENST1 + TCS* dT= C1 * 2^16 + (C3 * dT )/ 2^7
//温度补偿部分 逻辑看芯片手册
if ( Temperature < 2000 ) // second order temperature compensation when under 20 degrees C
{
T2 = ( dT * dT ) / (( uint64_t )0x0000000001 << 31);
OFF2 = 61 * (( Temperature - 2000 ) * ( Temperature - 2000 )) / 16;
SENS2 = 2 * (( Temperature - 2000 ) * ( Temperature - 2000 )) ;
if ( Temperature < -1500 )
{
OFF2 = OFF2 + 15 * (( Temperature + 1500 ) * ( Temperature + 1500 )) ;
SENS2 = SENS2 + 8 * (( Temperature + 1500 ) * ( Temperature + 1500 )) ;
}
}
else //(Temperature > 2000)
{
T2 = 0;
OFF2 = 0;
SENS2 = 0;
}
Temperature = Temperature - T2;
OFF = OFF - OFF2;
SENS = SENS - SENS2;
Pressure = ( D1_Pres * SENS / (0x00000001 << 21) - OFF ) / (0x00000001 << 15); //公式 P = D1 * SENS - OFF = (D1 * SENS / 2^21 - OFF) / 2^15
Ms5607.Temperature=(float)Temperature/100; //单位 ℃
Ms5607.Pressure=(float)Pressure/100; //单位 mbar
if(Ms5607.Temperature<-40) Ms5607.Temperature=-40;
if(Ms5607.Temperature>85) Ms5607.Temperature=85;
if(Ms5607.Pressure<10) Ms5607.Pressure=10;
if(Ms5607.Pressure>1200) Ms5607.Pressure=1200;
printf("Pressure=%4f mbar\r\n",Ms5607.Pressure);
return 1;
}
温度补偿部分的逻辑
补充1
/********************************************************************************
** 1MPa=1000,000Pa=10bar=10000mbar hPa(百帕)=mbar(毫巴)。
** 每提高12m,大气压下降1mmHg(1毫升水银柱)或者每上升9m,大气压降低100Pa。
** 例如已知气压970hpa,如何求出海拔是多少? 标准大气压为1013.25百帕(hpa)
** 所以,海拔高度为h=(1013.25-970)*9=389.25(米)。
********************************************************************************/
补充2
SPI时序:在分辨率=4096的是时候 adc的转换时间是8.22ms 所以要留够延时时序。
D1和D2虽然变量定义是32的 但是实际读取字节的时候是读取24bit的数据,也就是3个字节的数据。
//SPI 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
uint8_t MS56XX_ReadWriteByte(uint8_t TxData)
{
uint8_t Rxdata;
HAL_SPI_TransmitReceive(&hspi2,&TxData,&Rxdata,1, 1000);
return Rxdata; //返回收到的数据
}
uint8_t MS56XX_Read_Buf(uint8_t reg ,uint8_t *pbuf,uint8_t len)//参数说明 发送寄存器位置 读取指定长度的数据 放在pbuf里
{
u8 status,_ctr;
MS5607_SPI_CS(0);
delay_xms(9);//预留给ad转换时间,因为osr=4096分辨率 时序上转换时间约为8.2ms
status=MS56XX_ReadWriteByte(reg); //发送寄存器位置
for(_ctr=0;_ctr<len;_ctr++)
{
pbuf[_ctr] = MS56XX_ReadWriteByte(0XFF);
}
MS5607_SPI_CS(1);
return status;//返回收到的数据
}
uint8_t MS56XX_Write_Buf(u8 reg, u8 *pBuf, u8 len)//参数说明 发送寄存器位置 写指定长度的数据
{
u8 status,_ctr;
MS5607_SPI_CS(0); //使能 SPI 传输
status = MS56XX_ReadWriteByte(reg); //发送寄存器值(位置),并读取状态值
for(_ctr=0; _ctr<len; _ctr++)
{
MS56XX_ReadWriteByte(*pBuf++); //写入数据
}
MS5607_SPI_CS(1); //关闭 SPI 传输
return status; //返回读到的状态值
}
结果
笔者在调试时,参考了以下博文,向这些博主及作者表示感谢!
MS56XX:
https://blog.csdn.net/zhxlx/article/details/93984496
https://bbs.21ic.com/icview-1722818-1-3.html
https://blog.csdn.net/xian_z/article/details/76461696
SPI:
https://www.jianshu.com/p/5de187bf5b75