PCF8574A INT pin to 555

Hello,

I have a PCF8574A connected to a 4x4 keypad. I wanted to add a NE555 (monostable mode) to the INT pin on the PCF8574A so that each time a key is pressed a LED light up for a short period.

The problem is that it does only work now and then. I can press a key and it work. When pressing the next key nothing happens (but the UNO, which is scanning the I2C bus, detect it). It seem to be randomly when the LED lights up.

Any suggestions?

This is the NE555 circuit:

Any suggestions?

I don't understand the point of the 555 timer, I suggest you connect the LED to an output on the Uno and have it flash the LED.

That is not an option neither what I asked for :wink:

But since you ask - no,I cannot. The cable from the keyboard PCB and the controller do not offer any feedback line. And there is no available port on the controller for the LED.

I think you need to figure out if the INT from the pcf is not triggered, or the 555 is not triggered, when the led does not flash.

A 'scope or logic analyser would be good for this.

Or attach a led directly to the INT to see if it is triggered. Slow down the sketch so that the led will be on for long enought to see it.

I have checked the 555 by connecting the input to a push button. It seems to work OK.

Also, when doing a test with the PCF8574A to a controller attaching the INT to a pin and using an interrupt function, it seems to work OK.

I have not thought about attaching a LED directly to INT. That can be with a sketch that does nothing (at least not scan the state of the PCF). Good idea :slight_smile:

I modified the sketch so that it did not scan the PFC. Nothing happens - the LED did not turn on at all. I suspect that has to do with how the Interrupt logic work.

I think the INT pin is probably open-collector so can only sink current. When an input pin changes, INT will pull low and stay low until the Arduino reads data from the chip. Does that help? If not, maybe post a schematic of the whole circuit and your test sketch.

Yes, I know. It is an open drain output. The datasheet say it should have 10k pull-up, which I have. The 555 trigger when the input goes to LOW.

I don't think a complete schematic would help, since the rest of the components should not interfere with the 555. The sketch can be summarized to:

void loop()
{
  char key = keypad.getKey();
  if (key) {
    // process the key
  }
}

thehardwareman:
I don't think a complete schematic would help... The sketch can be summarized to...

If you keep us in the dark, we can't help. You must understand that?

I know, but the current sketch is about 3000 lines and a lot of components. Guess I have to build a new board and make a smaller sketch in order to post.

And posting everything does not always make sense. It's like having a flat tire on you car and you ask "Why". You don't need the complete drawing of the car to tell - you look at the tire and say "hey, there is a nail in it"...

Maybe it's more like your car won't start, so you take out the battery and take it to the garage and tell the mechanic "my car won't start, here's the battery, which I already tested and it seems to work".

My only ideas at the moment revolve around how the keypad is wired and how the keypad library uses the pcf chip. Maybe between scans, it doesn't leave the pcf in a state where any keypress would cause an interrupt, except on certain rows or columns.

The more I think about it, the more I think that could be it. If the keypad library only scanned the keypad when an interrupt was generated, and after left the pcf pins in a state where any keypress would cause an interrupt, then that might work. But if the keypad is constantly scanned, then interrupts could easily be missed because the scan process isn't on the right row or column at the moment the key is pressed.

I show you the complete drawing of the "car" since my tire is still flat :wink:

The keypad PCB:

The LCD PCB:

The test sketch:

#ifndef ARDUINO_AVR_UNO
#error Must use a UNO card
#endif

#include <Board_4x4_keypad.h>
#include <Board_2x1602LCD.h>

const uint8_t keypadAdr = 0x38;
Board_4x4_keypad  keypad(keypadAdr);

const uint8_t lcdAdr1 = 0x27;
const uint8_t lcdAdr2 = 0x26;
Board_2x1602LCD lcds(lcdAdr1, lcdAdr2);


void setup()
{
  Serial.begin(9600);
  Serial.println(F("\n\n\nInitialzing..."));

  // Set up the LCD displays.
  Serial.println(F("Initializing LCDs"));
  lcds.setup();
  lcds[0].print(F("Initializing"));
  
  // Set up the keyboard.
  lcds[0].setCursor(0, 1);
  lcds[0].print(F("Keypad..."));
  keypad.setup();
}

void loop() 
{
  char key = keypad.getKey();
  if (key) {
    lcds[0].setCursor(0, 1);
    lcds[0].print(key);
  }
}

Libraries:

#ifndef BOARD_2X1602LCD_H_INCLUDED
#define BOARD_2X1602LCD_H_INCLUDED

#include <Wire.h>
#include <MyLcd_I2C.h>

const uint8_t   lcdLineLength  = 16;
const uint8_t   lcdColumnCount = 2;

class Board_2x1602LCD
{
  private:
    uint8_t   m_adr1;
    uint8_t   m_adr2;
    
    MyLcd_I2C *m_lcd[2];
    
  public:
    Board_2x1602LCD(const uint8_t adr1, const uint8_t adr2);
    virtual ~Board_2x1602LCD();
    
    void setup();
    void backlight(const bool on);
    
    MyLcd_I2C &operator [] (int i);
};

#endif
#include "Board_2x1602LCD.h"

Board_2x1602LCD::Board_2x1602LCD(const uint8_t adr1, const uint8_t adr2)
{
  m_adr1 = adr1;
  m_adr2 = adr2;
  
  m_lcd[0] = new MyLcd_I2C(m_adr1, 16, 2);
  m_lcd[1] = new MyLcd_I2C(m_adr2, 16, 2);
}

Board_2x1602LCD::~Board_2x1602LCD()
{
  delete m_lcd[0];
  delete m_lcd[1];
}

void Board_2x1602LCD::setup()
{
  for (int i = 0; 2 > i; i++) {
    m_lcd[i]->begin();
    m_lcd[i]->backlight();
    m_lcd[i]->setCursor(0, 0);
  }
}

void Board_2x1602LCD::backlight(const bool on)
{
  if (on) {
    m_lcd[0]->backlight();
    m_lcd[1]->backlight();
  } else {
    m_lcd[0]->noBacklight();
    m_lcd[1]->noBacklight();
  }
}

MyLcd_I2C &Board_2x1602LCD::operator [] (int i)
{
  if (0 == i || 1 == i) {
    return *(m_lcd[i]);
  }
  
  return *(m_lcd[0]);
}
#ifndef MY_LCD_I2C_INCLUDED
#define MY_LCD_I2C_INCLUDED

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

class MyLcd_I2C : public LiquidCrystal_I2C
{
  private:
    uint8_t     m_cols;
    uint8_t     m_rows;
    
  public:
    MyLcd_I2C(uint8_t lcd_addr, uint8_t lcd_cols, uint8_t lcd_rows);
    
    void clearLine(const int lineNumber, const bool firstPos = true);
    size_t writeLine(const int lineNumber, const char *string);
    size_t writeLine(const int lineNumber, const __FlashStringHelper* string);
    void at(const int lineNumber, const int cursorPosition, const char *string);
    void at(const int lineNumber, const int cursorPosition, const __FlashStringHelper *string);
    
    uint8_t getCols() const         {return m_cols;}
    uint8_t getRows() const         {return m_rows;}
};

#endif
#include <Arduino.h>
#include "MyLcd_I2C.h"

MyLcd_I2C::MyLcd_I2C(uint8_t lcd_addr, uint8_t lcd_cols, uint8_t lcd_rows)
  : LiquidCrystal_I2C(lcd_addr, lcd_cols, lcd_rows)
{
  m_cols = lcd_cols;
  m_rows = lcd_rows;
}

void MyLcd_I2C::clearLine(const int lineNumber, const bool firstPos)
{
  if (lineNumber < m_rows) {
    setCursor(0, lineNumber);
    for (int i = 0; m_cols > i; i++) {
      write(' ');
    }
    if (firstPos) {
      setCursor(0, lineNumber);
    }
  }
}

size_t MyLcd_I2C::writeLine(const int lineNumber, const char *string)
{
  if (lineNumber < m_rows) {
    clearLine(lineNumber);
    return print(string);
  }
  
  return 0;
}

size_t MyLcd_I2C::writeLine(const int lineNumber, const __FlashStringHelper* string)
{
  size_t n = 0;

  if (lineNumber < m_rows) {
    clearLine(lineNumber);
    
    PGM_P p = reinterpret_cast<PGM_P>(string);
    while (true) {
      unsigned char c = pgm_read_byte(p++);
      if (0 == c) {
        break;
      }
      if (!write(c)) {
        break;
      }
      ++n;
    }
  }
  
  return n;
}

void MyLcd_I2C::at(const int lineNumber, const int cursorPosition, const char *string)
{
  if (lineNumber < m_rows) {
    setCursor(cursorPosition, lineNumber);
    print(string);
  }
}

void MyLcd_I2C::at(const int lineNumber, const int cursorPosition, const __FlashStringHelper *string)
{
  if (lineNumber < m_rows) {
    setCursor(cursorPosition, lineNumber);
    print(string);
  }
}
#ifndef BOARD_4x4_KEYAPD_PCF8574A_H_INCLUDED
#define BOARD_4x4_KEYAPD_PCF8574A_H_INCLUDED

#include <Keypad_I2C.h>

class Board_4x4_keypad : public Keypad_I2C {
  private:
    uint8_t   m_adr;

    static char keys[4][4];
    static byte colPins[4];
    static byte rowPins[4];
    
  public:
    Board_4x4_keypad(const uint8_t adr);
    ~Board_4x4_keypad();
    
    void setup();
};

#endif
#include "Board_4x4_keypad.h"

char Board_4x4_keypad::keys[4][4] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

byte Board_4x4_keypad::colPins[4] = {0, 1, 2, 3};
byte Board_4x4_keypad::rowPins[4] = {4, 5, 6, 7};

Board_4x4_keypad::Board_4x4_keypad(const uint8_t adr) : Keypad_I2C(makeKeymap(keys), rowPins, colPins, 4, 4, adr, W_PCF8574)
{
  m_adr = adr;
}

Board_4x4_keypad::~Board_4x4_keypad()
{
}

void Board_4x4_keypad::setup()
{
  Wire.begin();
  begin(makeKeymap(keys));
}

So, why is my tire still flat? :wink:

thehardwareman:
That is not an option neither what I asked for :wink:

But since you ask - no,I cannot. The cable from the keyboard PCB and the controller do not offer any feedback line. And there is no available port on the controller for the LED.

Thank you. Honestly, when you've been answering questions here for a while you come to realise that sometimes the answer lies in a different approach. If you don't know why someone is doing something the way they are, as I didn't when I asked that, then you don't know what other options might be useful. I have seen cases of people adding completely pointless extra electronics to do what the micro-controller could do perfectly well on its own. I guess at the time the person asking had not considered how they might achieve in software what they were doing in hardware, all a reasonable part of the learning curve and understandable as such. I see part of what I do on here is to point out alternatives.

Now I understand your reasons I am having trouble thinking of a viable alternative, although I have this nagging feeling there is one.

What is the purpose of the wire from pin 2 on one bus connector to pin 2 on the other connector? I can't see that it does anything, what am I missing?

PerryBebbington:
What is the purpose of the wire from pin 2 on one bus connector to pin 2 on the other connector? I can't see that it does anything, what am I missing?

The 4x4keypad is one of many test boards (individual PCBs) that can be daisy chained in random order. The other cards are:

  • 5x4 keypad
  • dual LCD 1602
  • dual PCF8574A with LEDs
  • a dual MCP23017 with RG LEDs
  • a 16 channel ADC and a MCP23017 reading the state of push buttons (momentary switches triggering 555 chips and indicator LEDs).

I have one UNO card with a 6 pins databus and are putting together a MEAG2560 card as well.

A fresh image of some of the cards:

(notice that the image is a bit screwed up on this forum. Direct link: http://www.dahl-stamnes.net/dahls/Ymse/developmentcards.jpg)

When I need to develop code for a real project, I can connect the required cards together and start coding. No loose wires.

OK, so here is my suggestion, which you can use or ignore as you see fit:

Forget the PCF8574. Use a micro-controller to scan the keypads, then you will have an output you can use to flash the LED when a key is pressed, or even make an annoying bleep if you like. The micro-controller can be a slave on the I2C bus. No need for a 555 timer.

I reckon you could make them self addressing with a bit of ingenuity too.

Yes, I know, you've already got the boards...

There is a lot of options to add a LED that blink when a key is pressed. Adding a LED is not required but was an option. It's like "been there - done that".

If I really needed the LED I would have considered this during design.

But still, I wonder why my solution does work (all the time). What have I missed/not understood? About every 1 of 4 keystrokes are detected by the 555 and in a random order.

If I cannot make this work, I just drop it.

As someone suggested before the interrupt will be activated only when the corresponding row (or column?) is being scanned when the button is pressed. Which is about 1/4 of time. To make it work you probably can write all rows LOW and columns HIGH (or vice versa - I don't know how it is wired) - if a button is pressed a column pin will read LOW (and the INT will be triggered) and you do the proper scan to find which button was pressed. This way the chance to miss the interrupt will be much lower (only shortly around the I2C ACK).

Smajdalf:
As someone suggested before the interrupt will be activated only when the corresponding row (or column?) is being scanned when the button is pressed. Which is about 1/4 of time. To make it work you probably can write all rows LOW and columns HIGH (or vice versa - I don't know how it is wired) - if a button is pressed a column pin will read LOW (and the INT will be triggered) and you do the proper scan to find which button was pressed. This way the chance to miss the interrupt will be much lower (only shortly around the I2C ACK).

That may explain why it work some of the times. Thanks.

I'll drop this idea...