pasko-zh / brzo_i2c

Brzo I2C is a fast I2C Implementation written in Assembly for the esp8266
GNU General Public License v3.0
244 stars 47 forks source link

Migrating ADS1015 from Wire #31

Closed k-korn closed 6 years ago

k-korn commented 6 years ago

Hi, I'm trying to re-implement TI ADS1015 ADC interface using brzo_i2c library, since Adafruit's implementation using Wire.h is not fast enough to read data at the rate ADS1015 cat produce.

On the same hardware (esp-12F, 4.7k pull-up resistors, ADS1015 test board), the following code in Wire.h works:

#include <Arduino.h>
#include <Wire.h>

#define SCL_PIN 5
#define SDA_PIN 4
#define ADS1X15_ADDRESS   (0x48) 
#define I2C_SPEED     100

void setup() {
  Serial.begin(115200);
  Wire.begin(SDA_PIN,SCL_PIN);
  Wire.setClock(I2C_SPEED * 1000L);
}

void loop() {
    uint16_t result = 0;
    uint16_t cfg_value = 50627;
    uint8_t b0, b1;
    // Configure to start ADC
    Wire.beginTransmission(ADS1X15_ADDRESS);
    Wire.write(1); // Config register
    Wire.write((uint8_t)(cfg_value >> 8)); // 2 bytes of config
    Wire.write((uint8_t)(cfg_value & 0xFF));
    Wire.endTransmission();

    delay(2);  // Some sleep for ADC conversion to be done

    // Read data
    Wire.beginTransmission(ADS1X15_ADDRESS);
    Wire.write(0); // Data register
    Wire.endTransmission();
    // now, actually read
    Wire.requestFrom(ADS1X15_ADDRESS,2);
    b0 = Wire.read();
    b1 = Wire.read();
    result = ((b0 << 8) | b1);
    Serial.printf("Received: %u\n",result);
    // Sleep for a while
    delay(1000);

}

And the following code in brzo_i2c does not:

#include <Arduino.h>
#include <brzo_i2c.h>

#define SCL_PIN 5
#define SDA_PIN 4
#define ADS1X15_ADDRESS   (0x48) 
#define I2C_SPEED     100

void setup() {
    Serial.begin(115200);
    brzo_i2c_setup(SDA_PIN, SCL_PIN,20000);
}

void loop() {
    uint16_t cfg_value = 50627;
    uint8_t rc;
    uint8_t buffer[2];

    // Configure to start ADC
    brzo_i2c_start_transaction(ADS1X15_ADDRESS, I2C_SPEED);
    buffer[0] = 1;
    brzo_i2c_write(buffer,1, true);
    buffer[0] = (uint8_t)(cfg_value >> 8);
    buffer[1] = (uint8_t)(cfg_value & 0xFF);
    brzo_i2c_write(buffer,2,false);

    rc = brzo_i2c_end_transaction();
    if (rc > 0) {
        Serial.printf("Write failed with rc=%u\n",rc);
    }
    delay(2);  // Some sleep for ADC conversion to be done
    // Read data
    brzo_i2c_start_transaction(ADS1X15_ADDRESS, I2C_SPEED);
    buffer[0] = 0;
    brzo_i2c_write(buffer,1, true);
    brzo_i2c_read(buffer,2,false);
    rc=brzo_i2c_end_transaction();
    if (rc > 0) {
        Serial.printf("Read failed with rc=%u\n",rc);
    }
    result=((buffer[0]<<8)|buffer[1]);
    Serial.printf("Received: %u\n",result);
    delay(1000);
}

Wire-based code correctly returns different ADC values, depending on what the signal is.

brzo-i2c based code executes correctly (rc is always 0), but data coming from the device is always the same.

Hardware is OK, clock frequency of 100kHz is low enough (Wire-based example runs good at up to 600kHz). Changing repeated_start from true to false has no effect.

Is there anything else that can be done to make this setup work? Thanks.

pasko-zh commented 6 years ago

I suspect some timing issues while reading from the slave during/after ADC.

According to the datasheet it is also possible to read from the config register. Thus, could try to write to the config register value x and read it back? And repeat this with a value y? If you get different values back, i.e. x and y then the basic i2c communication is fine and we have to look deeper at the ADC part.

k-korn commented 6 years ago

Thanks, have tried reading back the config register now. Results are positive with Wire library: value written can be immediately read back with no issues.

With bgzo_i2c, however, value received back after writing does not match the one I've attempted to write. Moreover, in the following sequence:

I am actually receiving back value X, which has been written previously with Wire example.

So I assume, brzo_i2c can read data. But it's having issues writing 2-byte config register.

pasko-zh commented 6 years ago

I had a closer look at the datasheet. Figure 17 shows the expected i2c communication: image

In other words:

i. START ii. Slave Address iii. Send Pointer Register (1 Byte) iv. Send Data (here in the example 2 Bytes) v. STOP

However, what you are sending is:

  1. START
  2. Slave Address
  3. Pointer Register (1 Byte) (NO STOP)
  4. (REPEATED) START
  5. Slave Address
  6. Data (2 Bytes)
  7. STOP

In your code brzo_i2c_write(buffer,1, true); corresponds to 1., 2., 3. then brzo_i2c_write(buffer,2,false); corresponds to 4., 5., 6., 7.

Note that the wire library behaves differently, see the wiki.

That's why the wire library code works.

So, what you need to do is:

k-korn commented 6 years ago

Thanks a lot, the following actually works:

    uint8_t buffer[3];
    brzo_i2c_start_transaction(ADS1X15_ADDRESS, BRZO_I2C_SPEED);
    buffer[0] = 1;
    buffer[1] = (uint8_t)(cfg_value >> 8);
    buffer[2] = (uint8_t)(cfg_value & 0xFF);
    brzo_i2c_write(buffer,3,false);

    rc = brzo_i2c_end_transaction();
pasko-zh commented 6 years ago

You are welcome. I've updated the wiki on how to migrate from Wire Library to brzo, I hope it is better explained now.