melexis / mlx90640-library

MLX90640 library functions
Apache License 2.0
241 stars 192 forks source link

New data is never available #12

Closed Makodan closed 6 years ago

Makodan commented 6 years ago

Hello!

I have an interesting problem. I tried to port the driver onto STM32 board. I rewrote the mlx90640_i2c_driver.

With my code, I can dump calibration data, then extract the parameters, and them seem correct. But the "MLX90640_GetFrameData" always stuck in a while loop, because the status register's data ready bit will never be 1.

    paramsMLX90640 sensor_params;
    static uint16_t eeData[832];
    static uint16_t mlx90640Frame[834];
    static float32_t pixels[768];

    HAL_Delay(5000);

//Tried with another resolution, refresh-rate too

    MLX90640_DumpEE(MLX90640_ADDR,eeData);
    MLX90640_ExtractParameters(eeData,&sensor_params);

    MLX90640_GetFrameData(MLX90640_ADDR,mlx90640Frame);    // stuck here
    MLX90640_CalculateTo(mlx90640Frame,&sensor_params,1,1,pixels);

In debug mode I can clearly see that the status register is always 0x0900.

I tried to solve it with direct register writings:

//initializations etc...

    if(StartMeasTempSensor() != 0){
        Error_Handler();
    }
    HAL_Delay(5000);
    while(!CheckTempSensor()){ }      // stuck here

int StartMeasTempSensor(){

    uint16_t status_register;
    uint16_t control_register;
    int error  = 0;

    //Set control register
    error += MLX90640_I2CRead(MLX90640_ADDR,0x800D,1,&control_register);
    control_register = (control_register & 0xE000) | 0x1901;
    error += MLX90640_I2CWrite(MLX90640_ADDR,0x800D,control_register);

    //Set status register
    error += MLX90640_I2CRead(MLX90640_ADDR,0x8000,1,&status_register);
    status_register = (status_register & 0xFFC0) | 0x0030;
    MLX90640_I2CWrite(MLX90640_ADDR,0x8000,status_register);

    return error;
}

int CheckTempSensor(){

    uint16_t status_register;
    uint16_t control_register;

    MLX90640_I2CRead(MLX90640_ADDR,0x800D,1,&control_register);
    MLX90640_I2CRead(MLX90640_ADDR,0x8000,1,&status_register);

    //Data ready
    if((status_register & 0x0008) > 0){
        return 1;
    }
    else{

        if((status_register & 0x0020) == 0){
            status_register = (status_register & 0xFFC0) | 0x0020;
            MLX90640_I2CWrite(MLX90640_ADDR,0x8000,status_register);

        }
        return 0;
    }

}

I noticed another interesting thing, if I write the control register, I can read back the correct value (e.g. 0x1901), so I think I2C communication is OK. But if I write the status register (e.g. 0x0930), I always read back 0x0900. I have two senors, but both produce this (error). Do you have any idea? Thanks

Edit: If I write 0x0901 to control register, i read out 0x0800 from the status register, I don't write to the status register, but new data never comes.

slavysis commented 6 years ago

Hi, this does not make sense. How did you verify that the extracted parameters are correct? Did you check the register reading with an oscilloscope. It looks a bit like you are reading a different address or the data is not properly aligned. Could you please check the I2C with an oscilloscope? Could you share your I2C driver?

Best regards

Makodan commented 6 years ago

Thank you for your answer! The scale of extracted parameters are similar to the example parameters. E.g. kVdd = -3904, vdd25 = -11296 The reader function:

