//*****************************************************************
// ******** 4X4 MATRIX KEY-BOARD INTERFACING *******
//*****************************************************************
//Controller: ATmega32 (Crystal: 16 Mhz)
//Compiler: ImageCraft ICCAVR
//Author: CC Dharmani, Chennai (India)
//Date: Aug 2008
//********************************************************

//******** HEADER FILE - MATRIX_KB.H ****************

// *** LCD Functions declaration *** //
void LCD_init(void);
void LCD_WriteCommand (unsigned char CMD);
void LCD_WriteData (unsigned char Data);

void LCD_DisplayString_F(char row, char column, const unsigned char *string);
void LCD_Cursor(char row, char column);
void delay_ms(int miliSec);

#define ENABLE_LCD PORTD |= 0x80
#define DISABLE_LCD PORTD &= ~0x80
#define SET_LCD_DATA PORTD |= 0x20
#define SET_LCD_CMD PORTD &= ~0x20
#define KB_PORT_OUT PORTB
#define KB_PORT_IN PINB

//******** END OF HEADER FILE – MATRIX_KB.H ************

// ******** SOURCE CODE FILE - MATRIX_KB.C ***********

#include <iom32v.h>
#include <macros.h>
#include "matrix_KB.h"

void port_init(void)
{
DDRA = 0x00;
PORTA = 0x00;
DDRB = 0x0f; //Key-board port, higer nibble - input, lower nibble - output
PORTB = 0xff; //pull-up enabled for higher nibble
DDRC = 0xff;
PORTC = 0x00;
DDRD = 0xf0;
PORTD = 0x00;
}


//call this routine to initialize all peripherals
void init_devices(void)
{
//stop errant interrupts until set up
CLI(); //disable all interrupts

port_init();
LCD_init();

MCUCR = 0x00;
TIMSK = 0x00; //timer interrupt sources
}

//****************** MAIN FUNCTION *******************

void main(void)
{
unsigned char upperNibble, keyCode, keyPressed, i;
init_devices();

LCD_DisplayString_F(1,1," WELCOME ");
LCD_WriteCommand(0xc0); //moving LCD cursor to second row

while(1)
{
upperNibble = 0xff;

for(i=0; i<4; i++)
{
delay_ms(1);
KB_PORT_OUT = ~(0x01 << i);
delay_ms(1); //delay for port o/p settling
upperNibble = KB_PORT_IN | 0x0f;

if (upperNibble != 0xff)
{
delay_ms(20); //key debouncing delay
upperNibble = KB_PORT_IN | 0x0f;
if(upperNibble == 0xff) goto OUT;

keyCode = (upperNibble & 0xf0) | (0x0f & ~(0x01 << i));

while (upperNibble != 0xff)
upperNibble = KB_PORT_IN | 0x0f;

delay_ms(20); //key debouncing delay

switch (keyCode) //generating key characetr to display on LCD
{
case (0xee): keyPressed = '0';
break;
case (0xed): keyPressed = '1';
break;
case (0xeb): keyPressed = '2';
break;
case (0xe7): keyPressed = '3';
break;
case (0xde): keyPressed = '4';
break;
case (0xdd): keyPressed = '5';
break;
case (0xdb): keyPressed = '6';
break;
case (0xd7): keyPressed = '7';
break;
case (0xbe): keyPressed = '8';
break;
case (0xbd): keyPressed = '9';
break;
case (0xbb): keyPressed = 'A';
break;
case (0xb7): keyPressed = 'B';
break;
case (0x7e): keyPressed = 'C';
break;
case (0x7d): keyPressed = 'D';
break;
case (0x7b): keyPressed = 'E';
break;
case (0x77): keyPressed = 'F';
break;
default : keyPressed = 'X';
}//end of switch

LCD_WriteData(keyPressed);

OUT:;
}//end of if
}//end of for
}//end of while(1)
}//end of main()


//*********************** LCD Functions *****************************

// *********************************
// *** Initialize the LCD driver ***
// *********************************
void LCD_init(void)
{
delay_ms(100); // wait for 100ms

LCD_WriteCommand (0x38); // 8 data lines
LCD_WriteCommand (0x08); // display off
LCD_WriteCommand (0x01); // clear LCD memory
delay_ms (10);           // 10ms delay after clearing LCD
LCD_WriteCommand (0x06); // cursor setting
LCD_WriteCommand (0x0f); // display ON
}


// **********************************************
// *** Write a command instruction to the LCD ***
// **********************************************
void LCD_WriteCommand (unsigned char Command)
{

SET_LCD_CMD; // Set LCD in command mode

PORTC = Command; // Load data to port

ENABLE_LCD; // Write data to LCD

asm("nop");
asm("nop");

DISABLE_LCD; // Disable LCD

delay_ms(1); // wait for 1ms
}


// *****************************************
// *** Write one byte of data to the LCD ***
// *****************************************
void LCD_WriteData (unsigned char Data)
{
SET_LCD_DATA; // Set LCD in data mode

PORTC = Data; // Load data to port

ENABLE_LCD; // Write data to LCD

asm("nop");
asm("nop");

DISABLE_LCD; // Disable LCD

delay_ms(1); // wait for 1ms
}


// *********************************************************************
// *** Display a string at the specified row and column, from FLASH ****
// *********************************************************************
void LCD_DisplayString_F (char row, char column ,const unsigned char *string)
{
LCD_Cursor (row, column);
while (*string)
LCD_WriteData(*string++);
}


// ***************************************************
// *** Position the LCD cursor at "row", "column". ***
// ***************************************************
void LCD_Cursor (char row, char column)
{
switch (row)
{
case 1: LCD_WriteCommand (0x80 + column - 1); break;
case 2: LCD_WriteCommand (0xc0 + column - 1); break;
default: break;
}
}


// ********************************************************
// **** Function for delay of 1 msec (appx.) at 16Mhz *****
// ********************************************************
void delay_ms(int miliSec) //for 16 Mhz crystal
{
int i,j;

for(i=0;i<miliSec;i++)
for(j=0;j<1550;j++)
{
asm("nop");
asm("nop");
}
}

// ******** END OF SOURCE CODE FILE – MATRIX_KB.C *********

// ***** CC Dharmani, www.dharmanitech.com ******