Key Scan and key debounce machine implementation file. More...
#include "config.h"
#include <stdio.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "xpal_board.h"
#include "xpal_power.h"
#include "display.h"
#include "keys.h"
#include "xpal_async32khz.h"
Functions | |
void | hlMatrixInit (void) |
Matrix Hardware Init Function. | |
uint16_t | KeyPwrCallBack (void) |
ISR (INT0_vect) | |
Col0 Key ISR's. | |
ISR (INT1_vect) | |
Col1 Key ISR's. | |
ISR (INT2_vect) | |
Col2 Key ISR's. | |
ISR (INT3_vect) | |
Col3 Key ISR's. | |
void | hl_Key_Init (void) |
Key Engine Init Function. | |
uint16_t | hlKeyScan (void) |
Scan Key Matrix. | |
uint16_t | hlKeyDebounce (uint16_t ScCode) |
Key debounce state machine. | |
uint16_t | hlKeyTmTick (void) |
Timer call to drive the key scan. | |
uint8_t | hlGetKeySc (keyscan_t *Key) |
peek a key from the key buffer | |
void | hlAllKeyWakeup (void) |
init wakeup on all keys | |
void | hlRow3KeyWakeup (void) |
init wakeup on row 3 keys only | |
Variables | |
keyscan_t | LastScCode |
Scan Code on previous call. | |
keyscan_t | CurrentScCode |
Scan Code of the key currently down. | |
int8_t | ScanCnt |
counts how often the same key was scanned down in a consecutive order | |
keyscan_t | KeyScBuf [HL_KEY_BUFSZ] |
Key input buffer, Apps will peek inputs from there. | |
uint8_t | KeyScBufW |
KeyBuffer Write Count. | |
uint8_t | KeyScBufR |
KeyBuffer Read Count. | |
uint8_t | KeyScBufCC |
KeyBuffer Char Count. | |
hl_key_DebounceStates | DState |
Debounce State Variable. | |
volatile uint16_t | KeyPwrManVar |
Power Management callback function. | |
volatile keyscan_t | ksc |
Key Scan and key debounce machine implementation file.
void hl_Key_Init | ( | void | ) |
Key Engine Init Function.
Initialize key scan system.
Init has to be called once at program start
{ hlMatrixInit(); LastScCode = CurrentScCode = HL_NOKEY; ScanCnt = 0; KeyScBufW=0; KeyScBufR=0; KeyScBufCC=0; KeyPwrManVar=HL_PWR_ALLOW_IDLE; // IntCycleCnt = 0; DState = KEY_UP; hl_asyn_RegisterFastTimer( (cb_function_t)(&hlKeyTmTick), NULL); hl_RegisterPwrCb( (cb_function_t)(&KeyPwrCallBack), NULL); }
void hlAllKeyWakeup | ( | void | ) |
init wakeup on all keys
Function will activate interrupts for all 4 key rows
{ // Init key interrupts // Init Matrix to be ready for low level IRQ on port D hlMatrixInit(); // all int's low level sens EICRA = 0x00; // Clear pending interrupts EIFR |= (_BV(INTF0) | _BV(INTF1) | _BV(INTF2) | _BV(INTF3)); // Allow EIMSK |= (_BV(INT0) | _BV(INT1) | _BV(INT2) | _BV(INT3)); }
uint8_t hlGetKeySc | ( | keyscan_t * | Key | ) |
peek a key from the key buffer
Get input keys from the character buffer
[out] | Key | Pointer to store the key value |
{ if(KeyScBufCC > 0){ // There is something in the buffer *Key = KeyScBuf[KeyScBufR]; KeyScBufR++; if(KeyScBufR >= HL_KEY_BUFSZ){ KeyScBufR=0; } return KeyScBufCC--; // -------------------------------------> }else{ return HL_NOKEY; // -------------------------------------> } }
uint16_t hlKeyDebounce | ( | uint16_t | ScCode | ) |
Key debounce state machine.
Debounce and power key recognition The function will return each scan code only once, or NO_KEY or KEY_PRESSED as long as any key is down.
ScCode | 16bit Scancode, returned from hlKeyScan() |
{ uint8_t StateInputs = HL_NOKEY; uint16_t DebounceReturn = HL_NOKEY; // Determine State Machine input values if (ScCode != HL_NOKEY){ if (LastScCode == ScCode){ StateInputs |= HL_KEYSTATE_SAME; }else{ StateInputs |= HL_KEYSTATE_OTHER; LastScCode = ScCode; } if (CurrentScCode == ScCode){ // value required to debounce the key release StateInputs |= HL_KEYSTATE_PREVIOUS; } } switch (DState){ case KEY_UP : //------------------------------------------- if(ScCode == HL_NOKEY){ // if no key, stay here for a while before sleep is allowed ScanCnt++; if(ScanCnt > HL_KEY_STAY_AKTIVE){ ScanCnt = 0; DState = ALLOW_SLEEP; } }else{ // ScanCode != NOKEY ScanCnt = 0; DState = MOVING_DOWN; } DebounceReturn = HL_NOKEY; // debug !!! // printf("U"); break; case ALLOW_SLEEP :// this state is left when any key interrupt happened // allow all power saving modes KeyPwrManVar=HL_PWR_ALLOW_IDLE | HL_PWR_ALLOW_SLEEP | HL_PWR_ALLOW_33SLEEP | HL_PWR_ALLOW_OFF; ScanCnt = 0; if (ScCode != HL_NOKEY){ // if system did not go to sleep, we are still here without an interrupt ScanCnt = 0; DState = MOVING_DOWN; } DebounceReturn = HL_NOKEY; // debug !!! // printf("S"); break; case MOVING_DOWN : //------------------------------------------- if(StateInputs && HL_KEYSTATE_SAME){ // if same key, count debounce cycles ScanCnt++; if(ScanCnt > HL_KEY_DEBOUNCE_CYCLES){ ScanCnt = 0; DState = KEY_DOWN; } }else{ // Not same key, still bouncing ScanCnt = 0; DState = KEY_UP; } DebounceReturn = HL_NOKEY; // debug !!! // printf("m"); break; case KEY_DOWN : //------------------------------------------- // Stable key recognized, state to branch out for special functions DState = KEY_PROCESSED; DebounceReturn = ScCode; CurrentScCode = ScCode; if (ScCode == HL_SCAN_POWER){ // Power key scan code found DState = OFF_KEY_UP; ScanCnt = 0; } // debug !!! // printf("D"); break; case OFF_KEY_UP : //------------------------------------------- // wait until power key is released if(ScCode == HL_NOKEY){ // if no key, stay here for a while before sleep is allowed ScanCnt++; if(ScanCnt > HL_KEY_STAY_AKTIVE){ ScanCnt = 0; DState = PWR_OFF; } }else{ // ScanCode != NOKEY ScanCnt = 0; } DebounceReturn = HL_NOKEY; // debug !!! // printf("u"); break; case PWR_OFF : //------------------------------------------- KeyPwrManVar=HL_PWR_ALLOW_IDLE | HL_PWR_ALLOW_SLEEP | HL_PWR_ALLOW_33SLEEP | HL_PWR_ALLOW_OFF; hl_pwr_OffNow(); // Exit from this state by INT3 // debug !!! // printf("F"); break; case POWER_ON : //------------------------------------------- // Check if INT3 came from PWR key or an other key in COL3 if (ScCode == HL_SCAN_POWER){ // Power key scan code found DState = KEY_PROCESSED; DebounceReturn = HL_SCAN_POWER; }else{ // back to PWR_OFF DState = PWR_OFF; DebounceReturn = HL_NOKEY; } // debug !!! // printf("N"); break; case KEY_PROCESSED : //------------------------------------------- if(StateInputs & HL_KEYSTATE_SAME){ // do nothing, stay here and wait }else{ // key seems to move again DState = MOVING_UP; } hl_pwr_AppBusy(); DebounceReturn = HL_NOKEY; // debug !!! // printf("P"); break; case MOVING_UP : //------------------------------------------- // this state is implemented to debounce the key release if(StateInputs & HL_KEYSTATE_PREVIOUS){ // scan reported the key code again, go back DState = KEY_PROCESSED; }else{ // did not return to last key, go to key_up for next key DState = KEY_UP; } DebounceReturn = KEY_UP; // debug !!! // printf("M"); break; default: DState = KEY_UP; break; } return DebounceReturn; }
uint16_t hlKeyScan | ( | void | ) |
Scan Key Matrix.
Scan the key matrix and return the 16bit key scan code The Scan code is determined by: ROW_ID<< & COL_PIN & ROW_PIN
{ keyscan_t KeyScanCode = HL_NOKEY; hlMatrixInit(); /* Check if one of the COL lines is low */ if( (HL_KEY_COL_PIN & HL_KEY_COL_MASK) != HL_KEY_COL_MASK){ // there is something going on, no sleep allowed KeyPwrManVar=HL_PWR_ALLOW_IDLE; // Store COL in upper byte KeyScanCode = (((~HL_KEY_COL_PIN) & HL_KEY_COL_MASK)<<8); /* Switch ROW's to input and activate pull up. Drive high first to charge the ROW lines */ HL_KEY_ROW_PORT_A |= (HL_KEY_MASK_ROW_A); HL_KEY_ROW_PORT_B |= (HL_KEY_MASK_ROW_B); HL_KEY_ROW_PORT_C |= (HL_KEY_MASK_ROW_C); HL_KEY_ROW_DDR_A &= ~(HL_KEY_MASK_ROW_A); HL_KEY_ROW_DDR_B &= ~(HL_KEY_MASK_ROW_B); HL_KEY_ROW_DDR_C &= ~(HL_KEY_MASK_ROW_C); /* Turn the matrix drive low over to the COL lines*/ HL_KEY_COL_PORT &= ~(HL_KEY_COL_MASK); HL_KEY_COL_DDR |= (HL_KEY_COL_MASK); _delay_us(10); //settle lines /* Check ROW A */ if((HL_KEY_ROW_PIN_A & HL_KEY_MASK_ROW_A) != HL_KEY_MASK_ROW_A){ /* A key in section A is active */ KeyScanCode |= (_BV(HL_KEY_ROW_A_ID))<<8; KeyScanCode |= ((~HL_KEY_ROW_PIN_A) & HL_KEY_MASK_ROW_A); return KeyScanCode; /* ---------------------------> */ } /* Check ROW B */ if((HL_KEY_ROW_PIN_B & HL_KEY_MASK_ROW_B) != HL_KEY_MASK_ROW_B){ /* A key in section B is active */ KeyScanCode |= (_BV(HL_KEY_ROW_B_ID))<<8; KeyScanCode |= ((~HL_KEY_ROW_PIN_B) & HL_KEY_MASK_ROW_B); return KeyScanCode; /* ---------------------------> */ } /* Check ROW C */ if((HL_KEY_ROW_PIN_C & HL_KEY_MASK_ROW_C) != HL_KEY_MASK_ROW_C){ /* A key in section C is active */ KeyScanCode |= (_BV(HL_KEY_ROW_C_ID))<<8; KeyScanCode |= ((~HL_KEY_ROW_PIN_C) & HL_KEY_MASK_ROW_C); return KeyScanCode; /* ---------------------------> */ } /* Misdetection, COL but no ROW, probably bouncing */ return HL_NOKEY; /* ---------------------------> */ }else{ // When keys up, allow pwrManager to enter sleep or lower modes // KeyPwrManVar=HL_PWR_ALLOW_IDLE | HL_PWR_ALLOW_SLEEP | HL_PWR_ALLOW_OFF; /* All keys are up -------------------------------------> */ return KeyScanCode; } }
uint16_t hlKeyTmTick | ( | void | ) |
Timer call to drive the key scan.
Provide regular CPU time to the key scan engine.
Function to be called by a timer with the polling interval frequency
{ ksc = hlKeyScan(); // debug !!! // printf("\r\n"); ksc = hlKeyDebounce(ksc); if(ksc != HL_NOKEY){ // KeyBuffer will always contain the last 8 keys KeyScBuf[KeyScBufW]=ksc; KeyScBufW++; KeyScBufCC++; if(KeyScBufW >= HL_KEY_BUFSZ){ KeyScBufW=0; } if (KeyScBufCC > HL_KEY_BUFSZ){ KeyScBufCC--; // Buffer is full, forget the oldest entry KeyScBufR++; if(KeyScBufR >= HL_KEY_BUFSZ){ KeyScBufR=0; } } } return 0; }
void hlMatrixInit | ( | void | ) |
Matrix Hardware Init Function.
Init key Matrix ports in a way that an active key can be detected at the COL lines as low level. This setting can also trigger an interrupt if enabled.
{ // activate COL pull up resistors HL_KEY_COL_DDR &= ~(HL_KEY_COL_MASK); // in HL_KEY_COL_PORT |= (HL_KEY_COL_MASK); // pull // Set All Rows to OUT and Low HL_KEY_ROW_DDR_A |= (HL_KEY_MASK_ROW_A); // out HL_KEY_ROW_PORT_A &= ~(HL_KEY_MASK_ROW_A); HL_KEY_ROW_DDR_B |= (HL_KEY_MASK_ROW_B); HL_KEY_ROW_PORT_B &= ~(HL_KEY_MASK_ROW_B); HL_KEY_ROW_DDR_C |= (HL_KEY_MASK_ROW_C); HL_KEY_ROW_PORT_C &= ~(HL_KEY_MASK_ROW_C); }
void hlRow3KeyWakeup | ( | void | ) |
init wakeup on row 3 keys only
Function will activate the interrupt for key row 3 only. On the base board, the power key is the only one key in this row.
{ // Init key interrupts // Init Matrix to be ready for low level IRQ on port D hlMatrixInit(); // all int's low level sens EICRA = 0x00; // Clear pending interrupts EIFR |= _BV(INTF3); // Allow EIMSK |= _BV(INT3); }
ISR | ( | INT1_vect | ) |
Col1 Key ISR's.
Only to generate the wake-up and start the system timer if off
{ hl_pwr_SetWakeSource(HL_PWR_WAKE_BY_KEY); /* in case the timer is not running */ hl_asyn_StartSysTimer(); /* Now the system is up and running, we do not need any more key interrupts */ EIMSK &= ~(_BV(INT0) | _BV(INT1) | _BV(INT2) | _BV(INT3)); // IntCycleCnt = 0; // debug !!! printf("1"); // there is something going on, no sleep allowed KeyPwrManVar=HL_PWR_ALLOW_IDLE; // Set debounceMachine state DState = MOVING_DOWN; }
ISR | ( | INT2_vect | ) |
Col2 Key ISR's.
Only to generate the wake-up and start the system timer if off
{ hl_pwr_SetWakeSource(HL_PWR_WAKE_BY_KEY); /* in case the timer is not running */ hl_asyn_StartSysTimer(); /* Now the system is up and running, we do not need any more key interrupts */ EIMSK &= ~(_BV(INT0) | _BV(INT1) | _BV(INT2) | _BV(INT3)); // IntCycleCnt = 0; // debug !!! printf("2"); // there is something going on, no sleep allowed KeyPwrManVar=HL_PWR_ALLOW_IDLE; // Set debounceMachine state DState = MOVING_DOWN; }
ISR | ( | INT3_vect | ) |
Col3 Key ISR's.
Only to generate the wake-up and start the system timer if off
{ hl_pwr_SetWakeSource(HL_PWR_WAKE_BY_PWR_KEY | HL_PWR_WAKE_BY_KEY); /* in case the timer is not running */ hl_asyn_StartSysTimer(); /* Now the system is up and running, we do not need any more key interrupts */ EIMSK &= ~(_BV(INT0) | _BV(INT1) | _BV(INT2) | _BV(INT3)); // IntCycleCnt = HL_KEY_PWR_ON_SEQUENCE; // Keys at this IRQ cause a power on // debug !!! printf("3"); // there is something going on, no sleep allowed KeyPwrManVar=HL_PWR_ALLOW_IDLE; // Set debounceMachine state if (DState == PWR_OFF){ DState = POWER_ON; }else{ DState = MOVING_DOWN; } }
ISR | ( | INT0_vect | ) |
Col0 Key ISR's.
Only to generate the wake-up and start the system timer if off
{ hl_pwr_SetWakeSource(HL_PWR_WAKE_BY_KEY); /* in case the timer is not running */ hl_asyn_StartSysTimer(); /* Now the system is up and running, we do not need any more key interrupts */ EIMSK &= ~(_BV(INT0) | _BV(INT1) | _BV(INT2) | _BV(INT3)); // IntCycleCnt = 0; // debug !!! printf("0"); // there is something going on, no sleep allowed KeyPwrManVar=HL_PWR_ALLOW_IDLE; // Set debounceMachine state DState = MOVING_DOWN; }
volatile uint16_t KeyPwrManVar |
Power Management callback function.
Do not allow sleep when key down, to make sure the fast system timer is running.