I2C problem reading registers

Hi,

I want to read the ADC values of a PCF8591 module like here:
https://sunupradana.info/tkr/category/embedded-systems/arduino/

With the code

#include "Wire.h"
#define PCF8591 (0x90 >> 1) //0x48
byte adcvalue0, adcvalue1, adcvalue2, adcvalue3;
 
void setup()
{
  Wire.begin();
  Serial.begin(115200);
  }
 
void loop()
{
  Wire.beginTransmission(PCF8591);
  Wire.write(0x04); // SDA // PC4
  Wire.endTransmission();
  Wire.requestFrom(PCF8591, 5); //SCL //PC5
 
  adcvalue0=Wire.read();
  adcvalue0=Wire.read();
  adcvalue1=Wire.read();
  adcvalue2=Wire.read();
  adcvalue3=Wire.read();
 
  Serial.print(adcvalue0);
  Serial.print(" ,");
  Serial.print(adcvalue1); 
  Serial.print(" ,");
  Serial.print(adcvalue2); 
  Serial.print(" ,");
  Serial.print(adcvalue3); 
  Serial.println();
 
  delay(1000);
}

The first ADC input is grounded. When I run the code I get results like this:

0 ,215 ,219 ,148
0 ,215 ,219 ,148
0 ,216 ,219 ,148
215 ,219 ,147 ,0
215 ,215 ,219 ,149
0 ,215 ,219 ,148
0 ,215 ,219 ,148
215 ,219 ,148 ,0
215 ,215 ,219 ,148
0 ,215 ,219 ,148
0 ,215 ,219 ,148
0 ,215 ,219 ,148
0 ,215 ,219 ,148
0 ,215 ,219 ,148
0 ,215 ,219 ,148
0 ,215 ,219 ,148
0 ,215 ,219 ,148
215 ,219 ,148 ,0
215 ,215 ,219 ,148
215 ,219 ,148 ,0
215 ,219 ,148 ,0
215 ,215 ,219 ,148
0 ,215 ,219 ,148
0 ,215 ,219 ,148
0 ,215 ,220 ,148
0 ,215 ,219 ,148
0 ,215 ,219 ,148
0 ,215 ,219 ,148
0 ,215 ,219 ,147
0 ,216 ,219 ,149
0 ,216 ,219 ,148
0 ,217 ,219 ,148
0 ,217 ,219 ,148
217 ,219 ,148 ,0
217 ,217 ,220 ,148
0 ,217 ,219 ,148
0 ,216 ,219 ,148
0 ,216 ,219 ,148
0 ,216 ,219 ,148
216 ,219 ,148 ,0
216 ,217 ,219 ,148
0 ,217 ,219 ,148
0 ,217 ,219 ,148
0 ,217 ,219 ,148

It seems to me like the first register is sometimes skipped or missed.
I tried different variants like

byte readADCIn(byte pin){
  Wire.beginTransmission(PCF8591); // wake up PCF8591
  Wire.write(pin); // control byte - read ADC2
  Wire.endTransmission(); // end tranmission
  Wire.requestFrom(PCF8591, 2);
  Wire.read();
  byte value=Wire.read();
  Serial.print("Pin ");
  Serial.print(pin);
  Serial.print(" read: ");
  Serial.println(value);      
}

void update(){

  for(byte pin=0; pin<4; pin++){
    readADCIn(pin);
    delay(1000);
  }
...

or

...
  byte count = 0;
  while (Wire.available()) {
    byte val = Wire.read();
    Serial.print(count++);
    Serial.print(" read: ");
    Serial.println(val);    
  }
...

but the results are the same, even if I try to read a specific register, sometimes I read different values.
I'm using an Adafruit HUZZAH board.

Any idea what's going wrong here?

Best regards Charly

ch4rly:
Hi,

I want to read the ADC values of a PCF8591 module like here:
Arduino – Tinker

but the results are the same, even if I try to read a specific register, sometimes I read different values.
I'm using an Adafruit HUZZAH board.

Any idea what's going wrong here?

Best regards Charly

You need to read the datasheet. page 5. NXP Semiconductors PCF8591 rev 7 23 June 2013

8.2 Control byte
The second byte sent to a PCF8591 device is stored in its control register and is required
to control the device function. The upper nibble of the control register is used for enabling
the analog output, and for programming the analog inputs as single-ended or differential
inputs. The lower nibble selects one of the analog input channels defined by the upper
nibble (see Figure 4). If the auto-increment flag is set, the channel number is incremented
automatically after each A/D conversion.
If the auto-increment mode is desired in applications where the internal oscillator is used,
the analog output enable flag must be set in the control byte (bit 6). This allows the
internal oscillator to run continuously, by this means preventing conversion errors
resulting from oscillator start-up delay. The analog output enable flag can be reset at other
times to reduce quiescent power consumption.
The selection of a non-existing input channel results in the highest available channel
number being allocated. Therefore, if the auto-increment flag is set, the next selected
channel is always channel 0. The most significant bits of both nibbles are reserved for
possible future functions and must be set to logic 0. After a Power-On Reset (POR)
condition, all bits of the control register are reset to logic 0. The D/A converter and the
oscillator are disabled for power saving. The analog output is switched to a
high-impedance state

You are attempting to use the AutoIncrement, but never enabling the Analog Output. The device starts and stops its osciliator, which causes conversion errors.

Change your code:

{
  Wire.beginTransmission(PCF8591);
  Wire.write(0x44); // single Ended, Auto Increment, with oscillator enabled
  uint8_t err=Wire.endTransmission();
  if(err==0){ // Device accepted command
    uint8_t count =Wire.requestFrom(PCF8591, 4); // read 4 bytes, one for each of the 8bit A/D values
    if(count==4){ // successful read

      adcvalue0=Wire.read();
      adcvalue1=Wire.read();
      adcvalue2=Wire.read();
      adcvalue3=Wire.read();
 
      Serial.print(adcvalue0);
      Serial.print(" ,");
      Serial.print(adcvalue1);
      Serial.print(" ,");
      Serial.print(adcvalue2);
      Serial.print(" ,");
      Serial.print(adcvalue3);
      Serial.println();
 
      delay(1000);
      }
    else {// Read failed
       Serial.println("readFrom Failed");
      }
    }
  else {// End Transmission Failed
    Serial.print("The A/D converter did not respond, Err=");
    Serial.println(err,DEC);
    }

Chuck.

You are attempting to use the AutoIncrement, but never enabling the Analog Output. The device starts and stops its osciliator, which causes conversion errors.

This makes sense, but why did this variant which did not use auto increment have the same output?

byte readADCIn(byte pin){
  Wire.beginTransmission(PCF8591); // wake up PCF8591
  Wire.write(pin); // control byte - read ADC2
  Wire.endTransmission(); // end tranmission
  Wire.requestFrom(PCF8591, 2);
  Wire.read();
  byte value=Wire.read();
  Serial.print("Pin ");
  Serial.print(pin);
  Serial.print(" read: ");
  Serial.println(value);      
}

void update(){

  for(byte pin=0; pin<4; pin++){
    readADCIn(pin);
    delay(1000);
  }
...

The basic tutorial for the PCF8591 is at tronixstuiff, and it does not enable the Analog Output for the auto increment sketch. I think this is the basis of the sketches you see without the Wire.write(0x44).

chucktodd:
Change your code

Thanks a lot, that did it!

cattledog:
This makes sense, but why did this variant which did not use auto increment have the same output?

Seems like this code now also works fine :confused:
I could only imagine the PCF8591 was still in the wrong mode from an earlier example, my bad. Probably I should have cut the power while testing the variants.

Thanks again!
Best Charly

cattledog:
This makes sense, but why did this variant which did not use auto increment have the same output?

byte readADCIn(byte pin){

Wire.beginTransmission(PCF8591); // wake up PCF8591
  Wire.write(pin); // control byte - read ADC2
  Wire.endTransmission(); // end tranmission
  Wire.requestFrom(PCF8591, 2);
  Wire.read();
  byte value=Wire.read();
  Serial.print("Pin ");
  Serial.print(pin);
  Serial.print(" read: ");
  Serial.println(value);     
}

void update(){

for(byte pin=0; pin<4; pin++){
    readADCIn(pin);
    delay(1000);
  }
...




The basic turorial for the PCF8571 is at tronixstuiff, and it does not enable the Analog Output for the auto increment sketch. I think this is the basis of the sketches you see without the Wire.write(0x44).

http://tronixstuff.com/2013/06/17/tutorial-arduino-and-pcf8591-adc-dac-ic/

I think the device's behavior is dependent on the sequence of reads. Each time read it initiates a conversion. The new conversion is available next time you read it. If the oscillator is not enabled, it starts it up when is sees the read. the A/D convert requires a stable clock to produce consistent values. It is a successive approximation converter that compares a 'held' sample (which decays over time) to the DAC output. If this clock is not running at full speed the sample decays faster than expected. The device is calibrated for this decay rate. Look at the block diagram on page 3 of the data sheet.

In the example you posted do you see that wasted read? The two byte requestFrom() followed by wasting the first read? The device is configured with NON-INCREMENT. The first byte of the requestFrom() starts the oscilator, the second byte is the sample initiated by the first byte.

If they had just turned on the oscillator the two byte read would not be necessary.

Chuck.