天天看點

外設驅動庫開發筆記45:MS4515DO壓力傳感器驅動

  很多時候我們需要檢測流量和壓力這些參數,比如我們要檢測大氣壓,或者通過測量差壓來獲得輸送流體的流量等,都需要用到壓力傳感器。這一篇我們就來讨論MS4515DO壓力傳感器的資料擷取。

1、功能概述

  MS4515DO是TE公司推出的一款基于PCB安裝的小型陶瓷基壓力傳感器。該傳感器采用最新的CMOS傳感器調節電路,制造出一種低成本、高性能的數字輸出壓力(14bit)和溫度(11bit)傳感器,以滿足OEM客戶最嚴格的要求。

  MS4515DO完全校準和溫度補償,總誤差帶在補償範圍内小于1.0%。該傳感器采用直流3.3V或5.0V單電源供電模式,對外接口采用I2C總線或三線SPI的模式。其結構圖如下:

外設驅動庫開發筆記45:MS4515DO壓力傳感器驅動

  MS4515DO和MS4525DO擁有相同的功能和模式,差別隻在于輸出的實體量機關不同而已。它們都擁有可以檢測差壓和絕壓的型号,但操作是完全一樣的,是以本篇的讨論事實上适用于相關系列的全部型号的應用。

1.1、MS4515DO的I2C位址

  作為I2C接口的裝置都會有一個裝置位址,MS4515DO壓力傳感器也不例外。而MS4515DO和MS4525DO系列傳感器的I2C位址在出廠時已特定寫入,并根據型号中的字母來訓示其位址設定。具體如下:

外設驅動庫開發筆記45:MS4515DO壓力傳感器驅動

  預設的裝置位址是7位的,不包含讀寫位的訓示。我們使用時需要将其左移一位并根據讀寫操作來定義讀寫位,0為寫,1為讀。

1.2、資料輸出格式

  在I2C通訊模式下,MS4515DO和MS4525DO傳感器有四個I2C讀取指令,分别為:Read_MR、Read_DF2、Read_DF3和Read_DF4。這四個指令可以擷取不同的資料,這些指令的具體封包格式定義如下圖:

外設驅動庫開發筆記45:MS4515DO壓力傳感器驅動

  是以我們想要擷取MS4515DO和MS4525DO傳感器的資料就需要通過上述指令來實作。從上述的指令封包格式可以看出,這些指令在本質上是沒有差别的,都多少資料完全由主機來控制,也就是我們開發的驅動程式來控制。事實上,我們隻需要考慮Read_DF4這個指令就可以涵蓋所有想要的資料。

  我們需要注意的是,上述的封包中有兩位存儲的是狀态資訊,該狀态資訊表示擷取的資料是最新的資料還是舊資料或者錯誤報警。通過判斷這個資料可以決定我們在資料解析時如何處理相應的封包。

2、驅動設計與實作

  我們已經了解了MS4515DO和MS4525DO傳感器的結構、接口方式、裝置位址以及資料輸出格式。接下來我們就可以考慮如何實作MS4515DO和MS4525DO傳感器的驅動程式了。

2.1、對象定義

  我們依然還是先來考慮MS4515DO和MS4525DO傳感器的對象定義。我們定義一個對象無非考慮屬性和操作兩個部分。

  首先我們來考慮MS4515DO和MS4525DO傳感器對象的屬性。首先MS4515DO和MS4525DO傳感器采用I2C接口通訊,是以每台都有一個裝置位址。這個位址辨別了I2C總線上該裝置的唯一性,是以我們将裝置位址作為MS4515DO和MS4525DO傳感器對象的一個屬性。對于MS4515DO和MS4525DO傳感器來說存在多種類型,而不同的類型對應不同的資料計算方式,是以針對某一具體執行個體,我們需要記錄它的類型,是以我們為其定義一個類型屬性。我們在計算壓力值時,不同的量程最後得到的壓力值與測量量程有關,是以我們還需要記錄執行個體的量程上下限,是以将這兩個資料也定義為對象的屬性。為了操作友善我們将最終得到的溫度和壓力資料也都作為對象的屬性。

  從前面的描述中,我們知道MS4515DO和MS4525DO傳感器的資料輸出格式是固定的,這為我們解析這一資料提供了思路。我們将讀出的4個位元組與我們想要得到的資料組成聯合體,利用結構體和聯合體在記憶體中的關系可以友善的解析資料對象,如下圖所示:

外設驅動庫開發筆記45:MS4515DO壓力傳感器驅動

  這些個資料即是我們想要的先要得到的,同時他們也記錄了MS4515DO和MS4525DO傳感器對象目前的狀态,是以我們将其也作為對象的屬性。

  其次我們來考慮MS4515DO和MS4525DO傳感器對象的操作。我們需要将對象的哪些行為定義為操作呢?一般的我們考慮那些不能直接實作,而是要依賴特定的軟硬體平台才能實作的對象行為。我們需要向MS4515DO和MS4525DO傳感器發送指令,也需要從傳感器擷取對象,而無論讀還是寫都是依賴于具體的軟硬體平台才能去定的,是以我們将向傳感器寫資訊和從傳感器讀資訊作為對象的2個操作。為了控制時序,我們一般需要示範處理函數,而示範處理函數的實作也是依賴于具體的軟硬體平台的,是以我們将延時函數定義為對象的一個操作。

  我們分析了MS4515DO和MS4525DO傳感器對象可能的屬性和操作。根據前述的分析,我們可以定義MS4515DO和MS4525DO傳感器對象的類型如下:

/* 定義MS45x5DO對象類型 */
typedef struct MS45x5DOObject {
    uint8_t devAddress;   //裝置位址
    union {
        struct {
            uint16_t pressure:14;
            uint16_t status:2;
            uint16_t insignificance:5;
            uint16_t temperature:11;            
        }pData;
        uint8_t rData[4];
    }msData;            //讀出的數值
    MS45x5DOType type;  //MS4515DO的類型
    float pUpperRange;  //壓力量程上限
    float pLowerRange;  //壓力量程下限
    float fTemperature; //計算的溫度值
    float fPressure;    //計算的壓力值
    void (*Write)(struct MS45x5DOObject *ms,uint8_t *wData,uint16_t wSize); //向MS45x5DO寫資料
    void (*Read)(struct MS45x5DOObject *ms,uint8_t *rData,uint16_t rSize);  //從MS45x5DO讀資料
    void (*Delayms)(volatile uint32_t nTime);     //毫秒秒延時函數
}MS45x5DOObjectType;      

  我們定義了MS4515DO和MS4525DO傳感器對象的類型,使用該類型我們可以定義我們想要的對象變量,但對象變量需要進行必要的配置才能真正的執行個體化,這個過程我們将其稱之為對象的初始化。

/* 初始化MS45x5DO對象 */
void MS45x5DOInitialization(MS45x5DOObjectType *ms, //MS5837對象
                            uint8_t devAddress,     //裝置位址
                            MS45x5DOType type,      //MS4515DO的類型
                            float pMax,             //壓力量程上限
                            float pMin,             //壓力量程下限
                            MS45x5DOWrite write,    //向MS45x5DO寫資料函數指針
                            MS45x5DORead read,      //從MS45x5DO讀資料函數指針
                            MS45x5DODelayms delayms //毫秒延時函數指針
                                )
{
    if((ms==NULL)||(write==NULL)||(read==NULL)||(delayms==NULL))
    {
        return; 
    }
    
    ms->Write=write;
    ms->Read=read;
    ms->Delayms=delayms;
    
    if((devAddress==0x28)||(devAddress==0x36)||(devAddress==0x46)||((0x48<=devAddress)&&(devAddress<=0x51)))
    {
        ms->devAddress=(devAddress<<1);
    }
    else if((devAddress==0x50)||(devAddress==0x6C)||(devAddress==0x8C)||((0x48<=(devAddress/2))&&((devAddress/2)<=0x51)))
    {
        ms->devAddress=devAddress;
    }
    else
    {
        ms->devAddress=0x00;
    }
    
    ms->type=type;
    
    ms->fPressure=0.0;
    ms->fTemperature=0.0;
    ms->msData.rData[0]=0;
    ms->msData.rData[1]=0;
    ms->msData.rData[2]=0;
    ms->msData.rData[3]=0;
    
    if((fabs(pMax)<=0.0000001)&&(fabs(pMin)<=0.0000001))
    {
        ms->pUpperRange=100.0;
        ms->pLowerRange=0.0;
    }
    else
    {
        ms->pUpperRange=pMax;
        ms->pLowerRange=pMin;
    }
}      

