1. 程式人生 > 實用技巧 >外設驅動庫開發筆記19:BMP280壓力溫度感測器驅動

外設驅動庫開發筆記19:BMP280壓力溫度感測器驅動

  壓力和溫度監測在嵌入式系統開發中是非常常見的需求,特別是對環境大氣壓力和溫度的檢測需求就更常見了。我們一般都會選擇一些封裝較小操作比較方便的壓力感測器。BMP280就是滿足這一要求的器件。在這一篇中我們將設計並實現BMP280的驅動。

1、功能概述

  BMP280是一款絕對壓力感測器產品。BMP280是一款絕對的氣壓感測器,專為移動應用而設計。感測器模組採用極其緊湊的封裝。其小尺寸和低功耗允許在諸如行動電話,GPS模組或手錶的電池供電裝置中實現。

1.1、硬體介面

  BMP280基於博世經過驗證的壓阻式壓力感測器技術,具有高精度和線性度以及長期穩定性和高EMC穩健性。眾多器件操作選項提供了最高的靈活性,可針對功耗,解析度和濾波器效能優化器件。為開發人員提供了一組經過測試的預設設定(例如用例),以便儘可能簡化設計。

  BMP280壓力溫度感測器採用了小巧的8引腳LGA封裝形式。其引腳排布就功能如下圖所示:

  BMP280壓力溫度感測器支援3種通訊介面方式:四線SPI、三線SPI以及I2C。在不同的介面模式下,各引腳的定義也是有差異的,關於這三種介面模式各引腳的定義如下:

  對應3種不同的介面方式,BMP280壓力溫度感測器存在三種與匯流排連線的方式。首先我們來看四線SPI介面方式,包括CSB片選、SCK時鐘、SDI數字輸入、SDO數字輸出。其匯流排連線方式如下圖:

  接下來我們來看三線SPI介面方式,包括CSB片選、SCK時鐘、SDI數字輸入/SDO數字輸出。其與4線SPI的區別是數字輸入輸出使用同一引腳,第3腳就是輸入也是輸出,而第5腳浮空。其匯流排連線方式如下圖:

  最後我們來看I2C介面方式,包括SCL時鐘、SDA數字輸入輸出。在I2C介面模式下,第2腳CSB連線到高電平,以設定BMP280壓力溫度感測器使用I2C介面。而第5腳則可以通過連線高電平或低電平來設定裝置地址的最後一位,不可以浮空。所以根據第5腳電頻不同,BMP280壓力溫度感測器的I2C裝置7位地址為:0x76和0x77。其匯流排連線方式如下圖:

  BMP280壓力溫度感測器在使用SPI介面時,支援SPI模式0(CPOL=CPHA=0)和模式3(CPOL=CPHA=1)。而在使用I2C介面時,支援標準模式、快速模式以及高速模式。介面的選擇實際上是通過CSB的電位實現的,低電平時就是SPI,高電平時就是I2C。

1.2、資料儲存結構

  對BMP280壓力溫度感測器的所有操作都是通過讀寫對應的暫存器來實現的。BMP280壓力溫度感測器中所有的暫存器都是8位的。這些暫存器在儲存器中的地址分配如下圖所示。

  在上圖並未包括系統保留的暫存器,這些暫存器不可以進行寫操作,讀出來的值也是無意義的。接下來我們來詳細描述上圖中的這些暫存器。

  先來看看兩個比較特殊的暫存器。首先是ID暫存器,這個暫存器是隻讀的,而且其儲存的值也固定為0x58,用來代表裝置為BMP280壓力溫度感測器。這個暫存器在系統上電後即可讀取。還有復位暫存器,這個暫存器是隻寫的,固定向其寫0xB6來實現BMP280壓力溫度感測器的復位。同樣只要系統上電後即可以寫復位暫存器。

  狀態暫存器是隻讀的,其實只使用了其中的兩位,這兩位分別表示資料測量是否完成和影響暫存器是否更新。下圖是狀態暫存器的詳細說明:

  測量控制暫存器是可讀寫的,用以配置BMP280壓力溫度感測器資料獲取的方式。分別配置溫度取樣、壓力取樣和工作模式。工作模式有三種:休眠模式、強制模式、正常模式。系統上電後即為休眠模式,通過這一暫存器的配置可以使BMP280壓力溫度感測器進入強制模式或正常模式執行。測量控制暫存器的各位定義如下圖:

  配置暫存器用於設定BMP280壓力溫度感測器的速率、過濾器以及介面模式。在休眠模式下寫配置暫存器是允許的,但在正常模式下會被忽略,所以在系統復位後,進入正常模式前先寫配置暫存器。配置暫存器各位的定義如下圖所示:

  壓力資料暫存器儲存有壓力測量資料輸出的原始值。使用了三個暫存器中的20位來下儲存壓力資料。壓力資料暫存器各位的定義如下圖所示:

  溫度資料暫存器儲存有溫度測量資料輸出的原始值。使用了三個暫存器中的20位來下儲存溫度資料。溫度資料暫存器各位的定義如下圖所示:

  此外還有校準資料暫存器,總共是26個暫存器,儲存了計算壓力溫度最終值的廠家校準資料。這些校準暫存器的定義及地址分配如下圖所示:

  我們已經說過面向BMP280壓力溫度感測器的所有操作都是基於暫存器進行的,我們已經瞭解了BMP280壓力溫度感測器的各個暫存器,現在可以來實現它的操作了。

2、驅動設計與實現

  我們已經比較詳細的說明了BMP280的引腳定義、通訊介面、資料儲存格式,在此基礎上我們將設計並實現BMP280壓力溫度感測器的驅動程式。

2.1、物件定義

  在使用一個物件之前我們需要獲得一個物件。同樣的我們想要BMP280壓力溫度感測器就需要先定義BMP280壓力溫度感測器的物件。

2.1.1、物件型別抽象

  我們要得到BMP280壓力溫度感測器物件,需要先分析其基本特性。一般來說,一個物件至少包含兩方面的特性:屬性與操作。接下來我們就來從這兩個方面思考一下BMP280壓力溫度感測器的物件。

  先來考慮屬性,作為屬性肯定是用於標識或記錄物件特徵的東西。我們來考慮BMP280壓力溫度感測器物件屬性。BMP280壓力溫度感測器的ID暫存器用於標識裝置是否為BMP280;配置暫存器和測量控制暫存器都用關於系統配置,指示了裝置的工作狀態,所以我們將這三個暫存器定義為物件的屬性。而使用的通訊介面決定了訪問BMP280壓力溫度感測器的行為,所以我們需要記住這一配置;而校準資料則在計算資料時所要使用的,我們也需要記住這些引數,所以我們將它們也都定義為屬性。在I2C介面模式時,裝置地址是區分總線上裝置的唯一標誌,所以我們將其定義為屬性。同樣測量資料指示了裝置當前的工作狀態,我們將器作為屬性。

  接著我們還需要考慮BMP280壓力溫度感測器物件的操作問題。我們需要與BMP280壓力溫度感測器通訊就需要向其寫資料並從其讀資料,而不論是SPI介面還是I2C介面,讀寫操作都以來與具體的硬體平臺,所以我們將他們作為物件的操作。此外,為控制時序,我們需要延時操作,而延時行為的實現亦依賴於具體的軟硬體平臺,所以我們將延時也作為物件的操作。

  根據上述我們對BMP280壓力溫度感測器的分析,我們可以定義BMP280壓力溫度感測器的物件型別如下:

 1 /*定義BMP280操作物件*/
 2 typedef struct BMP280Object{
 3        uint8_t bmpAddress; //I2C介面時裝置地址
 4        uint8_t chipID;   //晶片ID
 5        uint8_t config;    //配置暫存器
 6        uint8_t ctrlMeas; //測量控制暫存器
 7        BMP280PortType port;    //介面選擇
 8        Bmp280CalibParamType caliPara;       //校準引數
 9        float pressure;     //壓力值
10        float temperature;      //溫度值
11        void (*Read)(struct BMP280Object *bmp,uint8_t regAddress,uint8_t *rData,uint16_t rSize);  //讀資料操作指標
12        void (*Write)(struct BMP280Object *bmp,uint8_t regAddress,uint8_t command);   //寫資料操作指標
13        void (*Delayms)(volatile uint32_t nTime);    //延時操作指標
14        void (*ChipSelect)(BMP280CSType en);   //使用SPI介面時,片選操作
15 }BMP280ObjectType;

