I've got it working now. Was a combination of using the wrong address for the PCF8574; it's a PCF8574AP, so the address for A0, A1, & A2 all grounded is 0x38. Plus had the columns and rows for the keypad connected to the wrong P0:P7 pins of the PCF8574AP. Currently just using a sparkfun 4x3 keypad to get it working to start with.
I have a 220ohm resistor between the column pins of the keypad and the PCF8574AP connected to Vcc.
Here's the current code:
Display.h:
class LedControl;
class CDisplay
{
public:
CDisplay(const int dataPin, const int clkPin, const int csPin, const int numDevices);
~CDisplay();
void Init();
void ShowNumber(int deviceId, unsigned long number);
void ClearDisplay(int deviceId);
private:
void printNumber(int addr, unsigned long number);
unsigned int didgitCount(const unsigned long num, const unsigned int max);
const int m_nDataPin;
const int m_nClkPin;
const int m_nCsPin;
const int m_nNumDevices;
LedControl* m_pLC;
};
Display.cpp:
#include "Display.h"
#include <LedControl.h>
CDisplay::CDisplay(int dataPin, int clkPin, int csPin, int numDevices)
: m_nDataPin(dataPin)
, m_nClkPin(clkPin)
, m_nCsPin(csPin)
, m_nNumDevices(numDevices)
{
m_pLC = new LedControl(dataPin, clkPin, csPin, numDevices);
}
CDisplay::~CDisplay()
{
delete m_pLC;
}
void CDisplay::Init()
{
for (int addr = 0; addr < m_pLC->getDeviceCount(); ++addr)
{
// Initialize the module
m_pLC->shutdown(addr , false);
// display brightness adjustment
m_pLC->setIntensity(addr , 0x06);
}
for (int num = 1; num < 10; ++num)
{
for (int addr = 0; addr < m_pLC->getDeviceCount(); ++addr)
{
ShowNumber(addr+1, num);
}
delay(500);
}
for (int addr = 0; addr < m_pLC->getDeviceCount(); ++addr)
{
m_pLC->clearDisplay(addr);
}
}
void CDisplay::ShowNumber(int deviceId, unsigned long number)
{
printNumber(deviceId-1, number);
}
void CDisplay::ClearDisplay(int deviceId)
{
m_pLC->clearDisplay(deviceId-1);
}
void CDisplay::printNumber(int addr, unsigned long number)
{
unsigned int digits = didgitCount(number, 8);
// Calculate the value of each digit
int digit1 = number % 10 ;
int digit2 = (number / 10)% 10 ;
int digit3 = (number / 100)% 10 ;
int digit4 = (number / 1000)% 10 ;
int digit5 = (number / 10000)% 10 ;
int digit6 = (number / 100000)% 10 ;
int digit7 = (number / 1000000)% 10 ;
int digit8 = (number / 10000000)% 10 ;
// Display the value of each digit in the display
switch (digits)
{
case 8:
m_pLC->setDigit(addr, 7, (byte)digit8, false);
case 7:
m_pLC->setDigit(addr, 6, (byte)digit7, false);
case 6:
m_pLC->setDigit(addr, 5, (byte)digit6, false);
case 5:
m_pLC->setDigit(addr, 4, (byte)digit5, false);
case 4:
m_pLC->setDigit(addr, 3, (byte)digit4, false);
case 3:
m_pLC->setDigit(addr, 2, (byte)digit3, false);
case 2:
m_pLC->setDigit(addr, 1, (byte)digit2, false);
case 1:
m_pLC->setDigit(addr, 0, (byte)digit1, false);
}
}
//
// Returns the number of digits that need to
// be lit up on the LED, to show num
//
unsigned int CDisplay::didgitCount(const unsigned long num, const unsigned int max)
{
if (num == 0 || num == 1) return 1;
if (num == 10) return 2;
unsigned int count(max);
for (; count > 0; --count)
{
if (pow((double)10, (double)(count - 1)) < num)
break;
}
return count;
}
Keypad.ino:
/*
* Test program for PCF8574AP I2C I/O expander
* - for use with 4 x 3 keypad.
*
* Sparkfun keypad (pin row or column):
* 7 6 5 4 3 2 1
* R1 R2 C2 R3 C0 R0 C1
*
* On port-expander (port - row or column):
* P7 P6 P5 P4 P3 P2 P1 P0
* - R3 R2 R1 R0 C2 C1 C0
*
*/
#include <Wire.h>
#include "Display.h"
#define DIN_PIN 5
#define CS_PIN 6
#define CLK_PIN 7
#define NUM_DISPLAYS 1
int m_nRow, m_nColumn;
char m_keymap[4][3] = {{'1','2','3'},{'4','5','6'},{'7','8','9'},{'*','0','#'}};
CDisplay m_display(DIN_PIN, CLK_PIN, CS_PIN, NUM_DISPLAYS);
#define m_addr 0x38 // Address with three address pins grounded.
void setup()
{
Wire.begin();
Serial.begin(9600);
m_display.Init();
}
void loop()
{
char key = getKey();
if (key >= 0x30 && key <= 0x39)
{ // numeric
long num = key - 0x30;
m_display.ShowNumber(1, num);
}
delay(300);
}
char getKey()
{
char key = '\0';
byte send_pattern, receive_pattern,test_pattern;
byte send_pattern_array[]={B11110111, B11101111, B11011111, B10111111};
byte test_pattern_array[]={B00000001, B00000010, B00000100};
int i=0;
for (i=0; i<4;i++)
{
// Try each row. Send 0 on R1 port, the bit on the pressed column
// will turn from 1 to 0
send_pattern = send_pattern_array[i];
expanderWrite(send_pattern);
receive_pattern=expanderRead();
if (send_pattern != receive_pattern)
{
m_nRow = i;
for (int j = 0; j < 3; j++)
{
test_pattern = test_pattern_array[j] & receive_pattern;
Serial.println(btoa(test_pattern));
if(test_pattern == 0)
m_nColumn = j;
}
Serial.print("key pressed: row:");
Serial.print(m_nRow);
Serial.print(" column: ");
Serial.print(m_nColumn);
Serial.println();
key = m_keymap[m_nRow][m_nColumn];
Serial.print("key: ");
Serial.println(key);
}
}
return key;
}
char* btoa(int i)
{
static char zeros[] = "00000000";
char tmpstr[9];
for (int i = 0; i < 8; i++)
{
zeros[i] = '0';
}
tmpstr[0] = '\0';
itoa(i, tmpstr, 2);
zeros[8 - strlen(tmpstr)] = '\0';
strcat(zeros, tmpstr);
return zeros;
}
void expanderWrite(byte data)
{
Wire.beginTransmission(m_addr);
Wire.write(data);
Wire.endTransmission();
}
byte expanderRead()
{
byte data;
Wire.requestFrom(m_addr, 1);
if (Wire.available())
{
data = Wire.read();
}
return data;
}
I'll adapt the code to work with my 5x3 keypad later. But I've got it working.
However, it doesn't always capture the key press. Maybe I should adapt the code to get the key pressed on an event if that's possible?