#include <lcd.h> //
LCD driver routines
#include <Mega32.h> //
Mega32 definitions
#include <delay.h> //
Using for delay functions
#include <stdio.h> //
For standard input and output
#define maxkeys 16 //
The number of keys in our keypad
#define release 1 //
These are all definitions for our keypad state machine
#define debounce 2
#define endchar 3
#define still_pressed 4
#define debounce_release 5
#define done 6
#define initial 11 //
These are all definitions for our main state machine
#define record 22
#define play 33
#define erase 44
#define view 55
#define ARRAY_SIZE 70 //
These are the number of 'wave changes' recorded
#define ICP PIND.6 //
Defining the input capture pin
#define LCDwidth 16 //
Characters in our LCD
#asm
.equ
__lcd_port=0x18 //
Defining the port for our LCD
#endasm
#include <lcd.h> //
LCD driver routines
#define begin {
#define end }
sfrw ICR1=0x26;
unsigned char key, butnum, maybe, keyready, lockoutuser,
flag;
unsigned char keypad_state, mainState, j;
unsigned char keystring, IRon;
char lcd_buffer[17];
// LCD display buffer
unsigned char timer0count;//, freqcount;
//key pad scan table
flash unsigned char keytbl[16]={0x7d, 0xee, 0xed, 0xeb,
0xde, 0xdd, 0xdb, 0xbe,
0xbd, 0xbb, 0xe7, 0xd7, 0x7e, 0xb7, 0x77, 0x7b};
/*
Here is the original
layout
1 2
3 A 4
5 6 B
7 8 9
{0xee, 0xed, 0xeb, 0xe7, 0xde, 0xdd, 0xdb, 0xd7, 0xbe, 0xbd,
0xbb,
C *
0 # D
0xb7, 0x7e, 0x7d, 0x7b, 0x77};
The modified keypad layout.
A zero will show up as a zero, a one as a one...
0 1
2 3 4
5 6 7
8 9 A
B
{0x7d, 0xee, 0xed, 0xeb, 0xde, 0xdd, 0xdb, 0xbe, 0xbd, 0xbb,
0x7d, 0xd7,
* C
D #
0x7e, 0xb7, 0x77,
0x7b};
*/
int button_array[10][ARRAY_SIZE]; // This matrix will contain the information recorded for the buttons
int inuse[10]; //
This array will contain information regarding wether a button is being used or
not
int button, timeframe; //
Variables to access the matrix later in our code
// Declaration of signatures of other functions
void scan_keypad(void);
void keypad_state_machine(void);
void initialize(void);
void state_Machine(void);
void record_key(void);
void play_key(void);
/* Function record_key records the shape of the pulse by
setting the timers appropriately,
* prompting the user
for information, and waiting for the interrupts to happen as expected.
* At the end, it
resents the variables and the timers for non-recording operation
*/
void record_key(void)
begin
TCNT1 = 0; //
Reset the clock here
timeframe = -1; //
Initialize our timeframe variable
if (ICP) //
Poll the signal once
TCCR1B
= TCCR1B & 0xBF; //
Interrupt trigger on a falling edge
else
TCCR1B
= TCCR1B | 0x40; // Interrupt
trigger on a rising edge
TIMSK =
0x20; //
Enable the ICP interrupt and disable all other interrupts
lcd_clear(); //
Clear the LCD display
lcd_gotoxy(0,0); //
Goto coordinates (0,0)
lcd_putsf("Press
button now"); // Display
message
/* We will
loop here, waiting for interrupts to happen that will update the timeframe
variable
Interrupts for Input Capture will occur here
*/
while
(timeframe < ARRAY_SIZE);
timeframe =
0; //
Reset this variable
TIMSK =
0x02; //
Turn all other interrupts on and turn the input capture ISR off
inuse[button]
= 1; //
Set this button to used
end
/* Function play_key plays the shape that has been recorded
by the pulse function, by setting the
* Timer 2 to generate
a 40Khz wave when needed, and by reading the array and setting Timer 1 compare
to
* correctly set on
and off timer 2 in order to generate the desired wave
*/
void play_key(void){
int i;
TIMSK = 0b00010000; //
This should turn off all active interrupts, but Timer 1 compare
for(i = 0; i <
2; i++){ //
Output the recorded wave twice
IRon =
0; // Initialize
the IRon variable to be zero
timeframe =
0; //
Reset timeframe variable
OCR1A =
button_array[button][timeframe]; //
Set output compare register to be the first pulse width
while
(timeframe <ARRAY_SIZE); // Wait for
Timer 1 compare interrupts to do their thing
}
TCCR2 =
0b00001000; //
Reset the timer 2 register
TIMSK =
0x02; //
Reset the timer 1 mask
#asm
nop;
nop;
#endasm
PORTD =
PORTD & 0b01111111; //
Turn off the IR LED
}
/* Main function will initialize all the variables
approriately and run the
* state machine for
our project infitely many times
*/
void main(void)
begin
initialize(); //
Initialize all variables
while (1) {
state_Machine(); //
Run state machine
}
end
/* Initialize function. It sets the variables appropriate
and display a welcome message.
* Also set timers 1,
0 and 2 to the right values, and initialize pins and ports
*/
void initialize(void)
begin
int i,j; //
Local variables
lcd_init(LCDwidth); //
Initialize the display
lcd_clear(); //
Clear the LCD
lcd_gotoxy(0,0); // Goto
certain coordinates
lcd_putsf("Programmable remote control"); // Display greeting
message
// Init port B to
show keyboard result
DDRB = 0xff; //
All outputs for the LCD
// Init
port D for LED. 8th pin for output, all others for inputs
DDRD =
0b10000000;
// Set up timer 0
TIMSK = 0x02;
OCR0 = 250;
TCCR0=0b00001101;
timer0count =
0; // timer will be on a 16/32 ms time
base
// Set up
timer 1
TCCR1A = 0x00;
TCCR1B =
0x03;
// Set up timer 2
OCR2 = 200;
// State
Machine Initialization
mainState =
initial;
keypad_state =
done;
//
Initialize global variables
keyready = 0;
lockoutuser = 0;
flag = 0;
// Enable
interrputs
#asm
sei
#endasm
delay_ms(1000); // Wait for LCD message to be
perceived by user
lcd_clear(); // Clear said message
end
/* The function keypad_state_machine is used for debouncing
the keypad, and
* setting other
variables appropriately.
*/
void keypad_state_machine(void)
begin
if
(!lockoutuser) {
switch(keypad_state)
begin
case release:
if (butnum != 0)
begin
maybe = butnum;
scan_keypad();
keypad_state = debounce;
end
else
scan_keypad();
break;
case debounce:
if (butnum == maybe) {
keypad_state = endchar;
}
else
begin
keypad_state = release;
scan_keypad();
end
break;
case
endchar:
keystring = butnum;
keyready = 1;
keypad_state = still_pressed;
scan_keypad();
break;
case still_pressed:
if
(butnum == keystring)
begin
scan_keypad();
end
else
begin
scan_keypad();
keypad_state =
debounce_release;
end
break;
case debounce_release:
if
(butnum == keystring)
begin
scan_keypad();
keypad_state = still_pressed;
end
else
begin
scan_keypad();
keypad_state = release;
end
break;
case done:
keypad_state = release;
break;
end
end // if
statement
end
/* The function state_machine is used for switching between
states to execute commands
* such as playing,
recording, erasing and viewing. For this it checks what has been input
* on the keypad, and
calls the appropriate functions.
*/
void state_Machine (void)
begin
switch (mainState)
begin
case
initial:
lcd_gotoxy(0,0);
lcd_putsf("A-Record B-Play"); // Display prompt for user input
lcd_gotoxy(0,1);
lcd_putsf("C-Erase D-View");
if (keyready == 1) {
keyready = 0;
switch (keystring)
begin
case 11: //
Button for Recording
mainState
= record; // Change state to record
butnum = 0;
lcd_clear(); // Clear LCD
lcd_gotoxy(0,0); // Display another prompt for user
input
lcd_putsf("Choose a
button");
lcd_gotoxy(0,1);
lcd_putsf("from
0-9.");
break;
case 12: //
Button for playing
mainState = play; // Change state to play
lcd_clear(); // Clear LCD
keyready
= 0; // Reset variables
break;
case 14: //
Button for erasing
lcd_clear(); // Clear LCD
lcd_gotoxy(0,0); // Display prompt for user input
lcd_putsf("Enter
button to");
lcd_gotoxy(0,1);
lcd_putsf("erase.");
mainState
= erase; // Change state to erase
break;
case 15: //
Button for viewing buttons in use
lcd_clear(); // Clear LCD
lcd_gotoxy(0,0); // Display prompt for user input
lcd_putsf("In
use: ");
mainState
= view; // Change state to view
break;
end // switch
}
break;
case record:
// If we get to state record
if ((keyready == 1) && (keystring
< 11) && (keystring > 0) && (inuse[keystring-1] == 0)) {
//Check this is a valid button to reccord
button = keystring - 1; //
reindex button variable
keyready = 0; //
reset appropriate variable. read the key
lcd_clear(); //
Clear LCD
lcd_gotoxy(0,0); //
Display prompt to inform user
lcd_putsf("Button ");
lcd_gotoxy(8,0);
sprintf(lcd_buffer,
"%-i", button);
lcd_puts(lcd_buffer);
lcd_gotoxy(0,1);
lcd_putsf("selected.");
delay_ms(350); //
Wait for user to perceive information
lcd_clear();
lcd_putsf("Press A
to"); //
Display prompt for user input
lcd_gotoxy(0,1);
lcd_putsf("start.");
while
((keyready != 1) | (keystring != 11)); // Wait for user to press A
record_key(); //
Call recording function
keyready = 0; //
Reset appropriate variable. Read the key.
lcd_clear(); //
Clear LCD
lcd_gotoxy(0,0); //
Display prompt to inform user
lcd_putsf("Programming
Done");
delay_ms(1000); //
Wait for user to perceive information
lcd_clear();
mainState = initial; //
Reset state to initial
}
else if (keyready == 1 &&
(inuse[keystring-1] != 0)) { //Not a valid button to record
lcd_clear(); //
Clear LCD
lcd_gotoxy(0,0); // Display
information to user
lcd_putsf("Button in use
or");
lcd_gotoxy(0,1);
lcd_putsf("bad
selection.");
delay_ms(1000); //
Wait for user to perceive information
lcd_clear();
mainState = initial; //
Reset state to initial
}
break;
case play: //
We have reached the play state
lcd_gotoxy(0,0); //
Clear LCD
lcd_putsf("Push a button or"); //
Display message for user input
lcd_gotoxy(0,1);
lcd_putsf("push A to return");
if ((keyready == 1) && (keystring
< 11) && (keystring > 0)) { //If
this is a valid key pressed
lcd_clear();
if (inuse[keystring-1] == 0) { // Check if button is
not in used
lcd_clear(); //
Clear LCD
lcd_gotoxy(0,0); //
Display information to user
lcd_putsf("Button
not used.");
delay_ms(1000); //
Wait for user to perceive it
lcd_clear(); //
Clear LCD
}
else {
keyready = 0; //
Reset appropriate variable. Read the key.
lcd_clear();
lcd_gotoxy(0,0); //
Display message for user input.
lcd_putsf("Playing");
button = keystring-1;
play_key(); //
Call playing function
PORTD = PORTD &
0b01111111; //
Set IR LCD off.
lcd_clear(); //
Clear LCD
}
}
else if ((keyready == 1) &&
(keystring == 11)) { // If this is an invalid button
keyready = 0; //
Reset appropriate variable. Key has been read.
lcd_clear(); //
Clear LCD
mainState = initial; //
Change state to initial
}
break;
case erase: //
We have rached the erase state
//
Check if it is a valid key press
if ((keyready == 1) && (keystring
< 11) && (keystring > 0) && (inuse[keystring-1] == 1)) {
button = keystring -1; //
reindex button.
inuse[button] = 0; //
set inuse button array to reflect button has been deleted
keyready
= 0; //
Reset appropriate variable. Key has been read
lcd_clear(); //
Clear LCD
lcd_gotoxy(0,0); //
Display information to user
lcd_putsf("Button ");
sprintf(lcd_buffer,
"%-i", button);
lcd_gotoxy(8,0);
lcd_puts(lcd_buffer);
lcd_gotoxy(0,1);
lcd_putsf("cleared");
delay_ms(1000); //
Wait for user to perceive
mainState = initial; //
Change state to initial
}
//
This is not a valid button
else if (keyready == 1 &&
(inuse[keystring] != 0)) {
keyready = 0; //
Reset appropriate variable. Key has been read
lcd_clear();
lcd_gotoxy(0,0); //
Display information to user
lcd_putsf("Not in use or
bad");
lcd_putsf("selection.");
delay_ms(1000); //
Wait for user to detect message
mainState = initial; //
Change state to initial
}
break;
case
view: //
We have reached the view state
for ( j = 0; j < 17; j++) { //
We are going to set up strings to display which buttons are in use
lcd_buffer[j] = 0x20; //
Initializing to spaces
}
j = 0;
for (button = 0; button < 10 && j
< 16; button++) { // Check if button is in use and start appending to string
if (inuse[button] == 1) {
lcd_buffer[j] =
button + 48; // Set to ASCII
code
j = j + 2;
}
}
lcd_buffer[j+1] = 0x00;
lcd_puts(lcd_buffer);
for(j = 0; j < 17; j++){ //
Initializing agin the string. This is to display second half
lcd_buffer[j] = 0x20;
}
j = 0;
for (;button<10 && j < 16;
button++){ // Check if
button is in use and start appending to string
if (inuse[button] == 1) {
lcd_buffer[j] =
button + 48;
j = j + 2;
}
}
lcd_puts(lcd_buffer); //
Display it to user
delay_ms(1000); //
Wait for user to perceive
mainState = initial; //
Change state to initial
break;
end
end
/* The function scan_keypad scans the port that is connected
to the keypad
* to see if there is
a button being pressed.
*/
void scan_keypad(void)
begin
//get lower
nibble
DDRC =
0x0f;
PORTC =
0xf0;
delay_us(5);
key = PINC;
//get upper
nibble
DDRC = 0xf0;
PORTC =
0x0f;
delay_us(5);
key = key |
PINC;
//find
matching keycode in keytbl
if (key !=
0xff)
begin
for
(butnum=0; butnum < maxkeys; butnum++)
begin
if
(keytbl[butnum] == key)
break;
end
if
(butnum == maxkeys)
butnum = 0;
else
butnum++; //adjust by one to
make range 1-16
end
else
butnum=0;
end
/* Timer 0 compare ISR. Used for scanning the keypad
*/
interrupt [TIM0_COMP] void timer0_int(void)
begin
timer0count++;
if (timer0count ==
2) {
timer0count =
0; // Reset timer count
keypad_state_machine(); // Run keypad state machine to detect which key
has been pressed
}
end
/* Timer 1 input capture ISR
* It is used for
detecting the wave shape in record_key.
* We shift between
calling this on a positive or a negative edge
*/
interrupt [TIM1_CAPT] void timer1_capt_isr(void)
begin
int tmp; //
Local variables
tmp = ICR1; //
Read the width of the register
// We need
to take care of the first interrupt we take.
if
(timeframe > -1)
{
if (ICP)
{
button_array[button][timeframe] = tmp; // Record pulse width
TCCR1B = TCCR1B & 0xBF; //
set to trigger on falling edge
}
else
{
button_array[button][timeframe] = tmp; // Record pulse width
TCCR1B = TCCR1B | 0x40; //
Set to trigger on rising edge
}
}
timeframe++; //
Update timeframe
//
Reset clock
TCNT1 = 0;
end
/* Timer 1 Compare Match ISR
* On this ISR we
start or stop Timer 2 to generate a 40KHz wave
*/
interrupt [TIM1_COMPA] void t1_compmatch(void)
begin
// Need
some more stuff here obviously, toggling the IRon variable, among other things
probably
// Still
need to set up the timer 1 stuff, change the TIMSK settings in the play
function
TCNT1 = 0;
if (IRon) {
IRon
= 0;
PORTD
= PORTD & 0b01111111;
//disable
the timer
TCCR2
= 0b00011000;
}
else
{
IRon
= 1;
//enable
the timer
TCCR2
= 0b00011001;
}
timeframe++; //
Update timeframe
OCR1A =
button_array[button][timeframe]; //
Update register to be called for the next value (width)
end