0

Attempting to retrieve data from an adafruit sensor (sht31d).

Compiling using GCC on a Fio , microcontroller is an atmega328p.

The original Arduino Implementation using Wire, in readTempHum():

for (uint8_t i=0; i<6; i++) 
{
  readbuffer[i] = Wire.read();
}

Since GCC doesn't use Wire, I tried to populate the buffer myself using fleury i2c:

for (uint8_t i=0; i<6; i++)
  {
    if (i==0) //Activate device
    {
      readbuffer[i] = read8(SHT31_DEFAULT_ADDR);
    }
    else if (i2c_readAck())
    {
      readbuffer[i] = i2c_readAck();
    }
    else
    {
      readbuffer[i] = i2c_readNak();
    }
  }

My intentions for read8 was to simply begin receiving data:

uint16_t read8(uint8_t reg)
{
  uint16_t val = -1;

  if (i2c_start(SHT31_DEFAULT_ADDR<<1 | I2C_WRITE) == 0) //Section 4.2 of datasheet
  {

    i2c_write((uint8_t)reg);
    i2c_stop();

    if (i2c_start(SHT31_DEFAULT_ADDR<<1 | I2C_READ) == 0) //Section 4.4 of datasheet
    {

      val |= ((uint16_t)i2c_readAck());

    }
  }
  return val;
}

It has been really difficult debugging. I have little clue where this is and is not working even after thoroughly reviewing the datasheet. The output through UART using FTDI is nothing.

Also, I tested this with the Arduino library, where it worked flawlessly, so everything is hooked up properly.


Currently digging through the Wire TWI library to better understand where I'm going wrong..

Spektre
  • 49,595
  • 11
  • 110
  • 380
Womble
  • 345
  • 3
  • 13
  • 1
    There are two obvious errors in your code even before you start debugging - `if (i=0)` sets the value of i to zero on every loop, and `read8` does not return anything. – Pete Kirkham Jun 03 '16 at 14:40
  • 1
    Please tell me you have an oscilloscope or some other way to see what you're actually doing on the I2C pins. – unwind Jun 03 '16 at 14:48
  • @PeteKirkham Thank you, fixed. Probably still wrong, but no longer syntactically so. – Womble Jun 03 '16 at 15:00
  • Since the `Wire` code for arduino is all available, might I suggest that you trace it and see where your solution differs? – KevinDTimm Jun 03 '16 at 15:11
  • @KevinDTimm I've tried and basically get lost in the TwoWire library – Womble Jun 03 '16 at 15:28
  • @Womble I finally got my hands on some I2C sensor to test I2C reading of my lib. Its working now so I updated my I2C lib code in the answer too... My best bet is your lib is not working correctly or at all (had see a lot of those and none was working on my ATMega328P as should most of them just almost worked but once you need to send more than single BYTE they fail due to error in ACK and TWI status handling)... btw from a really quick look at the datasheet If I see it tight you need to send some command(s) to the sensor first and then read – Spektre Mar 30 '23 at 07:28

1 Answers1

0

if it helps had similar problem today as I needed I2C for my ATMega328P and there is no I2C support in my ASF for 8bit AVR (I am used to AVR32 UC3 platform where is all much easier) so I decided to write my own I2C lib as I found no working or reliable lib for it...

Here the I2C.h lib:

//-----------------------------------------------------------------------------
//--- I2C lib for ATMega328P ver: 1.02 ----------------------------------------
//-----------------------------------------------------------------------------
#ifndef _I2C_h
#define _I2C_h
//-----------------------------------------------------------------------------
volatile static bool _I2C_initiated=false;
void TWIStart(void)
    {
    TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
    while ((TWCR & (1<<TWINT)) == 0);
    }
//-----------------------------------------------------------------------------
void TWIStop(void)
    {
    TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
    cpu_delay_us(10,clk_cpu);   // some recover wait
    }
//-----------------------------------------------------------------------------
void TWIWrite(U8 u8data)
    {
    TWDR = u8data;
    TWCR = (1<<TWINT)|(1<<TWEN);
    while ((TWCR & (1<<TWINT)) == 0);
    }
//-----------------------------------------------------------------------------
U8 TWIReadACK(void)
    {
    TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
    while ((TWCR & (1<<TWINT)) == 0);
    return TWDR;
    }
//-----------------------------------------------------------------------------
U8 TWIReadNACK(void)
    {
    TWCR = (1<<TWINT)|(1<<TWEN);
    while ((TWCR & (1<<TWINT)) == 0);
    return TWDR;
    }
//-----------------------------------------------------------------------------
#define TWIGetStatus (TWSR&0xF8)
//-----------------------------------------------------------------------------
void I2C_init(U32 clk_i2c)  // init I2C with clk_i2c baudrate
    {
    _I2C_initiated=true;
    // clk_i2c=clk_cpu/(16+2*TWBR*prescaler)
    U32 d=((clk_cpu>>1)/clk_i2c)-8; // TWBR*prescaler
    U32 p=d>>8; if (d&255!=0) p++;  // prescaler = ceil(d/256)
    if (p>3){ p=3; d=255; } // too low freq, set lowest possible
    else if (p) d/=p;
    TWSR = (d>>8)&3;    // set prescaler, clear flags
    TWBR = d&255;       // set clock
    TWCR = (1<<TWEN);   // enable TWI
    }
//-----------------------------------------------------------------------------
bool I2C_probe(U8 adr)      // probe slave with address adr return true if present.
    {
    bool ret=true;
    TWIStart();             if (TWIGetStatus!=0x08) ret=false;  // Start OK?
    TWIWrite((adr<<1)|0);   if (TWIGetStatus!=0x18) ret=false;  // ACK received after address+R/W passed?
    TWIStop();
    return ret;
    }
//-----------------------------------------------------------------------------
bool I2C_write(U8 adr,U8 *dat,U8 siz) // send dat[siz] data to slave adr
    {
    TWIStart(); if (TWIGetStatus!=0x08) return false;
    TWIWrite((adr<<1)|0); if (TWIGetStatus != 0x18) return false;
    for (;siz;dat++,siz--)
        {
        TWIWrite(*dat); if (TWIGetStatus != 0x28) return false;
        }
    TWIStop();
    return true;
    }
//----------------------------------------------------------------------------- 
U8 I2C_read(U8 adr,U8 *dat,U8 siz)  // read dat[siz] data from slave adr (untested yet!!!)
    {
    TWIStart(); if (TWIGetStatus!=0x08) return false;
    TWIWrite((adr<<1)|1); if (TWIGetStatus != 0x40) return false;
    for (;siz;dat++,siz--)
        {
        if (siz){ *dat=TWIReadACK();  if (TWIGetStatus != 0x50) return false; }
        else    { *dat=TWIReadNACK(); if (TWIGetStatus != 0x58) return false; }
        }
    for (;siz;dat++,siz--) *dat=0;
    TWIStop();
    return true;
    }
//-----------------------------------------------------------------------------
#endif
//-----------------------------------------------------------------------------

It requires cpu_delay_us routine (add it with ASF wizard) and also the used CPU clock defined prior to include of this for example mine:

#define clk_cpu 16000000
#include "I2C.h"

I tested/debugged probing and sending data (with LCD1602 + I2C expander) and reading data (Honeywell Pressure Sensor) to slaves. Looks like its working well.

Here typical usage:

// init I2C to 400KHz baudrate
I2C_init(400000);

// probe first I2C device (and obtain its address)
volatile U8 LCD_I2C_adr=0;
for (LCD_I2C_adr=1;LCD_I2C_adr<128;LCD_I2C_adr++)
 if (I2C_probe(LCD_I2C_adr))
  break;

// send some packet
U8 packet[]={1,2,3,4,5}; // some packet
I2C_write(LCD_I2C_adr,packet,sizeof(packet));

// read some packet
I2C_read(LCD_I2C_adr,packet,sizeof(packet));

The lib itself is simple blocking without using interrupts but its a good start point...

Here example of the sensor reading:

// 015PG2A3 H 6329-03N  
// 1KHz analog update
// 2KHz digital update
// preasure 14 bit
// temperature 11 bit
// power up 5ms
//        I2C (adr=40)        SPI                     Analog
//  GND 1     8 NC      GND  1     8 NC         NC   1     8 NC
//  Vcc 2 [ ] 7 NC      Vcc  2 [ ] 7 NC         Vcc  2 [ ] 7 NC
//  SDA 3 [ ] 6 NC      MISO 3 [ ] 6 NC         Vout 3 [ ] 6 NC
//  SCL 4     5 NC      SCLK 4     5 SS         GND  4     5 NC
volatile U8 sensor_I2C_adr=0;           // I2C sensor 7bit address
U8 sensor_read(int32_t *t,int32_t *p)   // update t,p and return status:
    {                                   // 0 normal
    int32_t a;                          // 1 command mode
    U8 status,buf[4];                   // 2 no new data yet (called too often)
    I2C_read(sensor_I2C_adr,buf,4);     // 3 diagnostics
    status=(buf[0]>>6)&3; 
    a = buf[0]    &63; a<<=8; a&=0xFF00;
    a|= buf[1]; 
    // preasure [0.01 Pa]
    a-=0x0666; a*=689476<<1; a/=0x3999-0x0666; /*a-=689476;*/ *p=a;
    a = buf[2];        a<<=3; a&=0xFFF8;
    a|=(buf[3]>>5)& 7;
    // temp [0.1 C]
    a*=2000; a/=2047; a-=500; *t=a;     
    return status;
    }
Spektre
  • 49,595
  • 11
  • 110
  • 380