2014-02-10 183 views
3

如何使用bcm2835庫更改mlx90614的從地址?我試過下面的代碼...通過SMBus/I2C通過bcm2835更改mlx90614的從地址

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]); 
} 

程序的輸出如下:

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 

如果我運行i2cdetect後-y 1,該設備沒有出現在該表,但它響應從0x00或0x5a調用它的程序。在我使用這樣的程序後,i2cdetect通常從地址0x5a中檢測到設備。

所以我想真正的問題是,爲什麼我不能清除並重寫eeprom 0x0e?

Mlx90614 SMBus通信的說明可以在下面找到。最相關的頁面是IMO第19頁,它實際上給出了我想要做的僞代碼示例。 http://www.melexis.com/Assets/SMBus-communication-with-MLX90614-5207.aspx

下面是MLX90614 http://www.melexis.com/Assets/IR-sensor-thermometer-MLX90614-Datasheet-5152.aspx

的數據表和下面是你需要添加一個錯誤字節爲bcm2835 www.airspayce.com/mikem/bcm2835/group__i2c.html

回答

0

掙扎與我mlx90614s完全相同的問題。這是我用來解決它的寫程序(請注意,在調用例程之前,bcm2835-library已正確啓動)。

首先我稱爲寫入程序與 「正確」 Slaveaddress,命令= 0x2E之間(EEPROMAccess | SMBusAddressReg)和數據= 0×0000(擦除)。 「正確的」從地址可以是0x00或出廠默認的0x5a(或任何芯片的真實地址)。

擦除後,我使用相同的寫程序,但現在與數據= 0x005b,從出廠默認0x5a更改爲0x5b,做了斷電重置(POR)和設備顯示與其新地址(0x5b)使用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); 
} 

我使用的CRC8例程是:

// 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); 
} 

此外:根據用於所述MLX90614的數據手冊,上電後出廠默認狀態是PWM輸出。將處於工廠PWM狀態的mlx90614掛接到RPi2上的I2C總線時,i2cdetect會報告總線上數百個I2C器件。試圖通過使用bcm2835庫訪問mlx90614失敗。所需要的是通過將SCL保持低電平至少2ms來強制mlx90614處於其PWM狀態。下面是我做的:

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); 
} 

然而,這只是擱置,直到下一次電。因此,需要在mlx90614的eeprom中寫入pwmctrl寄存器(禁用pwm輸出並強制SDA爲OpenDrain)。我使用前面描述的寫入例程,命令= 0x22(即EEPROMAccess | PWMCTRLAddressRegister),並在擦除pwmctrl寄存器內容後,我寫了0x0200(第一個3個半字節在我的設備中爲020)。關斷復位(POR),器件以SMBus模式啓動(I2C總線不干擾)。 mlx90614是一個棘手的小組件...