/*
*********************************************************************************************************
*                                     Simple Keypad driver for AT89S52
*
* Programmer 	: John Leung, TechToys. Hong Kong
* Web presence  : www.TechToys.com.hk
* Note			: 
* Language		: Keil C 7.xx 2K limited version under uVision2 IDE
* Hardware		: AT89S52-gLCD-STK1
* Date			: Version 0.0 (7th June 2006)
*********************************************************************************************************
*                                          USER DEFINED CONSTANTS
*
* Note: These #defines would normally reside in your application specific code.
*********************************************************************************************************
*                                             DESCRIPTION
*
*    The keypad is assumed to be a non-matrix simple keypad. Only single pin (or a few pins)is/are needed
*
*    xKEY_RPT_DLY             Number of scan times before auto repeat executes the function again
*    xKEY_RPT_START_DLY       Number of scan times before auto repeat function engages
*
*    xKEY_SCAN_TASK_DLY       The number of milliseconds between keyboard scans
*
*********************************************************************************************************
*/

/*
*********************************************************************************************************
* Date : 7th June 2006, ported to AT89S52 
*
* A new scan state 'xKEY_STATE_DOWN' and a global variable xKeyRptEn added
* Functions modified: xKeyInit(void), xKeyScanTask(void).
* One more scan-state in xKeyScanTask(void) as xKEY_STATE_DOWN adds the flexiblity of auto repeat
* enable/disable feature. 
* If xKeyRptEn==FALSE, which is the default condition set by xKeyInit(), no auto repeat engages. 
* On the other hand, if xKeyRptEn==TRUE, auto repeat engages after a scan delay of xKEY_RPT_START_DLY
* Please notice that xKEY_RPT_START_DLY increment by one scan-time because of one more scan-state.
*********************************************************************************************************
*/

/*
*********************************************************************************************************
* Date : 7th June 2006, ported to AT89S52
*
* Modification history : Add unsigned int  xKeyGetKeyDownTime(void)
*********************************************************************************************************
*/


/*$PAGE*/
/*
*********************************************************************************************************
*                                              INCLUDE FILES
*********************************************************************************************************
*/
#include <REGX52.h>
#define  xKEY_GLOBALS
#include "xKey.h"

/*
*********************************************************************************************************
*                                            LOCAL CONSTANTS
*********************************************************************************************************
*/

#define xKEY_STATE_UP                 	1
#define xKEY_STATE_DEBOUNCE           	2
#define xKEY_STATE_DOWN					3
#define xKEY_STATE_RPT_START_DLY      	4
#define xKEY_STATE_RPT_DLY            	5

/*
*********************************************************************************************************
*                                            LOCAL VARIABLES
*********************************************************************************************************
*/

static  unsigned char		xKeyBuf[xKEY_BUF_SIZE]; /* Keypad buffer										*/
static  unsigned char		xKeyBufInIx;			/* Index into key buf where next scan code inserted		*/
static  unsigned char		xKeyBufOutIx;			/* Index into key buf where next scan code removed		*/
static	unsigned int		xKeyDownTmr;			/* Counts how long a key has been presssed				*/
static  unsigned char		xKeyNRead;

static  unsigned char		xKeyRptStartDlyCtr;		/* Number of scan times before auto repeat is started 	*/
static  unsigned char		xKeyRptDlyCtr;			/* Number of scan times before auto repeat execute again*/

static  unsigned char   	xKeyScanState;          /* Current state of key pins			                */

/*
*********************************************************************************************************
*                                        LOCAL FUNCTION PROTOTYPES
*********************************************************************************************************
*/
static void	xKeyBufIn(unsigned char keyCode); 	/* Insert scan code into keypad buffer				*/
static bit	xKeyIsKeyDown(void);   				/* See if key has been pressed		 				*/

/*
*********************************************************************************************************
*                          			INSERT KEY CHARACTER INTO KEYPAD BUFFER
*
* Description : This function inserts a key character into the keypad buffer
* Arguments   : keyCode    is the key scan code to insert into the buffer
* Returns     : none
*********************************************************************************************************
*/

static  void  xKeyBufIn (unsigned char keyCode)
{
    if (xKeyNRead < xKEY_BUF_SIZE) {              /* Make sure that we don't overflow the buffer        */
        xKeyNRead++;                              /* Increment the number of keys read                  */
        xKeyBuf[xKeyBufInIx++] = keyCode;         /* Store the scan code into the buffer                */
        if (xKeyBufInIx >= xKEY_BUF_SIZE) {       /* Adjust index to the next scan code to put in buffer*/
            xKeyBufInIx = 0;
        }
	}
}

/*$PAGE*/
/*
*********************************************************************************************************
*                                                 GET KEY
*
* Description : Get a keypad scan code from the keypad buffer
* Arguments   : none
* Returns     : != 0xFF  is the key scan code of the key pressed
*               == 0xFF  indicates that there is no key in the buffer
*********************************************************************************************************
*/

unsigned char  xKeyGetKey (void)
{
    unsigned char keyCode;

    if (xKeyNRead > 0) {                        /* See if we have keys in the buffer                  */
        xKeyNRead--;                            /* Decrement the number of keys read                  */
        keyCode = xKeyBuf[xKeyBufOutIx];        /* Get scan code from the buffer                      */
        xKeyBufOutIx++;
        if (xKeyBufOutIx >= xKEY_BUF_SIZE) {    /* Adjust index into the keyboard buffer              */
            xKeyBufOutIx = 0;
        }
        return (keyCode);                       /* Return the scan code of the key pressed            */
    } else {
        return (0xFF);                          /* No scan codes in the buffer, return -1             */
    }
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                        GET HOW LONG KEY HAS BEEN PRESSED
*
* Description : This functions return the amount of time the key has been pressed
* Arguments   : none
* Returns     : key down time in millisec
*********************************************************************************************************
*/
unsigned int  xKeyGetKeyDownTime(void)
{
	unsigned int tmr;

	tmr = xKeyDownTmr;
	return (tmr*xKEY_SCAN_TASK_DLY);	
}

/*$PAGE*/
/*
*********************************************************************************************************
*                                        SEE IF ANY KEY IN BUFFER
*
* Description : This function checks to see if a key was pressed
* Arguments   : none
* Returns     : TRUE   if a key has been pressed
*               FALSE  if no key pressed
*********************************************************************************************************
*/

bit  xKeyHit (void)
{
	static bit hit;

   	hit =(bit)(xKeyNRead > 0) ? TRUE : FALSE;

	return (hit);
}


/*
*********************************************************************************************************
*                                           SEE IF KEY PRESSED
*
* Description : This function checks to see if a key is pressed
* Arguments   : none
* Returns     : TRUE   if a key is     pressed
*               FALSE  if a key is not pressed
* Note        :
*********************************************************************************************************
*/

static bit  xKeyIsKeyDown (void)
{
	if (xKeyCheckPins()){
		xKeyDownTmr++;
		return (TRUE);
	} else {
		return (FALSE);
	}
}

/*
*********************************************************************************************************
*                                         KEYPAD SCANNING TASK
*
* Description : This function contains the body of the keypad scanning task. 
*				The scanning period is determined by KEY_SCAN_TASK_DLY defined under xKey.h.
* Arguments   : none
* Returns     : xKeyScanTask() never returns.
* Remarks      : - An auto repeat of the key pressed will be executed after the key has been pressed for
*                 more than KEY_RPT_START_DLY scan times.  Once the auto repeat has started, the key will
*                 be repeated every KEY_RPT_DLY scan times as long as the key is pressed.  For example,
*                 if the scanning of the keyboard occurs every 50 mS and KEY_RPT_START_DLY is set to 40
*                 and KEY_RPT_DLY is set to 2, then the auto repeat function will engage after 2 seconds
*                 and will repeat every 100 mS (10 times per second).
*********************************************************************************************************
*/

void xKeyScanTask(void)
{
	unsigned char	keyCode;

	switch(xKeyScanState) {
		case xKEY_STATE_UP:
				if(xKeyIsKeyDown()){
					xKeyScanState = xKEY_STATE_DEBOUNCE;
					xKeyDownTmr = 0;
				}
			break;

		case xKEY_STATE_DEBOUNCE:
				if(xKeyIsKeyDown()){
					keyCode = xKeyDecode();
					xKeyBufIn(keyCode);
					xKeyScanState 		= xKEY_STATE_DOWN;
				} else {
					xKeyScanState	   	= xKEY_STATE_UP;
				}
			break;

		case xKEY_STATE_DOWN:
				if(xKeyIsKeyDown()){
					if (xKeyRptEn == TRUE){				   /* Start delay to auto-repeat function */
						xKeyRptStartDlyCtr 	= xKEY_RPT_START_DLY;	
                    	xKeyScanState      	= xKEY_STATE_RPT_START_DLY;
					}
				} else {
					xKeyScanState = xKEY_STATE_DEBOUNCE;
				}
			break;

		case xKEY_STATE_RPT_START_DLY:
				if(xKeyIsKeyDown()){
					if (xKeyRptStartDlyCtr > 0) {          /* See if we need to delay before auto rpt  */
                         xKeyRptStartDlyCtr--;             /* Yes, decrement counter to start of rpt   */
                         if (xKeyRptStartDlyCtr == 0) {    /* If delay to auto repeat is completed ... */
							 keyCode = xKeyDecode();
							 xKeyBufIn(keyCode);
                             xKeyRptDlyCtr = xKEY_RPT_DLY; /* Load delay before next repeat            */
                             xKeyScanState = xKEY_STATE_RPT_DLY;
                         }
                     }
				} else {
					xKeyScanState = xKEY_STATE_DEBOUNCE;   /* Key was not pressed after all            */
				}
			break;

		case xKEY_STATE_RPT_DLY:
				if (xKeyIsKeyDown()){
					if (xKeyRptDlyCtr > 0) {              /* See if we need to wait before repeat key */
                         xKeyRptDlyCtr--;                 /* Yes, dec. wait time to next key repeat   */
                         if (xKeyRptDlyCtr == 0) {        /* See if it's time to repeat key           */
							 keyCode = xKeyDecode();
							 xKeyBufIn(keyCode);
                             xKeyRptDlyCtr = xKEY_RPT_DLY;/* Reload delay counter before auto repeat  */
                         }
                     }
				} else {
					xKeyScanState = xKEY_STATE_DEBOUNCE;  /* Key was not pressed after all            */
				}
			break;
			}
}

/*
*********************************************************************************************************
*                                       KEYPAD INITIALIZATION
*
* Description: Keyboard initialization function.  xKeyInit() must be called before calling any other of
*              the user accessible functions.
* Arguments  : none
* Returns    : none
* Remarks	 : 
*********************************************************************************************************
*/

void  xKeyInit (void)
{   
    xKeyScanState = xKEY_STATE_UP;          /* Keypad should not have a key pressed					*/
	xKeyNRead	  = 0;
	xKeyDownTmr   = 0;
	xKeyBufInIx	  = 0;
	xKeyBufOutIx  = 0;
	xKeyRptEn	  = FALSE;
	xKeyInitPins();							/* Initialize I/O pins									*/
}

/*
*********************************************************************************************************
*                                  INITIALIZE I/O PINS (HARDWARE DEPENDENT)
*********************************************************************************************************
*/

void  xKeyInitPins (void)
{
	xKEY_DN = 1;	//set all pins to input
	xKEY_RT = 1;
	xKEY_LT	= 1;
	xKEY_UP = 1;
	xKEY_CTR= 1;
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                              READ PINS
*
* Description : This function is called to read the pins.
* Arguments   : none
* Returns     : Reading individual pins with internal/external pullup, zeros are keys pressed
*				TRUE 		if any key pressed
*				FALSE 		if no key pressed
* Remarks	  : 
*********************************************************************************************************
*/

bit xKeyCheckPins(void)
{
	static bit hit;

	hit = (bit)((xKEY_UP==0)||(xKEY_DN==0)||(xKEY_RT==0)||(xKEY_LT==0)||(xKEY_CTR==0)) ? TRUE: FALSE;
	return (hit);
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                             DECODE KEYPAD
*
* Description : This function is called to determine the key scan code of the key pressed.
* Arguments   : none
* Returns     : The key scan code (1...MAX. NUMBER OF KEYS DEFINED), else -1 if invalid key.
* Remarks	  : 
*********************************************************************************************************
*/

unsigned char  xKeyDecode (void)
{
	if (xKEY_UP == 0)
		return (xKEY_UP_FLAG);
	else if (xKEY_DN == 0)
		return (xKEY_DN_FLAG);
	else if (xKEY_RT == 0)
		return (xKEY_RT_FLAG);
	else if (xKEY_LT == 0)
		return (xKEY_LT_FLAG);
	else if (xKEY_CTR == 0)
		return (xKEY_CTR_FLAG);
	else
		return (0xFF);
}

