This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

I2C Returning Same Wrong Value

Trying to keep this as short as possible. I have 3 I2C devices on a bus. A LSM303(accelerometer + magnetometer), a high precision temp sensor, tricolor LED driver TCA6057,and a L3GD20(gyroscope). The gyro and the accelo are made by the same company and have virtually identical register mappings. I base my library on the arduino library for the LSM303. I can read the accelo just fine and every seems to be working.

I used a similar arduino library to interact with the L3GD20. The arduino library works fine interacting with the device. However whenever I try to interact with it using my port of the arduino library it basically just returns the last address value that I set. so for example if I set it to 0x6F, and I attempt to call the readGyroReg method it returns 6F not matter what register I try to read. It has something to do with the enable method because we acutally started up our circuit and attempted a read, got 0x6F, then wired in the arduino, ran the arduino enable method, then had our device do a read and it read the values fine. I feel like I may be using the TWI functions incorrectly even though it works using almost an identical protocol for the LSM303. The problem is driving me crazy.

here is my library code where I implement the twi any ideas. relatively new to proper controller c code so criticism is welcome, but don't make me cry ;)

Code I am referring to and full files attached below.

CODE

void writeGyro(uint8_t* valToWrite, uint8_t numBytesToWrite){
    uint32_t err_code;
    m_tx_done = false;
    err_code = nrf_drv_twi_tx(&m_twi_device, D20_SA0_HIGH_ADDRESS, (uint8_t*)valToWrite, numBytesToWrite, false);  
    APP_ERROR_CHECK(err_code);
    while(!m_tx_done);
}

uint8_t* readGyro(uint8_t addrToRead,uint8_t numBytesToRead){
    uint32_t err_code;
    writeGyro(&addrToRead, 1);
    m_rx_done = false;
    err_code = nrf_drv_twi_rx(&m_twi_device, D20_SA0_HIGH_ADDRESS, (uint8_t*)&m_fromI2Cdevice, numBytesToRead);
    APP_ERROR_CHECK(err_code);
    while(!m_rx_done);
    return m_fromI2Cdevice;
}

uint8_t readGyroReg(uint8_t addrToRead){
    uint32_t err_code;
    m_fromI2Cdevice[0] = 0x00;
    writeGyro(&addrToRead, 1);
    m_rx_done = false;
    err_code = nrf_drv_twi_rx(&m_twi_device, D20_SA0_HIGH_ADDRESS, (uint8_t*)&m_fromI2Cdevice, 1);
    APP_ERROR_CHECK(err_code);
    while(!m_rx_done);
    return m_fromI2Cdevice[0];
}

void enableGyro(){
    // 0x00 = 0b00000000
    // Low_ODR = 0 (low speed ODR disabled)
    uint8_t setCtrlReg[2] = {LOW_ODR,0x00};
    writeGyro(setCtrlReg, 2);
    nrf_delay_ms(10);
    // 0x6F = 0b01101111
    // DR = 01 (200 Hz ODR); BW = 10 (50 Hz bandwidth); PD = 1 (normal mode); Zen = Yen = Xen = 1 (all axes enabled)
    setCtrlReg[0] = CTRL1G;
    setCtrlReg[1] = 0x6F;
    writeGyro(setCtrlReg,2);//CTRL1G, 0x6F);
    nrf_delay_ms(10);
    // 0x00 = 0b00000000
    // FS = 00 (+/- 250 dps full scale)
    setCtrlReg[0] = CTRL4G;
    setCtrlReg[1] = 0x00;
    writeGyro(setCtrlReg,2);//CTRL4G, 0x00);
    nrf_delay_ms(10);   
}

int16_t* readGyroXYZ(){

    uint8_t msbassert = (OUT_X_L_G | (1 << 7));
    
    uint8_t* results = readGyro(msbassert,6);
    
    uint8_t xla = results[0];
    uint8_t xha = results[1];
    
    uint8_t yla = results[2];
    uint8_t yha = results[3];
    
    uint8_t zla = results[4];
    uint8_t zha = results[5];

    // combine high and low bytes
    // This no longer drops the lowest 4 bits of the readings from the DLH/DLM/DLHC, which are always 0
    // (12-bit resolution, left-aligned). The D has 16-bit resolution
    int16_t gx = (int16_t)(xha << 8 | xla);
    int16_t gy = (int16_t)(yha << 8 | yla);
    int16_t gz = (int16_t)(zha << 8 | zla);
    
    gyroVals[0] = gx;
    gyroVals[1] = gy;
    gyroVals[2] = gz;

    return gyroVals; 
}

TCA6057.c TCA6057.h

  • I suggest you simplify you code and write a test function that simply gets the value of the ID register (or something similar) in those devices (most devices have some sort of ID or WHOAMI register which contains a code that indicates what device it is)

    Also.

    I don't know about TWI, but for SPI, if the first byte of the data is the register number, the response is in rx_buf[1] not rx_buf[0]

    Did you try looking in m_fromI2Cdevice[1]; (if your buff is longer than 1 byte)

  • When reading a register you should use repeated start (this is specified in the L3GD20 datasheet, repeated START (SR) condition). You can do this by setting no_stop to true in the twi tx function:

    nrf_drv_twi_tx( ... , true);
    

    Reading the WHOAMI (0x0F) register like Roger said is also a good idea to check that the twi connection to the chip is good.

Related