紅外溫度傳感器一般用于非接觸式的溫度檢測。在我們的系統(tǒng)中經(jīng)常會有這樣的需求。所以我們將其設(shè)計(jì)為通用的驅(qū)動庫以備復(fù)用。這一篇我們將講述MLX90614紅外溫度傳感器驅(qū)動的設(shè)計(jì)與實(shí)現(xiàn)。
1 、功能概述
MLX90614是一種紅外溫度計(jì),用于非接觸式溫度測量。紅外測溫是根據(jù)被測物體的紅外輻射能量來確定物體的溫度,不與被測物體接觸,具有不影響被測物體溫度分布場,溫度分辨率高、響應(yīng)速度快、測溫范圍廣、不受測溫上限的限制、穩(wěn)定性好等特點(diǎn)。MLX90614被測目標(biāo)溫度和環(huán)境溫度通過IIC接口輸出,適合于汽車空調(diào)、室內(nèi)暖氣、家用電器、手持設(shè)備以及醫(yī)療設(shè)備應(yīng)用等。
1.1 、硬件描述
MLX90614 是一款無接觸式的紅外線溫度感應(yīng)芯片,它在同一TO-39封裝內(nèi)整合了紅外熱電堆感應(yīng)器與一款定制的信號調(diào)節(jié)芯片。MLX90614在信號調(diào)節(jié)芯片中使用了先進(jìn)的低噪音放大器,一枚17-bit ADC以及功能強(qiáng)大的DSP元件,從而實(shí)現(xiàn)高精度溫度測量。其引腳封裝及功能如下:
MLX90614的出廠校準(zhǔn)溫度范圍很廣:環(huán)境溫度為-40°C…125°C,目標(biāo)溫度為-70°C…380°C。測量值是傳感器視場中所有物體的平均溫度。在室溫下,MLX90614的標(biāo)準(zhǔn)精確度為±0.5度。有一種特殊的醫(yī)療應(yīng)用版本,在人體體溫的有限溫度范圍內(nèi)精確呈現(xiàn)±0.2恒溫。
1.2 、數(shù)據(jù)通訊
MLX90614紅外溫度傳感器具有數(shù)字PWM和SMBus(系統(tǒng)管理總線)輸出。10位PWM作為標(biāo)準(zhǔn)配置,可以在-20…120完美呈現(xiàn)測量溫度,輸出分辨率為0.14完美呈現(xiàn)。工廠默認(rèn)的設(shè)置是SMBus總線接口。
MLX90614紅外溫度傳感器又有一個參數(shù)存儲EEPROM內(nèi)存。在EEPROM內(nèi)存中有限數(shù)量的地址可以由客戶更改,這些地址存儲著傳感器可修改配置信息。整個EEPROM都可以通過SMBus接口讀取。具體分配如下:
在這個EEPROM中,我們需要注意的有PWM控制寄存器以及配置寄存器。PWMCTRL寄存器的各位定義如下:
ConfigRegister1由控制位組成,用于配置模擬和數(shù)字部分,ConfigRegister1寄存器的各位定義如下:
MLX90614紅外溫度傳感器還擁有一個32個字的RAM。用于存儲一些實(shí)時更新的數(shù)據(jù),如我們的測量數(shù)據(jù)。這一部分是只讀的,其地址分配如下:
我們了解了MLX90614紅外溫度傳感器的EEPROM和RAM的地址分配,它們的地址是分別編碼的,似乎看不出差別。那我們?nèi)绾文軄韰^(qū)別訪問它們呢?于是MLX90614紅外溫度傳感器定義有如下的功能碼:
讀寫EEPROM和RAM的命令,前三位表示命令,后5為對應(yīng)所讀參數(shù)在EEPROM和RAM中的地址。標(biāo)志位是只讀的,MLX90614在16位數(shù)據(jù)之后返回PEC,其中只有4位是有意義的,如果MD需要它,它可以在第一個字節(jié)之后停止通信。讀數(shù)據(jù)和讀標(biāo)志的區(qū)別在于后者沒有重復(fù)的起始位。
MLX90614紅外溫度傳感器設(shè)備地址默認(rèn)為0xB4,可以通過寫EEPROM來修改設(shè)備地址。
2 、驅(qū)動設(shè)計(jì)與實(shí)現(xiàn)
我們已經(jīng)了解了MLX90614紅外溫度傳感器的基本情況,接下來我們將根據(jù)這些資料開發(fā)MLX90614紅外溫度傳感器的驅(qū)動程序。
2.1 、對象定義
在使用一個對象之前我們需要獲得一個對象。同樣的我們想要使用MLX90614紅外溫度傳感器對象就需要先定義MLX90614紅外溫度傳感器的對象。
2.1.1 、對象的抽象
我們要得到MLX90614溫度傳感器對象,需要先分析其基本特性。一般來說,一個對象至少包含兩方面的特性:屬性與操作。接下來我們就來從這兩個方面思考一下MLX90614溫度傳感器的對象。
先來考慮屬性,作為屬性肯定是用于標(biāo)識或記錄對象特征的東西。我們來考慮MLX90614紅外溫度傳感器對象屬性。作為SMBus總線設(shè)備需要一個設(shè)備地址,這一地址用來區(qū)分總線上不同設(shè)備,所以我們將設(shè)備地址作為它的一個屬性。對象的狀態(tài)標(biāo)識,PWM控制寄存器、配置寄存器都只是了MLX90614紅外溫度傳感器當(dāng)前所處的狀態(tài),所以我們將它們也作為屬性。設(shè)備ID號是唯一標(biāo)識各個MLX90614紅外溫度傳感器的,我們也將其作為屬性。測量的溫度信號也表示了MLX90614紅外溫度傳感器當(dāng)前的狀態(tài),我們也將他們作為屬性。標(biāo)志寄存器在MLX90614紅外溫度傳感器中是16位的,但真正有效的只有4位,所以我們以8位表示。
接著我們還需要考慮MLX90614紅外溫度傳感器對象的操作問題。對于MLX90614紅外溫度傳感器來說,我們需要對它進(jìn)行讀數(shù)據(jù)和寫數(shù)據(jù),而讀寫操作依賴于具體的硬件平臺,所以我們將其作為對象的操作,以回調(diào)函數(shù)的方式使用。在操作MLX90614紅外溫度傳感器的過程中,有些時序需要控制,所以需要延時函數(shù),但延時操作依賴于具體的軟硬件平臺,所以我們將其作為對象的操作,以回調(diào)函數(shù)的方式使用。
根據(jù)上述我們對MLX90614紅外溫度傳感器的分析,我們可以定義MLX90614紅外溫度傳感器的對象類型如下:
typedef structMLXObject {
uint8_t devAddress; //對象的地址
uint8_t flags; //對象狀態(tài)標(biāo)志
uint16_t pwmctrl; //PWM控制寄存器
uint16_t ConfigRegister; //配置寄存器
uint16_t ID[4]; //對象的ID值
float tempAmbient; //溫度值
float tempObject1; //溫度值
float tempObject2; //溫度值
void (*Read)(struct MLXObject *mlx,uint8_t cmd,uint8_t *rData,uint16_trSize); //讀數(shù)據(jù)操作指針
void (*Write)(struct MLXObject *mlx,uint8_t cmd,uint8_t *wData,uint16_twSize); //寫數(shù)據(jù)操作指針
void (*Delayus)(volatile uint32_tnTime); //延時操作指針
}MLXObjectType;
2.1.2 、對象初始化
我們知道,一個對象僅作聲明是不能使用的,我們需要先對其進(jìn)行初始化,所以這里我們來考慮MLX90614紅外溫度傳感器對象的初始化函數(shù)。一般來說,初始化函數(shù)需要處理幾個方面的問題。一是檢查輸入?yún)?shù)是否合理;二是為對象的屬性賦初值;三是對對象作必要的初始化配置。據(jù)此我們設(shè)計(jì)MLX90614紅外溫度傳感器對象的初始化函數(shù)如下:
/* 紅外溫度傳感器對象初始化 */
void MLXInitialization(MLXObjectType*mlx, //MLX90614對象
uint8_t address, //設(shè)備地址
MLXRead read, //讀數(shù)據(jù)函數(shù)指針
MLXWrite write, //寫數(shù)據(jù)函數(shù)指針
MLXDelayus delayus //微秒巖石函數(shù)指針
)
{
if((mlx==NULL)||(read==NULL)||(write==NULL)||(delayus==NULL))
{
return;
}
mlx->Read=read;
mlx->Write=write;
mlx->Delayus=delayus;
mlx->tempAmbient=0.0;
mlx->tempObject1=0.0;
mlx->tempObject2=0.0;
if(address>0x00)
{
mlx->devAddress=address;
}
else
{
mlx->devAddress=MLXSlaveAddress;
}
mlx->Delayus(200);
GetIDFromMLX90614(mlx);
mlx->flags=(uint8_t)ReadFlagFromMLX(mlx);
mlx->pwmctrl=ReadDataFromMLX(mlx,EEPROMAccess|PWMCTRL);
mlx->ConfigRegister=ReadDataFromMLX(mlx,EEPROMAccess|ConfigRegister1);
}
2.2 、對象操作
我們已經(jīng)完成了MLX90614紅外溫度傳感器對象類型的定義和對象初始化函數(shù)的設(shè)計(jì)。但我們的主要目標(biāo)是獲取對象的信息,接下來我們還要實(shí)現(xiàn)面向MLX90614紅外溫度傳感器的各類操作。
2.2.1 、讀數(shù)據(jù)操作
我們需要從MLX90614紅外溫度傳感器讀取數(shù)據(jù),不管這個數(shù)據(jù)是在EEPROM還是在RAM,我們都可以采用相同的方式讀取。我們已經(jīng)說過,操作EEPROM和RAM的命令字節(jié)由3位命令和5位地址組成。我們實(shí)現(xiàn)數(shù)據(jù)讀取函數(shù)如下:
/*讀數(shù)據(jù)操作*/
static uint16_t ReadDataFromMLX(MLXObjectType *mlx,uint8_t cmd)
{
uint8_t data[3];
uint16_t tempCode=0;
uint8_t pec[6];
mlx->Read(mlx,cmd,data,3);
pec[0]=mlx->devAddress;
pec[1]=cmd;
pec[2]=mlx->devAddress+1;
pec[3]=data[0];
pec[4]=data[1];
pec[5]=data[2];
if(PECCalculation(pec,6)==0x00)
{
tempCode=(data[1]<<8)+data[0];
}
return tempCode;
}
2.2.2 、寫數(shù)據(jù)操作
我們需要向MLX90614紅外溫度傳感器寫一些數(shù)據(jù)用以配置傳感器,這些可寫的寄存器處于EEPROM之中,如配置寄存器、PWM控制信息、設(shè)備地址等。這里我們設(shè)計(jì)一個配置這些數(shù)據(jù)的操作函數(shù)。
/* 寫數(shù)據(jù)操作 */
static void WriteDataToMLX(MLXObjectType *mlx,uint8_t cmd,uint16_t data)
{
uint8_t wData[3];
uint8_t pec[4];
pec[0]=mlx->devAddress;
pec[1]=cmd;
pec[2]=(uint8_t)data;
pec[3]=(uint8_t)(data>>8);
wData[0]=(uint8_t)data;
wData[1]=(uint8_t)(data>>8);
wData[2]=PECCalculation(pec,4);
mlx->Write(mlx,cmd,wData,3);
}
2.2.3 、操作休眠模式
有些時候在我們不使用設(shè)備時,我們希望能夠讓設(shè)備休眠以節(jié)省資源,在需要時再將其喚醒投入工作。MLX90614具備有休眠的功能,這里我們看看如何使用這一功能。
/* 使設(shè)備進(jìn)入休眠模式 */
void EnterSleepModeForMLX(MLXObjectType *mlx)
{
uint8_t cmd;
static uint8_t pec;
uint8_t data[2];
cmd=EnterSLEEPMode;
data[0]=mlx->devAddress;
data[1]=cmd;
pec=PECCalculation(data,2);
mlx->Write(mlx,cmd,&pec,1);
}
3 、驅(qū)動的使用
我們已經(jīng)設(shè)計(jì)并實(shí)現(xiàn)了MLX90614紅外溫度傳感器的驅(qū)動程序。我們還需要對這一驅(qū)動進(jìn)行驗(yàn)證。接下來我們將基于這一驅(qū)動程序開發(fā)獲取MLX90614紅外溫度傳感器數(shù)據(jù)的簡單應(yīng)用。
3.1 、聲明并初始化對象
使用基于對象的操作我們需要先得到這個對象,所以我們先要使用前面定義的MLX90614紅外溫度傳感器對象類型聲明一個MLX90614紅外溫度傳感器對象變量,具體操作格式如下:
MLXObjectTypemlx;
我們聲明了這個對象變量,但還不能立即使用。我們還需要使用驅(qū)動中定義的初始化函數(shù)對這個對象變量進(jìn)行初始化。初始化函數(shù)有一些用于對象初始化的參數(shù)需要輸入:
MLXObjectType*mlx,MLX90614對象
uint8_t address,設(shè)備地址
MLXRead read,讀數(shù)據(jù)函數(shù)指針
MLXWrite write,寫數(shù)據(jù)函數(shù)指針
MLXDelayusdelayus,微秒延時函數(shù)指針
對于這些參數(shù),對象變量我們已經(jīng)定義了,它正是我們需要初始化的對象。在這些參數(shù)中,我們需要注意的是幾個函數(shù)指針,我們需要在應(yīng)用中定義這幾個函數(shù),并將函數(shù)指針作為參數(shù)。這幾個函數(shù)的類型如下:
/*定義讀MLX90614數(shù)據(jù)操作指針類型*/
typedef void (*MLXRead)(struct MLXObject*mlx,uint8_t cmd,uint8_t *rData,uint16_t rSize);
/*定義寫MLX90614數(shù)據(jù)操作指針類型*/
typedef void (*MLXWrite)(structMLXObject *mlx,uint8_t cmd,uint8_t *wData,uint16_t wSize);
/*定義微秒延時操作指針類型*/
typedef void (*MLXDelayus)(volatileuint32_t nTime);
對于這幾個函數(shù)我們根據(jù)樣式定義就可以了,具體的操作可能與使用的軟硬件平臺有關(guān)系。在這里我們使用STM32F4的第2個I2C接口及其外設(shè)庫,所以具體函數(shù)定義如下:
/*從MLX90614接收數(shù)據(jù)*/
static void ReceiveFromMLX(MLXObjectType*mlx,uint8_t cmd,uint8_t *rData,uint16_t rSize)
{
HAL_I2C_Master_Transmit(&hlpti2c,mlx->devAddress,&cmd,1,1000);
HAL_I2C_Master_Receive(&hlpti2c, mlx->devAddress,rData, rSize, 1000);
}
/*向MLX90614傳送數(shù)據(jù)*/
static void TransmitToMLX(MLXObjectType*mlx,uint8_t cmd,uint8_t *tData,uint16_t tSize)
{
uint8_t data[10];
data[0]=cmd;
for(int i=0;i1]=tData[i];
}
HAL_I2C_Master_Transmit(&hlpti2c,mlx->devAddress,data,tSize+1,1000);
}
對于延時函數(shù)我們可以采用各種方法實(shí)現(xiàn)。我們采用的STM32平臺和HAL庫則可以直接使用HAL_Delay()函數(shù)。于是我們可以調(diào)用初始化函數(shù)如下:
MLXInitialization(&mlx,0xB4,ReceiveFromMLX,TransmitToMLX,HAL_Delay);
3.2 、基于對象進(jìn)行操作
我們定義了對象變量并使用初始化函數(shù)給其作了初始化。接著我們就來考慮操作這一對象獲取我們想要的數(shù)據(jù)。我們在驅(qū)動中已經(jīng)將獲取數(shù)據(jù)并轉(zhuǎn)換為轉(zhuǎn)換值的比例值,接下來我們使用這一驅(qū)動開發(fā)我們的應(yīng)用實(shí)例。
/*讀取溫度值*/
void GetTemperatureDataFromMLX(void)
{
float tempObject;
float tempAmbient;
GetMLXTemperature(&mlx);
tempObject=mlx.tempObject1;
tempAmbient=mlx.tempAmbient;
}
4 、應(yīng)用總結(jié)
我們設(shè)計(jì)并實(shí)現(xiàn)了MLX90614紅外溫度傳感器的驅(qū)動程序,并在此基礎(chǔ)上設(shè)計(jì)了一個簡單的應(yīng)用來驗(yàn)證之。事實(shí)上我們在實(shí)際項(xiàng)目中也是使用這一驅(qū)動程序來實(shí)現(xiàn)應(yīng)用的,并且效果良好。
讀標(biāo)志與讀數(shù)據(jù)在時序控制上是有一些差別的,下發(fā)命令與接收數(shù)據(jù)之間沒有重啟的操作,這一點(diǎn)在操作它時需要注意。
我們在使用驅(qū)動時,一般會選擇使用GPIO模擬的I2C收發(fā)器,這樣穩(wěn)定性更好。而使用硬件I2C接口及其庫函數(shù)實(shí)現(xiàn)時,經(jīng)常會有死鎖的事情發(fā)生。我們曾多次遇到,特別是在STM32的第一路I2C接口。至于第1路I2C接口與其它的I2C接口有什么差異尚未確定。總之使用GPIO模擬I2C收發(fā)器更為穩(wěn)定。
評論