int MLX90640_I2CRead(uint8_t slave_address, uint16_t start_address,
        uint16_t numb_words, uint16_t *data) {

    if (HAL_I2C_Mem_Read(&hi2c1, slave_address, start_address,
    I2C_MEMADD_SIZE_16BIT, (uint8_t *) data, 2*numb_words, 500) == HAL_OK) {
        return 0;
    }

    else {
        return -1;
    }

The writer:

int MLX90640_I2CWrite(uint8_t slave_address, uint16_t write_address,
        uint16_t data) {

    uint16_t data_check;

    if (HAL_I2C_Mem_Write(&hi2c1, slave_address, write_address,
    I2C_MEMADD_SIZE_16BIT, (uint8_t*) &data, sizeof(data), 200) != HAL_OK) {
        return -1;
    }

    else {

        MLX90640_I2CRead(slave_address, write_address, 2, &data_check);

        if (data_check == data) {
            return 0;
        }

        else {
            return -2;
        }

    }

}

After a lot of thinking I came the same conclusion, maybe I write and read to/from wrong addresses, or the data is not properly aligned. I will try to get an oscilloscope, or at least a digital analyzer and I will check the communication, after that I will post the result. What do you think, are the i2c driver functions right?

slavysis commented 6 years ago

The functions seem to be OK. Can you share a log of frameData? While you are checking with analyzer or an oscilloscope, I could check the data for something suspicious.

Best regards

Makodan commented 6 years ago

I check the I2C waveforms with oscilloscope, the write function and the read function both was bad. I did not make screenshot, but I write down the values.

Write function, 0x0901 value to the 0x800D (a - acknowledge): Start|0x66|a|0x80|a|0x0D|a|0x01|a|0x09|a| As we can see MSB and LSB are in reverse order.

The waveform of the read function is good, but the processing is bad. The function puts the data into the array in reverse order too.
After I fix it, I will write.

Thanks for being so helpful! Best regards

slavysis commented 6 years ago

It more or less fits with my suspicion. I could not find a setting for the HAL function to use big-endian storage rather than little-endian so I assumed it simply reads and stores the data as on the bus. It is still a bit strange though, as I would expect that the same arrangement would be applied to the EERPOM and frame data. Thus the parameters should also be wrong.

Makodan commented 6 years ago

Okay, I fixed it. I did it rather with low level functions. There is the output. ( my hand, 640x480 pixel interpolated image, made with OpenCV)

thermo_cam

There are the I2C read and write functions:

uint16_t MLX90640_I2CReadWord(uint8_t slave_address, uint16_t start_address) {

    uint16_t puff;
    volatile uint8_t reg_m,reg_l,dat_m,dat_l;

    reg_m = (uint8_t) ((start_address & 0xFF00) >> 8);  //Address MSB
    reg_l = (uint8_t) (start_address & 0x00FF);         //Address LSB

    while (LL_I2C_IsActiveFlag_BUSY(I2C1)) {
    }

    //LL_I2C_AcknowledgeNextData(I2C1, LL_I2C_ACK);
    LL_I2C_GenerateStartCondition(I2C1);
    while (!LL_I2C_IsActiveFlag_SB(I2C1)) {
    }

    //Send  device address
    LL_I2C_TransmitData8(I2C1, slave_address);
    while (!LL_I2C_IsActiveFlag_ADDR(I2C1)) {
    }
    LL_I2C_ClearFlag_ADDR(I2C1);

    while (!LL_I2C_IsActiveFlag_TXE(I2C1)) {
    }

    //Send start address MSB
    LL_I2C_TransmitData8(I2C1, reg_m);
    while (!LL_I2C_IsActiveFlag_TXE(I2C1)) {
    }

    //Send start address LSB
    LL_I2C_TransmitData8(I2C1, reg_l);
    while (!LL_I2C_IsActiveFlag_TXE(I2C1)) {
    }

    //Repeat start condition
    LL_I2C_GenerateStartCondition(I2C1);
    while (!LL_I2C_IsActiveFlag_SB(I2C1)) {
    }

    //Send device address again + read
    LL_I2C_TransmitData8(I2C1, slave_address | 0x01);
    while (!LL_I2C_IsActiveFlag_ADDR(I2C1)) {
    }

    //Read out data MSB, send ACK
    LL_I2C_ClearFlag_ADDR(I2C1);
    LL_I2C_AcknowledgeNextData(I2C1, LL_I2C_ACK);
    while (!LL_I2C_IsActiveFlag_RXNE(I2C1)) {
    }
    dat_m = LL_I2C_ReceiveData8(I2C1);

    //Read out data LSB, send NACK
    while (!LL_I2C_IsActiveFlag_RXNE(I2C1)) {
    }
    dat_l = LL_I2C_ReceiveData8(I2C1);
    LL_I2C_AcknowledgeNextData(I2C1, LL_I2C_NACK);
    LL_I2C_GenerateStopCondition(I2C1);

    return ((uint16_t) (dat_m << 8)) | ((uint16_t)((dat_l) & 0x00FF));

}

int MLX90640_I2CRead(uint8_t slave_address, uint16_t start_address,
        uint16_t numb_words, uint16_t *data){

    uint16_t temp_address = start_address;
    uint16_t temp_data;
    for(int i=0; i < numb_words; i++){

        temp_data = MLX90640_I2CReadWord(slave_address,temp_address);
        temp_address++;
        *(data + i) = temp_data;

    }
    return 0;
}

void MLX90640_I2CFreqSet(int freq) {
    //i2c.frequency(1000*freq);
}

int MLX90640_I2CWrite(uint8_t slave_address, uint16_t start_address,
        uint16_t data) {

    uint8_t reg_m,reg_l,dat_m,dat_l;
    reg_m = (uint8_t) ((start_address & 0xFF00) >> 8);          //Address MSB
    reg_l = (uint8_t) (start_address & 0x00FF);                 //Address LSB
    dat_m = (uint8_t) ((data & 0xFF00) >> 8);   // Data MSB
    dat_l = (uint8_t) (data & 0x00FF);          //Data LSB

    //reg_m = 0x80;
    //reg_l = 0x0D;
    //dat_m = 0x09;
    //dat_l = 0x01;
    //Check bus busy flag
    while (LL_I2C_IsActiveFlag_BUSY(I2C1)) {
    }

    LL_I2C_GenerateStartCondition(I2C1);
    while (!LL_I2C_IsActiveFlag_SB(I2C1)) {
    }

    //Send device address
    LL_I2C_TransmitData8(I2C1, slave_address);
    while (!LL_I2C_IsActiveFlag_ADDR(I2C1)) {
    }
    LL_I2C_ClearFlag_ADDR(I2C1);

    while (!LL_I2C_IsActiveFlag_TXE(I2C1)) {
    }

    //Send write address MSB
    LL_I2C_TransmitData8(I2C1, reg_m);
    while (!LL_I2C_IsActiveFlag_TXE(I2C1)) {
    }

    //Send write address LSB
    LL_I2C_TransmitData8(I2C1, reg_l);
    while (!LL_I2C_IsActiveFlag_TXE(I2C1)) {
    }

    //Send data MSB
    LL_I2C_TransmitData8(I2C1, dat_m);
    while (!LL_I2C_IsActiveFlag_TXE(I2C1)) {
    }

    //Send data LSB
    LL_I2C_TransmitData8(I2C1, dat_l);
    while (!LL_I2C_IsActiveFlag_BTF(I2C1)) {
    }

    LL_I2C_GenerateStopCondition(I2C1);

    return 0;
}
imliubo commented 5 years ago

Hi @Makodan : I did this with your read and write functions,but it still not work.It will stop here:

    //Send  device address
    LL_I2C_TransmitData8(I2C1, slave_address);
    while (!LL_I2C_IsActiveFlag_ADDR(I2C1)) {
    }
    LL_I2C_ClearFlag_ADDR(I2C1);

But the slave_address was right,I detect it with my raspberry pi.So I don't know why it can't works!Can you give me some advices?I use whole day to do it,but I waste the day...

Makodan commented 5 years ago

Hi @imliubo! What kind of device do you use? There are some difference between STM32F and STM32L family's LL library

imliubo commented 5 years ago

Hi @Makodan Thanks for reply me.I use STM32F405RGT6 MCU to do it.Do you have any advices?I open a repositry want to slove it,can you share your code with LL functions?I just completed the code to read the temperature value in SWI2C mode. MLX90640-With-STM32