氣壓計 MS5611-01BA03 數據讀取

1、簡介及注意事項

  氣壓計MS5611-01BA03 採用24位的氣壓和溫度AD轉換值,SPIIIC接口協議讀取,採用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 工程文件

如有雷同,純屬我抄你,有問題可以直接聯繫郵箱,在個人資料裏面。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章