Advertisement
alexpetro000

Untitled

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