/* ********************************************************************************************************* * Embedded Systems Building Blocks * Complete and Ready-to-Use Modules in C * * Matrix Keyboard Driver * * (c) Copyright 1999, Jean J. Labrosse, Weston, FL * All Rights Reserved * * Filename : KEY.C * Programmer : Jean J. Labrosse ********************************************************************************************************* * DESCRIPTION * * The keyboard is assumed to be a matrix having 4 rows by 6 columns. However, this code works for any * matrix arrangements up to an 8 x 8 matrix. By using from one to three of the column inputs, the driver * can support "SHIFT" keys. These keys are: SHIFT1, SHIFT2 and SHIFT3. * * Your application software must declare (see KEY.H): * * KEY_BUF_SIZE Size of the KEYBOARD buffer * * KEY_MAX_ROWS The maximum number of rows on the keyboard * KEY_MAX_COLS The maximum number of columns on the keyboard * * KEY_RPT_DLY Number of scan times before auto repeat executes the function again * KEY_RPT_START_DLY Number of scan times before auto repeat function engages * * KEY_SCAN_TASK_DLY The number of milliseconds between keyboard scans * KEY_SCAN_TASK_PRIO Sets the priority of the keyboard scanning task * KEY_SCAN_TASK_STK_SIZE The size of the keyboard scanning task stack * * KEY_SHIFT1_MSK The mask which determines which column input handles the SHIFT1 key * (A 0x00 indicates that a SHIFT1 key is not present) * KEY_SHIFT1_OFFSET The scan code offset to add when the SHIFT1 key is pressed * * KEY_SHIFT2_MSK The mask which determines which column input handles the SHIFT2 key * (A 0x00 indicates that an SHIFT2 key is not present) * KEY_SHIFT2_OFFSET The scan code offset to add when the SHIFT2 key is pressed * * KEY_SHIFT3_MSK The mask which determines which column input handles the SHIFT3 key * (A 0x00 indicates that a SHIFT3 key is not present) * KEY_SHIFT3_OFFSET The scan code offset to add when the SHIFT3 key is pressed * * * KEY_PORT_ROW The port address of the keyboard matrix ROWs * KEY_PORT_COL The port address of the keyboard matrix COLUMNs * KEY_PORT_CW The port address of the keyboard I/O ports control word * * KeyInitPort, KeySelRow() and KeyGetCol() are the only three hardware specific functions. This has * been done to localize the interface to the hardware in only these two functions and thus make is * easier to adapt to your application. ********************************************************************************************************* */ /******************************************************************************************************************* *File Name : key.c *Description : key function define. base on Jean J. Labrosse's KEY.c file. *Date : 2008/06/13 *Author : yuanguiyou --History: ------------------------------------------------------------------------------------------------------------------ *change info: *******************************************************************************************************************/ /*$PAGE*/ /* ********************************************************************************************************* * INCLUDE FILES ********************************************************************************************************* */ #include "../includes.h" #include "keyPort.h" /* ********************************************************************************************************* * LOCAL CONSTANTS ********************************************************************************************************* */ #define KEY_STATE_UP 1 /* Key scanning states used in KeyScan() */ #define KEY_STATE_DEBOUNCE 2 #define KEY_STATE_RPT_START_DLY 3 #define KEY_STATE_RPT_DLY 4 /* ********************************************************************************************************* * GLOBAL VARIABLES ********************************************************************************************************* */ static INT8U KeyBuf[KEY_BUF_SIZE]; /* Keyboard buffer */ static INT8U KeyBufInIx; /* Index into key buf where next scan code will be inserted */ static INT8U KeyBufOutIx; /* Index into key buf where next scan code will be removed */ static INT16U KeyDownTmr; /* Counts how long key has been pressed */ static INT8U KeyNRead; /* Number of keys read from the keyboard */ static INT8U KeyRptStartDlyCtr; /* Number of scan times before auto repeat is started */ static INT8U KeyRptDlyCtr; /* Number of scan times before auto repeat executes again */ static INT8U KeyScanState; /* Current state of key scanning function */ static OS_STK KeyScanTaskStk[KEY_SCAN_TASK_STK_SIZE]; /* Keyboard scanning task stack */ static OS_EVENT *KeySemPtr; /* Pointer to keyboard semaphore */ /* ********************************************************************************************************* * LOCAL FUNCTION PROTOTYPES ********************************************************************************************************* */ static void KeyBufIn(INT8U code); /* Insert scan code into keyboard buffer */ static INT8U KeyDecode(void); /* Get scan code from current key pressed */ static BOOLEAN KeyIsKeyDown(void); /* See if key has been pressed */ static void KeyScanTask(void *data); /* Keyboard scanning task */ /*$PAGE*/ /* ********************************************************************************************************* * INSERT KEY CHARACTER INTO KEYBOARD BUFFER * * Description : This function inserts a key character into the keyboard buffer * Arguments : code is the keyboard scan code to insert into the buffer * Returns : none ********************************************************************************************************* */ static void KeyBufIn (INT8U code) { OS_ENTER_CRITICAL(); /* Start of critical section of code, disable ints */ if (KeyNRead < KEY_BUF_SIZE) { /* Make sure that we don't overflow the buffer */ KeyNRead++; /* Increment the number of keys read */ KeyBuf[KeyBufInIx++] = code; /* Store the scan code into the buffer */ if (KeyBufInIx >= KEY_BUF_SIZE) { /* Adjust index to the next scan code to put in buffer */ KeyBufInIx = 0; } OS_EXIT_CRITICAL(); /* End of critical section of code */ OSSemPost(KeySemPtr); /* Signal sem if scan code inserted in the buffer */ } else { /* Buffer is full, key scan code is lost */ OS_EXIT_CRITICAL(); /* End of critical section of code */ } } /*$PAGE*/ /* ********************************************************************************************************* * DECODE KEYBOARD * * Description : This function is called to determine the key scan code of the key pressed. * Arguments : none * Returns : the key scan code ********************************************************************************************************* */ static INT8U KeyDecode (void) { INT8U col; INT8U row; INT8U offset; BOOLEAN done; INT8U col_id; INT8U msk; done = FALSE; row = 0; while (row < KEY_MAX_ROWS && !done) { /* Find out in which row key was pressed */ KeySelRow(row); /* Select a row */ if (KeyIsKeyDown()) { /* See if key is pressed in this row */ done = TRUE; /* We are done finding the row */ } else { row++; /* Select next row */ } } col = KeyGetCol(); /* Read columns */ offset = 0; /* No SHIFT1, SHIFT2 or SHIFT3 key pressed*/ if (col & KEY_SHIFT1_MSK) { /* See if SHIFT1 key was also pressed */ offset += KEY_SHIFT1_OFFSET; } if (col & KEY_SHIFT2_MSK) { /* See if SHIFT2 key was also pressed */ offset += KEY_SHIFT2_OFFSET; } if (col & KEY_SHIFT3_MSK) { /* See if SHIFT3 key was also pressed */ offset += KEY_SHIFT3_OFFSET; } msk = 0x01; /* Set bit mask to scan for the column */ col_id = 0; /* Set column value (0..7) */ done = FALSE; while (col_id < KEY_MAX_COLS && !done) { /* Go through all columns */ if (col & msk) { /* See if key was pressed in this columns */ done = TRUE; /* Done, i has column value of the key (0..7)*/ } else { col_id++; msk <<= 1; } } return (row * KEY_MAX_COLS + offset + col_id); /* Return scan code */ } /*$PAGE*/ /* ********************************************************************************************************* * FLUSH KEYBOARD BUFFER * * Description : This function clears the keyboard buffer * Arguments : none * Returns : none ********************************************************************************************************* */ void KeyFlush (void) { while (KeyHit()) { /* While there are keys in the buffer... */ KeyGetKey(0); /* ... extract the next key from the buffer */ } } /*$PAGE*/ /* ********************************************************************************************************* * GET KEY * * Description : Get a keyboard scan code from the keyboard driver. * Arguments : 'to' is the amount of time KeyGetKey() will wait (in number of ticks) for a key to be * pressed. A timeout of '0' means that the caller is willing to wait forever for * a key to be pressed. * Returns : != 0xFF is the key scan code of the key pressed * == 0xFF indicates that there is no key in the buffer within the specified timeout ********************************************************************************************************* */ INT8U KeyGetKey (INT16U to) { INT8U code; INT8U err; OSSemPend(KeySemPtr, to, &err); /* Wait for a key to be pressed */ OS_ENTER_CRITICAL(); /* Start of critical section of code, disable ints*/ if (KeyNRead > 0) { /* See if we have keys in the buffer */ KeyNRead--; /* Decrement the number of keys read */ code = KeyBuf[KeyBufOutIx]; /* Get scan code from the buffer */ KeyBufOutIx++; if (KeyBufOutIx >= KEY_BUF_SIZE) { /* Adjust index into the keyboard buffer */ KeyBufOutIx = 0; } OS_EXIT_CRITICAL(); /* End of critical section of code */ return (code); /* Return the scan code of the key pressed */ } else { OS_EXIT_CRITICAL(); /* End of critical section of code */ return (0xFF); /* No scan codes in the buffer, return -1 */ } } /*$PAGE*/ /* ********************************************************************************************************* * GET HOW LONG KEY HAS BEEN PRESSED * * Description : This function returns the amount of time the key has been pressed. * Arguments : none * Returns : key down time in 'milliseconds' ********************************************************************************************************* */ INT32U KeyGetKeyDownTime (void) { INT16U tmr; OS_ENTER_CRITICAL(); tmr = KeyDownTmr; OS_EXIT_CRITICAL(); return (tmr * KEY_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 ********************************************************************************************************* */ BOOLEAN KeyHit (void) { BOOLEAN hit; OS_ENTER_CRITICAL(); hit = (BOOLEAN)(KeyNRead > 0) ? TRUE : FALSE; OS_EXIT_CRITICAL(); return (hit); } /* ********************************************************************************************************* * KEYBOARD INITIALIZATION * * Description: Keyboard initialization function. KeyInit() must be called before calling any other of * the user accessible functions. * Arguments : none * Returns : none ********************************************************************************************************* */ void KeyInit (void) { KeyScanState = KEY_STATE_UP; /* Keyboard should not have a key pressed */ KeyNRead = 0; /* Clear the number of keys read */ KeyDownTmr = 0; KeyBufInIx = 0; /* Key codes inserted at the beginning of the buffer */ KeyBufOutIx = 0; /* Key codes removed from the beginning of the buffer */ KeySemPtr = OSSemCreate(0); /* Initialize the keyboard semaphore */ KeyInitPort(); /* Initialize I/O ports used in keyboard driver */ KeySelRow(KEY_ALL_ROWS); /* Select all row */ OSTaskCreate(KeyScanTask, (void *)0, &KeyScanTaskStk[KEY_SCAN_TASK_STK_SIZE], KEY_SCAN_TASK_PRIO); } /*$PAGE*/ /* ********************************************************************************************************* * 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 : (1 << KEY_MAX_COLS) - 1 is used as a mask to isolate the column inputs (i.e. mask off * the SHIFT keys). ********************************************************************************************************* */ static BOOLEAN KeyIsKeyDown (void) { if (KeyGetCol() & ((1 << KEY_MAX_COLS) - 1)) { /* Key not pressed if 0 */ OS_ENTER_CRITICAL(); KeyDownTmr++; /* Update key down counter */ OS_EXIT_CRITICAL(); return (TRUE); } else { return (FALSE); } } /*$PAGE*/ /* ********************************************************************************************************* * KEYBOARD SCANNING TASK * * Description : This function contains the body of the keyboard scanning task. The task should be * assigned a low priority. The scanning period is determined by KEY_SCAN_TASK_DLY. * Arguments : 'data' is a pointer to data passed to task when task is created (NOT USED). * Returns : KeyScanTask() never returns. * Notes : - 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). ********************************************************************************************************* */ /*$PAGE*/ static void KeyScanTask (void *data) { INT8U code; data = data; /* Avoid compiler warning (uC/OS-II req.) */ for (;;) { OSTimeDlyHMSM(0, 0, 0, KEY_SCAN_TASK_DLY); /* Delay between keyboard scans */ switch (KeyScanState) { case KEY_STATE_UP: /* See if need to look for a key pressed */ KeyUpHook(); if (KeyIsKeyDown()) { /* See if key is pressed */ KeyScanState = KEY_STATE_DEBOUNCE; /* Next call we will have debounced the key */ KeyDownTmr = 0; /* Reset key down timer */ KeyDownHook(); } break; case KEY_STATE_DEBOUNCE: /* Key pressed, get scan code and buffer */ if (KeyIsKeyDown()) { /* See if key is pressed */ code = KeyDecode(); /* Determine the key scan code */ KeyBufIn(code); /* Input scan code in buffer */ KeyRptStartDlyCtr = KEY_RPT_START_DLY;/* Start delay to auto-repeat function */ KeyScanState = KEY_STATE_RPT_START_DLY; PORTD &= 0xEF; } else { KeySelRow(KEY_ALL_ROWS); /* Select all row */ KeyScanState = KEY_STATE_UP; /* Key was not pressed after all! */ } break; case KEY_STATE_RPT_START_DLY: if (KeyIsKeyDown()) { /* See if key is still pressed */ if (KeyRptStartDlyCtr > 0) { /* See if we need to delay before auto rpt */ KeyRptStartDlyCtr--; /* Yes, decrement counter to start of rpt */ if (KeyRptStartDlyCtr == 0) { /* If delay to auto repeat is completed ... */ code = KeyDecode(); /* Determine the key scan code */ KeyBufIn(code); /* Input scan code in buffer */ KeyRptDlyCtr = KEY_RPT_DLY; /* Load delay before next repeat */ KeyScanState = KEY_STATE_RPT_DLY; } } } else { KeyScanState = KEY_STATE_DEBOUNCE; /* Key was not pressed after all */ } break; case KEY_STATE_RPT_DLY: if (KeyIsKeyDown()) { /* See if key is still pressed */ if (KeyRptDlyCtr > 0) { /* See if we need to wait before repeat key */ KeyRptDlyCtr--; /* Yes, dec. wait time to next key repeat */ if (KeyRptDlyCtr == 0) { /* See if it's time to repeat key */ code = KeyDecode(); /* Determine the key scan code */ KeyBufIn(code); /* Input scan code in buffer */ KeyRptDlyCtr = KEY_RPT_DLY; /* Reload delay counter before auto repeat */ } } } else { KeyScanState = KEY_STATE_DEBOUNCE; /* Key was not pressed after all */ } break; } } }