Wednesday, March 5, 2014

driving a phone keypad matrix

I got given this old phone to take apart:

and decided to make it work as a numpad.

I had to solder the wires from the ribbon cable onto headers so that I could plug it into my breadboard.. that's really difficult to do - I think you're supposed to have a PCB between the plastic of the headers and the metal you're working with. I ended up soldering 11/12 pins successfully, luckily I didn't need the last one.

on the very first pin the head and tension managed to pull the pin up a bit, this happened even worse on the final pin.

For the first circuit I made a wrong assumption about the internals, plugged it in and tried it and it didn't work:



So I traced out the schematic of the keypad and the way it function is that when you press key on row r and column c it completes a circuit between pin Rr and Cc.


I spent a while trying to figure out how to drive this circuit without having to pull the header out of the breadboard:

I came up with the plan that I could scan through the columns by firing an output on them one by one, and check if that came though any of the row pins as inputs... but thinking back to the http://www.ruggedcircuits.com/10-ways-to-destroy-an-arduino I was aware of the danger of shorting a high output to a low output (if you held down two keys on the same row but different columns).

There are actually two solutions to this, I think. In the end I did both even though - I think - only one is necessary. First of all (and this works without pulling the thing out the breadboard) you can just set your pins to be inputs pins when they're not outputting HIGH, that means you'll never have a LOW output short to a HIGH output! N.B. when the teensy starts up its pins are all configured to be inputs, since that is safer. You can set the ones you're not using to be low outputs for reduced energy consumption. The other solution is that since LOW outputs can sink a few milliamps, just use resistors to limit the current.

In fact I went with that and coded it up and tried it and.... without pressing anything it entered "1" and then did absolutely nothing. I had to put debugging info in and it turned out all my inputs were coming in as 1's all the time. So I added pull-down resistors and it all worked nicely! I actually reasoned that I did not need pull-down resistors earlier, but I was wrong and don't understand why.0

Here's the schematic I ended up with


a photo of the working build!



and the code which is based on PJRC usb_keyboard_debug example:

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include "usb_keyboard_debug.h"

#define LED_ON        (PORTD |= (1<<6))
#define LED_OFF        (PORTD &= ~(1<<6))
#define LED_CONFIG    (DDRD |= (1<<6))

#define CPU_PRESCALE(n)    (CLKPR = 0x80, CLKPR = (n))

#define DIT 10
#define PRESS(r,c) (pressed_tmp = 1, (pressed?0:(pressed = 1, usb_keyboard_press(number_keys[c+4*r], 0))))

uint8_t number_keys[]=
  {KEY_1,KEY_2,KEY_3,KEY_MINUS,
   KEY_4,KEY_5,KEY_6,KEYPAD_PLUS,
   KEY_7,KEY_8,KEY_9,KEY_TILDE,
   KEYPAD_ASTERIX,KEY_0,KEY_SLASH,KEY_EQUAL};

int main(void) {
  uint8_t d;
  int pressed;
  int pressed_tmp;
 
  // set for 16 MHz clock, and make sure the LED is off
  CPU_PRESCALE(0);
  LED_CONFIG;
  LED_OFF;
 
  // we have the following pinout
  //
  // C7, C6, C5, C4, C3, C2, C1, C0
  // [ 10k outputs ] [ inputs     ]
  // 10k changd to 470
 
  // See the "Using I/O Pins" page for details.
  // http://www.pjrc.com/teensy/pins.html
  DDRC = 0; // make everything an input to start with
 
  // Initialize the USB, and then wait for the host to set configuration.
  // If the Teensy is powered without a PC connected to the USB port,
  // this will wait forever.
  usb_init();
  while (!usb_configured()) /* wait */ ;
 
  // Wait an extra second for the PC's operating system to load drivers
  // and do whatever it does to actually be ready for input
  _delay_ms(1000);
 
  pressed = 0;
  while(1) {
    pressed_tmp = 0;
  
    // col1
    DDRC |= 1<<4; // make this an output
    PORTC |= 1<<4; // enable it
    _delay_ms(DIT);
    d = PINC;
    usb_debug_putchar((d&(1<<0))?'#':'_');
    usb_debug_putchar((d&(1<<1))?'#':'_');
    usb_debug_putchar((d&(1<<2))?'#':'_');
    usb_debug_putchar((d&(1<<3))?'#':'_');
    usb_debug_putchar('\n');
    if(d&(1<<0)) { PRESS(0,0); } // row1
    if(d&(1<<1)) { PRESS(0,1); } // row2
    if(d&(1<<2)) { PRESS(0,2); } // row3
    if(d&(1<<3)) { PRESS(0,3); } // row4
    PORTD &= ~(1<<4); // disable it
    DDRC &= ~(1<<4); // make it an input again
  
    // col2
    DDRC |= 1<<5; // make this an output
    PORTC |= 1<<5; // enable it
    _delay_ms(DIT);
    d = PINC;
    usb_debug_putchar((d&(1<<0))?'#':'_');
    usb_debug_putchar((d&(1<<1))?'#':'_');
    usb_debug_putchar((d&(1<<2))?'#':'_');
    usb_debug_putchar((d&(1<<3))?'#':'_');
    usb_debug_putchar('\n');
    if(d&(1<<0)) { PRESS(1,0); } // row1
    if(d&(1<<1)) { PRESS(1,1); } // row2
    if(d&(1<<2)) { PRESS(1,2); } // row3
    if(d&(1<<3)) { PRESS(1,3); } // row4
    PORTD &= ~(1<<5); // disable it
    DDRC &= ~(1<<5); // make it an input again
  
    // col3
    DDRC |= 1<<6; // make this an output
    PORTC |= 1<<6; // enable it
    _delay_ms(DIT);
    d = PINC;
    usb_debug_putchar((d&(1<<0))?'#':'_');
    usb_debug_putchar((d&(1<<1))?'#':'_');
    usb_debug_putchar((d&(1<<2))?'#':'_');
    usb_debug_putchar((d&(1<<3))?'#':'_');
    usb_debug_putchar('\n');
    if(d&(1<<0)) { PRESS(2,0); } // row1
    if(d&(1<<1)) { PRESS(2,1); } // row2
    if(d&(1<<2)) { PRESS(2,2); } // row3
    if(d&(1<<3)) { PRESS(2,3); } // row4
    PORTD &= ~(1<<6); // disable it
    DDRC &= ~(1<<6); // make it an input again
  
    // col4
    DDRC |= 1<<7; // make this an output
    PORTC |= 1<<7; // enable it
    _delay_ms(DIT);
    d = PINC;
    usb_debug_putchar((d&(1<<0))?'#':'_');
    usb_debug_putchar((d&(1<<1))?'#':'_');
    usb_debug_putchar((d&(1<<2))?'#':'_');
    usb_debug_putchar((d&(1<<3))?'#':'_');
    usb_debug_putchar('\n');
    if(d&(1<<0)) { PRESS(3,0); } // row1
    if(d&(1<<1)) { PRESS(3,1); } // row2
    if(d&(1<<2)) { PRESS(3,2); } // row3
    if(d&(1<<3)) { PRESS(3,3); } // row4
    PORTD &= ~(1<<7); // disable it
    DDRC &= ~(1<<7); // make it an input again
  
    usb_debug_putchar('\n');
    if(!pressed_tmp) pressed = 0; // nothing was pressed this whole scan
    // so reset and allow a new keypress
  }
}

No comments:

Post a Comment