Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

All values from the EEPROM are "FFFF", excepted for the first that is "00BF" #104

Open
eleogrande opened this issue Apr 21, 2023 · 12 comments

Comments

@eleogrande
Copy link

Hi, i'm using the MLX90640 thermal camera on mbed on the STM32F429-Disc. The main function is as follows:

#include "mbed.h"
#include <MLX90640_I2C_Driver.h>
#include <MLX90640_API.h>

#define FRAMES_NUM 15
#define TA_SHIFT 8

Serial pc(USBTX, USBRX);// tx, rx, Virtual serial port over USB
DigitalOut greenLED(LED1);

void PrintRawData(uint16_t *p);

int main()
{
uint8_t printme=1;
pc.baud(115200);
greenLED = !greenLED;
MLX90640_I2CFreqSet(1000000);

paramsMLX90640 mlx90640;
uint8_t slaveAddress = 0x33;
uint16_t *pEE;
uint16_t *pFrame;
float *toFrame;
static uint16_t eeMLX90640[832];
static uint16_t mlx90640Frame[834];
static float mlx90640To[768];
float emissivity = 0.95;
float eTa;  //Ta for emissivity compensation
float vdd; //voltage of the circuit inside the camera
int status;

pEE = eeMLX90640;
pFrame = mlx90640Frame;
toFrame = mlx90640To;

status=MLX90640_SetRefreshRate(slaveAddress,0x07);//64Hz
printf("status_SetRefreshRate: %d\n", status);
status=MLX90640_SetResolution(slaveAddress,0x03);
printf("status_SetResolution: %d\n", status);
status=MLX90640_SetInterleavedMode(slaveAddress);
printf("status_InterLMode: %d\n",status);
status=MLX90640_DumpEE(slaveAddress, pEE);
printf("status_DumpEE: %d\n", status);
PrintRawData(pEE);

status=MLX90640_ExtractParameters(pEE, &mlx90640); 

PrintRawData(pEE);
   
    while (true){
        wait_us(500000);
        greenLED = 1;    // turn on LED until initialization is complete
        MLX90640_GetFrameData(slaveAddress, pFrame);
        greenLED = 0;    // turn on LED until initialization is complete
        eTa = MLX90640_GetTa(pFrame, &mlx90640) - TA_SHIFT;
        MLX90640_CalculateTo(pFrame, &mlx90640, emissivity, eTa, mlx90640To);
        MLX90640_GetImage(pFrame, &mlx90640, toFrame);
        
        if (printme)
        {            
            PrintRawData(pFrame);
        }
    }

}

void PrintRawData(uint16_t *p)
{
for(int i=0; i<832; i++) {
pc.printf("%04X,",*p++);
}
pc.printf("\r\n");

}

I added to the project the files "MLX90640_API.h", "MLX90640_API.cpp", "MLX90640_I2C_Driver.h", and "MLX640_SWI2C_Driver.cpp" provided by you. When I try to debug the code, it seems to hang when I call the function "MLX90640_ExtractParameters(pEE, &mlx90640)", so I thought the problem was in the parameter "pEE", which is the EEPROM data, and which actually turns out to be incorrect. Is it possible that the code hangs when I call the function "MLX90640_ExtractParameters(pEE, &mlx90640)" due to incorrect EEPROM data?

@slavysis
Copy link
Collaborator

Hi,
The code should not hang, unless there is something awfully wrong with the memory allocation and you somehow mess-up the stack. I would suggest that you start by fixing the communication and getting proper data.
You are using the SW I2C lib and this one requires that you trim the SCL frequency.
The I2C speed when reading the EEPROM should be no more than 400KHz, So you should do something like:
MLX90640_I2CFreqSet(400000);
status=MLX90640_DumpEE(slaveAddress, pEE);
MLX90640_I2CFreqSet(1000000);

@eleogrande
Copy link
Author

eleogrande commented Apr 24, 2023 via email

@slavysis
Copy link
Collaborator

slavysis commented Apr 24, 2023

Hi,

-2 means that the value written via I2C in a register is not the one that you wanted to write.
Using the mbed on-line compiler is not a problem.
It does seem like you have issues with the i2c communication - I suspect it is timing related. Could you share scope traces of a failing communication?

Best regards

@eleogrande
Copy link
Author

Hi,
i'm sorry but i don't have proper instrumentation. Can i do it on mbed on-line compiler?

@slavysis
Copy link
Collaborator

Hi,
I am not sure I can think of a way to check the if the I2C waveforms are correct without having some sort of a visualization. Do you have a logical analyzer?

Best regards

@eleogrande
Copy link
Author

Hi,
sorry my instrumentation consist only in the camera and the board. So i think i cannot have some visualization. Is there a way to do it without an external device? Do you reccommend to use the "MLX90640_I2C_Driver.cpp" or "MLX90640_SWI2C_Driver.cpp" file? I take this opportunity to ask if the other files are correct. This is the MLX90640_API.cpp:

#include <MLX90640_I2C_Driver.h>
#include <MLX90640_API.h>
#include <math.h>
#include <stdio.h>
#include

void ExtractVDDParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
void ExtractPTATParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
void ExtractGainParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
void ExtractTgcParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
void ExtractResolutionParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
void ExtractKsTaParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
void ExtractKsToParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
void ExtractAlphaParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
void ExtractOffsetParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
void ExtractKtaPixelParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
void ExtractKvPixelParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
void ExtractCPParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
void ExtractCILCParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
int ExtractDeviatingPixels(uint16_t *eeData, paramsMLX90640 *mlx90640);
int CheckAdjacentPixels(uint16_t pix1, uint16_t pix2);
float GetMedian(float *values, int n);
int IsPixelBad(uint16_t pixel,paramsMLX90640 *params);

int MLX90640_DumpEE(uint8_t slaveAddr, uint16_t *eeData)
{
return MLX90640_I2CRead(slaveAddr, 0x2400, 832, eeData);
}

int MLX90640_CheckInterrupt(uint8_t slaveAddr)
{
uint16_t statusRegister;
MLX90640_I2CRead(slaveAddr, 0x8000, 1, &statusRegister);
return (statusRegister & 0b1000) > 0;
}

void MLX90640_StartMeasurement(uint8_t slaveAddr, uint8_t subPage)
{
uint16_t controlRegister1;
uint16_t statusRegister;
MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
controlRegister1 &= 0b1111111111101111;
controlRegister1 |= subPage << 4;
MLX90640_I2CWrite(slaveAddr, 0x800D, controlRegister1);
MLX90640_I2CRead(slaveAddr, 0x8000, 1, &statusRegister);
statusRegister &= 0b1111111111110111; // Clear b3: new data available in RAM
statusRegister |= 0b0000000000110000; // Set b5: start of measurement
// Set b4: enable RAM overwrite
MLX90640_I2CWrite(slaveAddr, 0x8000, statusRegister);
}

int MLX90640_GetData(uint8_t slaveAddr, uint16_t *frameData)
{
int error = 0;
uint16_t statusRegister;
uint16_t controlRegister1;

// Get page data
error = MLX90640_I2CRead(slaveAddr, 0x0400, 832, frameData);

// Get status reguster
MLX90640_I2CRead(slaveAddr, 0x8000, 1, &statusRegister);

// Get control register
MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);

frameData[832] = controlRegister1;
frameData[833] = statusRegister & 0x0001; // Populate the subpage number

return error;

}

int MLX90640_InterpolateOutliers(uint16_t *frameData, uint16_t *eepromData)
{
for(int x = 0; x < 768; x++){
int broken = eepromData[64 + x] == 0;
int outlier = eepromData[64 + x] & 0x0001;
if (broken){
float val = 0;
int count = 0;
if(x - 33 > 0){
val += frameData[x - 33];
val += frameData[x - 31];
count += 2;
} else if (x - 31 > 0){
val += frameData[x - 31];
count += 1;
}
if(x + 33 < 768){
val += frameData[x + 33];
val += frameData[x + 31];
count += 2;
} else if (x + 31 < 768){
val += frameData[x + 31];
count += 1;
}
frameData[x] = (uint16_t)((float)(val / count) * 1.0003);
}
}

return 0;

}

int MLX90640_GetFrameData(uint8_t slaveAddr, uint16_t *frameData)
{
uint16_t dataReady = 1;
uint16_t controlRegister1;
uint16_t statusRegister;
int error = 1;
uint8_t cnt = 0;

auto t_start = std::chrono::system_clock::now();
dataReady = 0;
while(dataReady == 0)
{
    error = MLX90640_I2CRead(slaveAddr, 0x8000, 1, &statusRegister);
    if(error != 0)
    {
        return error;
    }
    dataReady = statusRegister & 0x0008;

auto t_end = std::chrono::system_clock::now();
auto t_elapsed = std::chrono::duration_cast<std::chrono::seconds>(t_end - t_start);
if (t_elapsed.count() > 5) {

#ifdef DEBUG
printf("frameData timeout error waiting for dataReady \n");
#endif
return -1;
}
}

while(dataReady != 0 && cnt < 5)
{
    error = MLX90640_I2CWrite(slaveAddr, 0x8000, 0x0030);
    if(error == -1)
    {
        return error;
    }

    error = MLX90640_I2CRead(slaveAddr, 0x0400, 832, frameData);
    if(error != 0)
    {

#ifdef DEBUG
printf("frameData read error \n");
#endif
return error;
}

    error = MLX90640_I2CRead(slaveAddr, 0x8000, 1, &statusRegister);
    if(error != 0)
    {
        return error;
    }
    dataReady = statusRegister & 0x0008;
    cnt = cnt + 1;
}

if(cnt > 4)
{

#ifdef DEBUG
fprintf(stderr, "cnt > 4 error \n");
#endif
// return -8;
}
//printf("count: %d \n", cnt);
error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
frameData[832] = controlRegister1;
frameData[833] = statusRegister & 0x0001;

if(error != 0)
{
    return error;
}

return frameData[833];

}

int MLX90640_ExtractParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
{
int error = 0;

ExtractVDDParameters(eeData, mlx90640);
ExtractPTATParameters(eeData, mlx90640);
ExtractGainParameters(eeData, mlx90640);
ExtractTgcParameters(eeData, mlx90640);
ExtractResolutionParameters(eeData, mlx90640);
ExtractKsTaParameters(eeData, mlx90640);
ExtractKsToParameters(eeData, mlx90640);
ExtractCPParameters(eeData, mlx90640);
ExtractAlphaParameters(eeData, mlx90640);
ExtractOffsetParameters(eeData, mlx90640);
ExtractKtaPixelParameters(eeData, mlx90640);
ExtractKvPixelParameters(eeData, mlx90640);
ExtractCILCParameters(eeData, mlx90640);
error = ExtractDeviatingPixels(eeData, mlx90640);

return error;

}

//------------------------------------------------------------------------------

int MLX90640_SetResolution(uint8_t slaveAddr, uint8_t resolution)
{
uint16_t controlRegister1;
int value;
int error;

value = (resolution & 0x03) << 10;

error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);

if(error == 0)
{
    value = (controlRegister1 & 0xF3FF) | value;
    error = MLX90640_I2CWrite(slaveAddr, 0x800D, value);
}

return error;

}

//------------------------------------------------------------------------------

int MLX90640_GetCurResolution(uint8_t slaveAddr)
{
uint16_t controlRegister1;
int resolutionRAM;
int error;

error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
if(error != 0)
{
    return error;
}
resolutionRAM = (controlRegister1 & 0x0C00) >> 10;

return resolutionRAM;

}

//------------------------------------------------------------------------------

int MLX90640_SetRefreshRate(uint8_t slaveAddr, uint8_t refreshRate)
{
uint16_t controlRegister1;
int value;
int error;

value = (refreshRate & 0x07)<<7;
//value = (refreshRate)<<7;
error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
if(error == 0)
{
    value = (controlRegister1 & 0xFC7F) | value;
    error = MLX90640_I2CWrite(slaveAddr, 0x800D, value);
}

return error;

}

//------------------------------------------------------------------------------

int MLX90640_GetRefreshRate(uint8_t slaveAddr)
{
uint16_t controlRegister1;
int refreshRate;
int error;

error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
if(error != 0)
{
    return error;
}
refreshRate = (controlRegister1 & 0x0380) >> 7;

return refreshRate;

}

//------------------------------------------------------------------------------

int MLX90640_SetInterleavedMode(uint8_t slaveAddr)
{
uint16_t controlRegister1;
int value;
int error;

error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);

if(error == 0)
{
    value = (controlRegister1 & 0xEFFF);
    error = MLX90640_I2CWrite(slaveAddr, 0x800D, value);
}

return error;

}

//------------------------------------------------------------------------------

int MLX90640_SetChessMode(uint8_t slaveAddr)
{
uint16_t controlRegister1;
int value;
int error;

error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);

if(error == 0)
{
    value = (controlRegister1 | 0x1000);
    error = MLX90640_I2CWrite(slaveAddr, 0x800D, value);
}

return error;

}

//------------------------------------------------------------------------------

int MLX90640_GetCurMode(uint8_t slaveAddr)
{
uint16_t controlRegister1;
int modeRAM;
int error;

error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
if(error != 0)
{
    return error;
}
modeRAM = (controlRegister1 & 0x1000) >> 12;

return modeRAM;

}

//------------------------------------------------------------------------------

int MLX90640_SetDeviceMode(uint8_t slaveAddr, uint8_t deviceMode)
{
uint16_t controlRegister1;
int value;
int error;

value = (deviceMode & 0x01)<<4;

error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
if(error == 0)
{
    value = (controlRegister1 & 0b1111111111111101) | value;
    error = MLX90640_I2CWrite(slaveAddr, 0x800D, value);
}

return error;

}

//------------------------------------------------------------------------------

int MLX90640_SetSubPageRepeat(uint8_t slaveAddr, uint8_t subPageRepeat)
{
uint16_t controlRegister1;
int value;
int error;

value = (subPageRepeat & 0x01)<<3;

error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
if(error == 0)
{
    value = (controlRegister1 & 0b1111111111110111) | value;
    error = MLX90640_I2CWrite(slaveAddr, 0x800D, value);
}

return error;

}

//------------------------------------------------------------------------------

int MLX90640_SetSubPage(uint8_t slaveAddr, uint8_t subPage)
{
uint16_t controlRegister1;
int value;
int error;

value = (subPage & 0x01)<<4;

error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
if(error == 0)
{
    value = (controlRegister1 & 0b1111111110001111) | value;
    error = MLX90640_I2CWrite(slaveAddr, 0x800D, value);
}

return error;

}

//------------------------------------------------------------------------------

void MLX90640_CalculateTo(uint16_t *frameData, const paramsMLX90640 *params, float emissivity, float tr, float *result)
{
float vdd;
float ta;
float ta4;
float tr4;
float taTr;
float gain;
float irDataCP[2];
float irData;
float alphaCompensated;
uint8_t mode;
int8_t ilPattern;
int8_t chessPattern;
int8_t pattern;
int8_t conversionPattern;
float Sx;
float To;
float alphaCorrR[4];
int8_t range;
uint16_t subPage;
float ktaScale;
float kvScale;
float alphaScale;
float kta;
float kv;

subPage = frameData[833];
vdd = MLX90640_GetVdd(frameData, params);
ta = MLX90640_GetTa(frameData, params);

ta4 = (ta + 273.15);
ta4 = ta4 * ta4;
ta4 = ta4 * ta4;
tr4 = (tr + 273.15);
tr4 = tr4 * tr4;
tr4 = tr4 * tr4;
taTr = tr4 - (tr4-ta4)/emissivity;

ktaScale = pow(2,(double)params->ktaScale);
kvScale = pow(2,(double)params->kvScale);
alphaScale = pow(2,(double)params->alphaScale);

alphaCorrR[0] = 1 / (1 + params->ksTo[0] * 40);
alphaCorrR[1] = 1 ;
alphaCorrR[2] = (1 + params->ksTo[1] * params->ct[2]);
alphaCorrR[3] = alphaCorrR[2] * (1 + params->ksTo[2] * (params->ct[3] - params->ct[2]));

//------------------------- Gain calculation -----------------------------------
gain = frameData[778];
if(gain > 32767)
{
gain = gain - 65536;
}

gain = params->gainEE / gain;

//------------------------- To calculation -------------------------------------
mode = (frameData[832] & 0x1000) >> 5;

irDataCP[0] = frameData[776];
irDataCP[1] = frameData[808];
for( int i = 0; i < 2; i++)
{
    if(irDataCP[i] > 32767)
    {
        irDataCP[i] = irDataCP[i] - 65536;
    }
    irDataCP[i] = irDataCP[i] * gain;
}
irDataCP[0] = irDataCP[0] - params->cpOffset[0] * (1 + params->cpKta * (ta - 25)) * (1 + params->cpKv * (vdd - 3.3));
if( mode ==  params->calibrationModeEE)
{
    irDataCP[1] = irDataCP[1] - params->cpOffset[1] * (1 + params->cpKta * (ta - 25)) * (1 + params->cpKv * (vdd - 3.3));
}
else
{
  irDataCP[1] = irDataCP[1] - (params->cpOffset[1] + params->ilChessC[0]) * (1 + params->cpKta * (ta - 25)) * (1 + params->cpKv * (vdd - 3.3));
}

for( int pixelNumber = 0; pixelNumber < 768; pixelNumber++)
{
    ilPattern = pixelNumber / 32 - (pixelNumber / 64) * 2;
    chessPattern = ilPattern ^ (pixelNumber - (pixelNumber/2)*2);
    conversionPattern = ((pixelNumber + 2) / 4 - (pixelNumber + 3) / 4 + (pixelNumber + 1) / 4 - pixelNumber / 4) * (1 - 2 * ilPattern);

    if(mode == 0)
    {
      pattern = ilPattern;
    }
    else
    {
      pattern = chessPattern;
    }

    if(pattern == frameData[833])
    {
        irData = frameData[pixelNumber];
        if(irData > 32767)
        {
            irData = irData - 65536;
        }
        irData = irData * gain;

        kta = params->kta[pixelNumber]/ktaScale;
        kv = params->kv[pixelNumber]/kvScale;
        irData = irData - params->offset[pixelNumber]*(1 + kta*(ta - 25))*(1 + kv*(vdd - 3.3));

        if(mode !=  params->calibrationModeEE)
        {
          irData = irData + params->ilChessC[2] * (2 * ilPattern - 1) - params->ilChessC[1] * conversionPattern;
        }

        irData = irData - params->tgc * irDataCP[subPage];
        irData = irData / emissivity;

        alphaCompensated = SCALEALPHA*alphaScale/params->alpha[pixelNumber];
        alphaCompensated = alphaCompensated*(1 + params->KsTa * (ta - 25));

        Sx = alphaCompensated * alphaCompensated * alphaCompensated * (irData + alphaCompensated * taTr);
        Sx = sqrt(sqrt(Sx)) * params->ksTo[1];

        To = sqrt(sqrt(irData/(alphaCompensated * (1 - params->ksTo[1] * 273.15) + Sx) + taTr)) - 273.15;

        if(To < params->ct[1])
        {
            range = 0;
        }
        else if(To < params->ct[2])
        {
            range = 1;
        }
        else if(To < params->ct[3])
        {
            range = 2;
        }
        else
        {
            range = 3;
        }

        To = sqrt(sqrt(irData / (alphaCompensated * alphaCorrR[range] * (1 + params->ksTo[range] * (To - params->ct[range]))) + taTr)) - 273.15;

        result[pixelNumber] = To;
    }
}

}

//------------------------------------------------------------------------------

void MLX90640_GetImage(uint16_t *frameData, paramsMLX90640 *params, float *result)
{
float vdd;
float ta;
float gain;
float irDataCP[2];
float irData;
float alphaCompensated;
uint8_t mode;
int8_t ilPattern;
int8_t chessPattern;
int8_t pattern;
int8_t conversionPattern;
float image;

uint16_t subPage;
float ktaScale;
float kvScale;
float kta;
float kv;

subPage = frameData[833];

vdd = MLX90640_GetVdd(frameData, params);
ta = MLX90640_GetTa(frameData, params);

ktaScale = pow(2,(double)params->ktaScale);
kvScale = pow(2,(double)params->kvScale);

//------------------------- Gain calculation -----------------------------------
gain = frameData[778];
if(gain > 32767)
{
gain = gain - 65536;
}

gain = params->gainEE / gain; 

//------------------------- Image calculation -------------------------------------
mode = (frameData[832] & 0x1000) >> 13;

irDataCP[0] = frameData[776];  
irDataCP[1] = frameData[808];
for( int i = 0; i < 2; i++)
{
    if(irDataCP[i] > 32767)
    {
        irDataCP[i] = irDataCP[i] - 65536;
    }
    irDataCP[i] = irDataCP[i] * gain;
}

irDataCP[0] = irDataCP[0] - params->cpOffset[0] * (1.0f + params->cpKta * (ta - 25.0f)) * (1.0f + params->cpKv * (vdd - 3.3f));
if( mode ==  params->calibrationModeEE)
{
    irDataCP[1] = irDataCP[1] - params->cpOffset[1] * (1.0f + params->cpKta * (ta - 25.0f)) * (1.0f + params->cpKv * (vdd - 3.3f));
}
else
{
    irDataCP[1] = irDataCP[1] - (params->cpOffset[1] + params->ilChessC[0]) * (1.0f + params->cpKta * (ta - 25.0f)) * (1 + params->cpKv * (vdd - 3.3f));
}

for( int pixelNumber = 0; pixelNumber < 768; pixelNumber++)
{
    ilPattern = int((pixelNumber) / 32) - int(int((pixelNumber) / 32) / 2) * 2; 
    chessPattern = ilPattern ^ (pixelNumber - int(pixelNumber/2)*2); 
    conversionPattern = (int((pixelNumber - 2) / 4) - int((pixelNumber - 1) / 4) + int((pixelNumber + 1) / 4) - int((pixelNumber) / 4)) * (1 - 2 * ilPattern);
    
    if(mode == 0)
    {
      pattern = ilPattern; 
    }
    else 
    {
      pattern = chessPattern; 
    }
    if(pattern == frameData[833])
    {    
        irData = frameData[pixelNumber];
        if(irData > 32767)
        {
            irData = irData - 65536;
        }
        irData = irData * gain;
        irData = irData - params->offset[pixelNumber]*(1 + params->kta[pixelNumber]*(ta - 25.0f))*(1.0f + params->kv[pixelNumber]*(vdd - 3.3f));
        if(mode !=  params->calibrationModeEE)
        {
          irData = irData + params->ilChessC[2] * (2 * ilPattern - 1) - params->ilChessC[1] * conversionPattern; 
        }
        
        irData = irData - params->tgc * ((1-pattern) * irDataCP[0] + pattern * irDataCP[1]);
        
        alphaCompensated = (params->alpha[pixelNumber] - params->tgc * ((1-pattern) * params->cpAlpha[0] + pattern * params->cpAlpha[1]));
        
        image = irData/alphaCompensated;
        
        //result[pixelNumber] = image;
    }
}

}

//------------------------------------------------------------------------------

float MLX90640_GetVdd(uint16_t *frameData, const paramsMLX90640 *params)
{
float vdd;
float resolutionCorrection;

int resolutionRAM;

vdd = frameData[810];
if(vdd > 32767)
{
    vdd = vdd - 65536;
}
resolutionRAM = (frameData[832] & 0x0C00) >> 10;
resolutionCorrection = pow(2, (double)params->resolutionEE) / pow(2, (double)resolutionRAM);
vdd = (resolutionCorrection * vdd - params->vdd25) / params->kVdd + 3.3;

return vdd;

}

//------------------------------------------------------------------------------

float MLX90640_GetTa(uint16_t *frameData, const paramsMLX90640 *params)
{
float ptat;
float ptatArt;
float vdd;
float ta;

vdd = MLX90640_GetVdd(frameData, params);

ptat = frameData[800];
if(ptat > 32767)
{
    ptat = ptat - 65536;
}

ptatArt = frameData[768];
if(ptatArt > 32767)
{
    ptatArt = ptatArt - 65536;
}
ptatArt = (ptat / (ptat * params->alphaPTAT + ptatArt)) * pow(2, (double)18);

ta = (ptatArt / (1 + params->KvPTAT * (vdd - 3.3)) - params->vPTAT25);
ta = ta / params->KtPTAT + 25;

return ta;

}

//------------------------------------------------------------------------------

int MLX90640_GetSubPageNumber(uint16_t *frameData)
{
return frameData[833];

}

//------------------------------------------------------------------------------
void MLX90640_BadPixelsCorrection(uint16_t *pixels, float *to, int mode, paramsMLX90640 *params)
{
float ap[4];
uint8_t pix;
uint8_t line;
uint8_t column;

pix = 0;
while(pixels[pix] != 0xFFFF)
{
    line = pixels[pix]>>5;
    column = pixels[pix] - (line<<5);

    if(mode == 1)
    {
        if(line == 0)
        {
            if(column == 0)
            {
                to[pixels[pix]] = to[33];
            }
            else if(column == 31)
            {
                to[pixels[pix]] = to[62];
            }
            else
            {
                to[pixels[pix]] = (to[pixels[pix]+31] + to[pixels[pix]+33])/2.0;
            }
        }
        else if(line == 23)
        {
            if(column == 0)
            {
                to[pixels[pix]] = to[705];
            }
            else if(column == 31)
            {
                to[pixels[pix]] = to[734];
            }
            else
            {
                to[pixels[pix]] = (to[pixels[pix]-33] + to[pixels[pix]-31])/2.0;
            }
        }
        else if(column == 0)
        {
            to[pixels[pix]] = (to[pixels[pix]-31] + to[pixels[pix]+33])/2.0;
        }
        else if(column == 31)
        {
            to[pixels[pix]] = (to[pixels[pix]-33] + to[pixels[pix]+31])/2.0;
        }
        else
        {
            ap[0] = to[pixels[pix]-33];
            ap[1] = to[pixels[pix]-31];
            ap[2] = to[pixels[pix]+31];
            ap[3] = to[pixels[pix]+33];
            to[pixels[pix]] = GetMedian(ap,4);
        }
    }
    else
    {
        if(column == 0)
        {
            to[pixels[pix]] = to[pixels[pix]+1];
        }
        else if(column == 1 || column == 30)
        {
            to[pixels[pix]] = (to[pixels[pix]-1]+to[pixels[pix]+1])/2.0;
        }
        else if(column == 31)
        {
            to[pixels[pix]] = to[pixels[pix]-1];
        }
        else
        {
            if(IsPixelBad(pixels[pix]-2,params) == 0 && IsPixelBad(pixels[pix]+2,params) == 0)
            {
                ap[0] = to[pixels[pix]+1] - to[pixels[pix]+2];
                ap[1] = to[pixels[pix]-1] - to[pixels[pix]-2];
                if(fabs(ap[0]) > fabs(ap[1]))
                {
                    to[pixels[pix]] = to[pixels[pix]-1] + ap[1];
                }
                else
                {
                    to[pixels[pix]] = to[pixels[pix]+1] + ap[0];
                }
            }
            else
            {
                to[pixels[pix]] = (to[pixels[pix]-1]+to[pixels[pix]+1])/2.0;
            }
        }
    }
    pix = pix + 1;
}

}

//------------------------------------------------------------------------------

void ExtractVDDParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
{
int16_t kVdd;
int16_t vdd25;

kVdd = eeData[51];

kVdd = (eeData[51] & 0xFF00) >> 8;
if(kVdd > 127)
{
    kVdd = kVdd - 256;
}
kVdd = 32 * kVdd;
vdd25 = eeData[51] & 0x00FF;
vdd25 = ((vdd25 - 256) << 5) - 8192;

mlx90640->kVdd = kVdd;
mlx90640->vdd25 = vdd25;

}

//------------------------------------------------------------------------------

void ExtractPTATParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
{
float KvPTAT;
float KtPTAT;
int16_t vPTAT25;
float alphaPTAT;

KvPTAT = (eeData[50] & 0xFC00) >> 10;
if(KvPTAT > 31)
{
    KvPTAT = KvPTAT - 64;
}
KvPTAT = KvPTAT/4096;

KtPTAT = eeData[50] & 0x03FF;
if(KtPTAT > 511)
{
    KtPTAT = KtPTAT - 1024;
}
KtPTAT = KtPTAT/8;

vPTAT25 = eeData[49];

alphaPTAT = (eeData[16] & 0xF000) / pow(2, (double)14) + 8.0f;

mlx90640->KvPTAT = KvPTAT;
mlx90640->KtPTAT = KtPTAT;
mlx90640->vPTAT25 = vPTAT25;
mlx90640->alphaPTAT = alphaPTAT;

}

//------------------------------------------------------------------------------

void ExtractGainParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
{
int16_t gainEE;

gainEE = eeData[48];
if(gainEE > 32767)
{
    gainEE = gainEE -65536;
}

mlx90640->gainEE = gainEE;

}

//------------------------------------------------------------------------------

void ExtractTgcParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
{
float tgc;
tgc = eeData[60] & 0x00FF;
if(tgc > 127)
{
tgc = tgc - 256;
}
tgc = tgc / 32.0f;

mlx90640->tgc = tgc;

}

//------------------------------------------------------------------------------

void ExtractResolutionParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
{
uint8_t resolutionEE;
resolutionEE = (eeData[56] & 0x3000) >> 12;

mlx90640->resolutionEE = resolutionEE;

}

