Driver for the Melexis MLX90620 infrared temperature sensor array. 64 pixels in a 16 x 4 format. Updated driver for KL25Z
Melexis MLX90620 home page:
Note: I had to delete the original repository and start a new one due to issues I had with publishing. It should be at Rev 2, but it starts fresh at Rev 0. The reason for the new revision was basic ic2 command cleanup in order to work with the KL25Z board.
The datasheet for the MLX90620 can be found here: http://www.melexis.com/MLX90620
Note that the conversion-start process that is used in the mbed driver, the "Step" mode in section 9.3.2, is not longer in the datasheet. It was last described in the datasheet dated 20120620. Step mode was bit 6 of the Configuration Register 0x92, section 8.2.2.1, Table 10. Melexis claims that the temperature readings were not as accurate as the back-to-back-to-back "Normal" continous conversion method.
However, in my main.cpp code, I do start another conversion immediately after the current conversion completes and all of the sensor array RAM values have been read. Then, I perform all of the floating point math and update the display while the new conversion is taking place. At 4 conversions per second using the KL25Z board, there is about 110mS of idle time before the new conversion is ready to be read out. Reading bits 8 and 9 in the Configuration Register tell you when it is time to read the sensor array RAM values.
The datasheet says the the MLX90620 needs to operate at 2.6V for best performance (certain versions can operate up to 3.0V). Use an LDO from 3.3V to generate 2.6V. Though you could use a regular diode to drop 3.3V to 2.6V, it is not consistent from diode-to-diode and the voltage drop will vary with temperature. I highly recommend using an LDO. And don't forget to use bypass caps!!! BTW: Though the device VCC is limited to 2.6V, the i2c I/O pins can operate at 3.3V.
With an mbed1768, the MLX90620 operates with an i2c frequency of 400KHz and 4 feet of 4-wire cable. I have not tried 4 feet on the KL25Z, but it does work at 1 foot. The i2c pullup resistors are 2.2k.
In the overall program that I use for the MLX90620 (to be published), an mbed1768 can run with a USB Serial output of 921600 baud, whereas the KL25Z is limited to 115200 baud. Anything faster on the KL25Z, output characters get dropped.
...kevin
MLX90620.cpp
- Committer:
- loopsva
- Date:
- 2013-07-29
- Revision:
- 0:a86b8d0294ee
File content as of revision 0:a86b8d0294ee:
//NOTE: "Step Measurement Mode" was removed from new MLX90620 data sheet, page 22 dated Sept 19 2012 // which is used in this implementation #include "mbed.h" #include "MLX90620.h" extern char* EEbuf; extern char* RamBuf; extern char* RamCmmd; //must reside in main.cpp const int PTATSENS = 0x90; //ram offset = 0x90, PTAT sensor reading, 16b const int TGCSENS = 0x91; //ram offset = 0x91, TGC sensor reading, 16b const int MLXCONFIG = 0x92; //ram offset = 0x92, config register, 16b const int MLXTRIM = 0x93; //ram offset = 0x93, oscillator trim, lsb>6b of 16b const int EETRIM = 0xf7; //eep offset = 0xf0, 1 byte, oscillator trim value unsigned short Config = 0; //MLX90620 configuration register unsigned short OscTrim = 0; //MLX90620 oscillator trim register unsigned short PtatD = 0; //MLX90620 PTAT data register short VCP = 0; //VCP / TGC short Vth25X = 0; float TaXX = 0.0; //For To signed char AcpX = 0; signed char BcpX = 0; float Kt1fX = 0.0; float Kt2fX = 0.0; signed char TGCX = 0; char BiScaleX = 0; unsigned short theta0X = 0; char theta0ScaleX = 0; char deltaThetaScaleX = 0; unsigned short elipsonX = 0; signed char AiPixelX = 0; //eeprom address range 0x00 - 0x3f signed char BiPixelX = 0; //eeprom address range 0x40 - 0x7f char dThetaPixelX = 0; //eeprom address range 0x80 - 0xbf short VirPixelX = 0; double TempPxlX = 0; const int TOINDEX = 0xd4; //eep offset = 0xD4 and 0xE0 (0xD4 + 0x0C), 6 bytes + 6 bytes const int TAINDEX = 0xda; //eep offset = 0xDA, 6 bytes //--------------------------------------------------------------------------------------------------------------------------------------// // Constructor MLX90620::MLX90620(PinName sda, PinName scl, const char* name) : _i2c(sda, scl){ _i2c.frequency(400000); //set up i2c speed _i2c.stop(); } //--------------------------------------------------------------------------------------------------------------------------------------// //copy contents of EEPROM inside the MLX90620 into a local buffer. Data is used for lookup tables and parameters int MLX90620::LoadEEPROM() { //clear out buffer first for(int i = 0; i < 256; i++) { EEbuf[i] = 0; } //load the entire EEPROM EEbuf[0] = 0; if(!_i2c.write(0xa0, EEbuf, 1, true)) { //0 returned is ok _i2c.read(0xa0, EEbuf, 256); //load contents of EEPROM } else { _i2c.stop(); return(1); } return(0); } //--------------------------------------------------------------------------------------------------------------------------------------// //copy oscillator offset from EEbuf to MLX90620 (MS byte = 0) int MLX90620::SetOscTrimReg() { RamCmmd[0] = 4; //command RamCmmd[1] = EEbuf[EETRIM] - 0xaa; //LS byte check RamCmmd[2] = EEbuf[EETRIM]; //oscillator trim value RamCmmd[3] = 0x100 - 0xaa; //MS byte check RamCmmd[4] = 0; //MS byte = 0 int r = _i2c.write(0xc0, RamCmmd, 5, false); return(r); } //--------------------------------------------------------------------------------------------------------------------------------------// //get oscillator offset register from MLX90620 unsigned short MLX90620::GetOscTrimReg() { RamCmmd[0] = 2; //command RamCmmd[1] = MLXTRIM; //address of register RamCmmd[2] = 0; //address step RamCmmd[3] = 1; //# of reads _i2c.write(0xc0, RamCmmd, 4, true); _i2c.read(0xc0, RamCmmd, 2); OscTrim = (RamCmmd[1] << 8) + RamCmmd[0]; return(OscTrim); } //--------------------------------------------------------------------------------------------------------------------------------------// //initialize the configuration register //******* NOTE: Step measurement mode was removed from new data sheet dated Sept 19 2012 int MLX90620::SetConfigReg() { RamCmmd[0] = 3; //command RamCmmd[1] = 0x14c - 0x55; //LS byte check RamCmmd[2] = 0x4c; //LS config value, step meas mode, 4Hz array ******* RamCmmd[3] = 0x5c - 0x55; //MS byte check RamCmmd[4] = 0x5c; //MS config value, 8Hz Ta, 400k i2c int r = _i2c.write(0xc0, RamCmmd, 5, false); return(r); } //--------------------------------------------------------------------------------------------------------------------------------------// //get configuration register from MLX90620 unsigned short MLX90620::GetConfigReg() { RamCmmd[0] = 2; //command RamCmmd[1] = MLXCONFIG; //address of register RamCmmd[2] = 0; //address step RamCmmd[3] = 1; //# of reads _i2c.write(0xc0, RamCmmd, 4, true); _i2c.read(0xc0, RamCmmd, 2); Config = (RamCmmd[1] << 8) + RamCmmd[0]; return(Config); } //--------------------------------------------------------------------------------------------------------------------------------------// //get PTAT register from MLX90620 unsigned short MLX90620::GetPTATReg() { RamCmmd[0] = 2; //command RamCmmd[1] = PTATSENS; //address of register RamCmmd[2] = 0; //address step RamCmmd[3] = 1; //# of reads _i2c.write(0xc0, RamCmmd, 4, true); _i2c.read(0xc0, RamCmmd, 2); PtatD = (RamCmmd[1] << 8) + RamCmmd[0]; return(PtatD); } //--------------------------------------------------------------------------------------------------------------------------------------// //get VCP / TGC register from MLX90620 short MLX90620::GetTGCReg() { RamCmmd[0] = 2; //command RamCmmd[1] = TGCSENS; //address of register RamCmmd[2] = 0; //address step RamCmmd[3] = 1; //# of reads _i2c.write(0xc0, RamCmmd, 4, true); _i2c.read(0xc0, RamCmmd, 2); VCP = (RamCmmd[1] << 8) + RamCmmd[0]; return(VCP); } //--------------------------------------------------------------------------------------------------------------------------------------// //get RAM dump from MLX90620 bool firstDump = false; void MLX90620::LoadMLXRam() { RamCmmd[0] = 2; //command RamCmmd[1] = 0; //start address RamCmmd[2] = 1; //address step RamCmmd[3] = 0x40; //# of reads _i2c.write(0xc0, RamCmmd, 4, true); _i2c.read(0xc0, RamBuf, 0x80); PtatD = MLX90620::GetPTATReg(); VCP = MLX90620::GetTGCReg(); } //--------------------------------------------------------------------------------------------------------------------------------------// //start measurement MLX90620 int MLX90620::StartMeasurement() { RamCmmd[0] = 1; //command RamCmmd[1] = 8; //address of config register int r = _i2c.write(0xc0, RamCmmd, 2, false); return(r); } //--------------------------------------------------------------------------------------------------------------------------------------// // Initial Calculations for Ta and To float MLX90620::GetDieTemp() { PtatD = MLX90620::GetPTATReg(); float TaX = (-Kt1fX + sqrtf(powf(Kt1fX, 2.0) - 4.0 * Kt2fX * (Vth25X - PtatD)))/(2.0 * Kt2fX) + 25.0; return(TaX); } //--------------------------------------------------------------------------------------------------------------------------------------// // Initial Calculations for Ta and To void MLX90620::CalcTa_To() { //Calculate Ta first Vth25X = (EEbuf[TAINDEX + 1] << 8) + EEbuf[TAINDEX + 0]; short Kt1 = (EEbuf[TAINDEX + 3] << 8) + EEbuf[TAINDEX + 2]; short Kt2 = (EEbuf[TAINDEX + 5] << 8) + EEbuf[TAINDEX + 4]; Kt1fX = Kt1 / 1024.0; Kt2fX = Kt2 / 1048576.0; TaXX = MLX90620::GetDieTemp(); //Calculate To AcpX = EEbuf[TOINDEX + 0]; BcpX = EEbuf[TOINDEX + 1]; // unsigned short thetaCPX = (EEbuf[TOINDEX + 3] << 8) + EEbuf[TOINDEX + 2]; TGCX = EEbuf[TOINDEX + 4]; BiScaleX = EEbuf[TOINDEX + 5]; theta0X = (EEbuf[TOINDEX + 13] << 8) + EEbuf[TOINDEX + 12]; theta0ScaleX = EEbuf[TOINDEX + 14]; deltaThetaScaleX = EEbuf[TOINDEX + 15]; elipsonX = (EEbuf[TOINDEX + 17] << 8) + EEbuf[TOINDEX + 16]; /* printf("Vth(25) = %6d 0x%x\nTa1 = %6d 0x%x\nTa2 = %6d 0x%x\n", Vth25X, Vth25X, Kt1, Kt1, Kt2, Kt2); printf("Kt1fX = %f\nKt2fX = %f\nTaXX = %f\n\n", Kt1fX, Kt2fX, TaXX); printf("Acp = %6d 0x%x\nBcp = %6d 0x%x\nThCP = %6d 0x%x\n", AcpX, AcpX, BcpX, BcpX, thetaCPX, thetaCPX); printf("TGC = %6d 0x%x\nBiS = %6d 0x%x\nTh0 = %6d 0x%x\n", TGCX, TGCX, BiScaleX, BiScaleX, theta0X, theta0X); printf("T0s = %6d 0x%x\nDts = %6d 0x%x\nelip = %6d 0x%x\n\n", theta0ScaleX, theta0ScaleX, deltaThetaScaleX, deltaThetaScaleX, elipsonX, elipsonX); */ } //--------------------------------------------------------------------------------------------------------------------------------------// // Pixel Temperature Calculation double MLX90620::CalcPixel(int Pixel) { AiPixelX = EEbuf[Pixel]; //eeprom address range 0x00 - 0x3f BiPixelX = EEbuf[Pixel + 0x40]; //eeprom address range 0x40 - 0x7f dThetaPixelX = EEbuf[Pixel + 0x80]; //eeprom address range 0x08 - 0xbf VirPixelX = (RamBuf[Pixel * 2 + 1] << 8) + RamBuf[Pixel * 2]; //ram address range 0x000 - 0x08f, 16b float Vcp_off_comp = VCP - (AcpX + BcpX / powf(2.0,BiScaleX) * (TaXX - 25.0)); float VirPixel_off_comp = VirPixelX - (AiPixelX + BiPixelX / powf(2.0,BiScaleX) * (TaXX - 25.0)); float VirPixel_off_comp2 = (float(AiPixelX) + float(BiPixelX) / float(1 << BiScaleX) * (TaXX - 25.0)); VirPixel_off_comp2 = VirPixelX - VirPixel_off_comp2; float VirPixel_tgc_comp = VirPixel_off_comp - TGCX / 32.0 * Vcp_off_comp; float elipsonf = elipsonX / 32768.0; float VirPixel_comp = VirPixel_tgc_comp / elipsonf; double theta28 = theta0X / powf(2.0, theta0ScaleX) + dThetaPixelX / powf(2.0, deltaThetaScaleX); double TempPxl = powf((VirPixel_comp / theta28 + powf((TaXX + 273.15), 4.0)), (1.0 / 4.0)) - 273.15; /* printf("pixel = %d\n", Pixel); printf("Acp = %d\nBcp = %d\nBiS = %d\n", AcpX, BcpX, BiScaleX); printf("Vcp = %d\neps = %d\nTGC = %d\n", VCP, elipsonX, TGCX); printf("Vcp_off_comp = %f\n", Vcp_off_comp); printf("VirPixel_off_comp = %f\n", VirPixel_off_comp); printf("VirPixel = %d\n", VirPixelX); printf("AiPixel = %d\n", AiPixelX); printf("BiPixel = %d\n", BiPixelX); printf("BiScale = %d\n", BiScaleX); printf("2^BiScale = %f\n", (powf(2.0,BiScaleX))); printf("1 << BiScale = %d\n", (1 << BiScaleX)); printf("Ta-25.0 = %f\n", (TaXX - 25.0)); printf("BiPix/2^BiScale = %f\n", (BiPixelX / powf(2.0,BiScaleX))); printf("AiP+BiP/2^BiScale)*(Ta-25= %f\n", (AiPixelX + BiPixelX / powf(2.0,BiScaleX) * (TaXX - 25.0))); printf("VirPixel_off_comp again = %f\n", (VirPixelX - (AiPixelX + BiPixelX / powf(2.0,BiScaleX) * (TaXX - 25.0)))); printf("VirPixel_off_comp2 step = %f\n", VirPixel_off_comp2); printf("VirPixel_tgc_comp = %f\n", VirPixel_tgc_comp); printf("elipsonf = %f\n", elipsonf); printf("VirPixel_comp = %f\n", VirPixel_comp); printf("theta28 = %f << double print problem\n", (theta28 * 100000000.0)); //<<< can't print a double printf("TempPxl = %f\n", TempPxl); */ return(TempPxl); }