This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

RTOS/LAUNCHXL-CC2650: I2C read and write from specific register

Part Number: LAUNCHXL-CC2650
Other Parts Discussed in Thread: CC2650, CC2560, CC2650STK

Tool/software: TI-RTOS

Hello,

I have a laser based range finder (LiDAR Light) with I2C protocol to trigger the sensor and read distance measurement.

I already interfaced them with Arduino and raspberry pi and would like to add it to CC2650 Launchapd.

I checked the example of the Sensortag I2C. But the issue is that I don't know how I can define the register address and the value I want to write into that.

I need to have something like that:

To write: write(register_value , register_address , sensor_I2C_address).

To read: read(register_address , sensor_I2C_Address).

The Lidar sensor I have has the I2C address=0x62.

To trigger sensor to measure distance I need to write "0x03" to register 0x00 of sensor.

Then wait for few milliseconds and then read the measurement value from register 0x52 of sensor.

Could you please help me with implementing it with CC2560 I2C driver.

In driver how I can specify from or to which register I want to read or write. and how I can define the value that I want to write to specific register.

Thanks.

  • Hi Asad,

    You can use the examples on the driver's documentation to see how to implement your receive and transmit functions. It also shows how to set the address of the slave. It can be found here:
    I2CCC26XX driver reference

    Thanks,
    Gerardo

  • Hi Gerardo,

    I checked it before but I can not find the place that we define the address of register we want to read from or write to.
    i2cTrans.slaveAddress = 0x3C; sets the I2C address of slave (sensor). But for example in read basic example, I can not find where the code specify the address of register we want to read from.
    Could you please just show it to me?

    Thanks.
  • Hi Asad,

    The I2C protocol only specifies a slave address and then you transmit and receive to that. For devices that have registers the way it usually works is you first write to the slave device the register you are reading or writing to and then you perform subsequent read or writes to get the data. In that case you would just set the transmit buffer to the address of the register and then perform the operation.

    Your slave device's data sheet will probably have more info on how this should be done.

    Thanks,
    Gerardo
  • The datasheet of my sensors says: ",The device has a 7-bit slave address with default value 0x62"a
    Then based on your suggestion I wrote the code like this:

    I2C_Handle i2c;
    I2C_Params i2cParams;
    I2C_Transaction i2cTransaction;
    /* Create I2C for usage */
    I2C_Params_init(&i2cParams);
    i2cParams.bitRate = I2C_400kHz;
    //i2c = I2C_open(Board_I2C_TMP, &i2cParams);
    i2c = I2C_open(Board_I2C, &i2cParams);

    txBuffer[0] = 0x65; // Address of register I was to read from
    i2cTransaction.slaveAddress = 0x62;
    i2cTransaction.writeBuf = txBuffer;
    i2cTransaction.writeCount = 1;
    i2cTransaction.readBuf = rxBuffer;
    i2cTransaction.readCount = 2;

    /* Take 20 samples and print them out onto the console */
    for (i = 0; i < 20; i++) {
    if (I2C_transfer(i2c, &i2cTransaction)) {
    System_printf("Sample %u: %u %u\n", i,rxBuffer[0],rxBuffer[1]);
    }
    else {
    System_printf("I2C Bus fault\n");
    }

    But I'm not getting the expected value.
    And usually for the first reading (i=0) i get "I2C Bus fault\n".

    Asad.
  • Your code looks correct to me, you mentioned earlier though that "To trigger sensor to measure distance I need to write "0x03" to register 0x00 of sensor" do you need to do that before you start reading? I don't know how the device works so I was just wondering if there would already be meaningful data in register 0x65 that you're reading.

    Thanks,
    Gerardo
  • Hi,
    Thanks for the confirmation. You are correct but still without triggering the sensor the register I'm reading should have valid value.
    I'll try it again to see what is the issue.
    If I want to write using the same concept can I do this in one command since I need to first write the register address I want to write and then write the value. Can I put both of them in same rxRuffer and just change the "i2cTransaction.writeCount" to 2? Something like this that just trigger the sensor and then using the command from previous post I start reading the measurement:

    txBuffer[0] = 0x00; // Address of register I was to write to
    txBuffer[1] = 0x03; // The value that I want to write to register
    i2cTransaction.slaveAddress = 0x62;
    i2cTransaction.writeBuf = txBuffer;
    i2cTransaction.writeCount = 2;
    i2cTransaction.readBuf = rxBuffer;
    i2cTransaction.readCount = 0;

    I2C_transfer(i2c, &i2cTransaction)

    Thanks.

  • Yes that's correct. If that doesn't work could you share the model of the sensor you're using?

    Thanks,
    Gerardo
  • Sure.

    I'm using the Lidar Lite V3 from Garmin.

    You can download the datasheet from following link:

    In original I2Ctmp007 code that written for Sensortag module I just changed the DIO pin map in CC2650STK.h to pins number 4 and 5 since I have launchpad sensor.

    The main issue I have is that when I'm trying to check the DIO04 and 05 signals using oscilloscope, I'm not seeing any changes in signal value and it is always on 3.3volts. But as I know (and checked the signal from Arduino microcontroller signal) the SCL and SDA signals should change during reading and writing.

    Asad.

  • Yea if there's I2C communication happening you should see the SDA and SCL signals changing while data is being sent. This tells me that it's not an issue with the data you're sending but probably with the I2C configuration. Would you mind sharing your project so I can take a look at it?

    Thanks,
    Gerardo
  • To make sure that everything is fine in term of config I used even the main i2ctmp007 (without any changes in setting and just change the addresses and registers and monitored the pin DI05 and DI06 (the one used as SDA and SCL in sensor tag) to see what is the issue and still I don't see any changes in signals.
    Here are the link of the original one and the one I changed the board.c files to use with launchpad.

    www.dropbox.com/.../AADIRq925WRjoQqaKenNTi5na

    www.dropbox.com/.../AAC3C5eMhS8HYJLCm8JNH-m-a
  • Sure.

    I tried two settings, the first one I just simply used sensor tag I2C project without modifying anything and just checked pis 5 and 6. And second project I changed the pin settings in in CC2650STK.h to pins 4 and 5 to use Launch pad pin assignment. Neither of them show changes in SCL and SDA signals during even attempt of I2C connection.

    Here I attached two projects. The first one is modified one and first one original one. I'm using CCS cloud.

  • Gerardo

    So to rule out it being a problem with the pull-up resistors, could you unplug the sensor from your Launchpad and run the code again to check if you see SDA & SCL changing.

    Thanks,
    Gerardo
  • I could run the code using CCS on a SensorTag.

    I had a version of the Lidar sensor that I already could read data from. The sensor has a newer version (based on datasheet the register addresses are the same to read and write but they made some changes on protocol not register values) I don't know why I can not read data from that.

    The I2C data transmission is OK and I don't get any error, but the register value I'm reading returns 0. I know they made some changes in protocol of the I2C in newer version but I'm not sure how I can implement it since I'm not familiar with I2C protocol implementation using your processors.

    Could you please help me with that?

    I already attached the datasheet of the sensor.

    I really appreciate your help.

    Thanks.

  • Asad,

    Were you able to confirm SDA and SCL toggle low and high when performing I2C transactions?

    The changes you have on your i2ctmp007_CC2650_LAUNCH_TI are correct, you need to be using that project. In the project you are reading the serial number of the device(register 0x96), which is a good idea to see if you get data, are you getting any values there?

    On the code block you posted earlier you are reading from register 0x65 which seems to just be the power state control register, which I believe will just return a 0. This register doesn't have a LOW and HIGH register so it will only return 1 byte thus you need to modify your code to be:

    txBuffer[0] = 0x65; // Address of register I was to read from
    i2cTransaction.slaveAddress = 0x62;
    i2cTransaction.writeBuf = txBuffer;
    i2cTransaction.writeCount = 1;
    i2cTransaction.readBuf = rxBuffer;
    i2cTransaction.readCount = 1;

    Thanks,
    Gerardo

  • Hi,

    The code I think is OK since I'm getting data from the old version of the sensor.
    I just checked with the company manufacturing sensor. The said the the made a change in new version of sensor I2C protocol. The primary change that might cause issue is that: "The multi byte read will need to use individual “Start” and “Stop” commands".
    But I don't know how I can implement it with your platform.
    For AVR microcontroller I'm using wire library that works very well and easy to use.

    Can you help how I can modify my code in regard of this important changes?
    I need to write first 0x04 to 0x00 with slave address: 0x62
    And then I need to read high byte of result from 0x0F and low byte from 0x10.

    I also tried reading first from 0x0F and then 0x10 completely independent but it didn't work.

    something like this:
    txBuffer[0] = 0x0f;
    i2cTransaction.writeBuf = txBuffer;
    i2cTransaction.writeCount = 1;
    i2cTransaction.readBuf = rx1;
    i2cTransaction.readCount = 1;
    I2C_transfer(i2c, &i2cTransaction);
    Task_sleep(10000 / Clock_tickPeriod);

    txBuffer[0] = 0x10;
    i2cTransaction.writeBuf = txBuffer;
    i2cTransaction.writeCount = 1;
    i2cTransaction.readBuf = rx2;
    i2cTransaction.readCount = 1;
    I2C_transfer(i2c, &i2cTransaction);
    result= (rx1[0] << 8) | (rx2[0]);
    System_printf("Sample %u: %d %d %d (C)\n", i, result,rx1[0],rx2[0]);

    I don't know how to implement this concept:
    "The multi byte read will need to use individual “Start” and “Stop” commands"

    Asad
  • Asad,

    I believe what they mean is that each transaction needs to have a start and stop bit, thus they don't allow the use of a Re-START bit. They mention this is only for multi byte reads though which would mean the code you just posted should work. But just to be sure, you can eliminate re-START from this as well by breaking up your read and writes into two transactions as such:

    /* Write first */
    txBuffer[0] = 0x0f;
    i2cTransaction.writeBuf = txBuffer;
    i2cTransaction.writeCount = 1;
    i2cTransaction.readBuf = rx1;
    i2cTransaction.readCount = 0;
    I2C_transfer(i2c, &i2cTransaction);
    Task_sleep(10000 / Clock_tickPeriod);
    
    /* Now Read */
    i2cTransaction.writeBuf = txBuffer;
    i2cTransaction.writeCount = 0;
    i2cTransaction.readBuf = rx1;
    i2cTransaction.readCount = 1;
    I2C_transfer(i2c, &i2cTransaction);

    Thanks,
    Gerardo

  • Ohhhh, I tested this approach and it is working well.

    I really appreciate your help.

    Asad.
  • Hi,

    I just have another issue with I2C. The speed of loop is very slow.

    I used arduino with same sensor and I get one reading every 4 milliseconds. But with CC2650 the same procedure takes around 500 and even more to get one loop and data.

    I don't know why the I2C is so slow with this processor.

    I'm pretty sure the speed I set is 400KHz supported by sensor.

    in my main() code I initialized everything like this:

    // Build I2C Object
    I2C_Params_init(&i2cParams);
    i2cParams.bitRate = I2C_400kHz;
    i2c = I2C_open(Board_I2C_TMP, &i2cParams);
    if (i2c == NULL) {
    	System_abort("Error Initializing I2C\n");
    }
    else {
    	System_printf("I2C Initialized!\n");
    }
    
    // Construct tmp007 Task thread
    
    Task_Params taskParams;
    Task_Params_init(&taskParams);
    taskParams.stackSize = TASKSTACKSIZE;
    taskParams.stack = &task0Stack;
    Task_construct(&task0Struct, (Task_FuncPtr)taskFxn, &taskParams, NULL);

    And Then my task:

    Void taskFxn(UArg arg0, UArg arg1)
    {
        unsigned int    i;
        for (i = 0; i < 10000; i++) {
    
        	if (toggle==0)
    			toggle=1;
    		else
    			toggle=0;
    		PIN_setOutputValue(ledPinHandle, Board_LED1, toggle);
    
    	uint32_t time1 = Clock_getTicks();
        	uint8_t *resp=i2c_read(0x8f,2,0x62);
    	uint32_t time2=Clock_getTicks();
    
        	uint16_t temperature = (*(resp+0) << 8) | (*(resp+1));
        	//System_printf("Sample %u: %d (C)\n", i, temperature);
    
    	i2c_write(0x00,0x04,0x62);
            //Task_sleep(10000 / Clock_tickPeriod);
    	System_printf("System time = %lu %lu %u\n", (ULong)time1,(ULong)time2,time2-time1);
    	//System_flush();
    
        }
        /* Deinitialized I2C */
        I2C_close(i2c);
        System_printf("I2C closed!\n");
    
        System_flush();
    }
    uint8_t i2c_write(uint8_t reg_add,uint8_t reg_val,uint8_t add){
    	uint8_t flag=1;
    	uint8_t rx[2];
    	uint8_t tx[2];
    	tx[0]=reg_add;
    	tx[1]=reg_val;
    	i2cTransaction.slaveAddress = add;
    	i2cTransaction.writeBuf = tx;
    	i2cTransaction.writeCount = 2;
    	i2cTransaction.readBuf = rx;
    	i2cTransaction.readCount = 0;
    	if (!I2C_transfer(i2c, &i2cTransaction)){
    		flag=0;
    		System_printf("I2C Bus fault_write\n");
    		System_flush();
    	}
    
    	return flag;
    }
    
    uint8_t * i2c_read(uint8_t reg_add,uint8_t rx_c,uint8_t add){
    	uint8_t rx[1];
    	uint8_t tx[2];
    	rx[0]=reg_add;
    	i2cTransaction.slaveAddress = add;
    	tx[0] = reg_add;
    	i2cTransaction.writeBuf = tx;
    	i2cTransaction.writeCount = 1;
    	i2cTransaction.readBuf = rx;
    	i2cTransaction.readCount = 0;
    	I2C_transfer(i2c, &i2cTransaction);
    
    	//Task_sleep(10000 / Clock_tickPeriod);
    
    	i2cTransaction.writeBuf = tx;
    	i2cTransaction.writeCount = 0;
    	i2cTransaction.readBuf = rx;
    	i2cTransaction.readCount = rx_c;
    
    	if (!I2C_transfer(i2c, &i2cTransaction)) {
    		System_printf("I2C Bus fault\n");
    		System_flush();
    	}
    
    	return rx;
    }

    I tried to get system tick before and after I2C read and write commands and found that the this time is about couple of hundred micro seconds that makes sense. Then I thought the problem is "system_print" and then flush. I removed them and put an LED to blink and saw that still the loop speed is very slow. When I remove the I2C commands from loop the speed is as it should be.

    Do you have any idea what can cause this issue?

  • Asad,

    What values are you getting printed out from your time1 and time2 variables?

    Also, there are two issues with your code:

    • On your i2c_read() function you seem to have the size of your rx and tx arrays mixed, right now you are reading two values into an rx array of size 1. You need to make the size at least 2.
    • On your i2c_read() function you are returning a pointer to a local variable inside the function which is a bad idea as that memory will not be in scope after the function call. An easy way to fix it would be to declare that variable as static as such:
      static uint8_t rx[2];

    Thanks,
    Gerardo

  • I modified tge code as you suggested and the distance measurements are accurate.

    But still the loop is very slow.

    Here is the results of two iteration:

    Sample 41: 221 (C)
    System time = 25762 25976 214
    Sample 42: 221 (C)
    System time = 26403 26617 214

    As you can see the reading time is not slow, it just take 214 clock_tick (I set clock tick duration as 1usec in config file).

    I tried again to comment the system print and flush but still the code is slow.

    I read something about blocking mode of I2C or something like that. Do you think it can cause the issue. I haven't set anything in my code about that.

    Here is my modified code:

    uint8_t i2c_write(uint8_t reg_add,uint8_t reg_val,uint8_t add){
    	uint8_t flag=1;
    	uint8_t rx[2];
    	uint8_t tx[2];
    	tx[0]=reg_add;
    	tx[1]=reg_val;
    	i2cTransaction.slaveAddress = add;
    	i2cTransaction.writeBuf = tx;
    	i2cTransaction.writeCount = 2;
    	i2cTransaction.readBuf = rx;
    	i2cTransaction.readCount = 0;
    	if (!I2C_transfer(i2c, &i2cTransaction)){
    		flag=0;
    		System_printf("I2C Bus fault_write\n");
    		System_flush();
    	}
    
    	return flag;
    }
    
    
    uint8_t * i2c_read(uint8_t reg_add,uint8_t rx_c,uint8_t add){
    	static uint8_t rx[2];
    	uint8_t tx[1];
    	i2cTransaction.slaveAddress = add;
    	tx[0] = reg_add;
    	i2cTransaction.writeBuf = tx;
    	i2cTransaction.writeCount = 1;
    	i2cTransaction.readBuf = rx;
    	i2cTransaction.readCount = 0;
    	I2C_transfer(i2c, &i2cTransaction);
    
    	//Task_sleep(10000 / Clock_tickPeriod);
    
    	i2cTransaction.writeBuf = tx;
    	i2cTransaction.writeCount = 0;
    	i2cTransaction.readBuf = rx;
    	i2cTransaction.readCount = rx_c;
    
    	if (!I2C_transfer(i2c, &i2cTransaction)) {
    		System_printf("I2C Bus fault\n");
    		System_flush();
    	}
    	return rx;
    }
    
    
    
    
    /*
     *  ======== echoFxn ========
     *  Task for this function is created statically. See the project's .cfg file.
     */
    Void taskFxn(UArg arg0, UArg arg1)
    {
        unsigned int    i;
        for (i = 0; i < 10000; i++) {
    
        	if (toggle==0)
    			toggle=1;
    		else
    			toggle=0;
    		PIN_setOutputValue(ledPinHandle, Board_LED1, toggle);
    
    		uint32_t time1 = Clock_getTicks();
        	uint8_t *resp=i2c_read(0x8f,2,0x62);
    		uint32_t time2=Clock_getTicks();
    
        	uint16_t temperature = (*(resp+0) << 8) | (*(resp+1));
        	System_printf("Sample %u: %d (C)\n", i, temperature);
    
    
    		i2c_write(0x00,0x04,0x62);
            //Task_sleep(10000 / Clock_tickPeriod);
    		System_printf("System time = %lu %lu %u\n", (ULong)time1,(ULong)time2,time2-time1);
    		System_flush();
    
        }
        /* Deinitialized I2C */
        I2C_close(i2c);
        System_printf("I2C closed!\n");
    
        System_flush();
    }
    

  • Asad,

    From those print outs it looks like that iteration of the loop only took 641 clock ticks which doesn't seem that much. If you wanted to try and make it faster you could comment out this section if it's not needed:

    /*if (toggle==0)
        toggle=1;
    else
        toggle=0;
    PIN_setOutputValue(ledPinHandle, Board_LED1, toggle); */

    As for the blocking mode vs callback mode, in this example it would not make a difference. In blocking mode, the task's execution is blocked during the I2C transaction. When the transfer has completed, code execution will resume. In callback mode, the task's execution is not blocked, allowing for other transactions to be queued up or to process some other code. Since the point of your code is to read i2c data and then use it you would not benefit from callback mode because you still need to wait for i2c data to be received.

    Thanks,
    Gerardo

  • Gerardo,

    I used my launchpad and the speed is OK with same code.

    I don't know what the problem was with sensortag board.

    Asad