Author Topic: Moteino M0 and I2C ADT7420 temperature sensor on EVAL-ADT7420-PMDZ  (Read 1907 times)

NickL

  • NewMember
  • *
  • Posts: 9
Update - Mistery solved: Answer below.

EVAL-ADT7420-PMDZ is a simple temperature measurement board around ADT7420 sensor. Pull-ups are 10K. The board is described at https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/eval-adt7420-pmdz.html and in better detail here: https://wiki.analog.com/resources/eval/user-guides/eval-adicup360/hardware/adt7420?rev=1495648070. It works well with Arduino Uno (EVAL-ADT7420-PMDZ powered from 3.3 volts coming from Uno), but I can't read even the chip ID register using Moteino M0 as host - available bytes count is zero on the read attempt. The power LED on EVAL-ADT7420-PMDZ is steady on, indicating that the power should be OK. Moteino M0 is powered via USB. ADT7420 draws 265 µA max if powered from 3.3 volts

At the same time, a similar board with ADT7410 sensor (Adafruit, https://www.adafruit.com/product/4089, also 10K pull-ups) works on both Arduino Uno and Moteino M0.

What should I try to do to make the ADT7420-based board work with Moteino M0?

ADT7420 control code I use (it is based on Analog Devices definitions and code here https://github.com/analogdevicesinc/EVAL-ADICUP360/tree/master/projects/ADuCM360_demo_adt7420_pmdz):

Code: [Select]
#include <Wire.h>

#define ADT7420_I2C_ADDRESS                    0x48
#define ADT7420_CHIP_ID                        0xCB
#define ADT7420_TEMP_IS_NEG_MASK               0x8000

/* ADT7420 register addresses */
#define ADT7420_TEMP_MSB_REG                   0x00
#define ADT7420_TEMP_LSB_REG                   0x01
#define ADT7420_STATUS_REG                     0x02
#define ADT7420_CONFIG_REG                     0x03
#define ADT7420_T_HIGH_SETPOINT_MSB_REG        0x04
#define ADT7420_T_HIGH_SETPOINT_LSB_REG        0x05
#define ADT7420_T_LOW_SETPOINT_MSB_REG         0x06
#define ADT7420_T_LOW_SETPOINT_LSB_REG         0x07
#define ADT7420_T_CRIT_SETPOINT_MSB_REG        0x08
#define ADT7420_T_CRIT_SETPOINT_LSB_REG        0x09
#define ADT7420_T_HYST_SETPOINT_REG            0x0A
#define ADT7420_ID_REG                         0x0B
#define ADT7420_SOFTWARE_RESET                 0x2F

/* ADT7420 status bits */
#define ADT7420_DATA_NOT_READY                 0x80

/* ADT7420 configuration bits */
#define ADT7420_CONTINUOUS_CONVERSION_MODE     0x00
#define ADT7420_ONE_SHOT_MODE                  0x20
#define ADT7420_ONE_SAMPLE_PER_SECOND_MODE     0x40
#define ADT7420_SHUTDOWN_MODE                  0x60
#define ADT7420_RESOLUTION_13_BITS             0x00
#define ADT7420_RESOLUTION_16_BITS             0x80

static const struct {
   uint8_t  RegAddr;
   char     RegName[32];
} ADT7420_RegAddr2RegName_table[] = {
  { ADT7420_TEMP_MSB_REG,            "ADT7420_TEMP_MSB_REG"            },
  { ADT7420_TEMP_LSB_REG,            "ADT7420_TEMP_LSB_REG"            },
  { ADT7420_STATUS_REG,              "ADT7420_STATUS_REG"              },
  { ADT7420_CONFIG_REG,              "ADT7420_CONFIG_REG"              },
  { ADT7420_T_HIGH_SETPOINT_MSB_REG, "ADT7420_T_HIGH_SETPOINT_MSB_REG" },
  { ADT7420_T_HIGH_SETPOINT_LSB_REG, "ADT7420_T_HIGH_SETPOINT_LSB_REG" },
  { ADT7420_T_LOW_SETPOINT_MSB_REG,  "ADT7420_T_LOW_SETPOINT_MSB_REG"  },
  { ADT7420_T_LOW_SETPOINT_LSB_REG,  "ADT7420_T_LOW_SETPOINT_LSB_REG"  },
  { ADT7420_T_CRIT_SETPOINT_MSB_REG, "ADT7420_T_CRIT_SETPOINT_MSB_REG" },
  { ADT7420_T_CRIT_SETPOINT_LSB_REG, "ADT7420_T_CRIT_SETPOINT_LSB_REG" },
  { ADT7420_T_HYST_SETPOINT_REG,     "ADT7420_T_HYST_SETPOINT_REG"     },
  { ADT7420_ID_REG,                  "ADT7420_ID_REG"                  },
  { ADT7420_SOFTWARE_RESET,          "ADT7420_SOFTWARE_RESET"          },
};
int nADT7420_Regs = sizeof ADT7420_RegAddr2RegName_table / sizeof *ADT7420_RegAddr2RegName_table;

typedef enum {
  ADT7420_NO_ERR,
  ADT7420_CHIP_ID_ERR,
  ADT7420_CONFIG_REG_ERR
} enErrNums;

typedef enum {
  I2C_BYTE = 1,
  I2C_WORD
} enNumBytes;

#define SERIAL_BAUD       115200

#if defined (MOTEINO_M0)
  #if defined(SERIAL_PORT_USBVIRTUAL)
    #define Serial SERIAL_PORT_USBVIRTUAL // Required for Serial on Zero based boards
  #endif
#endif

unsigned long delayTime = 3000;
unsigned long ADT7420_min_relaxationTime = 240 + 20;  // minimum of 240 ms for 1-shot mode
unsigned long ADT7420_timeout = 1000 - ADT7420_min_relaxationTime;
enErrNums err;
uint8_t avail_bytes, idReg, statusReg, configReg, otherReg;
uint16_t measurementWord;
float temperature;

uint8_t configADT7420 = ADT7420_RESOLUTION_16_BITS | ADT7420_ONE_SHOT_MODE;

void setup() {

  Serial.begin(SERIAL_BAUD);
  Serial.println("ADT7420 on Moteino M0 study");
  Wire.begin();
  Wire.setClock(400000);

// check ADT7420 chip ID
  avail_bytes = ADT7420_readByte ((uint8_t)ADT7420_I2C_ADDRESS,
                                  (uint8_t)ADT7420_ID_REG,
                                  idReg,
                                  "in setup");

// program configuration into ADT7420
  I2Cdev_sendByte ((uint8_t)ADT7420_I2C_ADDRESS,
                   (uint8_t)ADT7420_CONFIG_REG,
                   configADT7420);

// read ADT7420 configuration back
  avail_bytes = ADT7420_readByte ((uint8_t)ADT7420_I2C_ADDRESS,
                                  (uint8_t)ADT7420_CONFIG_REG,
                                  configReg,
                                  "in setup");
}

void loop() {

  unsigned long ADT7420_extra_relaxationTime = 0;
  unsigned long ADT7420_addTime = 10;

// check ADT7420 chip ID
  avail_bytes = ADT7420_readByte ((uint8_t)ADT7420_I2C_ADDRESS,
                                  (uint8_t)ADT7420_ID_REG,
                                  idReg,
                                  "loop head");

// program configuration into ADT7420
  I2Cdev_sendByte ((uint8_t)ADT7420_I2C_ADDRESS,
                   (uint8_t)ADT7420_CONFIG_REG,
                   configADT7420);

// read ADT7420 configuration back
  avail_bytes = ADT7420_readByte ((uint8_t)ADT7420_I2C_ADDRESS,
                                  (uint8_t)ADT7420_CONFIG_REG,
                                  configReg,
                                  "before measurement read");

  delay(ADT7420_min_relaxationTime);

  avail_bytes = I2Cdev_readByte ((uint8_t)ADT7420_I2C_ADDRESS,
                                 (uint8_t)ADT7420_STATUS_REG,
                                 statusReg);
  while ((statusReg & (uint8_t)ADT7420_DATA_NOT_READY) == (uint8_t)ADT7420_DATA_NOT_READY) {
    delay(ADT7420_addTime);
    ADT7420_extra_relaxationTime += ADT7420_addTime;
    if (ADT7420_extra_relaxationTime > ADT7420_timeout) {
      Serial.println("ADT7420 timeout error");
      return;
    }
    avail_bytes = I2Cdev_readByte ((uint8_t)ADT7420_I2C_ADDRESS,
                                   (uint8_t)ADT7420_STATUS_REG,
                                   statusReg);
  }

// read ADT7420 status
  avail_bytes = ADT7420_readByte ((uint8_t)ADT7420_I2C_ADDRESS,
                                  (uint8_t)ADT7420_STATUS_REG,
                                  statusReg,
                                  "after dwell");

  avail_bytes = I2Cdev_readWord ((uint8_t)ADT7420_I2C_ADDRESS,
                                 (uint8_t)ADT7420_TEMP_MSB_REG,
                                 measurementWord);

// read ADT7420 configuration after the measurement, for 1-shot mode IC should be sleeping
  avail_bytes = ADT7420_readByte ((uint8_t)ADT7420_I2C_ADDRESS,
                                  (uint8_t)ADT7420_CONFIG_REG,
                                  configReg,
                                  "after measurement read");

  temperature = ADT7420_Hex_To_Celsius (measurementWord);
  Serial.print ("got measurement word, available bytes: ");
  Serial.print (avail_bytes);
  Serial.print (", reg content: 0x");
  Serial.print (measurementWord, HEX);
  Serial.print (", temperature: ");
  Serial.print (temperature);
  Serial.println ("°C");
  if (ADT7420_extra_relaxationTime) {
    Serial.print ("Extra relaxation time:");
    Serial.println (ADT7420_extra_relaxationTime);
  }

  delay(delayTime-ADT7420_min_relaxationTime-ADT7420_extra_relaxationTime);
}

void I2Cdev_sendByte (uint8_t I2C_addr, uint8_t reg_addr, uint8_t val) {
  Wire.beginTransmission(I2C_addr);
  Wire.write(reg_addr);
  Wire.write(val);
  Wire.endTransmission();
}

int I2Cdev_readByte (uint8_t I2C_addr, uint8_t reg_addr, uint8_t &val) {
  int avail;
  Wire.beginTransmission(I2C_addr);
  Wire.write(reg_addr);
  Wire.endTransmission(false);
  Wire.requestFrom(I2C_addr, (uint8_t)I2C_BYTE);  // requesting 1 byte
  avail = Wire.available();
  val = Wire.read();
  return avail;
}

int I2Cdev_readWord (uint8_t I2C_addr, uint8_t reg_addr, uint16_t &val) {
  int avail;
  Wire.beginTransmission(I2C_addr);
  Wire.write(reg_addr);
  Wire.endTransmission(false);
  Wire.requestFrom(I2C_addr, (uint8_t)I2C_WORD);  // requesting 2 bytes
  avail = Wire.available();
  val = Wire.read();
  val <<= 8;
  val |= Wire.read();
  return avail;
}

float ADT7420_Hex_To_Celsius (uint16_t hex_val) {

// Check the status of the temperature sign bit (MSB)
  if ((hex_val & (uint16_t)ADT7420_TEMP_IS_NEG_MASK) ==
      (uint16_t)ADT7420_TEMP_IS_NEG_MASK) {
    /* If sign bit is 1 use the negative temperature equations */
    if ((configADT7420 & (uint8_t)ADT7420_RESOLUTION_16_BITS) ==
        (uint8_t)ADT7420_RESOLUTION_16_BITS) {
      return ((float)(hex_val - 65536))/128;       /* 16-bit temperature word data */

    } else {
      return ((float)((hex_val >> 3) - 8192))/16;  /* 13-bit temperature word data */
    }

  } else {
    /* If sign bit is 0, use the positive temperature equations */
    if ((configADT7420 & (uint8_t)ADT7420_RESOLUTION_16_BITS) ==
        (uint8_t)ADT7420_RESOLUTION_16_BITS) {
      return (float)hex_val/128;                   /* 16-bit temperature word data */

    } else {
      return ((float)(hex_val >> 3))/16;           /* 13-bit temperature word data */
    }
  }
}

int ADT7420_readByte (uint8_t I2C_addr, uint8_t reg_addr, uint8_t &val, char *msg) {
 
  int avail = I2Cdev_readByte (I2C_addr, reg_addr, val);
  if (strlen(msg)) {
    char reg_name[32];
    for (int i = 0; i < nADT7420_Regs; i++) {
      if ((uint8_t)ADT7420_RegAddr2RegName_table[i].RegAddr == reg_addr) {
        strcpy (reg_name, ADT7420_RegAddr2RegName_table[i].RegName);
        break;
      }
    }
    Serial.print("ADT7420 device at I2C addr. 0x");
    Serial.print(I2C_addr, HEX);
    Serial.print(", reading ");
    Serial.print(reg_name);
    Serial.print(" ");
    Serial.print(msg);
    Serial.print(", available bytes: ");
    Serial.print(avail);
    Serial.print(", reg content: 0x");
    Serial.println(val, HEX);
  }
  return avail;
}

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6866
  • Country: us
    • LowPowerLab
Re: Moteino M0 and ADT7420 temperature sensor on EVAL-ADT7420-PMDZ
« Reply #1 on: February 24, 2020, 10:55:40 AM »
I2C right?
If I understand correctly - are you powering the board separately (from UNO?) when you use it with M0?
The M0 can provide 3.3V to it.
Don't see a reason why it would not work. There are no I2c pullups on Moteino M0.

NickL

  • NewMember
  • *
  • Posts: 9
Re: Moteino M0 and ADT7420 temperature sensor on EVAL-ADT7420-PMDZ
« Reply #2 on: February 24, 2020, 11:15:14 AM »
I2C right?
If I understand correctly - are you powering the board separately (from UNO?) when you use it with M0?
The M0 can provide 3.3V to it.
Don't see a reason why it would not work. There are no I2c pullups on Moteino M0.

Thank you for the reply. Yes, the device is I2C, at 0x48 (and I don't see conflicting addresses on the bus). PMDZ board is powered from Uno when controlled from Uno, and from Moteino M0 when controlled from Moteino M0. I think I need to check signal levels and waveform on SCK.

NickL

  • NewMember
  • *
  • Posts: 9
Re: Moteino M0 and ADT7420 temperature sensor on EVAL-ADT7420-PMDZ
« Reply #3 on: February 29, 2020, 08:26:45 PM »
What happens on Moteino M0 looks like I2C lock-up, no SDA, no SCL, the bottom of the loop is not reached.

Code: [Select]
#include <Wire.h>

#define ADT7420_I2C_ADDRESS                0x48
#define ADT7420_ID_REG                         0x0B

typedef enum {
  I2C_BYTE = 1,
  I2C_WORD
} enNumBytes;

#define SERIAL_BAUD  115200
#define SCL_STD         100000
#define SCL_FAST        400000

#define SYNC_PIN (11)

void setup() {

  Serial.begin(SERIAL_BAUD);
  Serial.println("ADT7420 on Moteino M0, for oscilloscope");
  pinMode(SYNC_PIN, OUTPUT);
  digitalWrite(SYNC_PIN, LOW);
  Wire.begin();
  Wire.setClock(SCL_STD);
}

void loop() {

  digitalWrite(SYNC_PIN, HIGH);

  Wire.beginTransmission((uint8_t)ADT7420_I2C_ADDRESS);
  Wire.write((uint8_t)ADT7420_ID_REG);
  Wire.endTransmission(false);
  Wire.requestFrom((uint8_t)ADT7420_I2C_ADDRESS, (uint8_t)I2C_BYTE);  // requesting 1 byte
  int val = Wire.read();

  digitalWrite(SYNC_PIN, LOW);
 
  delay(10);
}

On Moteino M0 SYNC_PIN never goes low in the loop; SCL and SDA traces are both low - but everything is normal whith the same sensor board connected to Uno, and is controlled by the same code.

TomWS

  • Hero Member
  • *****
  • Posts: 1930
Re: Moteino M0 and ADT7420 temperature sensor on EVAL-ADT7420-PMDZ
« Reply #4 on: March 01, 2020, 09:20:59 AM »
On Moteino M0 SYNC_PIN never goes low in the loop; SCL and SDA traces are both low - but everything is normal whith the same sensor board connected to Uno, and is controlled by the same code.
In this case it's probably hanging because you don't test wire.available() and it's returning 0.  This doesn't explain why the I2C interface isn't working, but it should free up your loop for testing purposes.

Also, from elsewhere on the web (https://forum.arduino.cc/index.php?topic=92923.msg1911695#msg1911695):
"Always check the return value of   Wire.endTransmission();  // happens to be the factual sender of the datapacket

if it is not zero there is something wrong. Doing a requestFrom() after a faulty   Wire.endTransmission();  is like building on quicksand. "

« Last Edit: March 01, 2020, 09:24:32 AM by TomWS »

NickL

  • NewMember
  • *
  • Posts: 9
Re: Moteino M0 and ADT7420 temperature sensor on EVAL-ADT7420-PMDZ
« Reply #5 on: March 01, 2020, 09:27:21 AM »
You are right. I removed
Code: [Select]
int avail = Wire.available();
to make sure it's I2C and not something else.

NickL

  • NewMember
  • *
  • Posts: 9
Re: Moteino M0 and ADT7420 temperature sensor on EVAL-ADT7420-PMDZ
« Reply #6 on: March 02, 2020, 06:00:21 PM »
Happy to report that it was my mistake. The PMDZ board doesn't have pull-ups for SCL and SDA. It worked with Uno because of internal pull-ups. After adding pull-ups to the PMDZ board it immediately started to work with Moteino M0.

TomWS

  • Hero Member
  • *****
  • Posts: 1930
Re: Moteino M0 and ADT7420 temperature sensor on EVAL-ADT7420-PMDZ
« Reply #7 on: March 02, 2020, 07:46:42 PM »
You are right. I removed
Code: [Select]
int avail = Wire.available();
to make sure it's I2C and not something else.
That's a sort of odd response to my suggestion.  My point is that you shouldn't do a Wire.read UNLESS there is at least one byte available.  I would change the code to:
Code: [Select]
int I2Cdev_readByte (uint8_t I2C_addr, uint8_t reg_addr, uint8_t &val) {
  int avail;
  Wire.beginTransmission(I2C_addr);
  Wire.write(reg_addr);
  Wire.endTransmission(false);
  Wire.requestFrom(I2C_addr, (uint8_t)I2C_BYTE);  // requesting 1 byte
  avail = Wire.available();
  if (avail) // test that there is at least one byte BEFORE you call read.
     val = Wire.read();
  return avail;
}

In any case, I'm glad you found the problem, but I recommend following the protocol to make it easier and safer to detect problems in the future.

NickL

  • NewMember
  • *
  • Posts: 9
Re: Moteino M0 and ADT7420 temperature sensor on EVAL-ADT7420-PMDZ
« Reply #8 on: March 02, 2020, 10:52:03 PM »
That's a sort of odd response to my suggestion.  My point is that you shouldn't do a Wire.read UNLESS there is at least one byte available.

I appreciate your input.

In my case it never returned from Wire.endTransmission(false); so checking the return value or number of available bytes after the call was impossible.

In one of the previous posts I mentioned that "SCL and SDA traces are both low" - that often means something is wrong with pull-ups; turned out I didn't really paid attention to the schematics of PMDZ. Pull-ups it has are for other pins, but no pull-ups were there for SCL / SDA.
« Last Edit: March 02, 2020, 11:13:53 PM by NickL »

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6866
  • Country: us
    • LowPowerLab
Re: Moteino M0 and I2C ADT7420 temperature sensor on EVAL-ADT7420-PMDZ
« Reply #9 on: March 03, 2020, 09:36:10 AM »
NickL,
When you mention there are pullups on the UNO, are these the atmega's internal pullups? These are usually in the range of 50K, way too high for i2c IMO.
I would use no more than 10K or 4.7K, rarely less than that. Usually can't go wrong with 4.7K.

NickL

  • NewMember
  • *
  • Posts: 9
Re: Moteino M0 and I2C ADT7420 temperature sensor on EVAL-ADT7420-PMDZ
« Reply #10 on: March 03, 2020, 09:48:24 AM »
NickL,
When you mention there are pullups on the UNO, are these the atmega's internal pullups? These are usually in the range of 50K, way too high for i2c IMO.
I would use no more than 10K or 4.7K, rarely less than that. Usually can't go wrong with 4.7K.

Yes, those are atmega 50K internal pull-ups. Somehow it was enough current for the sensor to work, though the I2C specs suggest 3mA. The resistors I added to the board are 10K, as AD datasheet recommends. To quote the datasheet,

Code: [Select]
SCL
I2C Serial Clock Input. The serial clock is used to clock in and clock out data to and from any register of the ADT7420. Open-drain configuration. A pull-up resistor is required, typically 10 kΩ.

SDA
I2C Serial Data Input/Output. Serial data to and from the part is provided on this pin. Open-drain configuration. A pull-up resistor is required, typically 10 kΩ.

Thank you for editing my original post to point to the root of the problem.

« Last Edit: March 03, 2020, 09:54:36 AM by NickL »