2.1.2、物件初始化

  我們知道,一個物件僅作宣告是不能使用的,我們需要先對其進行初始化,所以這裡我們來考慮BMP280壓力溫度感測器物件的初始化函式。一般來說,初始化函式需要處理幾個方面的問題。一是檢查輸入引數是否合理;二是為物件的屬性賦初值;三是對物件作必要的初始化配置。據此我們設計BMP280壓力溫度感測器物件的初始化函式如下:

 1 /* 實現BMP280初始化配置 */
 2 void BMP280Initialization(BMP280ObjectType *bmp,       //BMP280物件
 3                           uint8_t bmpAddress,             //I2C介面是裝置地址
 4                           BMP280PortType port,                                    //介面選擇
 5                           TimeStandbyType t_sb,                                    //間隔週期
 6                           IIRFilterCoeffType filter,                   //過濾器
 7                           UseSPI3wType spi3W_en,            //3線SPI控制
 8                           TemperatureSampleType osrs_t,        //溫度精度
 9                           PressureSampleType osrs_p,                     //壓力精度
10                           PowerModeType mode,                   //電源模式
11                           BMP280Read Read,                    //讀資料操作指標
12                           BMP280Write Write,           //寫資料操作指標
13                           BMP280Delayms Delayms,               //延時操作指標
14                           BMP280ChipSelect ChipSelect                   //片選操作指標
15                           )
16 {
17        uint8_t try_count = 5;
18        uint8_t regAddress=0;
19        uint8_t command=0;
20  
21        bmp->chipID=0x00;
22        bmp->pressure=0.0;
23        bmp->temperature=0.0;
24        bmp->bmpAddress=0x00;
25        bmp->port=port;
26        if(bmp->port==I2C)
27        {
28               if((bmpAddress==0xEC)||(bmpAddress==0xEE))
29               {
30                      bmp->bmpAddress=bmpAddress;
31               }
32               bmp->ChipSelect=NULL;
33        }
34        else
35        {
36               bmp->ChipSelect=ChipSelect;
37        }
38        bmp->Read=Read;
39        bmp->Write=Write;
40        bmp->Delayms=Delayms;
41        bmp->caliPara.t_fine=0;
42       
43       if(!ObjectIsValid(bmp))
44       {
45              return;
46       }
47  
48       while(try_count--)
49       {
50             bmp->chipID=ReadBMP280Register(bmp,REG_BMP280_ID);
51             if(0x58==bmp->chipID)
52             {
53                    BMP280SoftReset(bmp);
54      
55                    break;
56             }
57       }
58  
59       if(try_count)
60       {
61              /*配置配置暫存器:間隔週期0.5ms、IIR濾波係數16、不使用SPI3線通訊*/
62               regAddress=REG_CONFIG;
63               command=t_sb|filter|spi3W_en;
64               WriteBMP280Register(bmp,regAddress,command);
65  
66               /*配置測量控制暫存器:溫度20位,壓力20位,電源正常模式*/
67               regAddress=REG_CTRL_MEAS;
68               command=osrs_t|osrs_p|mode;
69               WriteBMP280Register(bmp,regAddress,command);
70  
71               bmp->Delayms(10);
72               bmp->config=ReadBMP280Register(bmp,REG_CONFIG);
73               bmp->Delayms(10);
74               bmp->ctrlMeas=ReadBMP280Register(bmp,REG_CTRL_MEAS);
75               bmp->Delayms(10);
76               /*讀取校準值*/
77               GetBMP280CalibrationData(bmp);
78       }
79 }

2.2、物件操作

  我們已經完成了BMP280壓力溫度感測器物件型別的定義和物件初始化函式的設計。但我們的主要目標是獲取物件的資訊,接下來我們還要實現面向BMP280壓力溫度感測器的各類操作。

2.2.1、寫暫存器

  我們已經說過了,對BMP280的操作都是通過讀寫暫存器實現的。這裡我們先來看寫暫存器。在I2C介面方式下,寫暫存器操作是在從站地址的最後一位來識別的,再加上要寫的暫存器地址和資料來實現的,這也是I2C協議的標準做法。其時序圖如下所示:

  而在SPI介面方式下,由於SPI並未有裝置地址,也不存在用從還在那地址最後為來標記讀寫的模式。通常一些裝置需要定義操作碼來實現讀寫區分,但BMP280採取了將暫存器地址的最高位置零表示為寫。之所以可以這樣定義,是因為BMP280暫存器地址分配的特殊性決定的。改變暫存器地址的最高位也能區分不同的暫存器,絕不會重複。在SPI介面方式下,寫暫存器的時序圖如下所示:

  根據上述描述和時序圖,我們可以實現寫BMP280壓力溫度感測器暫存器的程式。

 1 /* 向BMP280暫存器寫一個位元組 */
 2 static void WriteBMP280Register(BMP280ObjectType *bmp,uint8_t regAddress,uint8_t command)
 3 {
 4        if(ObjectIsValid(bmp))
 5        {
 6               if(bmp->port==BMP280_SPI)
 7               {
 8                      regAddress&=0x7F;
 9                      bmp->ChipSelect(BMP280CS_Enable);
10                      bmp->Delayms(1);
11                      bmp->Write(bmp,regAddress,command);
12                      bmp->Delayms(1);
13                      bmp->ChipSelect(BMP280CS_Disable);
14               }
15               else
16               {
17                      bmp->Write(bmp,regAddress,command);
18               }
19        }
20 }

