i2c port expander PCF8574 as input

Hi, I have a PCF8574p and I am using it with an lcd. This works fine. The library that I use let a pin of the PCF, the 13, free. I wish to use this pin of the port expander as an input pin, to read the pressure of a button, but I can't understand how, through the wire library (i think that i need this). Is it possible to do this? Does anyone knows where I can look for understanding how to do this? Thanks, Fede

Basically reading the pcf8574 is not so hard. I would need to dig up the code when i get home later, but basically it's described in the datasheet. You might have problems IF you had to read a analog button - in that case its far mor difficult..

I will be absolutly fine with a normal digital button :) I need this button to change the state of the visualization on the lcd, but looks like every example on the forum is about using the expander as output, and not as input (on a single pin)

Federico,

Pin 13 is the interrupt output pin of the PCF. (See page 11 of the PCF datasheet in the library download for details) You can not reconfigure it for your use.

I suggest you don't use pin 12 for controlling the LCD backlight but instead use it for you button. You may have to tweak the LCD library so it doesn't use pin 12 for output.

Hi Mario, I have already planned not to use the backlight with your library because I will always need the backlight on. I could use the pin 12, but still I have to understand how. I am working on it... Thanks, Fede

hi all,

In my project i use a pcf8574 as input for some buttons and use this code. It uses external interrupt 5 on my arduino. (edited: arduino Mega) The detach in the isr (expanderInterrupt()) gave me a somewhat debounced button.

hope it helped,

Jeroen

#include <Wire.h>
#define expander B00100000 //expander address

volatile byte count = 0;

void setup() {
Wire.begin();
Serial.begin(9600);
expanderWrite(B11111111);
attachInterrupt(5, expanderInterrupt, LOW);
}

void loop() {
if(count = 1) {
Serial.println(expanderRead(), BIN);
count = 0;
attachInterrupt(5, expanderInterrupt, LOW);
}
}

void expanderInterrupt() {
detachInterrupt(5);
count = 1;
}

void expanderWrite(byte _data ) {
Wire.beginTransmission(expander);
Wire.send(_data);
Wire.endTransmission();
}

byte expanderRead() {
byte _data;
Wire.requestFrom(expander, 1);
if(Wire.available()) {
_data = Wire.receive();
}
return _data;
}

With the help of your code as test I have read a bit of documentation from the arduino site and now I am doing some test. If I understand it right when I press the button, connected to ground, I receve a different binary number, so I can hook this to actions. Am I right?

Still have to test it, but can you tell me if the pin used from the arduino to the external interrupt is still usable?

Federico

when I press the button, connected to ground, I receve a different binary number

Yes but make sure that the input pin is also connected to +5V through a resistor of value 1K to 10K. This is called a pull up resistor and ensures that the number read back contains a logic one when the button is not pressed. When the button is pressed that bit of the number will read as a logic zero.

In addition to grumpy_mike also make sure you have a pull up resistor on the interrupt line coming from the pcf8574. I'm using 10K ohms connected to +5V.

but can you tell me if the pin used from the arduino to the external interrupt is still usable?

You could use it for other external interrupts but then you don't know what caused the interrupt. To keep it simple; No, the pin is not usable for anything else.

You could use it for other external interrupts but then you don't know what caused the interrupt.

Form the data sheet:-

An interrupt is generated by any rising or falling edge of the port inputs in the input mode. After time tiv the signal INT is valid. Resetting and reactivating the interrupt circuit is achieved when data on the port is changed to the original setting or data is read from or written to the port which has generated the interrupt. Resetting occurs as follows: · In the READ mode at the acknowledge bit after the rising edge of the SCL signal · In the WRITE mode at the acknowledge bit after the HIGH-to-LOW transition of the SCL signal · Interrupts which occur during the acknowledge clock pulse may be lost (or very short) due to the resetting of the interrupt during this pulse. Each change of the I/Os after resetting will be detected and, after the next rising clock edge, will be transmitted as INT. Reading from or writing to another device does not affect the interrupt circuit.

