📄 ps2_kb.c
字号:
// Implementation of a PS/2 keyboard driver
#include "sfr62p.h"#include "kb.h"#include "machine.h"#include "skp_bsp.h"#include "misc.h"#include <ctype.h>// Scan code lookup table is in "scancodes.h"#include "scancodes.h"// Local definitions#define PS2_BUFFER_SIZE 8 // buffer for deoded keyboard keys
// Various flags used for control#define PS2_CTRL_FLAG 0x01 // mask for "ctrl down"#define PS2_SHIFT_FLAG 0x02 // mask for "shift down"#define PS2_CAPS_FLAG 0x04 // mask for "caps lock down"#define PS2_BREAK_FLAG 0x08 // mask for "break code was received"#define PS2_ARROW_FLAG 0x10 // mark for "first code for an arrow key was received"#define PS2_RAW_FLAG 0x20 // "raw" mode: the keys are not decoded, rather saved in the buffer as they are (for internal purposes)#define PS2_READY_FLAG 0x40 // 'data ready' indication in RAW mode
// Keyboard specific values#define PS2_BREAK_CODE 0xF0 // break code#define PS2_IGN7_CODE 0xE1 // when this code is received, the next 7 codes will be ignored#define PS2_CTRL_CODE 0x14 // code for left ctrl key (only the left one is used)#define PS2_SHIFT_CODE 0x12 // code for left shift key (only the left one is used)#define PS2_CAPS_CODE 0x58 // code for "caps lock"
#define PS2_ARROW_CODE 0xE0 // first scan code for arrow keys#define PS2_LEFT_CODE 0x6B // code for left key#define PS2_RIGHT_CODE 0x74 // code for right key#define PS2_UP_CODE 0x75 // code for up key#define PS2_DOWN_CODE 0x72 // code for down key
// PS2 keyboard commands#define PS2_CMD_RESET 0xFF // software reset#define PS2_CMD_LIGHTS 0xED // set kb lights#define PS2_CMD_SET_RATE 0xF3 // set typematic rate/delay#define PS2_CAPS_VALUE 0x04 // value for "caps lock" in previous command// Declare sections for program/const data//#pragma SECTION program remove_1_program//#pragma SECTION rom remove_1_rom// Lookup table for CTRL+char commandstatic const u8 ps2_ctrl_lookup[ ] = { 'c', KB_INTERRUPT, 'u', KB_PGUP, 'v', KB_PGDOWN, 'a', KB_HOME, 'e', KB_END, 'y', KB_CLRLINE, 'q', KB_CMDMODE, 'f', KB_FIND, 's', KB_SAVE, 'g', KB_GOTO};static u8 ps2_buf[ PS2_BUFFER_SIZE ]; // data bufferstatic u8 ps2_write_index, ps2_read_index, ps2_read_size; // buffer control variablesstatic u8 ps2_bits, ps2_data, ps2_flags, ps2_ignore, ps2_raw_data; // other control variables// *****************************************************************************// Internal functions// Waits for kbd clock to get high and then lowstatic void ps2_wait_highlow( void ){ while( PS2_CLOCK_LINE == 0 ); // loop until clock high while( PS2_CLOCK_LINE == 1 ); // loop until clock low }// Send a command to the PS2 keyboard// Input: ncmd - number of command bytes// Input: nres - number of expected responses to 'command'// Innput: pres - pointer to input and output data// Return: 1 - OK, 0 - errorstatic u8 ps2_send_command( u8 ncmd, u8 nres, u8 *pd ){ u8 i, par, cmd, k; int0ic = 0x00; // temporary disable the keyboard interrupt
// Now send the command(s) for( k = 0; k < ncmd; k ++ ) { PS2_CLOCK_DIR = 1; // set clock low delay( 64 ); PS2_DATA_DIR = 1; // set data low PS2_CLOCK_DIR = 0; // release clock ps2_wait_highlow(); cmd = pd[ k ]; for( i = par = 0; i < 8; i ++, cmd >>= 1 ) { PS2_DATA_DIR = ~( cmd & 1 ); // write data if( cmd & 1 ) par ++; ps2_wait_highlow(); }
// Send parity now PS2_DATA_DIR = par & 1 ; par = 0xFF; // will be used later as an error indicator ps2_wait_highlow();
// Release data line (stop bit) PS2_DATA_DIR = 0; ps2_wait_highlow();
// Read and interpret ACK bit if( PS2_DATA_LINE == 1 ) // error, mark it in 'par' par = 0; while( PS2_CLOCK_LINE == 0 ); PS2_CLOCK_DIR = 1; // clock is low to inhibit transmision for now if( par == 0 ) // ACK error { ir_int0ic = 0; // clear pending interrupts int0ic = 0x04; // restore the keyboard interrupt PS2_CLOCK_DIR = 0; // release the clock and allow transmission again return 0; } }
ir_int0ic = 0; // clear pending interrupts int0ic = 0x04; // restore the keyboard interrupt
if( nres > 0 ) ps2_flags |= PS2_RAW_FLAG; // set the isr in 'raw' mode (no data decode)
PS2_CLOCK_DIR = 0; // release clock, allow transmission from KB again
for( i = 0; i < nres; i ++ ) // read all responses { while( ( ps2_flags & PS2_READY_FLAG ) == 0 ); // wait for response ps2_flags &= ~PS2_READY_FLAG; pd[ i ] = ps2_raw_data; }
if( nres > 0 ) ps2_flags &= ~PS2_RAW_FLAG; // no more "raw" mode
return 1;}// Add the specified data to the keyboard buffer// Input: data - data to addstatic void ps2_add_to_buffer( u8 data ){ ps2_buf[ ps2_write_index ] = data; // save received data to buffer ps2_write_index = ( ps2_write_index + 1 ) % PS2_BUFFER_SIZE; // next position in buffer ps2_read_size = ( ps2_read_size < PS2_BUFFER_SIZE ) ? ps2_read_size + 1 : PS2_BUFFER_SIZE; }// INT0 interrupt routine (for keyboard clock)#pragma INTERRUPT int0_isrvoid int0_isr( void ){ s8 left, right, idx=0; u8 c, ca[ 2 ]; ca[ 0 ] = 0; // no command must be sent to the kb // Sample bit. Start/stop/parity are ignored if( ( ps2_bits >= 1 ) && ( ps2_bits <= 8 ) ) // actual data bits { ps2_data >>= 1; ps2_data |= PS2_DATA_LINE << 7; } ps2_bits ++; if( ps2_bits < 11 ) // still waiting for data return; if( ps2_flags & PS2_RAW_FLAG ) // raw data mode, write to buffer and return { ps2_flags |= PS2_READY_FLAG; // special indication in 'raw' mode: data is ready ps2_raw_data = ps2_data; goto isrexit; } if( ps2_ignore == 0 ) // this byte should not be ignored { // Check for special cases (shift/ctrl/alt/caps/arrows/break) switch( ps2_data ) { case PS2_IGN7_CODE: ps2_ignore = 7; goto isrexit; case PS2_BREAK_CODE: ps2_ignore = 1; ps2_flags |= PS2_BREAK_FLAG; goto isrexit; case PS2_ARROW_CODE: ps2_ignore = 1; ps2_flags |= PS2_ARROW_FLAG; goto isrexit; case PS2_CTRL_CODE: ps2_flags |= PS2_CTRL_FLAG; goto isrexit; case PS2_SHIFT_CODE: ps2_flags |= PS2_SHIFT_FLAG; goto isrexit; case PS2_CAPS_CODE: ps2_flags ^= PS2_CAPS_FLAG; // invert CAPS flag // Send "lights on" command ca[ 0 ] = PS2_CMD_LIGHTS; ca[ 1 ] = ( ps2_flags & PS2_CAPS_FLAG ) ? PS2_CAPS_VALUE : 0; goto isrexit;
default:
break; } // Now check for the other allowed keys (lookup) // As the scancode table is ordered, use binary search to speed things up left = 0; right = PS2_TABLE_SIZE - 1;
//二分查找法 while( left <= right ) { idx = ( left + right ) >> 1; c = ps2_key_table[ idx ]; if( c == ps2_data ) // found break; if( ps2_data < c ) right = idx - 1; else left = idx + 1; }
if( left > right ) // key not found, ignore goto isrexit;
ps2_data = ps2_key_map[ idx ][ PS2_SIMPLE_INDEX ]; // Key found, now look to see if CTRL is active and a valid combination was pressed if( ps2_flags & PS2_CTRL_FLAG ) { for( left = 0; left < sizeof( ps2_ctrl_lookup ); left += 2 ) if( ps2_ctrl_lookup[ left ] == ps2_data ) { ps2_data = ps2_ctrl_lookup[ left + 1 ]; break; } if( ps2_data == KB_INTERRUPT ) // special case, call LUA for interrupt handling //lua_setstophook();
asm("nop"); } else // no CTRL, check for SHIFT and CAPS { if( ps2_flags & PS2_SHIFT_FLAG ) // shift found, change character ps2_data = ps2_key_map[ idx ][ PS2_SHIFTED_INDEX ]; // If 'caps lock' is on, modify the character if it is a letter if( ( ps2_flags & PS2_CAPS_FLAG ) && ( isupper( ps2_data ) || islower( ps2_data ) ) ) ps2_data = toupper( ps2_data ); } // Add the decoded character to buffer ... finally :) ps2_add_to_buffer( ps2_data ); }
else // the received code is not actual data, check for special cases { if( ps2_flags & PS2_BREAK_FLAG ) // break code { // Check for break of ctrl/shift/caps switch( ps2_data ) { case PS2_CTRL_CODE: ps2_flags &= ~PS2_CTRL_FLAG; break; case PS2_SHIFT_CODE: ps2_flags &= ~PS2_SHIFT_FLAG; break; } ps2_flags &= ~PS2_BREAK_FLAG; }
if( ps2_flags & PS2_ARROW_FLAG ) // arrow code { c = 0; switch( ps2_data ) { case PS2_LEFT_CODE: c = KB_LEFT; break; case PS2_RIGHT_CODE: c = KB_RIGHT; break; case PS2_UP_CODE: c = KB_UP; break; case PS2_DOWN_CODE: c = KB_DOWN; break; case PS2_BREAK_CODE: // special break code, re-initialize the 'ignore' counter ps2_ignore = 2; break; } if( c != 0 ) ps2_add_to_buffer( c ); ps2_flags &= ~PS2_ARROW_FLAG; } ps2_ignore --; }
isrexit:
ps2_bits = ps2_data = 0;
if( ca[ 0 ] != 0 ) // must send a command ps2_send_command( 2, 0, ca );}// Initialize the PS2 keyboardvoid ps2_init( void ){
u8 res[ 2 ]; PS2_CLOCK_DIR = PS2_CLOCK_DIR = 0; // set to input, will be pulled high by external resistors PS2_DATA_LINE = PS2_CLOCK_LINE = 0; // this will be output when direction is set to 1
// Initialize internal variables ps2_write_index = ps2_read_index = ps2_read_size = 0; ps2_bits = ps2_data = ps2_flags = ps2_ignore = 0;
// Enable interrupt on clock line, level 3, falling edge polarity ir_int0ic = 0; // clear pending interrupts int0ic = 0x03;
// Reset the keyboard res[ 0 ] = PS2_CMD_RESET; ps2_send_command( 1, 2, res );
// Set typematic rate_delay; res[ 0 ] = PS2_CMD_SET_RATE; res[ 1 ] = 0; ps2_send_command( 2, 1, res );}// Returns 1 if a key is available, 0 otherwisestatic s8 keypressed( void ){ return ps2_read_size > 0;}// Get a character (without timeout)static u8 getchar( void ){
s8 c; c = ps2_buf[ ps2_read_index ]; DISABLE_IRQ ps2_read_size --; ENABLE_IRQ ps2_read_index = ( ps2_read_index + 1 ) % PS2_BUFFER_SIZE; return c;}// Wait for a character and return itstatic u8 waitchar( void ){ while( keypressed() == 0 ); return getchar();}// End of internal functions// *****************************************************************************// Initialize the driver// Input: a pointer to a keyboard driver context structure that must be filledvoid ps2_kb_init( KBDRV_CTX *pctx ) { ps2_init(); pctx->waitchar = waitchar; pctx->keypressed = keypressed; pctx->getchar = getchar;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -