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數據只需要讀取一次即可,這些數據都是廠家出廠時就校準好的,之後也不會更改。以下是樓主讀到的參數,每個氣壓計參數都不一樣,如果不一樣也無需要考慮這個。
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 工程文件
如有雷同,純屬我抄你,有問題可以直接聯繫郵箱,在個人資料裏面。