Advertisement
alexpetro000

Untitled

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