2.2.2、讀暫存器

  讀暫存器的處理方式與寫暫存器是類似。在I2C介面方式下,將從站地址的最低位置1來表示讀。在I2C介面方式下,讀暫存器的時序圖如下所示:

  而在SPI介面方式下,通過將暫存器地址的最高位置1來標識為讀操作。事實上,所有暫存器地址的最高位都是1,所以在讀操作時實際不需要做處理。在SPI介面方式下,讀暫存器的時序圖如下所示:

  根據上述描述和時序圖,我們可以實現讀BMP280壓力溫度感測器暫存器的程式。

 1 /*從BMP280暫存器讀取一個位元組*/
 2 static uint8_t ReadBMP280Register(BMP280ObjectType *bmp,uint8_t regAddress)
 3 {
 4   uint8_t regValue=0xFF;
 5  
 6        if(ObjectIsValid(bmp))
 7        {
 8               if(bmp->port==BMP280_SPI)
 9               {
10                      regAddress |= 0x80;
11                      bmp->ChipSelect(BMP280CS_Enable);
12                      bmp->Delayms(1);
13                      bmp->Read(bmp,regAddress,&regValue,1);
14                      bmp->Delayms(1);
15                      bmp->ChipSelect(BMP280CS_Disable);
16               }
17               else
18               {
19                      bmp->Read(bmp,regAddress,&regValue,1);
20               }
21        }
22  
23        return regValue;
24 }

3、驅動的使用

我們已經設計了BMP280壓力溫度感測器的驅動程式,接下來這一節我們將基於BMP280壓力溫度感測器的驅動程式設計一個簡單的驗證應用。

3.1、宣告並初始化物件

  使用基於物件的操作我們需要先得到這個物件,所以我們先要使用前面定義的BMP280壓力溫度感測器物件型別宣告一個BMP280壓力溫度感測器物件變數,具體操作格式如下:

  BMP280ObjectType bmp280;

  聲明瞭這個物件變數並不能立即使用,我們還需要使用驅動中定義的初始化函式對這個變數進行初始化。這個初始化函式所需要的輸入引數如下:

  BMP280ObjectType *bmp,BMP280物件

  uint8_t bmpAddress,I2C介面是裝置地址

  BMP280PortType port,介面選擇

  BMP280TimeStandbyType t_sb,間隔週期

  BMP280IIRFilterCoeffType filter,過濾器

  BMP280UseSPI3wType spi3W_en,3線SPI控制

  BMP280TemperatureSampleType osrs_t,溫度精度

  BMP280PressureSampleType osrs_,壓力精度

  BMP280PowerModeType mode,電源模式

  BMP280Read Read,讀資料操作指標

  BMP280Write Write,寫資料操作指標

  BMP280Delayms Delayms,延時操作指標

  BMP280ChipSelect ChipSelect,片選操作指標

  對於這些引數,物件變數我們已經定義了。介面選擇、間隔週期、過濾器、3線SPI控制、溫度精度、壓力精度、電源模式等都是列舉量我們根據實際情況輸入即可。而使用I2C介面時需要的裝置地址,也按具體地址給入就好。主要的是我們需要定義幾個函式,並將函式指標作為引數。這幾個函式的型別如下:

 1 /* 定義讀資料操作函式指標型別 */
 2 typedef void (*BMP280Read)(BMP280ObjectType *bmp,uint8_t regAddress,uint8_t *rData,uint16_t rSize);
 3 
 4 /* 定義寫資料操作函式指標型別 */
 5 typedef void (*BMP280Write)(BMP280ObjectType *bmp,uint8_t regAddress,uint8_t command);
 6 
 7 /* 定義延時操作函式指標型別 */
 8 typedef  void (*BMP280Delayms)(volatile uint32_t nTime);
 9 
10 /* 定義使用SPI介面時,片選操作函式指標型別 */
11 typedef  void (*BMP280ChipSelect)(BMP280CSType cs);

  對於這幾個函式我們根據樣式定義就可以了,具體的操作可能與使用的硬體平臺有關係。若採用的SPI介面則需注意片選操作,片選操作函式用於多裝置需要軟體操作時,如採用硬體片選可以傳入NULL即可。同樣如果採用的是I2C介面,則片選可以傳入NULL即可。具體函式定義如下:

 1 /*讀BMP280暫存器值*/
 2 static void ReadDataFromBMP280(BMP280ObjectType *bmp280,uint8_t regAddress,uint8_t *rData,uint16_t rSize)
 3 {
 4   HAL_I2C_Master_Transmit(&bmp280hi2c, bmp280->bmpAddress,&regAddress,1,1000);
 5  
 6   HAL_I2C_Master_Receive(&bmp280hi2c, bmp280->bmpAddress+1,rData, rSize, 1000);
 7 }
 8  
 9 /*寫BMP280暫存器值*/
10 static void WriteDataToBMP280(BMP280ObjectType *bmp280,uint8_t regAddress,uint8_t command)
11 {
12   uint8_t pData[2];
13  
14   pData[0]=regAddress;
15   pData[1]=command;
16  
17   HAL_I2C_Master_Transmit(&bmp280hi2c,bmp280->bmpAddress, pData, 2,1000);
18 }

  對於延時函式我們可以採用各種方法實現。我們採用的STM32平臺和HAL庫則可以直接使用HAL_Delay()函式。於是我們可以呼叫初始化函式如下:

 1 BMP280Initialization(&bmp280,  //BMP280物件
 2                      0xEC,           //I2C介面是裝置地址
 3                      BMP280_I2C,     //介面選擇
 4                      BMP280_T_SB_0P5,       //間隔週期
 5                      BMP280_IIR_FILTER_COEFF_X16,         //過濾器
 6                      BMP280_SPI3W_DISABLE,                //3線SPI控制
 7                      BMP280_TEMP_SAMPLE_X16,  //溫度精度
 8                      BMP280_PRES_SAMPLE_X16,        //壓力精度
 9                      BMP280_POWER_NORMAL_MODE,      //電源模式
10                      ReadDataFromBMP280,  //讀資料操作指標
11                      WriteDataToBMP280,     //寫資料操作指標
12                      HAL_Delay,             //延時操作指標
13                      NULL                   //片選操作指標
14                      );

3.2、基於物件進行操作

  我們定義了物件變數並使用初始化函式給其作了初始化。接著我們就來考慮操作這一物件獲取我們想要的資料。我們在驅動中已經將獲取資料並轉換為轉換值的比例值,接下來我們使用這一驅動開發我們的應用例項。

 1 /*獲取大氣壓力和溫度*/
 2 void BMP280GetEnvironmentalData(void)
 3 {
 4        float pressure;                   //壓力值
 5        float temperature;      //溫度值
 6       
 7        GetBMP280Measure(&bmp280);
 8  
 9        pressure=bmp280.pressure;
10        temperature=bmp280.temperature;
11 }

4、應用總結

  BMP280壓力溫度感測器的驅動已經實現並做了簡單的應用。在我們測試時,得到的資料與其它方法獲得的溫度壓力資料基本是一致的,這說明我們的驅動程式總體來說是正確的。

  BMP280壓力溫度感測器支援SPI和I2C兩種介面,而且SPI也支援3線和4線模式,但我們在測試應用中只使用了I2C介面,SPI介面還有待測試。

  在使用驅動時需注意,採用SPI介面的器件需要考慮片選操作的問題。如果片選訊號是通過硬體電路來實現的,我們在初始化時給其傳遞NULL值。如果是軟體操作片選則傳遞我們編寫的片選操作函式。而如果採用I2C介面,那麼在初始化時也應傳遞NULL值。

  BMP280壓力溫度感測器在使用SPI介面時,支援SPI模式0(CPOL=CPHA=0)和模式3(CPOL=CPHA=1)。而在使用I2C介面時,支援標準模式、快速模式以及高速模式。而且在使用I2C介面時,SDO引腳必須接高電平或低電平,以確定裝置地址。

原始碼獲取:https://github.com/foxclever/ExPeriphDriver

歡迎關注: