/******************************* * Sumul Shah and Maudie Hampden * 14 April 2004 * ECE 476 Final Project * Enter combo configuration program at user's request *******************************/ /******************************* * Includes *******************************/ #include #include // for pow() #include // for rand() #include // for printing to Hyperterm /******************************* * Definitions *******************************/ // - combination mappings #define num_mappings 92 #include // - signal names #define data PORTA.1 #define clock PORTA.0 #define in_clock PINA.0 #define in_data PINA.1 // - combo keys #define combocount 8 #define comboport PINB // - meta keys #define metacount 4 #define metaport PINC // - keyboard LEDs #define LED_ON 0 #define LED_OFF 1 #define LED_CONFIG PORTA.3 #define LED_SCROLL PORTA.4 #define LED_CAPS PORTA.5 #define LED_NUM PORTA.6 #define LED_HEART PORTA.7 // - configuration pushbuttons #define configcount 5 #define configport PIND #define config_startstop config_buttondown[4] #define config_back config_buttondown[3] #define config_next config_buttondown[2] #define config_undo config_buttondown[1] // - number of keyboard characters represented by device #define numcharacters 5 // - debounce states #define nopush 0 #define maybepush 1 #define pushed 2 #define maybenopush 3 // - button names #define press buttondown[0] #define unpress buttonup[0] // - concurrency window in milliseconds #define concurrency 50 /******************************* * Variables *******************************/ // - timer for scancode transmissions unsigned char bittimer; unsigned char timer0; // - combo states and flags unsigned char combo_pushstate[combocount]; // debounce states unsigned char combo_pushflag[combocount]; // pushflag[i] = 1 if button i is pressed unsigned char combo_buttondown[combocount]; // buttondown[i] = 1 for one tick if button i is pressed (edge triggered) unsigned char combo_buttonup[combocount]; // buttonup[i] = 1 for one tick if button i is let go (edge triggered) // - meta states and flags unsigned char meta_pushstate[metacount]; // debounce states unsigned char meta_pushflag[metacount]; // pushflag[i] = 1 if button i is pressed unsigned char meta_buttondown[metacount]; // buttondown[i] = 1 for one tick if button i is pressed (edge triggered) unsigned char meta_buttonup[metacount]; // buttonup[i] = 1 for one tick if button i is let go (edge triggered) // - config states and flags unsigned char config_pushstate[configcount]; // debounce states unsigned char config_pushflag[configcount]; // pushflag[i] = 1 if button i is pressed unsigned char config_buttondown[configcount]; // buttondown[i] = 1 for one tick if button i is pressed (edge triggered) unsigned char config_buttonup[configcount]; // buttonup[i] = 1 for one tick if button i is let go (edge triggered) unsigned char currently_configuring; // array number of character currently being configured // - integers that represent keypress combinations unsigned char currentkeycombo, keycombo, lastkeycombo; // - concurrency window variables unsigned char concurrencytimer; // counts down until concurrency window closes // - typematic variables unsigned int tmdelay; // typematic delay in milliseconds unsigned int tmdelaycounter; unsigned char tmrepeat; // high when repeating unsigned int tmrepeatcounter; unsigned int tmrepeatticks; // number of 1 ms ticks per typematic repeated character // - flag to debounce buttons; set to 1 every 30 milliseconds unsigned char checkbuttons; // - check for host requests unsigned char checkhost; unsigned char clocklowcounter; unsigned char lasthostrequest; // - millisecond timer unsigned int mstimer; // - last tranmitted byte unsigned char lastbyte; // - disable transmission of scancodes unsigned char disable; // - flags for configuration program unsigned char goconfig; // enter configuration program unsigned char goback; unsigned char gonext; unsigned char undochange; unsigned char newcombo, lastdeletedcombo, newmeta, lastdeletedmeta; unsigned int heartbeat; // for throbber LED /******************************* * Functions Declarations *******************************/ // - Initialization routines void initialize(void); // - Debounce combination keys void debounce_combo(void); // - Debounce meta keys void debounce_meta(void); // - Debounce configuration keys void debounce_config(void); // - Debounce combo keys without sending scancodes (for configuration purposes) void debounce_combo_config(void); // - Send make code void makecode(unsigned char); // - Send break code void breakcode(unsigned char); // - Send meta make code void make_meta(unsigned char); // - Send meta break code void break_meta(unsigned char); // - Transmit scan code to host void send_scancode(unsigned char); // - Returns 1 if number is odd // - Returns 0 if number is even unsigned char isodd(unsigned char); // - Returns 1 if number has even number of 1's // - Returns 0 if number has odd number of 1's unsigned char parity(unsigned char); // - translates a key combination to a keyboard scan code unsigned char decode(unsigned char); // - returns 1 if the combo maps to an extended scan code // - returns 0 if the combo does not map to an extended scan code unsigned char is_extended(unsigned char); // - returns 1 if the meta key maps to an extended scan code // - returns 0 if the meta key does not map to an extended scan code unsigned char is_extended_meta(unsigned char); // - translate a meta key to a keyboard scan code unsigned char decode_meta(unsigned char); // - allow host to send a request when clock is held down for 100 us void check_host(void); // - parse and react to host request void interpret_request(unsigned char); // - perform basic assurance test (BAT) void do_BAT(void); // - configure button mappings void config(void); // - print configuration status message void print_config_status(void); // - training program void train(void); // - print training status message void print_train_status(void); // - memory test void memory_test(void); // - print memory test screen void print_memory_status(void); // - character practice void character_practice(void); // - print character practice screen void print_practice_status(void); /******************************* * Functions Definitions *******************************/ /*------------------------------* | timer0 interrupt every 20 us *------------------------------*/ interrupt [TIM0_COMP] void timer() { // increment bittimer every 40 us if(timer0 == 2) { bittimer++; timer0 = 0; } timer0++; if(DDRA.0 == 0) { // if host has control of clock if(in_clock == 0) { // if host has set clock low // check if host keeps clock low for 100 us clocklowcounter++; if(clocklowcounter == 6) { checkhost = 1; } } else { clocklowcounter = 0; } } } /*------------------------------* | timer1 interrupt every 1 millisecond *------------------------------*/ interrupt [TIM1_COMPA] void debouncetimer(void) { checkbuttons = 1; if(concurrencytimer > 0) { concurrencytimer--; } if(tmdelaycounter > 0) { tmdelaycounter--; if(tmdelaycounter == 0) { tmrepeat = 1; } } if(tmrepeatcounter > 0) { tmrepeatcounter--; } if(mstimer > 0) { mstimer--; } heartbeat++; if(heartbeat == 1000) { heartbeat = 0; LED_HEART = LED_ON; } if(LED_HEART == LED_ON && heartbeat == 100) { LED_HEART = LED_OFF; } } /*------------------------------* | Main *------------------------------*/ main() { initialize(); while(1) { if(checkbuttons == 1) { checkbuttons = 0; debounce_meta(); debounce_combo(); debounce_config(); } if(checkhost == 1) { checkhost = 0; check_host(); } if(goconfig == 1) { // goconfig == 1 if configuration button pressed currently_configuring = 0; goconfig = 0; config(); } } } /*------------------------------* | Initialize *------------------------------*/ void initialize() { // initialize timer 1 interrupt for button debouncing TIMSK = 0b00010000; // turn on timer 1 interrupt TCCR1B = 0b00001011; // prescalar to 64 and turn on clear-on-match OCR1AH = 0x00; // set interrupt to occur every 1.0 milliseconds OCR1AL = 0xfa; // (compare match = 250) // set up timer0 to interrupt every 20 us TIMSK |= 0b00000010; // enable timer0 compare match interrupt TCCR0 = 0b00001011; // prescalar to clock/64 OCR0 = 5; // 20 us // set up PORTA.0 and PORTA.1 as an input with pull-ups (for data and clock) // set up rest of PORTA as outputs for LEDs DDRA = 0b11111000; PORTA = 0b11111011; // set up PORTB as an input (for combo keys) DDRB = 0x00; // set up PORTC as an input (for meta keys) DDRC = 0x00; // set up most significant 5 bits of PORTD for pushbutton input DDRD &= 0b00000011; // set up UART UCSRB = 0x98; // transmit and receive, and receive interrupt enable UBRRL = 103; // 9600 baud using 16 MHz crystal // initialize variables tmdelay = 500; // default to 500 ms typematic delay tmrepeatticks = 92; // default to 10.9 cps typematic repeat rate (1000/10.9 = 92) // enable interrupts #asm sei #endasm do_BAT(); } /*------------------------------* | Debounce Combo *------------------------------*/ void debounce_combo(void) { unsigned char j; for(j=0; j 0) { // still waiting for the current combination to settle keycombo = currentkeycombo; } else { // if there is a change in the combo if(currentkeycombo != keycombo) { concurrencytimer = concurrency; // open the concurrency window lastkeycombo = keycombo; // save the old combo keycombo = 0; // reset the new combo if(lastkeycombo != 0) { breakcode(lastkeycombo); // send the break code for the old combo } tmdelaycounter = 0; // stop typematic behavior tmrepeat = 0; } // send the new make code if(keycombo != 0) { if(tmdelaycounter == 0 && !tmrepeat) { makecode(keycombo); tmdelaycounter = tmdelay; } if(tmrepeat) { if(tmrepeatcounter == 0) { makecode(keycombo); tmrepeatcounter = tmrepeatticks; } } } } } /*------------------------------* | Debounce Meta Keys *------------------------------*/ void debounce_meta(void) { unsigned char j; for(j=0; j= 2 && bittimer <= 16) { // extract the appropriate bit in the sequence data = (scancode >> ((bittimer/2)-1)) & 1; } // parity bit comes next else if(bittimer == 18) { data = parity(scancode); } // stop bit is last else if(bittimer == 20) { data = 1; // stop bit is 1 } } } // end while // make sure clock and data are both high clock = 1; data = 1; // set up PORTA as an input for clock and data DDRA.0 = 0; DDRA.1 = 0; PORTA.0 = 1; PORTA.1 = 1; // save this scancode as the last transmitted byte (unless it is 0xFE: resend) if(scancode != 0xfe) { lastbyte = scancode; } } // end if(!disable) } // end send_scancode() /*------------------------------* | Isodd *------------------------------*/ unsigned char isodd(unsigned char n) { return (n%2); } /*------------------------------* | Parity *------------------------------*/ unsigned char parity(unsigned char n) { return !isodd( ((n >> 0) & 1) +((n >> 1) & 1) +((n >> 2) & 1) +((n >> 3) & 1) +((n >> 4) & 1) +((n >> 5) & 1) +((n >> 6) & 1) +((n >> 7) & 1) ); } /*------------------------------* | Decode *------------------------------*/ unsigned char decode(unsigned char combo) { unsigned char i; // counter // search through mappings for the requested combo // return the make code associated with that combo for(i=0; i= 2 && bittimer <= 16) { // record the appropriate bit in the sequence hostrequest |= (in_data << ((bittimer/2)-1)); } // stop bit // ack bit is last (set by device) } } // end while // make sure clock and data are 1 clock = 1; data = 1; // set data and clock as inputs with pullups DDRA.0 = 0; DDRA.1 = 0; PORTA.0 = 1; PORTA.1 = 1; // wait 240 us before sending any response bittimer = 0; while(bittimer < 6); // parse the host request and perform any necessary actions interpret_request(hostrequest); // remember the host request lasthostrequest = hostrequest; } /*------------------------------* | Interpret Host Request *------------------------------*/ void interpret_request(unsigned char request) { // send acknowledge code send_scancode(0xfa); // Reset Keyboard if(request == 0xff) { // set typematic delay and repeat rate to default values tmdelay = 500; // 500 ms for delay tmrepeatticks = 92; // 10.9 cps for repeat do_BAT(); // basic assurance test return; } // Resend Last Sent Byte if(request == 0xfe) { // wait 240 us before sending any response bittimer = 0; while(bittimer < 6); send_scancode(lastbyte); return; } // these command codes only affect scan code set 3 if(0xf7 <= request && request <= 0xfd) { return; } // Revert to Defaults if(request == 0xf6) { // set typematic delay and repeat rate to default values tmdelay = 500; // 500 ms for delay tmrepeatticks = 92; // 10.9 cps for repeat // all LEDs turn off LED_SCROLL = LED_NUM = LED_CAPS = LED_OFF; return; } // Disable and Revert to Defaults if(request == 0xf5) { disable = 1; // disable make codes // set typematic delay and repeat rate to default values tmdelay = 500; // 500 ms for delay tmrepeatticks = 92; // 10.9 cps for repeat // all LEDs turn off LED_SCROLL = LED_NUM = LED_CAPS = LED_OFF; return; } // Enable if(request == 0xf4) { disable = 0; // enable return; } // Set Typematic Delay and Typematic Repeat Rate if(request == 0xf3) { return; } if(lasthostrequest == 0xf3) { // if the last request was the set typematic command // set the typematic delay according to bits 5 and 6: // 6,5 rate (ms) // --------------- // 0b00 250 // 0b01 500 // 0b10 750 // 0b11 1000 tmdelay = 250 + ((unsigned int)((request >> 5) & 0b11)*(unsigned int)(250)); // set the typematic repeat rate according to bits 0 - 4: // (2^B)*(D+8)/240 seconds between repeated characters, where B gives Bits 4-3 and D gives Bits 2-0). tmrepeatticks = (unsigned int)(1000.0*((pow(2.0, ((float)((request >> 3) & 0b11)))*( ((float)(request & 0b111)) + 8.0)) / 240.0)); return; } // Device ID if(request == 0xf2) { // device ID is 0xab 0x83 // wait 240 us before sending any response bittimer = 0; while(bittimer < 6); send_scancode(0xab); // wait 240 us before sending any response bittimer = 0; while(bittimer < 6); send_scancode(0x83); return; } // Set Scan Code Set (only supports getting scan code set) if(lasthostrequest == 0xf0 && request == 0x00) { // wait 240 us before sending any response bittimer = 0; while(bittimer < 6); send_scancode(0x02); // scan code set 2 return; } // Echo if(request == 0xee) { // wait 240 us before sending any response bittimer = 0; while(bittimer < 6); send_scancode(0xee); // echo return; } // Set/Clear Keyboard LEDs if(lasthostrequest == 0xed) { // if the last request was the set/clear LEDs command if(request >> 3 == 0) { // if this request contains the LED argument LED_SCROLL = (request & 1) ? LED_ON : LED_OFF; // check Scroll Lock bit LED_NUM = (request >> 1 & 1) ? LED_ON : LED_OFF; // check Num Lock bit LED_CAPS = (request >> 2 & 1) ? LED_ON : LED_OFF; // check Caps Lock bit } return; } } /*------------------------------* | Basic Assurance Test (BAT) *------------------------------*/ void do_BAT(void) { // all LEDs turn on LED_SCROLL = LED_ON; LED_NUM = LED_ON; LED_CAPS = LED_ON; // wait 500 ms mstimer = 500; while(mstimer > 0); // send 0xaa (OK) send_scancode(0xaa); // all LEDs turn off LED_SCROLL = LED_OFF; LED_NUM = LED_OFF; LED_CAPS = LED_OFF; } /*------------------------------* | Configuration Button Combos *------------------------------*/ void config(void) { LED_CONFIG = LED_ON; // turn on configuration indicator LED on board mstimer = 30; // use mstimer to debounce configuration buttons // every 30 us newcombo = 0; // set newcombo to prevent re-configuration of // currently_configuring button // make sure no buttons are pressed when entering into config_debounce() config_startstop = 0; goback = 0; gonext = 0; undochange = 0; while(1) { // send initial configuration program screen to HyperTerminal window print_config_status(); while(1) { // checkbuttons == 1 every 1 us if(checkbuttons == 1) { checkbuttons = 0; debounce_combo_config(); } // if user presses a new combination and currently_configuring // is in the non-meta characters, configure current button // to entered configuration if(newcombo != 0 && (currently_configuring < num_mappings)) { lastdeletedcombo = mappings[currently_configuring].combo; mappings[currently_configuring].combo = newcombo; newcombo = 0; print_config_status(); } // if user presses a new key and currently_configuring // is in the meta characters, configure current button // to entered key if(newmeta != 0 && (currently_configuring >= num_mappings)) { lastdeletedmeta = meta_mappings[currently_configuring-num_mappings].key; meta_mappings[currently_configuring-num_mappings].key = newmeta; newmeta = 0; print_config_status(); } // decrement currently_configuring if 'Back' is pressed if(goback) { goback = 0; // don't save last deleted configurations once user // moves to a new character lastdeletedcombo = 0; lastdeletedmeta = 0; if(currently_configuring > 0) currently_configuring--; else currently_configuring = num_mappings+metacount-1; break; } // increment currently_configuring if 'Next' is pressed if(gonext) { gonext = 0; // don't save last deleted configurations once user // moves to a new character lastdeletedcombo = 0; lastdeletedmeta = 0; if(currently_configuring < num_mappings+metacount-1) currently_configuring++; else currently_configuring=0; break; } // revert button setting to last saved configuration if(undochange) { undochange = 0; if(currently_configuring < num_mappings) { if(lastdeletedcombo != 0) { mappings[currently_configuring].combo = lastdeletedcombo; lastdeletedcombo = 0; } } else if(currently_configuring >= num_mappings) { if(lastdeletedmeta != 0) { meta_mappings[currently_configuring-num_mappings].key = lastdeletedmeta; lastdeletedmeta = 0; } } break; } // exit configuration program and enter training program if(config_startstop) { config_startstop = 0; goconfig = 0; train(); printf("\f"); LED_CONFIG = LED_OFF; return; } // debounce configuration buttons every 30 us if(mstimer == 0) { debounce_config(); mstimer = 30; } } // end while } // end while } /*------------------------------* | Debounce Configuration Keys *------------------------------*/ void debounce_config(void) { unsigned char j; for(j=0; j> (7-i)) & 1 ? " X" : " -" ); if(i == 3) { printf(" - - | - - "); } } for(i=0; i= num_mappings) { printf("+---------------------------------------------------------------------+\r\n"); printf("| CONFIGURATION INSTRUCTIONS |\r\n"); printf("+---------------------------------------------------------------------+\r\n"); printf("| 'Back' configures previous character. |\r\n"); printf("| 'Next' configures next character. |\r\n"); printf("| 'Undo' reverts to last entered configuration for current meta key. |\r\n"); printf("| 'Config' enters training program for current mapping. |\r\n"); printf("| |\r\n"); printf("| Pressing thumb buttons configures current meta key. |\r\n"); printf("+---------------------------------------------------------------------+\r\n"); printf("\r\n"); printf("Currently configuring meta key: "); puts(default_meta_mappings[currently_configuring-num_mappings].name); printf("\r\n"); printf("\r\n"); printf("The current mapping for this meta key is:"); printf("\r\n"); printf("\r\n"); // simple routine that outputs formatted representation of finger mappings corresponding // to currently_configuring meta key printf("- - - - - "); for(i=0; i<4; i++) { printf( (meta_mappings[currently_configuring-num_mappings].key >> (3-i)) & 1 ? " X" : " -" ); if(i == 1) { printf(" | "); } } printf(" - - - - -"); // if configuration for current button overlaps with the configuration for another character, // display warning for(i=0; i= num_mappings) return; } /*------------------------------* | Debounce Combos for Config Program *------------------------------*/ // program debounces meta and combo keys as performed when typing normally, but // does not call for any scan codes to be written to the host; this allows // the user to select configurations in the configuration program by pressing // buttons on the handset without typing characters to the host void debounce_combo_config(void) { unsigned char j; for(j=0; j 0) { // still waiting for the current combination to settle keycombo = currentkeycombo; } else { // if there is a change in the combo if(currentkeycombo != keycombo) { concurrencytimer = concurrency; // open the concurrency window lastkeycombo = keycombo; // save the old combo newcombo = lastkeycombo; // new configuration combo set keycombo = 0; // reset the new combo } } // end else } /*------------------------------* | Training Program *------------------------------*/ void train(void) { mstimer = 30; // program monitors configuration button keypad, and if // a valid button is pushed, the specified action is // performed while(1) { print_train_status(); while(1) { if(checkbuttons == 1) { checkbuttons = 0; debounce_combo_config(); } // enter memory test training program if 'Back' button pushed if(goback) { goback = 0; memory_test(); config_startstop = 0; break; } // enter character practice training program if 'Next' button pushed if(gonext) { gonext = 0; character_practice(); config_startstop = 0; break; } // exit configuration and training programs and return to normal // input mode if 'Config' button pressed if(config_startstop) { goconfig = 0; return; } // debouce config keypad every 30 ms if(mstimer == 0) { debounce_config(); mstimer = 30; } } // end while } // end while } /*------------------------------* | Print Training Status *------------------------------*/ // function prints a simple set of instructions for the training program // to the HyperTerminal window void print_train_status(void) { printf("\f"); printf("\f"); printf("+---------------------------------------------------------------------+\r\n"); printf("| TRAINING INSTRUCTIONS |\r\n"); printf("+---------------------------------------------------------------------+\r\n"); printf("| 'Back' tests your memory of characters. |\r\n"); printf("| 'Next' lets you practice characters as they are shown to you. |\r\n"); printf("| 'Config' exits the training program and returns to input mode. |\r\n"); printf("+---------------------------------------------------------------------+\r\n"); return; } /*------------------------------* | Memory test routine *------------------------------*/ // prints a random keyboard character to the screen and advances to // next character when the user presses the correct combination // for the displayed character void memory_test(void) { mstimer = 30; // set up random number generator srand(TCNT1L); while(1) { print_memory_status(); while(1) { if(checkbuttons == 1) { checkbuttons = 0; debounce_combo_config(); } if(config_startstop) { goconfig = 0; return; } if(newcombo == mappings[currently_configuring].combo) { break; } if(mstimer == 0) { debounce_config(); mstimer = 30; } } // end while } // end while } /*------------------------------* | Print Memory Status *------------------------------*/ void print_memory_status(void) { currently_configuring = (unsigned char)(rand() % (num_mappings + 1)); printf("\f"); printf("+---------------------------------------------------------------------+\r\n"); printf("| MEMORY PRACTICE GAME |\r\n"); printf("+---------------------------------------------------------------------+\r\n"); printf("| 'Config' returns to the training program main screen. |\r\n"); printf("+---------------------------------------------------------------------+\r\n"); printf("\r\n"); printf("Press the combination for the following character: "); printf("\r\n\r\n\r\n\r\n\r\n"); printf(" ---------> "); puts(default_mappings[currently_configuring].name); return; } /*------------------------------* | Character practice routine *------------------------------*/ // prints a random keyboard character to the screen and advances to // next character when the user presses the correct combination // for the displayed character; routine displays the correct // combination to help the user learn the current configuration void character_practice(void) { mstimer = 30; // set up random number generator srand(TCNT1L); while(1) { print_practice_status(); while(1) { if(checkbuttons == 1) { checkbuttons = 0; debounce_combo_config(); } if(config_startstop) { goconfig = 0; return; } if(newcombo == mappings[currently_configuring].combo) { break; } if(mstimer == 0) { debounce_config(); mstimer = 30; } } // end while } // end while } /*------------------------------* | Print Practice Status *------------------------------*/ void print_practice_status(void) { unsigned char i; // counter currently_configuring = (unsigned char)(rand() % (num_mappings + 1)); printf("\f"); printf("+---------------------------------------------------------------------+\r\n"); printf("| CHARACTER PRACTICE GAME |\r\n"); printf("+---------------------------------------------------------------------+\r\n"); printf("| 'Config' returns to the training program main screen. |\r\n"); printf("+---------------------------------------------------------------------+\r\n"); printf("\r\n"); printf("Press the combination for the following character: "); printf("\r\n\r\n\r\n\r\n\r\n"); printf(" ---------> "); puts(default_mappings[currently_configuring].name); printf("\r"); printf("\r\n"); printf("\r\n"); printf("\r\n"); printf("\r\n"); printf("\r\n"); printf("\r\n"); printf("\r\n"); printf("\r\n"); printf("\r\n"); printf("The mapping for this character is:"); printf("\r\n"); printf("\r\n"); for(i=0; i<8; i++) { printf( (mappings[currently_configuring].combo >> (7-i)) & 1 ? " X" : " -" ); if(i == 3) { printf(" - - | - - "); } } return; }