c++croboticsmbedlidar

I2C Lidar Change Address


I have a problem to change the address of my lidar sensor. Its default address is 0x62 in 7 bits, but as the microcontroller only takes 8-bits so, the address is now 0xC4. Here is the code that I wrote:

void change_address(char new_addr)
{
    char addr = 0x16;
    char tab[2];
   
    i2c.write(LIDAR_ADDR,&addr, 1);
    i2c.read(LIDAR_ADDR,tab,2);
  
    char tab1[2] = {0x18,0};
    tab1[1] = tab[0];

    i2c.write(LIDAR_ADDR,tab1,2);

    char tab2[2] = {0x19,0};
    tab2[1] = tab[1];
    
    i2c.write(LIDAR_ADDR,tab2,2);

    char tab3[2] = {0x1a,new_addr};

    i2c.write(LIDAR_ADDR,tab3,2);

    char tab4[2] = {0x1e,0x08};

    i2c.write(LIDAR_ADDR,tab4,2);
}

Steps for changing the address

I have followed all the steps, but when I want to check the new address, the new address that I assigned doesn't exist. Does anyone know the reason? Thank you in advance

I did this code to check the presence of the I2C component, this code worked well with the default address of the lidar sensor but it doesn't work anymore when I tried to change the address of the lidar

int main() {

  int i;
  int rep;
  printf("Debut du Scan...\n\r");
  char new_add = 0x46 << 1;
  change_address(new_add);   
  for (i = 0x00; i <= 0xFF; i = i + 1) 
  {
    i2c.start();       // Start
    rep = i2c.write(i); // Envoi d’une adresse, y a-t-il une
    if (rep == 1)             // réponse ?
    {
      printf("Test de l’adresse %x Composant I2C present !!\n\r", i);
    } 
    else 
    {
        printf("Test de l’adresse %x ... rien ... \n\r",i);
    }
    i2c.stop(); // Stop
    wait(0.1);
  }
  printf("Fin du Scan...\n\r");
}

Solution

  • I think you are misunderstanding how I2C works. I2C addresses are 7bit by default (there's a 10bit mode, but we'll ignore that here), but what you do in order to perform I2C communications is for the master/host to initiate an I2C sequence via an I2C start (setting the SDA/SCL levels appropriately), then transmit the control byte (which consists of the 7bit I2C address + 1bit indicating whether you are using a read/write operation, so for writes this would be (0x62 << 1) | 0 (0xc4), for reads it would be (0x62 << 1) | 1 (0xc5)), waiting for the device on the bus to ACK that control byte, and then either reading data on the bus (ACK'ing each byte as the host except for the last) or sending further bytes (and waiting for device ACK's for each on the bus), and finally sending an I2C stop sequence.

    Furthermore, interacting with most I2C devices usually goes as follows: you first have to send one or more bytes that indicate the register on the device you want to interact with (reading/writing), and then you actually perform the write/read operation. If you're writing data to the device, this is simple: you first transfer the control byte, then you transfer the register, and then you transfer the data you want to write to that register. If you're reading data from the device, it gets more complicated, because you first have to initiate a write operation where you send the register (but no data for the register), and then restart the I2C sequence to initiate a read operation to read the data of that register.

    I don't exactly know which I2C library you are using here, but from reading your code, I suspect you'll want to do something like the following to change the address of the device (I've also abstracted away the read/write register functions so that these can be reused when talking to the device proper itself; note that they assume that you always have one byte for the register and one byte for the register data -- if you want to talk to other I2C devices, this may vary -- you'll have to read the documentation there):

    // Read a register from the I2C device
    //    This implementation will write the register index
    //    to the I2C device and then read the resulting
    //    value. (This only works for I2C devices that
    //    follow this very addressing scheme.)
    //
    // Parameters:
    //    i2c_addresss: The 7bit (!) I2C address of the device
    //    device_register: The 8bit device register to read
    //
    // Return value:
    //    The device register
    unsigned char read_register(unsigned char i2c_address, unsigned char device_register)
    {
        i2c.start();
        // send control byte (writing data)
        bool rep = i2c.write((i2c_address << 1) | 0);
        if (!rep) {
            i2c.stop();
            // feel free to do any kind of error handling you like here
            throw std::runtime_error("I2C transaction failed");
        }
        // send the register we want to read to the device
        rep = i2c.write(device_register);
        if (!rep) {
            i2c.stop();
            // feel free to do any kind of error handling you like here
            throw std::runtime_error("I2C transaction failed");
        }
        // now we want to read from the I2C device
        // for this we restart the I2C transaction
        // Note: most I2C devices don't require an
        //       I2C stop before the next I2C start
        //       here, but feel free to insert one
        //       if this doesn't work with your device
        i2c.start();
        // send control byte (reading data)
        bool rep = i2c.write((i2c_address << 1) | 1);
        if (!rep) {
            i2c.stop();
            // feel free to do any kind of error handling you like here
            throw std::runtime_error("I2C transaction failed");
        }
        // now I'm only guessing how your I2C library works...
        // no idea if i2c.read() actually looks this way in your case...
        unsigned char result = i2c.read();
        i2c.stop();
        return result;
    }
    
    // Write a register to the I2C device
    //     This implementation will write the register index, followed
    //     by the register value, to the I2C device. (This only works
    //     for I2C devices that follow this very addressing scheme.)
    //
    // Parameters:
    //    i2c_addresss: The 7bit (!) I2C address of the device
    //    device_register: The 8bit device register to read
    //    value: The register value to write
    void write_register(unsigned char i2c_address, unsigned char device_register, unsigned char value)
    {
        i2c.start();
        // send control byte (writing data)
        bool rep = i2c.write((i2c_address << 1) | 0);
        if (!rep) {
            i2c.stop();
            // feel free to do any kind of error handling you like here
            throw std::runtime_error("I2C transaction failed");
        }
        // send the register we want to write to the device
        rep = i2c.write(device_register);
        if (!rep) {
            i2c.stop();
            // feel free to do any kind of error handling you like here
            throw std::runtime_error("I2C transaction failed");
        }
        // send the value of the register to the device
        rep = i2c.write(value);
        if (!rep) {
            i2c.stop();
            // feel free to do any kind of error handling you like here
            throw std::runtime_error("I2C transaction failed");
        }
        i2c.stop();
    }
    
    void update_address_of_lidar(unsigned char new_address)
    {
        // This is the 7bit (!) address of the device
        unsigned char orig_address = 0x62;
    
        // read out the serial number of the I2C device
        unsigned char sn_high = read_register(orig_address, 0x16);
        unsigned char sn_low = read_register(orig_address, 0x17);
    
        // re-write the serial number of the device to unlock
        // the corresponding register
        write_register(orig_address, 0x18, sn_high);
        write_register(orig_address, 0x19, sn_low);
    
        // write the new i2c address to the device
        write_register(orig_address, 0x1a, new_address);
    
        // attempt to re-read the serial number from the new
        // address (to see if assignment has worked)
        unsigned char sn_high2 = read_register(new_address, 0x16);
        unsigned char sn_low2 = read_register(new_address, 0x17);
        if (sn_high2 != sn_high || sn_low2 != sn_low) {
            // feel free to do any kind of error handling you like here
            throw std::runtime_error("Could not update the I2C address successfully");
        }
    
        // Now that we know the new address works we can disable
        // the default address
        // (Note that while the instructions of the manual say
        // to write 0x08 here, the register documentation for
        // the register 0x1e indicates that 0x01 disables the
        // original I2C address. So if the following doesn't
        // work to disable the original address, you can also
        // try 0x01 here...)
        write_register(orig_address, 0x1e, 0x08);
    }
    
    // Other code:
    
    // NB: if you want the new control byte for writing
    //     to be 0x46, then the corresponding 7bit (!)
    //     address would actually be 0x23
    unsigned char new_address = 0x46;
    
    update_address_of_lidar(new_address);
    

    Standard disclaimers: