#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