3

How do I change slave address of mlx90614 with bcm2835 library? I've tried following code...

int main()
{
   // Buffer, where I store data which I'll send
   unsigned char buf[6];

   // bcm2835 i2c module intialisation code
   bcm2835_init();
   bcm2835_i2c_begin();
   bcm2835_i2c_set_baudrate(25000);
   bcm2835_i2c_setSlaveAddress(0x00);

   // For debug purposes, I read what reason codes operations give.
   bcm2835I2CReasonCodes why;
   bcm2835_i2c_begin();

   // function which reads and prints what value eeprom address 0x0e has. 
   // See below the main.

   printf("Initial check\n");
   check(); // this time it prints a factory default value 0x5a.

   // To access eeprom, the command must start with 0x2X, where x determines the          
   // address, resulting 0x2e.
   buf[0] = 0x2e;

   // According to datasheet, I first have to clear the address before 
   // real write operation.
   buf[1] = 0x00;
   buf[2] = 0x00;
   why = bcm2835_i2c_write(buf,3);
   reason(why); // resolves and prints the reason code. This time it prints OK

   // according to datasheet, eeprom needs 5ms to make a write operation,
   // but I give it 2 seconds.       
   sleep(2); 

   // Then I check did the value in eeprom 0x0e change. IT DOESN'T!
   printf("Check after clear\n");       
   check();

   // Then I try to write a new address to the eeprom but since the clearing 
   // the register didn't work, this is very unlikely to work either.
   buf[0] = 0x2e;
   buf[1] = 0x4b;
   buf[2] = 0x00;
   why = bcm2835_i2c_write(buf,3);
   reason(why);
   sleep(2); 

   // The datasheet says that I have to reset the power supply and after that
   // the device should respond to the new slave address.
   // I do that by pluging off the jumper wires and reconnecting them 
   // after the program has finnished.
   bcm2835_i2c_end();
   return 0;
}

// The function I use to determine what the reason code was.
void reason(bcm2835I2CReasonCodes why)
{
   printf("Reason is: ");
   if(why == BCM2835_I2C_REASON_OK)
   {
      printf("OK");
   }else if(why == BCM2835_I2C_REASON_ERROR_NACK){
      printf("NACK");
   }else if(why == BCM2835_I2C_REASON_ERROR_CLKT){
      printf("Clock stretch");
   }else if(why == BCM2835_I2C_REASON_ERROR_DATA ){
      printf("Data error");
   }else{
      printf("Dunno lol");
   }
   printf("\n");
   return;
}

// Here I read eeprom 0x2e.
void check()
{
   unsigned char buf[6];
   unsigned char reg = 0x2e;
   bcm2835I2CReasonCodes why;
   // better safe than sorry with the buffer :)
   buf[0] = 0;
   buf[1] = 0;
   buf[2] = 0;
   why = bcm2835_i2c_write (&reg, 1);
   reason(why);
   why = bcm2835_i2c_read_register_rs(&reg,&buf[0],3);
   reason(why);
   printf("Buffer values are: %x ; %x ; %x \n", buf[0], buf[1], buf[2]);
}

The output of the program is following:

Initial check
Reason is: OK
Reason is: OK
Buffer values are: 5a ; be ; dc
Reason is: OK
Check after clear
Reason is: OK
Reason is: OK
Buffer values are: 5a ; be ; dc
Reason is: OK

If I run i2cdetect -y 1 after that, the device doesn't appear in the table, but it responds to programs calling it from either 0x00 or 0x5a. After I've used such a program, the i2cdetect detects the device normally from address 0x5a.

So I guess the real question is, why I can't clear and rewrite the eeprom 0x0e?

The description of Mlx90614 SMBus communication can be found below. The most relevat page is IMO the page 19 which actually gives the pseudocode example of what I'm trying to do. http://www.melexis.com/Assets/SMBus-communication-with-MLX90614-5207.aspx

Here's the datasheet for mlx90614 http://www.melexis.com/Assets/IR-sensor-thermometer-MLX90614-Datasheet-5152.aspx

And here's the documentation for bcm2835 www.airspayce.com/mikem/bcm2835/group__i2c.html

TukeV
  • 641
  • 2
  • 6
  • 13

3 Answers3

0

You have to add an Error-Byte. Take a look at this website for an explanation: https://sf264.wordpress.com/2011/03/10/howto-mlx90614-und-pwm/

Calculating CRC-8 for 00002e4b00 gives 0xa3.

I used for calculating CRC-8 this website: http://smbus.org/faq/crc8Applet.htm

I haven't tested this, but I think this should work:

buf[0] = 0x2e;
buf[1] = 0x4b;
buf[2] = 0x00;
buf[3] = 0xa3;
why = bcm2835_i2c_write(buf,4);
Fux
  • 1
0

Struggled with the exact same problem with my mlx90614s. Here is the write routine I used to solve it (Please note that the bcm2835-library was properly initalized before the call to the routine).