2.2、對象操作

  我們已經可以得到一個對象變量并将它執行個體化,我們還需要考慮它的操作問題。對于MS4515DO和MS4525DO傳感器來說其操作比較簡單,最主要的操作包括資料擷取和位址設定。

2.2.1、擷取資料

  對于我們來說擷取MS4515DO和MS4525DO傳感器的測量資料是我們的主要目的。我們可以從MS4515DO和MS4525DO傳感器擷取壓力和溫度資料,其測量範圍與輸出資料的對應關系如下圖所示:

外設驅動庫開發筆記45:MS4515DO壓力傳感器驅動
外設驅動庫開發筆記45:MS4515DO壓力傳感器驅動

  根據上表中的資料對應關系,我們可以編寫擷取MS4515DO和MS4525DO傳感器的資料并解析的函數。

/*擷取轉換值,包括溫度和壓力*/
void GetMS45x5DOConversionValue(MS45x5DOObjectType *ms)
{
    uint8_t rData[4]={0,0,0,0};
    float maxCount=16383;
    float minCount=0;
    
    if(ms->type==MS45x5DO_TypeA)
    {
        maxCount=13106;
        minCount=1638;
    }
    else
    {
        maxCount=14746;
        minCount=819;
    }
    
    ms->Read(ms,rData,4);
    
    ms->msData.rData[0]=rData[1];
    ms->msData.rData[1]=rData[0];
    ms->msData.rData[2]=rData[3];
    ms->msData.rData[3]=rData[2];
    
    if(ms->msData.pData.status!=MS45x5DO_Fault)
    {
        ms->fPressure=(((float)ms->msData.pData.pressure-minCount)/maxCount)*(ms->pUpperRange-ms->pLowerRange)+ms->pLowerRange;
        ms->fTemperature=((float)ms->msData.pData.temperature/2047.0)*200.0-50.0;
    }
}      

2.2.2、位址設定

  關于MS4515DO和MS4525DO傳感器,在出廠時已經設定了裝置位址并在型号編碼中給予訓示。但在一些特殊情形下我們可能需要修改它的裝置位址,這就需要用到MS4515DO和MS4525DO傳感器的位址修改操作。

/*修改MS45x5DO的裝置位址*/
void ModifyMS45x5DODecAddress(MS45x5DOObjectType *ms,uint8_t newAddress)
{
    uint8_t eepromByte[3];
    uint16_t eepromTemp=0x00;
    
    //第1步、進入指令模式
    eepromByte[0]=0xA0;
    eepromByte[1]=0x00;
    eepromByte[2]=0x00;
    
    ms->Write(ms,eepromByte,3);
    
    //第2步、發送讀EEPROM指令
    eepromByte[0]=0x02;
    eepromByte[1]=0x00;
    eepromByte[2]=0x00;
    
    ms->Write(ms,eepromByte,3);
     
    //第3步、擷取EEPROM的值
    ms->Read(ms,eepromByte,3);
    
    //第4步、修改為新位址
    if(eepromByte[0]==0x5A)
    {
        eepromTemp=(eepromByte[1]<<8)+eepromByte[2];
        eepromTemp=(eepromTemp&0xE007)+0xC00+(newAddress<<3);
        
        eepromByte[1]=(uint8_t)((eepromTemp&0xFF00)>>8);
        eepromByte[1]=(uint8_t)(eepromTemp&0x00FF);
    }
    else
    {
        return;
    }
    
    //第5步、将新位址寫入EEPROM
    eepromByte[0]=0x02;
    
    ms->Write(ms,eepromByte,3);
    
    //第6步、退出指令模式
    eepromByte[0]=0x80;
    eepromByte[1]=0x00;
    eepromByte[2]=0x00;
    
    ms->Write(ms,eepromByte,3);
}      

3、驅動的使用

  我們已經設計并實作了MS4515DO和MS4525DO壓力傳感器的驅動程式。接下來我們将簡單的說明如何使用這一驅動,并設計一個簡單的示例驗證這一驅動程式的正确性。

3.1、聲明并初始化對象

  我們是基于對象設計的MS4515DO和MS4525DO壓力傳感器的驅動程式,是以在使用驅動時,我們需要先聲明一個對象變量,然後基于該對象變量來實作具體的對象操作。我們先聲明對象如下:

MS45x5DOObjectType msDP;      

  聲明了這個對象變量之後,我們還需要使用初始化函數對其進行初始化方可使用。這一初始化函數擁有8個參數:

MS45x5DOObjectType *ms, //MS5837對象
uint8_t devAddress,     //裝置位址
MS45x5DOType type,      //MS4515DO的類型
float pMax,             //壓力量程上限
float pMin,             //壓力量程下限
MS45x5DOWrite write,    //向MS45x5DO寫資料函數指針
MS45x5DORead read,      //從MS45x5DO讀資料函數指針
MS45x5DODelayms delayms //毫秒延時函數指針      

  第一個參數正是我們要初始化的對象變量。第二個參數為我們所要操作的MS4515DO對象的裝置位址。第三個參數是MS4515DO對象的具體類型,根據實際裝置選擇枚舉即可。第四和第五個參數是該對象的實體量量程,根據具體對象而定。後面三個參數是實作對象操作的函數指針。這三個函數指針需要我們根據具體的軟硬體平台來實作。它們的原型如下:

/*向MS45x5DO下發指令,指令格式均為1個位元組*/
typedef void (*MS45x5DOWrite)(struct MS45x5DOObject *ms,uint8_t *wData,uint16_t wSize);
/*從MS45x5DO讀取多個位元組資料的值*/
typedef void (*MS45x5DORead)(struct MS45x5DOObject *ms,uint8_t *rData,uint16_t rSize);
/*毫秒秒延時函數*/
typedef void (*MS45x5DODelayms)(volatile uint32_t nTime);      

  我們根據函數原型定義,在具體的實作平台上實作它們,如我們在STM32平台上可以實作如下:

/*向MS45x5DO下發指令,指令格式均為1個位元組*/
static void WriteToDP(MS45x5DOObjectType *ms,uint8_t *wData,uint16_t wSize)
{
    HAL_I2C_Master_Transmit(&hi2c2,ms->devAddress,wData,wSize,1000);
}
/*從MS45x5DO讀取多個位元組資料的值*/
static void ReadFromDP(MS45x5DOObjectType *ms,uint8_t *rData,uint16_t rSize)
{
    HAL_I2C_Master_Receive(&hi2c2,ms->devAddress,rData, rSize, 1000);
}      

  延時函數我們可以直接使用HAL庫中的HAL_Delay也可以自己編寫,在HAL庫中HAL_Delay是一個弱化定義的函數,我們可以重寫這一函數來實作不同的應用需求。到這裡我們就可以使用對象初始化函數來初始化前面聲明的對象變量了。具體如下:

MS45x5DOInitialization(&msDP, //MS5837對象
                     0x28,     //裝置位址
                     MS45x5DO_TypeA,      //MS4515DO的類型
                     DPUpperRange,          //壓力量程上限
                     DPLowerRange,          //壓力量程下限
                     WriteToDP,    //向MS45x5DO寫資料函數指針
                     ReadFromDP,   //從MS45x5DO讀資料函數指針
                     HAL_Delay //毫秒延時函數指針
                     );      

3.2、基于對象進行操作

  完成了對象的初始化後,我們就可以基于對象來實作相應的操作了。如我們使用驅動擷取msDP對象的差壓資料如下:

/*差壓資料擷取*/
void GetFlowDPDatas(void)
{
    GetMS45x5DOConversionValue(&msDP);
    
    aPara.phyPara.dPressure =msDP.fPressure;
    aPara.phyPara.dTemperature=msDP.fTemperature;
}      

4、應用總結

  我們設計并實作了MS4515DO和MS4525DO壓力傳感器對象的驅動程式,并基于驅動程式實作了一個簡單的測試執行個體,獲得的結果如下:

外設驅動庫開發筆記45:MS4515DO壓力傳感器驅動
外設驅動庫開發筆記45:MS4515DO壓力傳感器驅動

  從上述兩圖中我們可以知道我們的驅動程式是正确的。事實上這一驅動已應用于我們的流量測量裝置中,實作的效果良好。

歡迎關注: