Advertisement
alexpetro000

Untitled

Jun 22nd, 2015
556
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 13.17 KB | None | 0 0
  1. /*
  2.  2-16-2013
  3.  Spark Fun Electronics
  4.  Nathan Seidle
  5.  
  6.  This code is heavily based on maxbot's and IlBaboomba's code: http://arduino.cc/forum/index.php?topic=126244
  7.  They didn't have a license on it so I'm hoping it's public domain.
  8.  
  9.  This example shows how to read and calculate the 64 temperatures for the 64 pixels of the MLX90620 thermopile sensor.
  10.  
  11.  alpha_ij array is specific to every sensor and needs to be calculated separately. Please see the
  12.  'MLX90620_alphaCalculator' sketch to get these values. If you choose not to calculate these values
  13.  this sketch will still work but the temperatures shown will be very inaccurate.
  14.  
  15.  Don't get confused by the bottom view of the device! The GND pin is connected to the housing.
  16.  
  17.  To get this code to work, attached a MLX90620 to an Arduino Uno using the following pins:
  18.  A5 to 330 ohm to SCL
  19.  A4 to 330 ohm to SDA
  20.  3.3V to VDD
  21.  GND to VSS
  22.  
  23.  I used the internal pull-ups on the SDA/SCL lines. Normally you should use ~4.7k pull-ups for I2C.
  24.  
  25.  */
  26.  
  27. #include <i2cmaster.h>
  28. //i2cmaster comes from here: http://www.cheap-thermocam.bplaced.net/software/I2Cmaster.rar
  29.  
  30. #include "MLX90620_registers.h"
  31.  
  32. int refreshRate = 16; //Set this value to your desired refresh frequency
  33.  
  34.  
  35. //Global variables
  36. //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  37. int irData[64]; //Contains the raw IR data from the sensor
  38. float temperatures[64]; //Contains the calculated temperatures of each pixel in the array
  39. float Tambient; //Tracks the changing ambient temperature of the sensor
  40. //byte eepromData[256]; //Contains the full EEPROM reading from the MLX (Slave 0x50)
  41.  
  42. //These are constants calculated from the calibration data stored in EEPROM
  43. //See varInitialize and section 7.3 for more information
  44. #define v_th 6760
  45. #define a_cp -31
  46. #define b_cp -51
  47. #define tgc 0
  48. #define b_i_scale 8
  49. //int v_th = 6760, a_cp = -31, b_cp = -51, tgc = 0, b_i_scale = 8;
  50. float k_t1 = 22.73, k_t2 = 0.00, emissivity = 1.00;
  51. int a_ij[64] = {
  52.   -44, -42, -41, -40, -38, -38, -38, -39, -37, -36, -35, -36, -36, -34, -34, -36,
  53.   -32, -30, -32, -35, -32, -31, -31, -33, -31, -29, -29, -32, -29, -29, -29, -29,
  54.   -28, -29, -27, -28, -28, -26, -26, -26, -27, -25, -26, -25, -25, -26, -24, -23,
  55.   -24, -25, -24, -23, -25, -23, -23, -21, -26, -23, -25, -21, -27, -24, -23, -23
  56. };
  57. int b_ij[64] = {
  58.   -68, -60, -60, -60, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
  59.   -51, -51, -51, -51, -51, -51, -34, -51, -51, -43, -43, -51, -43, -43, -43, -43,
  60.   -51, -34, -34, -43, -43, -34, -34, -34, -43, -34, -34, -34, -51, -34, -34, -34,
  61.   -34, -34, -34, -34, -34, -34, -34, -34, -51, -34, -34, -34, -43, -34, -34, -34
  62. };
  63.  
  64. //These values are calculated using equation 7.3.3.2
  65. //They are constants and can be calculated using the MLX90620_alphaCalculator sketch
  66. float alpha_ij[64] = {
  67.   1.66583E-8, 1.85792E-8, 1.78807E-8, 1.57270E-8, 1.87538E-8, 2.05582E-8, 1.98597E-8, 1.81717E-8,
  68.   2.05582E-8, 2.21880E-8, 2.27119E-8, 1.96269E-8, 2.27701E-8, 2.45745E-8, 2.45745E-8, 2.10239E-8,
  69.   2.43417E-8, 2.62044E-8, 2.59715E-8, 2.31776E-8, 2.50402E-8, 2.77178E-8, 2.74267E-8, 2.46328E-8,
  70.   2.57969E-8, 2.83580E-8, 2.76596E-8, 2.50984E-8, 2.60297E-8, 2.88237E-8, 2.86491E-8, 2.57387E-8,
  71.   2.62044E-8, 2.86491E-8, 2.85909E-8, 2.50402E-8, 2.62626E-8, 2.90565E-8, 2.85909E-8, 2.50402E-8,
  72.   2.55059E-8, 2.83580E-8, 2.78924E-8, 2.57387E-8, 2.52730E-8, 2.76596E-8, 2.74267E-8, 2.52730E-8,
  73.   2.41089E-8, 2.62044E-8, 2.66700E-8, 2.45745E-8, 2.27701E-8, 2.57387E-8, 2.55059E-8, 2.31194E-8,
  74.   2.12567E-8, 2.41089E-8, 2.41089E-8, 2.21880E-8, 1.92194E-8, 2.27119E-8, 2.21880E-8, 2.05582E-8,
  75. };
  76.  
  77. byte loopCount = 0; //Used in main loop
  78. //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  79.  
  80.  
  81. //Begin Program code
  82.  
  83. void setup()
  84. {
  85.   Serial.begin(115200);
  86.   Serial.println("MLX90620 Example");
  87.  
  88.   i2c_init(); //Init the I2C pins
  89.   PORTC = (1 << PORTC4) | (1 << PORTC5); //Enable pull-ups
  90.  
  91.   delay(5); //Init procedure calls for a 5ms delay after power-on
  92.  
  93.   //read_EEPROM_MLX90620(); //Read the entire EEPROM
  94.   writeTrimmingValue(81);
  95.  
  96.   setConfiguration(refreshRate); //Configure the MLX sensor with the user's choice of refresh rate
  97.  
  98.   calculate_TA(); //Calculate the current Tambient
  99. }
  100.  
  101. void loop()
  102. {
  103.   if(loopCount++ == 16) //Tambient changes more slowly than the pixel readings. Update TA only every 16 loops.
  104.   {
  105.     calculate_TA(); //Calculate the new Tambient
  106.  
  107.     if(checkConfig_MLX90620()) //Every 16 readings check that the POR flag is not set
  108.     {
  109.       Serial.println("POR Detected!");
  110.       setConfiguration(refreshRate); //Re-write the configuration bytes to the MLX
  111.     }
  112.  
  113.     loopCount = 0; //Reset count
  114.   }
  115.  
  116.   readIR_MLX90620(); //Get the 64 bytes of raw pixel data into the irData array
  117.  
  118.   calculate_TO(); //Run all the large calculations to get the temperature data for each pixel
  119.  
  120.   prettyPrintTemperatures(); //Print the array in a 4 x 16 pattern
  121.   //rawPrintTemperatures(); //Print the entire array so it can more easily be read by Processing app
  122. }
  123.  
  124. //From the 256 bytes of EEPROM data, initialize
  125. /*void varInitialization(byte calibration_data[])
  126. {
  127.   //v_th = 256 * calibration_data[VTH_H] + calibration_data[VTH_L];
  128.   //k_t1 = (256 * calibration_data[KT1_H] + calibration_data[KT1_L]) / 1024.0; //2^10 = 1024
  129.   //k_t2 = (256 * calibration_data[KT2_H] + calibration_data[KT2_L]) / 1048576.0; //2^20 = 1,048,576
  130.   //emissivity = ((unsigned int)256 * calibration_data[CAL_EMIS_H] + calibration_data[CAL_EMIS_L]) / 32768.0;
  131.  
  132.   //a_cp = calibration_data[CAL_ACP];
  133.   //if(a_cp > 127) a_cp -= 256; //These values are stored as 2's compliment. This coverts it if necessary.
  134.  
  135.   //b_cp = calibration_data[CAL_BCP];
  136.   //if(b_cp > 127) b_cp -= 256;
  137.  
  138.   //tgc = calibration_data[CAL_TGC];
  139.   //if(tgc > 127) tgc -= 256;
  140.  
  141.   //b_i_scale = calibration_data[CAL_BI_SCALE];
  142.  
  143.   /*for(int i = 0 ; i < 64 ; i++)
  144.   {
  145.     //Read the individual pixel offsets
  146.     a_ij[i] = calibration_data[i];
  147.     if(a_ij[i] > 127) a_ij[i] -= 256; //These values are stored as 2's compliment. This coverts it if necessary.
  148.  
  149.     //Read the individual pixel offset slope coefficients
  150.     b_ij[i] = calibration_data[0x40 + i]; //Bi(i,j) begins 64 bytes into EEPROM at 0x40
  151.     if(b_ij[i] > 127) b_ij[i] -= 256;
  152.   }
  153.  
  154. }*/
  155.  
  156. //Receives the refresh rate for sensor scanning
  157. //Sets the two byte configuration registers
  158. //This function overwrites what is currently in the configuration registers
  159. //The MLX doesn't seem to mind this (flags are read only)
  160. void setConfiguration(int irRefreshRateHZ)
  161. {
  162.   byte Hz_LSB = 0b00001010; //0b00001001; for 32Hz
  163.  
  164.   /*switch(irRefreshRateHZ)
  165.   {
  166.   case 0:
  167.     Hz_LSB = 0b00001111;
  168.     break;
  169.   case 1:
  170.     Hz_LSB = 0b00001110;
  171.     break;
  172.   case 2:
  173.     Hz_LSB = 0b00001101;
  174.     break;
  175.   case 4:
  176.     Hz_LSB = 0b00001100;
  177.     break;
  178.   case 8:
  179.     Hz_LSB = 0b00001011;
  180.     break;
  181.   case 16:
  182.     Hz_LSB = 0b00001010;
  183.     break;
  184.   case 32:
  185.     Hz_LSB = 0b00001001;
  186.     break;
  187.   default:
  188.     Hz_LSB = 0b00001110;
  189.   }*/
  190.  
  191.   byte defaultConfig_H = 0b01110100; // x111.01xx, Assumes NA = 0, ADC low reference enabled, Ta Refresh rate of 2Hz
  192.  
  193.   i2c_start_wait(MLX90620_WRITE);
  194.   i2c_write(0x03); //Command = configuration value
  195.   i2c_write((byte)Hz_LSB - 0x55);
  196.   i2c_write(Hz_LSB);
  197.   i2c_write(defaultConfig_H - 0x55); //Assumes NA = 0, ADC low reference enabled, Ta Refresh rate of 2Hz
  198.   i2c_write(defaultConfig_H);
  199.   i2c_stop();
  200. }
  201.  
  202. //Read the 256 bytes from the MLX EEPROM and setup the various constants (*lots* of math)
  203. //Note: The EEPROM on the MLX has a different I2C address from the MLX. I've never seen this before.
  204. /*void read_EEPROM_MLX90620()
  205. {
  206.   i2c_start_wait(MLX90620_EEPROM_WRITE);
  207.   i2c_write(0x00); //EEPROM info starts at location 0x00
  208.   i2c_rep_start(MLX90620_EEPROM_READ);
  209.  
  210.   //Read all 256 bytes from the sensor's EEPROM
  211.   /*for(int i = 0 ; i <= 255 ; i++)
  212.     eepromData[i] = i2c_readAck();
  213.  
  214.   i2c_stop(); //We're done talking
  215.  
  216.   //varInitialization(eepromData); //Calculate a bunch of constants from the EEPROM data
  217. //
  218.   //writeTrimmingValue(81);
  219. }*/
  220.  
  221. //Given a 8-bit number from EEPROM (Slave address 0x50), write value to MLX sensor (Slave address 0x60)
  222. void writeTrimmingValue(byte val)
  223. {
  224.   i2c_start_wait(MLX90620_WRITE); //Write to the sensor
  225.   i2c_write(0x04); //Command = write oscillator trimming value
  226.   i2c_write((byte)val - 0xAA);
  227.   i2c_write(val);
  228.   i2c_write(0x56); //Always 0x56
  229.   i2c_write(0x00); //Always 0x00
  230.   i2c_stop();
  231. }
  232.  
  233. //Gets the latest PTAT (package temperature ambient) reading from the MLX
  234. //Then calculates a new Tambient
  235. //Many of these values (k_t1, v_th, etc) come from varInitialization and EEPROM reading
  236. //This has been tested to match example 7.3.2
  237.  
  238. void calculate_TA(void)
  239. {
  240.   unsigned int ptat = readPTAT_MLX90620();
  241.  
  242.   Tambient = (-k_t1 + sqrt(square(k_t1) - (4 * k_t2 * (v_th - (float)ptat)))) / (2*k_t2) + 25; //it's much more simple now, isn't it? :)
  243. }
  244.  
  245. //Reads the PTAT data from the MLX
  246. //Returns an unsigned int containing the PTAT
  247. unsigned int readPTAT_MLX90620()
  248. {
  249.   i2c_start_wait(MLX90620_WRITE);
  250.   i2c_write(CMD_READ_REGISTER); //Command = read PTAT
  251.   i2c_write(0x90); //Start address is 0x90
  252.   i2c_write(0x00); //Address step is 0
  253.   i2c_write(0x01); //Number of reads is 1
  254.   i2c_rep_start(MLX90620_READ);
  255.  
  256.   byte ptatLow = i2c_readAck(); //Grab the lower and higher PTAT bytes
  257.   byte ptatHigh = i2c_readAck();
  258.  
  259.   i2c_stop();
  260.  
  261.   return( (unsigned int)(ptatHigh << 8) | ptatLow); //Combine bytes and return
  262. }
  263.  
  264. //Calculate the temperatures seen for each pixel
  265. //Relies on the raw irData array
  266. //Returns an 64-int array called temperatures
  267. void calculate_TO()
  268. {
  269.   float v_ir_off_comp;
  270.   float v_ir_tgc_comp;
  271.   float v_ir_comp;
  272.  
  273.   //Calculate the offset compensation for the one compensation pixel
  274.   //This is a constant in the TO calculation, so calculate it here.
  275.   int cpix = readCPIX_MLX90620(); //Go get the raw data of the compensation pixel
  276.   float v_cp_off_comp = (float)cpix - (a_cp + (b_cp/pow(2, b_i_scale)) * (Tambient - 25));
  277.  
  278.   for (int i = 0 ; i < 64 ; i++)
  279.   {
  280.     v_ir_off_comp = irData[i] - (a_ij[i] + (float)(b_ij[i]/pow(2, b_i_scale)) * (Tambient - 25)); //#1: Calculate Offset Compensation
  281.  
  282.     v_ir_tgc_comp = v_ir_off_comp - ( ((float)tgc/32) * v_cp_off_comp); //#2: Calculate Thermal Gradien Compensation (TGC)
  283.  
  284.     v_ir_comp = v_ir_tgc_comp / emissivity; //#3: Calculate Emissivity Compensation
  285.  
  286.     temperatures[i] = sqrt( sqrt( (v_ir_comp/alpha_ij[i]) + pow(Tambient + 273.15, 4) )) - 273.15;
  287.   }
  288. }
  289.  
  290. //Reads 64 bytes of pixel data from the MLX
  291. //Loads the data into the irData array
  292. void readIR_MLX90620()
  293. {
  294.   i2c_start_wait(MLX90620_WRITE);
  295.   i2c_write(CMD_READ_REGISTER); //Command = read a register
  296.   i2c_write(0x00); //Start address = 0x00
  297.   i2c_write(0x01); //Address step = 1
  298.   i2c_write(0x40); //Number of reads is 64
  299.   i2c_rep_start(MLX90620_READ);
  300.  
  301.   for(int i = 0 ; i < 64 ; i++)
  302.   {
  303.     byte pixelDataLow = i2c_readAck();
  304.     byte pixelDataHigh = i2c_readAck();
  305.     irData[i] = (int)(pixelDataHigh << 8) | pixelDataLow;
  306.   }
  307.  
  308.   i2c_stop();
  309. }
  310.  
  311. //Read the compensation pixel 16 bit data
  312. int readCPIX_MLX90620()
  313. {
  314.   i2c_start_wait(MLX90620_WRITE);
  315.   i2c_write(CMD_READ_REGISTER); //Command = read register
  316.   i2c_write(0x91);
  317.   i2c_write(0x00);
  318.   i2c_write(0x01);
  319.   i2c_rep_start(MLX90620_READ);
  320.  
  321.   byte cpixLow = i2c_readAck(); //Grab the two bytes
  322.   byte cpixHigh = i2c_readAck();
  323.   i2c_stop();
  324.  
  325.   return ( (int)(cpixHigh << 8) | cpixLow);
  326. }
  327.  
  328. //Reads the current configuration register (2 bytes) from the MLX
  329. //Returns two bytes
  330. unsigned int readConfig_MLX90620()
  331. {
  332.   i2c_start_wait(MLX90620_WRITE); //The MLX configuration is in the MLX, not EEPROM
  333.   i2c_write(CMD_READ_REGISTER); //Command = read configuration register
  334.   i2c_write(0x92); //Start address
  335.   i2c_write(0x00); //Address step of zero
  336.   i2c_write(0x01); //Number of reads is 1
  337.  
  338.     i2c_rep_start(MLX90620_READ);
  339.  
  340.   byte configLow = i2c_readAck(); //Grab the two bytes
  341.   byte configHigh = i2c_readAck();
  342.  
  343.   i2c_stop();
  344.  
  345.   return( (unsigned int)(configHigh << 8) | configLow); //Combine the configuration bytes and return as one unsigned int
  346. }
  347.  
  348. //Poll the MLX for its current status
  349. //Returns true if the POR/Brown out bit is set
  350. boolean checkConfig_MLX90620()
  351. {
  352.   if ( (readConfig_MLX90620() & (unsigned int)1<<POR_TEST) == 0)
  353.     return true;
  354.   else
  355.     return false;
  356. }
  357.  
  358. //Prints the temperatures in a way that's more easily viewable in the terminal window
  359. void prettyPrintTemperatures()
  360. {
  361.   Serial.println();
  362.   for(int i = 0 ; i < 64 ; i++)
  363.   {
  364.     if(i % 16 == 0) Serial.println();
  365.     Serial.print(convertToFahrenheit(temperatures[i]));
  366.     //Serial.print(irData[i]);
  367.     Serial.print(", ");
  368.   }
  369. }
  370.  
  371. //Prints the temperatures in a way that's more easily parsed by a Processing app
  372. //Each line starts with '$' and ends with '*'
  373. void rawPrintTemperatures()
  374. {
  375.   Serial.print("$");
  376.   for(int i = 0 ; i < 64 ; i++)
  377.   {
  378.     Serial.print(convertToFahrenheit(temperatures[i]));
  379.     Serial.print(","); //Don't print comma on last temperature
  380.   }
  381.   Serial.println("*");
  382. }
  383.  
  384. //Given a Celsius float, converts to Fahrenheit
  385. float convertToFahrenheit (float Tc)
  386. {
  387.   float Tf = (9/5) * Tc + 32;
  388.  
  389.   return(Tf);
  390. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement