I2C Keypad & LCD liabrary

It was surprising to see that there is no single library for using I2c keypad & LCD using PCF8574 expander. I came across different library for each purpose conflicting when used in single project.

If anybody know a solution for this please guide me. I am willing to use 4x4 keypad & 20x4 LCD on single I2C for the blue pill STM32.

Hi there,

If you mean both things together in one library, then I think you will not found anything. Simply because there are many combinations - keypads, rotary encoders, buttons and so on.

BR, Jan

Hi Jan,

Thanks for the reply. I agree both library work but individually. But as I add header of <TextLcd.h> with PCF8574.h I do see an error " Error: Expected a declaration PCF8574 io(PB_8,PB_9,0x40)". I also didn’t find any example having both library together.

Nilesh

Yeah, there seems to be a conflict between macros from TextLCD with same name as Class name - PCF8574.
I made a fork of original and changed Class name to PCF8574IO and I also reworked constructor for I2C object instead of I2C pins

PCF8574 - Fork of https://os.mbed.com/users/simon/code/PCF8… | Mbed

// example
I2C i2cBus(D14, D15);
TextLCD_I2C lcd(&i2cBus, PCF8574A_SA7, TextLCD::LCD16x4);
PCF8574IO io(&i2cBus,0x40);

BR, Jan

Hi Jan,
I do really appreciate your efforts & help.
With your updated fork code do get compile.
However I do see the interrupt doesn’t work for the keypad.
Here is the complete code for bluepill i.e. Nucleo-F103RB for mbed:

#include "mbed.h"
#include "PCF8574.h"
#include <TextLCD.h>
 
#define BAUD 9600

I2C i2cBus(PB_7, PB_6);
TextLCD_I2C lcd(&i2cBus, 0x4E, TextLCD::LCD20x4);  
PCF8574IO io(&i2cBus,0x40);
InterruptIn event(PB_5);
Serial pc(PA_9, PA_10); 
int data1 , data2;

void display(int data)
{
    switch (data)
    {
        case 238: pc.printf("SW1 \n\r"); break;
        case 237: pc.printf("SW2 \n\r"); break;
        case 235: pc.printf("SW3 \n\r"); break;
        case 231: pc.printf("SW4 \n\r"); break;
        case 222: pc.printf("SW5 \n\r"); break;
        case 221: pc.printf("SW6 \n\r"); break;
        case 219: pc.printf("SW7 \n\r"); break;
        case 215: pc.printf("SW8 \n\r"); break;
        case 190: pc.printf("SW9 \n\r"); break;
        case 189: pc.printf("SW10 \n\r"); break;
        case 187: pc.printf("SW11 \n\r"); break;
        case 183: pc.printf("SW12 \n\r"); break;
        case 126: pc.printf("SW13 \n\r"); break;
        case 125: pc.printf("SW14 \n\r"); break;
        case 123: pc.printf("SW15 \n\r"); break;
        case 119: pc.printf("SW16 \n\r"); break;
    }    
    lcd.locate(0,0); 
    lcd.printf("KEY :%d",data);
}
 
 void trigger() 
{
  //event.fall(NULL);
  //printf("triggered!\n");
    io.write(0x0f);
    data1 = io.read();
            
    io.write(0xf0);
    data2 = io.read();
                
    display(data1+data2);
  //wait(0.2);
  //event.fall(&trigger);
}
 
int main()
{    
    data1=data2=0;
    event.mode(PullUp);
    pc.printf("Start\n\r");
    event.fall(&trigger);
    //event.rise(&trigger);
    lcd.locate(0,0); 
    lcd.printf("KEY :%d",data1);
    while(1) 
    {
    
    }
}

But by polling the key instead following code works perfectly :slight_smile: :

#include "mbed.h"
#include "PCF8574.h"
#include <TextLCD.h>

I2C i2cBus(PB_7, PB_6);
TextLCD_I2C lcd(&i2cBus, 0x4E, TextLCD::LCD20x4);
PCF8574IO io(&i2cBus,0x40);
Serial pc(PA_9, PA_10); 

int data1 , data2;
char key;
    
char GetKey()
{
    io.write(0x0f);
    data1 = io.read();
            
    io.write(0xf0);
    data2 = io.read();              
     
    switch (data1+data2)
    {
        case 235: key = '0'; break;
        case 119: key = '1'; break;
        case 123: key = '2'; break;
        case 125: key = '3'; break;
        case 183: key = '4'; break;
        case 187: key = '5'; break;
        case 189: key = '6'; break;
        case 215: key = '7'; break;
        case 219: key = '8'; break;
        case 221: key = '9'; break;
        case 126: key = 'A'; break;
        case 190: key = 'B'; break;
        case 222: key = 'C'; break;
        case 238: key = 'D'; break;
        case 231: key = '*'; break;
        case 237: key = '#'; break;
        case 255: key = 'N'; break;
    }    
    return(key);
}
 
 
int main()
{     
    data1=data2=0;
    pc.printf("Start\n\r");
    lcd.locate(0,0); 
    lcd.printf("KEY :%d",data1);
    while(1) 
    {
        key = GetKey();
        if(key != 'N') 
        {
            lcd.locate(0,0); 
            lcd.printf("KEY :%c",key);
        }
    }
}

Hello,

that is great.
I know nothing about your Keypad and connection with Expander, but I suppose you have the pin PB_5 connected to INT pin of the Expander. The INT pin also must have Pull-up resistor to Vcc, so internal pull-up resistor can be not enough.

You probably use the Bare Metal Mbed OS profile, but letting done all the work in ISR is not a good practice.

volatile bool flag = false;

void trigger(){
 if(!flag) flag = true;
}
 
int main(){    
    pc.printf("Start\n\r");
    data1=data2=0;
    event.mode(PullUp);
    event.fall(&trigger);
    //event.rise(&trigger);
    lcd.locate(0,0); 
    lcd.printf("KEY :%d",data1);
    while(1) 
    {
       if(flag){
        //printf("triggered!\n");
        io.write(0x0f);
        data1 = io.read();        
        io.write(0xf0);
        data2 = io.read();           
        display(data1+data2);
        //wait(0.2);
        flag = false;
       }
    }
}

BR, Jan

Great…the interrupt is working ! Its definitely better than polling what I was doing earlier !

I am also using an I2C MCP20338 liabrary & I do see it’s conflicting with the TextLCD library. The program do get compiled if I exclude MCP20338 liabrary or TextLCD, The code is given below:

#include "mbed.h"
#include "PCF8574.h"
#include <TextLCD.h>

I2C i2cBus(PB_7, PB_6);
TextLCD_I2C lcd(&i2cBus, 0x4E, TextLCD::LCD20x4);  
PCF8574IO io(&i2cBus,0x40);

InterruptIn event(PB_5);
Serial pc(PA_9, PA_10); 
char key;
bool KeyPressed = 0;

void GetKey()
{
    char data1=0;
    char data2=0;    
    
    io.write(0x0f);
    data1 = io.read();        
    io.write(0xf0);
    data2 = io.read();               
        
    switch (data1+data2)
    {
        case 235: key = '0'; break;
        case 119: key = '1'; break;
        case 123: key = '2'; break;
        case 125: key = '3'; break;
        case 183: key = '4'; break;
        case 187: key = '5'; break;
        case 189: key = '6'; break;
        case 215: key = '7'; break;
        case 219: key = '8'; break;
        case 221: key = '9'; break;
        case 126: key = 'A'; break;
        case 190: key = 'B'; break;
        case 222: key = 'C'; break;
        case 238: key = 'D'; break;
        case 231: key = '*'; break;
        case 237: key = '#'; break;
        case 255: key = 'N'; break;
    }    
    
    if(key != 'N') 
    {
        lcd.locate(0,0); 
        lcd.printf("KEY :%c",key);
    }
    KeyPressed = 0;
}

void trigger()
{
    if(!KeyPressed) KeyPressed = 1;
}
 
int main()
{    
    pc.printf("Start\n\r");
    event.mode(PullUp);
    event.fall(&trigger);    
    lcd.locate(0,0); 
    lcd.printf("KEY :");
    while(1) 
    {
       if(KeyPressed) GetKey();
    }
}

As I include the MCP20338 library I do get an error “Error: Expected either a definition or a tag name in “MCP23008/MCP23008.hpp”, Line: 8, Col: 8”

Well it got working by another corrected library : MCP23008_I2C

Thanks again for the help!!

Yeah, it will be same issue like the previous one, colision between Class name and macro name in both libraries.

I recommend to let the boolean variable as volatile.

BR, Jan