-1

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");
}
kiner_shah
  • 3,939
  • 7
  • 23
  • 37
Aqhar
  • 3
  • 2
  • 2
    You show two functions, in one `i2c.write` is surrounded by `i2c.start` and `i2c.stop`, in the other there is no start/stop. This seems a bit suspicious. – Frodyne Mar 01 '23 at 10:02
  • 1
    Also, you only check the return value of the write in `main`, are you sure none of the i2c calls in `change_address` fails? You might also try to print the two serial number bytes, just to check that the reads return something that looks right. – Frodyne Mar 01 '23 at 10:07
  • Aside: When you disable the device's default address, it is probably better to do that by writing to the register via the new address. (I'm not sure what the point is of changing the address though. If there are two devices on the same bus they would both start off with the default address after a power cycle and would interfere with each other when trying to change their address. You would need to disable all but one of the devices to configure it.) – Ian Abbott Mar 01 '23 at 10:28
  • I think it will not be a problem for the two of them to have the same address at the beginning since we can declare them as two different device of I2C – Aqhar Mar 01 '23 at 11:05
  • I might be misunderstanding something, doesn't it state that the address is 7 bits, with the LSB clear? Wouldn't that make `0xC4` illegal (as the 8th bit is set)? I'd expect to magically find that your device is now using address `0x44` (if it masked the value off). – Hasturkun Mar 01 '23 at 14:14

1 Answers1

1

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:

  • I don't know the I2C library you are using, the code here consists of mostly guesses for how it is to be used. It could be that I'm using your I2C library wrong because I simply don't know which one you are using.
  • I don't have your LIDAR device, so I can't test this at all.
  • If you use that with another I2C device where the registers in question mean something else, you might damage that hardware, depending on what these registers do.
  • The code above is just an illustration, and provided with no warranty whatsoever. Use at your own risk.
  • According to the documentation of your I2C LIDAR device the device will forget the address you set here when it's next powered on again. I therefore don't quite get why you'd want to change the address in the first place. (Unless you're using more than one device and have a GPIO pin controlling the enable pins of the LIDAR devices.)
  • I found the full manual of your LIDAR device on the internet (in case someone is wondering): https://forum.arduino.cc/uploads/short-url/8ZYkVAsROHL2dnqKxsz2qGSFHvz.pdf
chris_se
  • 1,006
  • 1
  • 7