//------------------------------------------------------------------------------

void ExtractKsTaParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
{
float KsTa;
KsTa = (eeData[60] & 0xFF00) >> 8;
if(KsTa > 127)
{
KsTa = KsTa -256;
}
KsTa = KsTa / 8192.0f;

mlx90640->KsTa = KsTa;

}

//------------------------------------------------------------------------------

void ExtractKsToParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
{
int KsToScale;
int8_t step;

step = ((eeData[63] & 0x3000) >> 12) * 10;

mlx90640->ct[0] = -40;
mlx90640->ct[1] = 0;
mlx90640->ct[2] = (eeData[63] & 0x00F0) >> 4;
mlx90640->ct[3] = (eeData[63] & 0x0F00) >> 8;

mlx90640->ct[2] = mlx90640->ct[2]*step;
mlx90640->ct[3] = mlx90640->ct[2] + mlx90640->ct[3]*step;
mlx90640->ct[4] = 400;

KsToScale = (eeData[63] & 0x000F) + 8;
KsToScale = 1 << KsToScale;

mlx90640->ksTo[0] = eeData[61] & 0x00FF;
mlx90640->ksTo[1] = (eeData[61] & 0xFF00) >> 8;
mlx90640->ksTo[2] = eeData[62] & 0x00FF;
mlx90640->ksTo[3] = (eeData[62] & 0xFF00) >> 8;

for(int i = 0; i < 4; i++)
{
    if(mlx90640->ksTo[i] > 127)
    {
        mlx90640->ksTo[i] = mlx90640->ksTo[i] - 256;
    }
    mlx90640->ksTo[i] = mlx90640->ksTo[i] / KsToScale;
}

mlx90640->ksTo[4] = -0.0002;

}

//------------------------------------------------------------------------------

void ExtractAlphaParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
{
int accRow[24];
int accColumn[32];
int p = 0;
int alphaRef;
uint8_t alphaScale;
uint8_t accRowScale;
uint8_t accColumnScale;
uint8_t accRemScale;
float alphaTemp[768];
float temp;

accRemScale = eeData[32] & 0x000F;
accColumnScale = (eeData[32] & 0x00F0) >> 4;
accRowScale = (eeData[32] & 0x0F00) >> 8;
alphaScale = ((eeData[32] & 0xF000) >> 12) + 30;
alphaRef = eeData[33];

for(int i = 0; i < 6; i++)
{
    p = i * 4;
    accRow[p + 0] = (eeData[34 + i] & 0x000F);
    accRow[p + 1] = (eeData[34 + i] & 0x00F0) >> 4;
    accRow[p + 2] = (eeData[34 + i] & 0x0F00) >> 8;
    accRow[p + 3] = (eeData[34 + i] & 0xF000) >> 12;
}

for(int i = 0; i < 24; i++)
{
    if (accRow[i] > 7)
    {
        accRow[i] = accRow[i] - 16;
    }
}

for(int i = 0; i < 8; i++)
{
    p = i * 4;
    accColumn[p + 0] = (eeData[40 + i] & 0x000F);
    accColumn[p + 1] = (eeData[40 + i] & 0x00F0) >> 4;
    accColumn[p + 2] = (eeData[40 + i] & 0x0F00) >> 8;
    accColumn[p + 3] = (eeData[40 + i] & 0xF000) >> 12;
}

for(int i = 0; i < 32; i ++)
{
    if (accColumn[i] > 7)
    {
        accColumn[i] = accColumn[i] - 16;
    }
}

for(int i = 0; i < 24; i++)
{
    for(int j = 0; j < 32; j ++)
    {
        p = 32 * i +j;
        alphaTemp[p] = (eeData[64 + p] & 0x03F0) >> 4;
        if (alphaTemp[p] > 31)
        {
            alphaTemp[p] = alphaTemp[p] - 64;
        }
        alphaTemp[p] = alphaTemp[p]*(1 << accRemScale);
        alphaTemp[p] = (alphaRef + (accRow[i] << accRowScale) + (accColumn[j] << accColumnScale) + alphaTemp[p]);
        alphaTemp[p] = alphaTemp[p] / pow(2,(double)alphaScale);
        alphaTemp[p] = alphaTemp[p] - mlx90640->tgc * (mlx90640->cpAlpha[0] + mlx90640->cpAlpha[1])/2;
        alphaTemp[p] = SCALEALPHA/alphaTemp[p];
    }
}

temp = alphaTemp[0];
for(int i = 1; i < 768; i++)
{
    if (alphaTemp[i] > temp)
    {
        temp = alphaTemp[i];
    }
}

alphaScale = 0;
while(temp < 32768)
{
    temp = temp*2;
    alphaScale = alphaScale + 1;
}

for(int i = 0; i < 768; i++)
{
    temp = alphaTemp[i] * pow(2,(double)alphaScale);
    mlx90640->alpha[i] = (temp + 0.5);

}

mlx90640->alphaScale = alphaScale;

}

//------------------------------------------------------------------------------

void ExtractOffsetParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
{
int occRow[24];
int occColumn[32];
int p = 0;
int16_t offsetRef;
uint8_t occRowScale;
uint8_t occColumnScale;
uint8_t occRemScale;

occRemScale = (eeData[16] & 0x000F);
occColumnScale = (eeData[16] & 0x00F0) >> 4;
occRowScale = (eeData[16] & 0x0F00) >> 8;
offsetRef = eeData[17];
if (offsetRef > 32767)
{
    offsetRef = offsetRef - 65536;
}

for(int i = 0; i < 6; i++)
{
    p = i * 4;
    occRow[p + 0] = (eeData[18 + i] & 0x000F);
    occRow[p + 1] = (eeData[18 + i] & 0x00F0) >> 4;
    occRow[p + 2] = (eeData[18 + i] & 0x0F00) >> 8;
    occRow[p + 3] = (eeData[18 + i] & 0xF000) >> 12;
}

for(int i = 0; i < 24; i++)
{
    if (occRow[i] > 7)
    {
        occRow[i] = occRow[i] - 16;
    }
}

for(int i = 0; i < 8; i++)
{
    p = i * 4;
    occColumn[p + 0] = (eeData[24 + i] & 0x000F);
    occColumn[p + 1] = (eeData[24 + i] & 0x00F0) >> 4;
    occColumn[p + 2] = (eeData[24 + i] & 0x0F00) >> 8;
    occColumn[p + 3] = (eeData[24 + i] & 0xF000) >> 12;
}

for(int i = 0; i < 32; i ++)
{
    if (occColumn[i] > 7)
    {
        occColumn[i] = occColumn[i] - 16;
    }
}

for(int i = 0; i < 24; i++)
{
    for(int j = 0; j < 32; j ++)
    {
        p = 32 * i +j;
        mlx90640->offset[p] = (eeData[64 + p] & 0xFC00) >> 10;
        if (mlx90640->offset[p] > 31)
        {
            mlx90640->offset[p] = mlx90640->offset[p] - 64;
        }
        mlx90640->offset[p] = mlx90640->offset[p]*(1 << occRemScale);
        mlx90640->offset[p] = (offsetRef + (occRow[i] << occRowScale) + (occColumn[j] << occColumnScale) + mlx90640->offset[p]);
    }
}

}

//------------------------------------------------------------------------------

void ExtractKtaPixelParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
{
int p = 0;
int8_t KtaRC[4];
int8_t KtaRoCo;
int8_t KtaRoCe;
int8_t KtaReCo;
int8_t KtaReCe;
uint8_t ktaScale1;
uint8_t ktaScale2;
uint8_t split;
float ktaTemp[768];
float temp;

KtaRoCo = (eeData[54] & 0xFF00) >> 8;
if (KtaRoCo > 127)
{
    KtaRoCo = KtaRoCo - 256;
}
KtaRC[0] = KtaRoCo;

KtaReCo = (eeData[54] & 0x00FF);
if (KtaReCo > 127)
{
    KtaReCo = KtaReCo - 256;
}
KtaRC[2] = KtaReCo;

KtaRoCe = (eeData[55] & 0xFF00) >> 8;
if (KtaRoCe > 127)
{
    KtaRoCe = KtaRoCe - 256;
}
KtaRC[1] = KtaRoCe;

KtaReCe = (eeData[55] & 0x00FF);
if (KtaReCe > 127)
{
    KtaReCe = KtaReCe - 256;
}
KtaRC[3] = KtaReCe;

ktaScale1 = ((eeData[56] & 0x00F0) >> 4) + 8;
ktaScale2 = (eeData[56] & 0x000F);

for(int i = 0; i < 24; i++)
{
    for(int j = 0; j < 32; j ++)
    {
        p = 32 * i +j;
        split = 2*(p/32 - (p/64)*2) + p%2;
        ktaTemp[p] = (eeData[64 + p] & 0x000E) >> 1;
        if (ktaTemp[p] > 3)
        {
            ktaTemp[p] = ktaTemp[p] - 8;
        }
        ktaTemp[p] = ktaTemp[p] * (1 << ktaScale2);
        ktaTemp[p] = KtaRC[split] + ktaTemp[p];
        ktaTemp[p] = ktaTemp[p] / pow(2,(double)ktaScale1);
        //ktaTemp[p] = ktaTemp[p] * mlx90640->offset[p];
    }
}

temp = fabs(ktaTemp[0]);
for(int i = 1; i < 768; i++)
{
    if (fabs(ktaTemp[i]) > temp)
    {
        temp = fabs(ktaTemp[i]);
    }
}

ktaScale1 = 0;
while(temp < 64)
{
    temp = temp*2;
    ktaScale1 = ktaScale1 + 1;
}

for(int i = 0; i < 768; i++)
{
    temp = ktaTemp[i] * pow(2,(double)ktaScale1);
    if (temp < 0)
    {
        mlx90640->kta[i] = (temp - 0.5);
    }
    else
    {
        mlx90640->kta[i] = (temp + 0.5);
    }

}

mlx90640->ktaScale = ktaScale1;

}

//------------------------------------------------------------------------------

void ExtractKvPixelParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
{
int p = 0;
int8_t KvT[4];
int8_t KvRoCo;
int8_t KvRoCe;
int8_t KvReCo;
int8_t KvReCe;
uint8_t kvScale;
uint8_t split;
float kvTemp[768];
float temp;

KvRoCo = (eeData[52] & 0xF000) >> 12;
if (KvRoCo > 7)
{
    KvRoCo = KvRoCo - 16;
}
KvT[0] = KvRoCo;

KvReCo = (eeData[52] & 0x0F00) >> 8;
if (KvReCo > 7)
{
    KvReCo = KvReCo - 16;
}
KvT[2] = KvReCo;

KvRoCe = (eeData[52] & 0x00F0) >> 4;
if (KvRoCe > 7)
{
    KvRoCe = KvRoCe - 16;
}
KvT[1] = KvRoCe;

KvReCe = (eeData[52] & 0x000F);
if (KvReCe > 7)
{
    KvReCe = KvReCe - 16;
}
KvT[3] = KvReCe;

kvScale = (eeData[56] & 0x0F00) >> 8;


for(int i = 0; i < 24; i++)
{
    for(int j = 0; j < 32; j ++)
    {
        p = 32 * i +j;
        split = 2*(p/32 - (p/64)*2) + p%2;
        kvTemp[p] = KvT[split];
        kvTemp[p] = kvTemp[p] / pow(2,(double)kvScale);
        //kvTemp[p] = kvTemp[p] * mlx90640->offset[p];
    }
}

temp = fabs(kvTemp[0]);
for(int i = 1; i < 768; i++)
{
    if (fabs(kvTemp[i]) > temp)
    {
        temp = fabs(kvTemp[i]);
    }
}

kvScale = 0;
while(temp < 64)
{
    temp = temp*2;
    kvScale = kvScale + 1;
}

for(int i = 0; i < 768; i++)
{
    temp = kvTemp[i] * pow(2,(double)kvScale);
    if (temp < 0)
    {
        mlx90640->kv[i] = (temp - 0.5);
    }
    else
    {
        mlx90640->kv[i] = (temp + 0.5);
    }

}

mlx90640->kvScale = kvScale;

}

//------------------------------------------------------------------------------

void ExtractCPParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
{
float alphaSP[2];
int16_t offsetSP[2];
float cpKv;
float cpKta;
uint8_t alphaScale;
uint8_t ktaScale1;
uint8_t kvScale;

alphaScale = ((eeData[32] & 0xF000) >> 12) + 27;

offsetSP[0] = (eeData[58] & 0x03FF);
if (offsetSP[0] > 511)
{
    offsetSP[0] = offsetSP[0] - 1024;
}

offsetSP[1] = (eeData[58] & 0xFC00) >> 10;
if (offsetSP[1] > 31)
{
    offsetSP[1] = offsetSP[1] - 64;
}
offsetSP[1] = offsetSP[1] + offsetSP[0];

alphaSP[0] = (eeData[57] & 0x03FF);
if (alphaSP[0] > 511)
{
    alphaSP[0] = alphaSP[0] - 1024;
}
alphaSP[0] = alphaSP[0] /  pow(2,(double)alphaScale);

alphaSP[1] = (eeData[57] & 0xFC00) >> 10;
if (alphaSP[1] > 31)
{
    alphaSP[1] = alphaSP[1] - 64;
}
alphaSP[1] = (1 + alphaSP[1]/128) * alphaSP[0];

cpKta = (eeData[59] & 0x00FF);
if (cpKta > 127)
{
    cpKta = cpKta - 256;
}
ktaScale1 = ((eeData[56] & 0x00F0) >> 4) + 8;
mlx90640->cpKta = cpKta / pow(2,(double)ktaScale1);

cpKv = (eeData[59] & 0xFF00) >> 8;
if (cpKv > 127)
{
    cpKv = cpKv - 256;
}
kvScale = (eeData[56] & 0x0F00) >> 8;
mlx90640->cpKv = cpKv / pow(2,(double)kvScale);

mlx90640->cpAlpha[0] = alphaSP[0];
mlx90640->cpAlpha[1] = alphaSP[1];
mlx90640->cpOffset[0] = offsetSP[0];
mlx90640->cpOffset[1] = offsetSP[1];

}

//------------------------------------------------------------------------------

void ExtractCILCParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
{
float ilChessC[3];
uint8_t calibrationModeEE;

calibrationModeEE = (eeData[10] & 0x0800) >> 4;
calibrationModeEE = calibrationModeEE ^ 0x80;

ilChessC[0] = (eeData[53] & 0x003F);
if (ilChessC[0] > 31)
{
    ilChessC[0] = ilChessC[0] - 64;
}
ilChessC[0] = ilChessC[0] / 16.0f;

ilChessC[1] = (eeData[53] & 0x07C0) >> 6;
if (ilChessC[1] > 15)
{
    ilChessC[1] = ilChessC[1] - 32;
}
ilChessC[1] = ilChessC[1] / 2.0f;

ilChessC[2] = (eeData[53] & 0xF800) >> 11;
if (ilChessC[2] > 15)
{
    ilChessC[2] = ilChessC[2] - 32;
}
ilChessC[2] = ilChessC[2] / 8.0f;

mlx90640->calibrationModeEE = calibrationModeEE;
mlx90640->ilChessC[0] = ilChessC[0];
mlx90640->ilChessC[1] = ilChessC[1];
mlx90640->ilChessC[2] = ilChessC[2];

}

//------------------------------------------------------------------------------

int ExtractDeviatingPixels(uint16_t *eeData, paramsMLX90640 *mlx90640)
{
uint16_t pixCnt = 0;
uint16_t brokenPixCnt = 0;
uint16_t outlierPixCnt = 0;
int warn = 0;
int i;

for(pixCnt = 0; pixCnt<5; pixCnt++)
{
    mlx90640->brokenPixels[pixCnt] = 0xFFFF;
    mlx90640->outlierPixels[pixCnt] = 0xFFFF;
}

pixCnt = 0;
while (pixCnt < 768 && brokenPixCnt < 5 && outlierPixCnt < 5)
{
    if(eeData[pixCnt+64] == 0)
    {
        mlx90640->brokenPixels[brokenPixCnt] = pixCnt;
        brokenPixCnt = brokenPixCnt + 1;
    }
    else if((eeData[pixCnt+64] & 0x0001) != 0)
    {
        mlx90640->outlierPixels[outlierPixCnt] = pixCnt;
        outlierPixCnt = outlierPixCnt + 1;
    }

    pixCnt = pixCnt + 1;

}

if(brokenPixCnt > 4)
{
    warn = -3;
}
else if(outlierPixCnt > 4)
{
    warn = -4;
}
else if((brokenPixCnt + outlierPixCnt) > 4)
{
    warn = -5;
}
else
{
    for(pixCnt=0; pixCnt<brokenPixCnt; pixCnt++)
    {
        for(i=pixCnt+1; i<brokenPixCnt; i++)
        {
            warn = CheckAdjacentPixels(mlx90640->brokenPixels[pixCnt],mlx90640->brokenPixels[i]);
            if(warn != 0)
            {
                return warn;
            }
        }
    }

    for(pixCnt=0; pixCnt<outlierPixCnt; pixCnt++)
    {
        for(i=pixCnt+1; i<outlierPixCnt; i++)
        {
            warn = CheckAdjacentPixels(mlx90640->outlierPixels[pixCnt],mlx90640->outlierPixels[i]);
            if(warn != 0)
            {
                return warn;
            }
        }
    }

    for(pixCnt=0; pixCnt<brokenPixCnt; pixCnt++)
    {
        for(i=0; i<outlierPixCnt; i++)
        {
            warn = CheckAdjacentPixels(mlx90640->brokenPixels[pixCnt],mlx90640->outlierPixels[i]);
            if(warn != 0)
            {
                return warn;
            }
        }
    }

}


return warn;

}

//------------------------------------------------------------------------------

int CheckAdjacentPixels(uint16_t pix1, uint16_t pix2)
{
int pixPosDif;

 pixPosDif = pix1 - pix2;
 if(pixPosDif > -34 && pixPosDif < -30)
 {
     return -6;
 }
 if(pixPosDif > -2 && pixPosDif < 2)
 {
     return -6;
 }
 if(pixPosDif > 30 && pixPosDif < 34)
 {
     return -6;
 }

 return 0;

}

//------------------------------------------------------------------------------

float GetMedian(float *values, int n)
{
float temp;

for(int i=0; i<n-1; i++)
{
    for(int j=i+1; j<n; j++)
    {
        if(values[j] < values[i])
        {
            temp = values[i];
            values[i] = values[j];
            values[j] = temp;
        }
    }
}

if(n%2==0)
{
    return ((values[n/2] + values[n/2 - 1]) / 2.0);

}
else
{
    return values[n/2];
}

}

//------------------------------------------------------------------------------

int IsPixelBad(uint16_t pixel,paramsMLX90640 *params)
{
for(int i=0; i<5; i++)
{
if(pixel == params->outlierPixels[i] || pixel == params->brokenPixels[i])
{
return 1;
}
}

return 0;

}

//------------------------------------------------------------------------------

This is "MLX90640_API.h":

#include <stdint.h>
#ifndef MLX640_API_H
#define MLX640_API_H

#define MLX90640_NO_ERROR 0
#define MLX90640_I2C_NACK_ERROR 1
#define MLX90640_I2C_WRITE_ERROR 2
#define MLX90640_BROKEN_PIXELS_NUM_ERROR 3
#define MLX90640_OUTLIER_PIXELS_NUM_ERROR 4
#define MLX90640_BAD_PIXELS_NUM_ERROR 5
#define MLX90640_ADJACENT_BAD_PIXELS_ERROR 6
#define MLX90640_EEPROM_DATA_ERROR 7
#define MLX90640_FRAME_DATA_ERROR 8
#define MLX90640_MEAS_TRIGGER_ERROR 9

#define BIT_MASK(x) (1UL << (x))
#define REG_MASK(sbit,nbits) (((~0UL << (nbits))) << (sbit))

#define MLX90640_EEPROM_START_ADDRESS 0x2400
#define MLX90640_EEPROM_DUMP_NUM 832
#define MLX90640_PIXEL_DATA_START_ADDRESS 0x0400
#define MLX90640_PIXEL_NUM 768
#define MLX90640_LINE_NUM 24
#define MLX90640_COLUMN_NUM 32
#define MLX90640_LINE_SIZE 32
#define MLX90640_COLUMN_SIZE 24
#define MLX90640_AUX_DATA_START_ADDRESS 0x0700
#define MLX90640_AUX_NUM 64
#define MLX90640_STATUS_REG 0x8000
#define MLX90640_INIT_STATUS_VALUE 0x0030
#define MLX90640_STAT_FRAME_MASK BIT_MASK(0)
#define MLX90640_GET_FRAME(reg_value) (reg_value & MLX90640_STAT_FRAME_MASK)
#define MLX90640_STAT_DATA_READY_MASK BIT_MASK(3)
#define MLX90640_GET_DATA_READY(reg_value) (reg_value & MLX90640_STAT_DATA_READY_MASK)

#define MLX90640_CTRL_REG 0x800D
#define MLX90640_CTRL_TRIG_READY_MASK BIT_MASK(15)
#define MLX90640_CTRL_REFRESH_SHIFT 7
#define MLX90640_CTRL_REFRESH_MASK REG_MASK(MLX90640_CTRL_REFRESH_SHIFT,3)
#define MLX90640_CTRL_RESOLUTION_SHIFT 10
#define MLX90640_CTRL_RESOLUTION_MASK REG_MASK(MLX90640_CTRL_RESOLUTION_SHIFT,2)
#define MLX90640_CTRL_MEAS_MODE_SHIFT 12
#define MLX90640_CTRL_MEAS_MODE_MASK BIT_MASK(12)

#define MLX90640_MS_BYTE_SHIFT 8
#define MLX90640_MS_BYTE_MASK 0xFF00
#define MLX90640_LS_BYTE_MASK 0x00FF
#define MLX90640_MS_BYTE(reg16) ((reg16 & MLX90640_MS_BYTE_MASK) >> MLX90640_MS_BYTE_SHIFT)
#define MLX90640_LS_BYTE(reg16) (reg16 & MLX90640_LS_BYTE_MASK)
#define MLX90640_MSBITS_6_MASK 0xFC00
#define MLX90640_LSBITS_10_MASK 0x03FF
#define MLX90640_NIBBLE1_MASK 0x000F
#define MLX90640_NIBBLE2_MASK 0x00F0
#define MLX90640_NIBBLE3_MASK 0x0F00
#define MLX90640_NIBBLE4_MASK 0xF000
#define MLX90640_NIBBLE1(reg16) ((reg16 & MLX90640_NIBBLE1_MASK))
#define MLX90640_NIBBLE2(reg16) ((reg16 & MLX90640_NIBBLE2_MASK) >> 4)
#define MLX90640_NIBBLE3(reg16) ((reg16 & MLX90640_NIBBLE3_MASK) >> 8)
#define MLX90640_NIBBLE4(reg16) ((reg16 & MLX90640_NIBBLE4_MASK) >> 12)

#define POW2(x) pow(2, (double)x)

#define SCALEALPHA 0.000001

typedef struct
{
int16_t kVdd;
int16_t vdd25;
float KvPTAT;
float KtPTAT;
uint16_t vPTAT25;
uint16_t alphaPTAT;
int16_t gainEE;
float tgc;
float cpKv;
float cpKta;
uint8_t resolutionEE;
uint8_t calibrationModeEE;
float KsTa;
float ksTo[5];
int16_t ct[5];
float alpha[768];
uint8_t alphaScale;
int16_t offset[768];
float kta[768];
float ktaScale;
float kv[768];
float kvScale;
float cpAlpha[2];
int16_t cpOffset[2];
float ilChessC[3];
uint16_t brokenPixels[5];
uint16_t outlierPixels[5];
} paramsMLX90640;

int MLX90640_DumpEE(uint8_t slaveAddr, uint16_t *eeData);
int MLX90640_GetFrameData(uint8_t slaveAddr, uint16_t *frameData);
int MLX90640_ExtractParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
float MLX90640_GetVdd(uint16_t *frameData, const paramsMLX90640 *params);
float MLX90640_GetTa(uint16_t *frameData, const paramsMLX90640 *params);
void MLX90640_GetImage(uint16_t *frameData, paramsMLX90640 *params, float *result);
void MLX90640_CalculateTo(uint16_t *frameData, const paramsMLX90640 *params, float emissivity, float tr, float *result);
int MLX90640_SetResolution(uint8_t slaveAddr, uint8_t resolution);
int MLX90640_GetCurResolution(uint8_t slaveAddr);
int MLX90640_SetRefreshRate(uint8_t slaveAddr, uint8_t refreshRate);   
int MLX90640_GetRefreshRate(uint8_t slaveAddr);  
int MLX90640_GetSubPageNumber(uint16_t *frameData);
int MLX90640_GetCurMode(uint8_t slaveAddr); 
int MLX90640_SetInterleavedMode(uint8_t slaveAddr);
int MLX90640_SetChessMode(uint8_t slaveAddr);
void MLX90640_BadPixelsCorrection(uint16_t *pixels, float *to, int mode, paramsMLX90640 *params);

int MLX90640_SetDeviceMode(uint8_t slaveAddr, uint8_t deviceMode);
int MLX90640_SetSubPageRepeat(uint8_t slaveAddr, uint8_t subPageRepeat);
int MLX90640_SetSubPage(uint8_t slaveAddr, uint8_t subPage);
int MLX90640_CheckInterrupt(uint8_t slaveAddr);
void MLX90640_StartMeasurement(uint8_t slaveAddr, uint8_t subPage);
int MLX90640_GetData(uint8_t slaveAddr, uint16_t *frameData);
int MLX90640_InterpolateOutliers(uint16_t *frameData, uint16_t *eepromData);
int MLX90640_SynchFrame(uint8_t slaveAddr);
int MLX90640_TriggerMeasurement(uint8_t slaveAddr);

#endif

This is "MLX90640_I2C_Driver.h":

#ifndef MLX90640_I2C_Driver_H
#define MLX90640_I2C_Driver_H

#include <stdint.h>
#include "MLX90640_API.h"
extern void MLX90640_I2CInit(void);
extern int MLX90640_I2CGeneralReset(void);
extern int MLX90640_I2CRead(uint8_t slaveAddr,uint16_t startAddress, uint16_t nMemAddressRead, uint16_t *data);
extern int MLX90640_I2CWrite(uint8_t slaveAddr,uint16_t writeAddress, uint16_t data);
extern void MLX90640_I2CFreqSet(int freq);
#endif

@eleogrande
Copy link
Author

The most stranger thing is that during debugging i have correctly 0 from the calibration functions, but during the run returns other values. Could you explain why? I'm sorry for the many questions and thank you a lot for the help.
Best regards.

@slavysis
Copy link
Collaborator

slavysis commented Apr 26, 2023

What do you mean when you say 'debugging'? Is it step-ing through the code?
My guess would be that there is something wrong with the timings.

If you do not have an oscilloscope To trim the clock frequency, you could connect the SCL to an GPIO of the MCU and measure the frequency while running I2C communication.

@eleogrande
Copy link
Author

eleogrande commented Apr 26, 2023 via email

@slavysis
Copy link
Collaborator

Since you are using the SW I2C and you are stepping through it this stretches the timings and the communication is good. In normal mode the SCL is too fast and the device returns NACK. Modify the void Wait(int freqCnt) function to get the proper I2C timings. The easiest would be to use an oscilloscope in order to trim the frequency. As you do not have one, you could use the MCU to measure the frequency. This would require you to connect the SCL output to an input of the MCU and measure the frequency when you send/receive data via I2C.

@eleogrande
Copy link
Author

I tried to use the oscilloscope but i can't see any waveform with the file i uploaded here. But using this code:

#include "mbed.h"

Serial pc(USBTX, USBRX);
#define WAIT_TIME_MS 1000
I2C i2c(PC_9, PA_8); // sda, clk

int main() {

pc.baud(115200);
i2c.frequency(100000);     

while (1) {
    
    pc.printf("\033[0m\033[2J\033[HI2C Searching!\n\n\n"); 
    int count = 0;
    pc.printf("Starting....\n\n");
    
    for (int address=0; address<256; address+=2) {
        if (!i2c.write(address, NULL, 0)) { // 0 returned is ok                
            pc.printf("I2C address 0x%02X\n", address);
            count++;
        }
    }        
    pc.printf("\n\n%d devices found\n", count);
    WAIT_TIME_MS;       
}

}

i get these two waves: the first refers to the SCL pin and the second to the SDA pin.

d94ec6d4-f174-4071-be71-8d9922e2747a

Going back to the main project, i have another question. When i run the code in debug mode, as mentioned above, the calibration functions correctly return 0, but the "MLX90640_ExtractParameters(pEE, &mlx90640)" functions seems to crash. Is it always due to timing?

Thank you so much for your availability and help, any suggestion on what i could do is always welcome.
Best regards.

@slavysis
Copy link
Collaborator

This is using the hardware I2C of the MCU, and you were trying to use the SW I2C driver. Make sure that your configuration is correct. If you want to use the HW I2C module of the MCU you should use the "MLX640_I2C_Driver.cpp" and implement the i2c read/write functions accordingly. You should definitely first make sure that your communication is working. Now that you have an oscilloscope, this should be a rather straight forward thing to do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants