Functions | Variables

keys.c File Reference

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

Detailed Description

Key Scan and key debounce machine implementation file.

General Key Scanning Algorithm:
The system will probe the key matrix using the controller internal pull-up resistors. This will avoid shorts and excessive current in case of multiple keys beeing pressed.
The drive end will activate the pull-up to drive the line high and sense the line state at the same time. The other end will drive active low.

The Key is recognized by a 16bit scan code. It is possible to recognize more than one key, pressed simultanously by únique scan codes.
The scan code is stored within a buffer. The Application can pick the inputs from the buffer using the hl_GetKeySc function.

The key scan will run in background, propelled from a timer interrupt.
Todo:
  • add definition of scan codes for multiple simultaneous keys
  • add key echo function

Function Documentation

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

Parameters:
[out] Key Pointer to store the key value
Returns:
returns number of keys in buffer or 0 if empty

                                  {

        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.

Parameters:
ScCode 16bit Scancode, returned from hlKeyScan()
Returns:
HL_NOKEY, HL_KEY_PRESSED or a valid key scan code

                                        {

        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

Returns:
KeyScanCode

                        {
        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

Todo:
At the moment only the system timer interrupt is enabled. It could take up to one second until the sys timer is running. The start time will depend on the current position of the timer2 counter and the Variable FastTimerInc. We will see later if this is ok, or if it behaves not fluid enough.

{               
        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

Todo:
At the moment only the system timer interrupt is enabled. It could take up to one second until the sys timer is running. The start time will depend on the current position of the timer2 counter and the Variable FastTimerInc. We will see later if this is ok, or if it behaves not fluid enough.

{               
        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

Note:
This is the Interrupt is the only interrupt to switch the system on again when in HL_PWR_ST_OFF
Todo:
At the moment only the system timer interrupt is enabled. It could take up to one second until the sys timer is running. The start time will depend on the current position of the timer2 counter and the Variable FastTimerInc. We will see later if this is ok, or if it behaves not fluid enough.

{               
        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

Todo:
At the moment only the system timer interrupt is enabled. It could take up to one second until the sys timer is running. The start time will depend on the current position of the timer2 counter and the Variable FastTimerInc. We will see later if this is ok, or if it behaves not fluid enough.

{               
        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;

}


Variable Documentation

volatile uint16_t KeyPwrManVar

Power Management callback function.

Do not allow sleep when key down, to make sure the fast system timer is running.