First I called the write routine with "correct" Slaveaddress, command=0x2E (EEPROMAccess | SMBusAddressReg) and data=0x0000 (for erase). The "correct" slave address can be 0x00 or the factory default 0x5a (or whatever is the chip's true address).

After erasing I used the same write routine but now with data=0x005b, to change from the factory default 0x5a to 0x5b, did a Power Off Reset (POR) and the device showed up with its new address (0x5b) using i2cdetect.

uint8_t memWriteI2C16(uint8_t SlaveAddress, uint8_t command, uint16_t data)
{    
unsigned char arr[5];
uint8_t status;

//Prepare for CRC8 calc
arr[0] = SlaveAddress<<1;        //NB! 7 bit address + a 0 write bit.      
arr[1] = command;                //Command byte in packet       
arr[2] = *((uint8_t *)(&data));  //Extract data low byte
arr[3] = *((uint8_t *)(&data)+1);//Extract data high byte
arr[4] = crc8(&arr[0],4)&0xFF;   //Calculate PEC by CRC8

bcm2835_i2c_setSlaveAddress(SlaveAddress);//Transmit address byte to I2C/SMBus
status = bcm2835_i2c_write (&arr[1], 4);  //Transmit Command,DataL, DataH and PEC       
bcm2835_delay(5);                         //Delay at least 5ms
return (status);
}

The CRC8 routine I used was:

// Return CRC-8 of the data, using x^8 + x^2 + x + 1 polynomial.  
// A table-based algorithm would be faster, but for only a few bytes 
// it isn't worth the code size. 
// Ref: https://chromium.googlesource.com/chromiumos/platform/vboot_reference/+/master/firmware/lib/crc8.c
uint8_t crc8(const void *vptr, int len)
{
 const uint8_t *data = vptr;
 unsigned crc = 0;
 int i, j;
 for (j = len; j; j--, data++) {
    crc ^= (*data << 8);
    for(i = 8; i; i--) {
        if (crc & 0x8000)
            crc ^= (0x1070 << 3);
        crc <<= 1;
    }
 }
 return (uint8_t)(crc >> 8);
}

In addition: according to the data sheet for the the mlx90614, its default factory state after power up is PWM output. When hooking an mlx90614 in the factory PWM state to the I2C bus on the RPi2, the i2cdetect reports hundreds of I2C devices on the bus. Trying to access the mlx90614 by using the bcm2835-library fails. What is required is to force the mlx90614 out of its PWM-state by holding the SCL low for at least 2ms. Here is what I did:

uint8_t mlx90614SMBusInit()
{
//Hold SCL low for at leat 2ms in order to force the mlx90614 into SMBus-mode
//Ref Melix app note regarding SMBus comm chapter 6.1 and table 5. 
uint8_t SCL1 = 3; //BCM2835 pin no 3 -RPi2 and RevB+. Use if i2cdetect -y 1
uint8_t SCL0 = 1; //BCM2835 pin no 1 -RPi2 and RevB+. Use if i2cdetect -y 0
uint8_t SCL;

SCL = SCL1; 
bcm2835_gpio_fsel(SCL, BCM2835_GPIO_FSEL_OUTP);
bcm2835_gpio_write(SCL ,LOW);
bcm2835_delay( 3); //Delay >2 ms
bcm2835_gpio_write(SCL ,HIGH); 
return (1);
}

However, this only hold until next power up. Hence it is required to write to the pwmctrl-register in mlx90614's eeprom (disable pwm output and force SDA to OpenDrain). I used the write routine as previously described with command=0x22 (i.e. EEPROMAccess | PWMCTRLAddressRegister) and after erasing the pwmctrl-register content, I wrote 0x0200 to it (the frst 3 nibbles was 020 in my devices...). Power Off Reset (POR) and the device started in SMBus-mode (no jamming of the I2C-bus). The mlx90614 is a tricky little component...

SteinarK
  • 1
  • 2
0

Also if you are using I2C-tools package from any linux distribution (in my case I'm using debian distro) you could change the address with the i2cset command (https://manpages.debian.org/buster/i2c-tools/i2cset.8.en.html), here is an example:

#Find your I2C bus in your linux with the command i2cdetect -l 
#(in my case is the i2c-1)
i2cdetect -l
i2c-1   i2c             bcm2835 I2C adapter                     I2C adapter

#Write the word 0x0000 to the address 0x2E and append the PEC check byte.
i2cset -y 1 0x5a 0x2E 0x0000 wp 

#Write the new address as a word, to the address 0x2E and append the PEC 
#check byte. In my case the new address is 0x005c
i2cset -y 1 0x5a 0x2E 0x005c wp

#Perform a power cycle of the Mlx90614 device
#Check the new address with the command i2cdetect -y 1
i2cdetect -y 1
Enmanuel Medina
  • 129
  • 1
  • 4