1. 程式人生 > >微控制器 BMP280(GY-BM E/P 280模組)大氣壓強與溫度感測器使用詳解

微控制器 BMP280(GY-BM E/P 280模組)大氣壓強與溫度感測器使用詳解

微控制器 BMP280大氣壓強與溫度感測器使用詳解

最近實習中一個專案要用到多種感測器,其中就包括BMP280模組,但是發現網上有用的資料非常少,只好從頭看datasheet,使用過程中也算積累了相關的知識,分享給大家。在這裡也給各位一個建議,使用一個晶片之前最好還是多看看datasheet,寫datasheet的人就是製造晶片的人,他們的操作手冊比任何人都有權威性。廢話不多說,開始正題:

目錄

模組引腳及相關引數

  • 本次使用的模組型號為GY-BM E/P 280,淘寶上到處可以買到,這是對德國博世公司的BMP280晶片的一個應用封裝(實物圖片和封裝基本電路都放在下面)。採用的主控晶片為STC8A8K64S4A12微控制器,所以寫的程式碼都是最底層的,比較適合小白看(程式碼貼在後面)。
  • 引腳說明:
    Pin1:VCC(3.3V供電)
    Pin2:GND
    Pin3:SCL(I2C通訊模式時鐘訊號)
    Pin4:SDA(I2C通訊模式資料訊號)
    Pin5:CSB(SPI通訊模式下用到的引腳,本次沒用到,可以懸空)
    Pin6:SDO(感測器地址控制位,接GND的時候I2C中器件地址為0xEC,接高電平為0xEC+ 1,本次接GND
  • 感測器測試範圍:
    溫度:-45℃~+85℃
    大氣壓強:0~20000hPa(百帕)
  • 測量主要模式:
    Sleep Mode:作電流達到uA級別,典型值為0.1uA,最大值為0.3uA,所有測量工作都停止。
    Normal Mode: 正常工作,相關工作間隔時間可以通過暫存器控制。
    Forced Mode:主控發起一次採集命令,感測器採集一次訊號,然後進入Sleep Mode,等待下次喚起(本次沒用到)。
    這裡寫圖片描述
    這裡寫圖片描述

相關暫存器說明

BMP280感測器內部所有暫存器及其地址如下圖所示:
這裡寫圖片描述

  • 測量控制暫存器(ctrl_meas)(0xF4):
    Bit7~Bit5:osrs_t[2:0] 控制溫度取樣模式,主要是取樣資料的位數(位數越大,精度越高),具體配置如下(本次三位都配置為1,最大采樣位數20Bit):
    這裡寫圖片描述

    Bit4~Bit2:osrs_p[2:0] 控制大氣壓強取樣模式,主要是取樣資料的位數(位數越大,精度越高),具體配置如下(本次三位都配置為1,最大采樣位數20Bit):
    這裡寫圖片描述
    Bit1~Bit0:mode[1:0] 感測器工作模式控制,00為Sleep Mode,01/10為Forced Mode,11為 Normal Mode(本次配置為11)。
  • 配置暫存器(config)(0xF5):
    Bit7~Bit5:t_sb[2:0] 設定Normal Mode下的轉換間隔時間,具體配置如下(本次配置為000,0.5ms轉換一次)
    這裡寫圖片描述
    Bit4~Bit2:filter[2:0] 設定感測器接收外界訊號時的,前端濾波電路的濾波係數的,我也沒仔細研究,就設定了個000,有興趣的童鞋可以自己研究一下,datasheet上說和穩定度有關,可以有效減少外界環境的干擾:
    這裡寫圖片描述
    Bit0:spi3w_en 與SPI模式有關,本次沒用到,沒設定。
  • 身份編號暫存器(id)(0xD0):
    暫存器內固定值為0x58,讀取0xD0資料的時候,感測器返回0x58,代表身份辨認完畢。
  • 復位暫存器(reset)(0xE0):
    寫入0xB6時,所有暫存器(除身份編號暫存器)資料全部清零。
  • 狀態暫存器(status)(0xF3):
    具體定義如下,感興趣的童鞋自己研究,本次沒用到:
    這裡寫圖片描述

資料處理基本過程

該感測器是使用測量值和校準值(初始化中獲得),通過公式計算得出的,相關公式在datasheet中已經貼出了,還給了樣本資料(公式挺複雜的,建議先把公式抄到程式中,然後用樣本資料傳進去,測試一遍結果對不對,保證公式沒抄錯)。
資料處理中有個坑,請注意,就是讀取補償值資料的時候,下圖的資料儲存位是LSB/MSB,即資料是反過來儲存的,低位位元組在前,高位位元組在後,所以處理資料的時候要注意,具體可以見我的程式碼中bmp280_MultipleReadTwo()函式:
這裡寫圖片描述
公式的話,datasheet中也很模糊,就貼了一張自己程式碼中的,配合datasheet中的圖片,湊合看吧:

long bmp280_GetValue(void)
{
    long adc_T;
    long adc_P;
    long var1, var2, t_fine, T, p;

    adc_T = bmp280_MultipleReadThree(BMP280_TEMP_ADDR);
    adc_P = bmp280_MultipleReadThree(BMP280_PRESS_ADDR);

    if(adc_P == 0)
    {
        return 0;
    }

    //Temperature
    var1 = (((double)adc_T)/16384.0-((double)dig_T1)/1024.0)*((double)dig_T2);
    var2 = ((((double)adc_T)/131072.0-((double)dig_T1)/8192.0)*(((double)adc_T)
                /131072.0-((double)dig_T1)/8192.0))*((double)dig_T3);

    t_fine = (unsigned long)(var1+var2);

    T = (var1+var2)/5120.0;

    //Pressure
    var1 = ((double)t_fine/2.0)-64000.0;
    var2 = var1*var1*((double)dig_P6)/32768.0;
    var2 = var2 +var1*((double)dig_P5)*2.0;
    var2 = (var2/4.0)+(((double)dig_P4)*65536.0);
    var1 = (((double)dig_P3)*var1*var1/524288.0+((double)dig_P2)*var1)/524288.0;
    var1 = (1.0+var1/32768.0)*((double)dig_P1);
    p = 1048576.0-(double)adc_P;
    p = (p-(var2/4096.0))*6250.0/var1;
    var1 = ((double)dig_P9)*p*p/2147483648.0;
    var2 = p*((double)dig_P8)/32768.0;
    p = p+(var1+var2+((double)dig_P7))/16.0;

    return p;
}

測量的基本流程

  • 初始化
    包括I2C初始化(和感測器通訊用),串列埠初始化(和上位機通訊用,檢視資料),感測器初始化,兩個通訊協議就不說了,不會的童鞋可以先去看看相關教程,下面主要說說感測器初始化:
    1. 資料全部清零:寫資料0xB6到地址0xE0;
    2. 讀晶片ID:讀地址0xD0;
    3. 設定測量控制暫存器:寫資料0xFF到地址0xF4(測量資料位20Bit,Normal Mode);
    4. 設定配置暫存器:寫資料0x00到地址0xF5(測量間隔時間0.5ms,濾波器我沒仔細看,感興趣童鞋自己研究);
    5. 讀取補償值資料
  • I2C迴圈讀取感測器引數,代入公式計算獲得結果,並且將結果通過串列埠輸出到上位機。

STC8A8K64S4A12微控制器程式(51微控制器,STM32等改一下就行了)

注意串列埠通訊引腳用的RXD:P3.0,TXD:P3.1(波特率9600,8位資料位,1位停止位,無奇偶校驗位),I2C引腳用的SCL:P1.5,SDA:P1.4,晶片供電電源用的3.3V

#include "intrins.h"
#include "stc8.h"

#define BMP280_ADDR         0xec
#define BMP280_TEMP_ADDR    0xfa
#define BMP280_PRESS_ADDR   0xf7

unsigned short dig_T1;
short dig_T2;
short dig_T3;
unsigned short dig_P1;
short dig_P2;
short dig_P3;
short dig_P4;
short dig_P5;
short dig_P6;
short dig_P7;
short dig_P8;
short dig_P9;


#define FOSC    11059200UL
#define BRT     (256 - FOSC / 9600 / 32)
bit Uart1_BusyFlag;
char bufferPtr;
char Uart1Buffer[16];


void delay_ms(int x)
{
    unsigned char i, j;

    while(x-- > 0)
    {
        i = 15;
        j = 90;
        do
        {
            while (--j);
        } while (--i);
    }
}

void Uart1Int() interrupt 4 using 1
{

    if(TI)
    {
        TI = 0;
        Uart1_BusyFlag = 0;
    }
    if(RI)
    {
        RI = 0;
        Uart1Buffer[bufferPtr++] = SBUF;
    }
}

void Uart1Init()
{
    SCON = 0x50;
    TMOD = 0x20;

    TL1 = BRT;
    TH1 = BRT;
    AUXR = 0x40;
    TR1 = 1;

    bufferPtr = 0;
    Uart1_BusyFlag = 0;
}

void Uart1SendByte(char dat)
{
    while(Uart1_BusyFlag);
    Uart1_BusyFlag = 1;
    SBUF = dat;
}

void Uart1SendStr(char *p)
{
    while(*p)
    {
        Uart1SendByte(*p++);
    }
}

void I2C_Wait()
{
    while(!(I2CMSST & 0x40));
        //Uart1SendStr("i2c wait...\r\n");
    I2CMSST &= ~0x40;
}

void I2C_Start()
{
    I2CMSCR = 0x01;
    I2C_Wait();
}

void I2C_SendData(char dat)
{
    I2CTXD = dat;
    I2CMSCR = 0x02;
    I2C_Wait();
}

void I2C_RecvACK()
{
    I2CMSCR = 0x03;
    I2C_Wait();
}

char I2C_RecvData()
{
    I2CMSCR = 0x04;
    I2C_Wait();
    return(I2CRXD);
}

void I2C_SendACK()
{
    I2CMSST = 0x00;
    I2CMSCR = 0x05;
    I2C_Wait();
}

void I2C_SendNAK()
{
    I2CMSST = 0x01;
    I2CMSCR = 0x05;
    I2C_Wait();
}

void I2C_Stop()
{
    I2CMSCR = 0x06;
    I2C_Wait();
}

void I2C_Init()
{
    P_SW2 = 0x80;
    I2CCFG = 0xe0;
    I2CMSST = 0x00;
}

void printHex(unsigned char i)
{
    unsigned char j;

    for(j = 0;j < 8;j++)
    {
        if(j == 4)
        {
            Uart1SendByte(' ');
        }

        if(i & 0x80)
        {
            Uart1SendByte('1');
        }
        else
        {
            Uart1SendByte('0');
        }
        i <<= 1;
    }

    Uart1SendByte(' ');
}

unsigned char bmp280_ReadByte(unsigned char addr)
{
    unsigned char temp;

    I2C_Start();
    I2C_SendData(BMP280_ADDR);
    I2C_RecvACK();
    I2C_SendData(addr);
    I2C_RecvACK();

    I2C_Start();
    I2C_SendData(BMP280_ADDR + 1);
    I2C_RecvACK();
    temp = I2C_RecvData();
    I2C_SendNAK();
    I2C_Stop();

    return temp;
}

void bmp280_WriteByte(unsigned char addr, unsigned char dat)
{
    I2C_Start();
    I2C_SendData(BMP280_ADDR);
    I2C_RecvACK();
    I2C_SendData(addr);
    I2C_RecvACK();
    I2C_SendData(dat);
    I2C_RecvACK();
    I2C_Stop();
}

long bmp280_MultipleReadThree(unsigned char addr)
{
    unsigned char msb, lsb, xlsb;
    long temp = 0;
    msb = bmp280_ReadByte(addr);
    lsb = bmp280_ReadByte(addr + 1);
    xlsb = bmp280_ReadByte(addr + 2);

    temp = (long)(((unsigned long)msb << 12)|((unsigned long)lsb << 4)|((unsigned long)xlsb >> 4));

    return temp;
}

short bmp280_MultipleReadTwo(unsigned char addr)
{
    unsigned char msb, lsb;
    short temp = 0;
    lsb = bmp280_ReadByte(addr);
    msb = bmp280_ReadByte(addr + 1);

    temp = (short)msb << 8;
    temp |= (short)lsb;

    return temp;
}

void bmp280_Init(void)
{
    unsigned char temp = 0;

    //狀態全部清零
    bmp280_WriteByte(0xe0, 0xb6);

    //讀取ID的時候不知道為啥讀不出來了,索性跳過去了
//  temp = bmp280_ReadByte(0xd0);
//  if(temp == 0x58)
//      Uart1SendStr("bmp280 id is right...\r\n");
//  else
//      Uart1SendStr("bmp280 id is error...\r\n");

    bmp280_WriteByte(0xf4, 0xff);
    bmp280_WriteByte(0xf5, 0x00);

    dig_T1 = bmp280_MultipleReadTwo(0x88);
    dig_T2 = bmp280_MultipleReadTwo(0x8A);
    dig_T3 = bmp280_MultipleReadTwo(0x8C);
    dig_P1 = bmp280_MultipleReadTwo(0x8E);
    dig_P2 = bmp280_MultipleReadTwo(0x90);
    dig_P3 = bmp280_MultipleReadTwo(0x92);
    dig_P4 = bmp280_MultipleReadTwo(0x94);
    dig_P5 = bmp280_MultipleReadTwo(0x96);
    dig_P6 = bmp280_MultipleReadTwo(0x98);
    dig_P7 = bmp280_MultipleReadTwo(0x9A);
    dig_P8 = bmp280_MultipleReadTwo(0x9C);
    dig_P9 = bmp280_MultipleReadTwo(0x9E);

    delay_ms(200);        
}

//  dig_T1 = 27504;
//  dig_T2 = 26435;
//  dig_T3 = -1000;
//  dig_P1 = 36477;
//  dig_P2 = -10685;
//  dig_P3 = 3024;
//  dig_P4 = 2855;
//  dig_P5 = 140;
//  dig_P6 = -7;
//  dig_P7 = 15500;
//  dig_P8 = -14600;
//  dig_P9 = 6000;
//  adc_T = 519888;
//  adc_P = 415148;

long bmp280_GetValue(void)
{
    long adc_T;
    long adc_P;
    long var1, var2, t_fine, T, p;

    adc_T = bmp280_MultipleReadThree(BMP280_TEMP_ADDR);
    adc_P = bmp280_MultipleReadThree(BMP280_PRESS_ADDR);

    if(adc_P == 0)
    {
        return 0;
    }

    //Temperature
    var1 = (((double)adc_T)/16384.0-((double)dig_T1)/1024.0)*((double)dig_T2);
    var2 = ((((double)adc_T)/131072.0-((double)dig_T1)/8192.0)*(((double)adc_T)
                /131072.0-((double)dig_T1)/8192.0))*((double)dig_T3);

    t_fine = (unsigned long)(var1+var2);

    T = (var1+var2)/5120.0;

    var1 = ((double)t_fine/2.0)-64000.0;
    var2 = var1*var1*((double)dig_P6)/32768.0;
    var2 = var2 +var1*((double)dig_P5)*2.0;
    var2 = (var2/4.0)+(((double)dig_P4)*65536.0);
    var1 = (((double)dig_P3)*var1*var1/524288.0+((double)dig_P2)*var1)/524288.0;
    var1 = (1.0+var1/32768.0)*((double)dig_P1);
    p = 1048576.0-(double)adc_P;
    p = (p-(var2/4096.0))*6250.0/var1;
    var1 = ((double)dig_P9)*p*p/2147483648.0;
    var2 = p*((double)dig_P8)/32768.0;
    p = p+(var1+var2+((double)dig_P7))/16.0;

    return p;
}

void main()
{
    long temp;
    unsigned char u8;

    I2C_Init();
    Uart1Init();
    bmp280_Init();
    ES = 1;
    EA = 1;

    while(1)
    {
        temp = bmp280_GetValue();


        Uart1SendStr("test : ");
        u8 = (temp >> 24) & 0xff;
        printHex(u8);
        u8 = (temp >> 16) & 0xff;
        printHex(u8);
        u8 = (temp >> 8) & 0xff;
        printHex(u8);
        u8 = (temp) & 0xff;
        printHex(u8);

        Uart1SendStr("\r\n\r\n\r\n");

        delay_ms(500);
    }
}