So in short if an input pin changes state this line goes low. You can then put this into one of the normal arduino pins and monitor that to see if an input has changed. This is a lot quicker than reading the I2C device itself.

@ Yot: I assume if(count = 1) { in the code above is a typo?

yes it is (in shame) it should be ==

at Grumpy_Mike;

I know the expander can 'draw attention' by generating an interrupt on an arduino pin. I meant to say that in that setup you could use another device to generate an interrupt on the same arduino pin but then it wont be clear wich device caused the interrupt.

Jeroen

Thank you for discussing about this topic, I am learning many things. By the way, still I can't undestanding I am doing it right. Following the code of Yot, in my serial debug I have

11001001
11001001
11001001
11001001
11001001

no stop (well, I have some wire attached to the expander) When I press the button for less than one sec i have

1001001
1001001
1001001
1001001
1001001
1001001

So I can see that something change, and when I release my button I have again, over and over :) the code inserted at the top (11001001). Is this supposed to be correct? It will be a little tricky to hook...

Federico

you did change if(count = 1) { to if(count == 1) { ?

Terrible typo that gets me time and time again.

Also it took me a while to realise i didn't have a pullup resistor on the int. wire to the arduino external int. pin. That gave a lot of weard interrupts aswell. You have one in place?

Jeroen

I have correct the "==" typo, but my pullup resistor (10k) is from the +5v line to the INT on che PCF, I thought it was to set like this reading your post... I will change if connecting the pullup to the pin "2" on my arduino diecimila and test again tonight. I have changed the external interrupt from 5 to 0 if I can remeber exactly. I will check as soon as I came back home! Fede

To only get one output each time you change the button state then change the lines:-

attachInterrupt(5, expanderInterrupt, LOW); to attachInterrupt(5, expanderInterrupt, CHANGE);

The pullup resistor was ok i think. In doubt check datasheet. :)

Fig.13 Application of multiple PCF8574s with interrupt.

Jeroen

Yei! I have double checked every connection again and I have ended up with a pair of codes in output what I press my button. I was able to idendify this to change the data on my lcd screen. I have still to semplify my code but more or less is working now. I will post it as soon as possible. The code of Yot was my key in this work, thanks again! Thanks to everyone :)

I am programming this system to sobstitute the lcd clock of my car, to have an enhanced clock and the possibility to switch visual to see the temperature of many electronics I have installed (via i2c) into.

Can you please post the working code

This is my code, it’s working but perhaps not perfect :slight_smile: Keep in mind that is my code there is also the part of code that is needed by my rtc.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <WProgram.h>
#include <DS1307.h> 

int rtc[7];
byte data[56];
char orario[16]; //non cambiare 16 perchè per qualche motivo non va piu' niente dopo
char dataOdierna[16];
int anno;
int changeScreen = 0;
int k = 0;
int primaAccensione = 0;

#define expander 0x20  // Address with three address pins grounded (B0100000).
                     // Note that the R/W bit is not part of this address.
volatile byte count = 0;


uint8_t bell[8]  = {0x4,0xe,0xe,0xe,0x1f,0x0,0x4};
uint8_t note[8]  = {0x2,0x3,0x2,0xe,0x1e,0xc,0x0};
uint8_t clock[8] = {0x0,0xe,0x15,0x17,0x11,0xe,0x0};
uint8_t heart[8] = {0x0,0xa,0x1f,0x1f,0xe,0x4,0x0};
uint8_t duck[8]  = {0x0,0xc,0x1d,0xf,0xf,0x6,0x0};
uint8_t check[8] = {0x0,0x1,0x3,0x16,0x1c,0x8,0x0};
uint8_t cross[8] = {0x0,0x1b,0xe,0x4,0xe,0x1b,0x0};
uint8_t retarrow[8] = {      0x1,0x1,0x5,0x9,0x1f,0x8,0x4};
  
LiquidCrystal_I2C lcd(expander,8,2);  // set the LCD address to 0x20 for a 16 chars and 2 line display


void setup()
{
  Wire.begin();
  attachInterrupt(0, expanderInterrupt, CHANGE);
 
  Serial.begin(9600); 
  lcd.init();        // initialize the lcd 
  lcd.noBacklight(); // accende e spegne l'uscita sul pin 12.
                     // lcd.backlight();
                     // sink current +5V------+LED-------330Rresistor------P8574pin
  
  lcd.createChar(0, bell);
  lcd.createChar(1, note);
  lcd.createChar(2, clock);
  lcd.createChar(3, heart);
  lcd.createChar(4, duck);
  lcd.createChar(5, check);
  lcd.createChar(6, cross);
  lcd.createChar(7, retarrow);
  lcd.home();
  
  lcd.print(" System");
  lcd.setCursor(0, 1);
  lcd.print(" Ready.");
  delay(1000);
  for(int i=0; i<9; i++)  {
    lcd.scrollDisplayLeft();
    delay(100);
  }
  lcd.clear();
  lcd.home();
  
  //RTC SETUP 
  /*RTC.stop();
  RTC.set(DS1307_SEC,1);
  RTC.set(DS1307_MIN,26);
  RTC.set(DS1307_HR,1);
  RTC.set(DS1307_DOW,4);
  RTC.set(DS1307_DATE,30);
  RTC.set(DS1307_MTH,10);
  RTC.set(DS1307_YR,9);
  RTC.start();*/
  
  for(int i=0; i<56; i++)  {
    RTC.set_sram_byte(65,i);
  }

}

void loop()
{
  RTC.get(rtc,true);

  /*for(int i=0; i<7; i++)
   {
   Serial.println(rtc[i]);
   Serial.print(" ");
   }
   Serial.println();*/

  //rtc[0] --> secondi
  //Tramite questo IF faccio lampeggiare il : per i secondi
  if (rtc[0]%2) {
    sprintf(orario,"%02d:%02d",rtc[2],rtc[1]); //%02d converte 9 in 09 e 32 in 32
  }
  else {
    sprintf(orario,"%02d %02d",rtc[2],rtc[1]);
  }
  Serial.println(orario);
  
  anno=rtc[6]-2000;
  sprintf(dataOdierna,"%02d/%02d/%02d",rtc[4],rtc[5],anno);
  
  
  if (changeScreen==0) {
    lcd.setCursor(1,0);
    lcd.print(orario);
    lcd.setCursor(7,0);
    lcd.print(2, BYTE);
    lcd.setCursor(0,1);
    lcd.print(dataOdierna);
  }
  else if (changeScreen==1) {
    lcd.setCursor(0,0);
    lcd.print(1, BYTE);
    lcd.setCursor(1,0);
    lcd.print("XX");
    lcd.setCursor(4,0);
    lcd.print(4, BYTE);
    lcd.setCursor(5,0);
    lcd.print("XX");
    lcd.setCursor(0,1);
    lcd.print("hcca XX");
  }
  
  if (count == 1) {
    //Serial.println(expanderRead(),BIN);
    count = 0;
    attachInterrupt(0, expanderInterrupt, LOW);
    
    k++;
    if (primaAccensione==0) {changeScreen=0; primaAccensione++;}
    else if (k==1) { changeScreen=!changeScreen; lcd.clear(); }
    else if (k>1) { k=0; }
    //Serial.println(k);
  }
    
}

// display all keycodes
void displayKeyCodes(void) {
  uint8_t i = 0;
  while (1) {
    lcd.clear();
    lcd.print("Codes 0x"); lcd.print(i, HEX);
    lcd.print("-0x"); lcd.print(i+8, HEX);
    lcd.setCursor(0, 1);
    for (int j=0; j<8; j++) {
      lcd.print(i+j, BYTE);
    }
    i+=8;   
    delay(4000);
  }
}

// Funzioni per interrupt da arduino
void expanderInterrupt() {    
  detachInterrupt(0);
  count = 1;
}

void expanderWrite(byte _data ) {
  Wire.beginTransmission(expander);
  Wire.send(_data);
  Wire.endTransmission();
}

byte expanderRead() {
  byte _data;
  Wire.requestFrom(expander, 1);
  if(Wire.available()) {
    _data = Wire.receive();
  }
  return _data;
}