Monday, December 3, 2012

I2C Keyboard and LCD panel - a little side project.


I obtained this nice unused TI99-4A keyboard on eBay. Because the price was right, I decided this was going to be a keyboard for my MKHBC-8-R1 system. But then I thought - why shouldn't it be a universal keyboard for all my hobby computer/microcontroller projects? 

TI99-4A keyboard is a matrix keyboard. It has 15 output pins, which form 8x7 matrix. To connect it to the computer system in a "classic" way, you need two 8-bit ports (less 1 pin). I'd have to add to my computer system a dedicated I/O chip that would serve only as a keyboard scanning circuit (with appropriate firmware in ROM). This seems to be a little bit wasteful. Perhaps instead I could equip my computer system with implementation of I2C bus, which is a widely used standard in chip to chip communication applications. I save 13 pins, since data only travels on 2 lines and I can use I2C port for other standard compatible devices, so it is not "wasted" to serve only one purpose.

Therefore I started this side project to develop an I2C "console", which will consist of TI99-4A keyboard and a basic 16x2 text LCD panel. My choice of hardware is 8051 compatible Atmel chip AT89S52.

Two reasons:

1) I know it.
2) I have a drawer full of them.

There are other practical reasons of course, like:


  • Widely known architecture with tons of code examples available all over the internet.
  • Four 8-bit 2-way digital ports.
  • 8 kB of flash program memory programmable in circuit via SPI interface with cheap flash programmer from eBay.
  • Built in timers, UART etc.
  • Really cheap.


 For starters I built this test circuit to test the keyboard. It has not I2C implemented yet.



NOTE: The internet sources say that pin# 15 of the TI99-4A keyboard is colored in red. However the one that I got has a pin# 1 marked with red wire. This gave me a bit of a trouble before I figured it out (my program would not work).

Here is thesource  code for SDCC. Program will display the numeric (not ASCII, just the matrix code) code of each pressed key on the LCD panel:


/*
 * Project: TI99-4A keyboard.
 * Module:  ti994a
 * Author:  Marek Karcz
 * Purpose: Testing TI99-4A keyboard.
 * 
 * Hardware:
 *    AT89S52
 *    1602 LCD display
 *    TI99-4A matrix keyboard (ports P2, P3).
 *
 * Keyboard to port pin connections:
 *
 * TI99-4a pin#      Pn.b
 * --------------------------
 *          11       P3.7
 *          10       P3.6
 *           3       P3.5
 *           7       P3.4
 *           2       P3.3
 *           1       P3.2
 *           4       P3.1
 *           5       P3.0
 *
 *           6       P2.6
 *           8       P2.5
 *           9       P2.4
 *          15       P2.3
 *          14       P2.2
 *          13       P2.1
 *          12       P2.0
 */


#include <at89x52.h>
#include <stdlib.h>
#include <string.h>

// keil -> sdcc
#define sbit __sbit
#define code __code
#define using __using
#define interrupt __interrupt
#define _nop_() __asm NOP __endasm

typedef unsigned char BYTE;
typedef unsigned int WORD;
typedef sbit BOOL ;


#define rs     P1_0 
#define rw     P1_1
#define ep     P1_2
#define KBP1   P3
#define KBP2   P2

BYTE code dis1[] = {"TI99-4A"};
BYTE code dis2[] = {"Keyboard test"};
//BYTE code msg1[] = {"Key pressed"};
BYTE dispbuf[17];
BYTE keycode=0;
BYTE row=0;
BYTE column=0;
BYTE code kbmatrix[8][7] =
{
{11, 43, 42, 41, 40, 22,  0},
{47, 31, 30, 29, 28, 32,  0},
{33, 20, 19, 18, 17, 21,  0},
{ 0,  9,  8,  7,  6, 10,  0},
{48,  2,  3,  4,  5,  1, 45},
{44, 24, 25, 26, 27, 23,  0},
{46, 13, 14, 15, 16, 12,  0},
{ 0, 36, 37, 38, 39, 35,  0}
};

void delay(BYTE ms);

void init_ports(void)
{
KBP1 = 0x00;
KBP2 = 0xFF;
P1 = 0xFF;
}

void init_dispbuf(void)
{
BYTE i=0;

for(i=0; i<16; i++)
{
dispbuf[i]=32;
}
dispbuf[i]=0;
}

void init_vars(void)
{
init_dispbuf();
keycode=0xFF;
row=0;
column=0;
}

void delay(BYTE ms)
{      
   BYTE i;
   while(ms--)
   {
      for(i = 0; i< 250; i++)
      {
         _nop_();
         _nop_();
         _nop_();
         _nop_();
      }
   }
}

BOOL lcd_bz()
{   
   BOOL result;
   rs = 0;
   rw = 1;
   ep = 1;
   _nop_();
   _nop_();
   _nop_();
   _nop_();
   result = (BOOL)(P0 & 0x80);
   ep = 0;
   return result; 
}

void lcd_wcmd(BYTE cmd)

   while(lcd_bz());
   rs = 0;
   rw = 0;
   ep = 0;
   _nop_();
   _nop_(); 
   P0 = cmd;
   _nop_();
   _nop_();
   _nop_();
   _nop_();
   ep = 1;
   _nop_();
   _nop_();
   _nop_();
   _nop_();
   ep = 0;  
}

void lcd_pos(BYTE pos)
{
   lcd_wcmd(pos | 0x80);
}

void lcd_wdat(BYTE dat) 

    while(lcd_bz());
    rs = 1;
    rw = 0;
    ep = 0;
    P0 = dat;
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    ep = 1;
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    ep = 0; 
}

void lcd_init()
{
   lcd_wcmd(0x38); 
   delay(1);
   lcd_wcmd(0x0c); 
   delay(1);
   lcd_wcmd(0x06);
   delay(1);
   lcd_wcmd(0x01);
   delay(1);
}

void lcd_clear(BYTE pos)
{
   BYTE i=0;
   lcd_pos(pos);
   for(i=0; i<16; i++)
   {
      lcd_wdat(32);
   }
}

void lcd_text(BYTE pos, BYTE txt[])
{
   BYTE i=0;
   lcd_clear(pos);
   lcd_pos(pos);
   i=0;
   while((int)txt[i] != (int)'\0')
   {
      lcd_wdat(txt[i]); 
      i++;
   }
}

/* Scans keyboard.
 * Returns 0 if no key pressed.
 * Returns row# (1-8) if key pressed.
 * Column is calculated from the read key code.
 */
BYTE read_kb(void)
{
BYTE i=0;
BYTE j=0;
BYTE ret=0;
BYTE keyscan=0;
BYTE pattern=0x01;

for (i=0,pattern=0x01; pattern!=0; pattern<<=1,i++)
{
  KBP2 = 0xFF;
  KBP1 = ~pattern;
  keyscan = KBP2;
  if(keyscan!=0xFF)
  {
 init_dispbuf();
 for(j=1,column=0; j!=0; j<<=1,column++)
 {
    if (~keyscan & j)
     {
        break;
     }
 }
 row = i;
          ret = i+1;
 keycode = kbmatrix[row][column];
 break;
  }
  delay(4);
}

return ret;
}

main()
{
   BYTE kcodehex[5];
   BYTE kcodedec[10];
   BYTE rowbuf[3];
   BYTE row=0;

   init_vars();
   init_ports();
   lcd_init();
   delay(10);
   lcd_text(4,dis1);
   lcd_text(0x41,dis2);
   delay(255);
   delay(255);
   while(1)
   {
      if((row=read_kb())!=0)
 {
     _uitoa(keycode,kcodehex,16);
     _uitoa(keycode,kcodedec,10);
     _uitoa(row,rowbuf,10);
     strcpy(dispbuf,kcodehex);
     strcat(dispbuf,":");
     strcat(dispbuf,kcodedec);
     strcat(dispbuf,":");
     strcat(dispbuf,rowbuf);
     lcd_text(0x41,dispbuf);
 }
 else
 {
    lcd_clear(0x41);
 }
   }
}



Hopefully this project will become something practical.
I built the prototype on a breadboard.




Thank you for reading.

Marek Karcz
12/3/2012