任何一种计算系统基本上都有三个部分,即输入单元、处理单元和输出单元。在个人电脑的情况下,标准的输入单元是鼠标和键盘,输出单元是液晶显示器或CRT显示器。输入和输出由中央处理器(CPU)控制,它内部有一个强大的处理器。当涉及到处理能力较低的基于小型单片机的系统时,由于其接口方法简单,大多数系统仍然可以与PC机的标准输入设备进行接口。就输出而言,最常用的是小型液晶模块,如单色16*2,图形或小型彩色液晶显示屏。
本课题探讨的方法将标准PS2键盘与Arduino板连接,并在16*2 LCD屏幕上显示按下的键.任何基于AVR微控制器的板,遵循标准Arduino原理图,并与Arduino闪烁引导装载程序可以称为arduino板。Arduino板上有让内置AVR微控制器运行所需的所有电路。当涉及到Arduino板编程时,任何具有C编程基础知识的人都可以快速开始使用Arduino IDE。没有其他工具可以像Arduino一样帮助您轻松创建原型。
本项目使用PS2连接器连接键盘和Arduino板。PS2连接器有一个用于数据的引脚和另一个用于时钟的引脚,键盘仅使用这两个引脚与主机设备通信。鼠标始终具有用于PS2接口的6脚mini-DIN公连接器,主机设备始终具有相应的母脚。PS2公母连接器的图片和引脚如下图所示,PS2键盘和鼠标连接器之间的唯一区别是颜色。
PS2男针的图片
图2:PS2接口的6引脚mini-DIN公连接器
PS2女别针的形象
图3:6引脚Mini DIN母连接器PS2接口插头
PS2公母连接器的引脚
图4:PS2公母连接器的引脚
当涉及到将母连接器与电路板连接时,应该能够识别PS2连接器底部的引脚,下面的图像将有所帮助。
图5:用于PS2接口的Mini DIN母连接器插头底部
在这个项目中使用Arduino pro-mini板,它是用Arduino预编程的引导载入.这个项目使用的编程IDE是Arduino IDE版本1.0.3在windows操作系统。Arduino pro-mini板和Arduino IDE的图像如下所示;
图6:典型Arduino Pro-Mini Board
图7:Arduino IDE软件窗口
另一个硬件可以进行USB到TTL的转换,用于将程序上传到arduino板。
图8:外接USB转TTL转换板,用于Arduino编程和串行通信
假定读者已经完成了这个项目如何开始arduino并且做了里面讨论的所有事情。为这个项目编写的代码使用了名为“PS2Keyboard.h”的自定义PS2库文件,其中有访问PS2鼠标的所有必要例程,如何使用这个库来连接PS2键盘的细节已经在之前的项目中讨论过了如何连接PS2键盘和Arduino.基本上有三个函数,用户可以直接在他们的代码中使用,即" keyboard.begin() ", " keyboard.available() "和" mouse.report(data) "。下面将详细讨论这些函数。
keyboard.begin ()
函数keyboard.begin()用于执行使用PS2协议初始化ASCII键盘所需的所有操作。一旦初始化完成,每次按下一个键,键盘开始发送已按下的键的等效ASCII值。该函数有一个参数,它是Arduino板中作为键盘数据引脚的引脚的编号。
keyboard.available ()
此功能可用于检查有关按键的数据是否从键盘可用。这个函数总是在调用keyboard.read()函数之前被调用,该函数用于从键盘读取数据。当检测到按键时,该函数返回一个非零的正数值。
keyboard.read ()
函数keyboard.read()可用于读取从键盘上按下的键的ASCII值。该函数不接受任何参数,它返回键的ASCII值。函数keyboard.read()总是在函数之后被调用Keyboard.read()返回一个非零的正值。
Arduino IDE提供了一个名为
在编译代码之前,请确保“PS2Keyboard.h”和“PS2Keyboard.cpp”文件与保存.pde文件的文件夹保持一致。当编码完成后,可以验证并将代码上传到Arduino板,如项目中所述如何开始使用Arduino.只要键盘与Arduino板连接,键盘上的led灯就会闪烁,键盘上每按一个键,在16*2 LCD上都能看到相同的字符。
项目源代码
# # #/*============================ 如实验室 ===================================//演示如何接口ps2keyboard与Arduino电路:液晶显示器:* LCD RS引脚到数字引脚12* LCD使引脚数字引脚11* LCD D4引脚到数字引脚7* LCD D5引脚到数字引脚6* LCD D6引脚到数字引脚5* LCD D7引脚到数字引脚4* LCD R/W引脚到地* 10K电阻:*端接+5V接地*雨刷到LCD引脚3* LED阳极连接到数字输出9* LED阴极通过1K电阻连接到地面键盘:数据引脚到引脚8时钟引脚到3号引脚============================== 如实验室 ===================================*/# include“PS2Keyboard.h”//包含库代码:# include < LiquidCrystal.h >使用接口引脚的编号初始化库液晶液晶(12,11,7,6,5,4);#定义DATA_PINPS2Keyboard键盘;无效的设置(){pinMode(9、输出);液晶显示器。开始(16日2);液晶显示器。打印(车库“工程师”);液晶显示器。setCursor (0,1);液晶显示器。打印(“PS2键盘”);keyboard.begin (DATA_PIN);//初始化PS2键盘Serial.begin (9600);系列。println(车库“工程师”);延迟(1000);digitalWrite(9日高);lcd.clear ();}无效循环(){If (keyboard.available()) //检查是否有来自键盘的数据{Char dat = keyboard.read();//从键盘读取数据lcd.print (dat);其他};}//************ PS2.h ************//
# include < avr /头文件># include < avr / interrupt.h ># include < avr / pgmspace.h ># include“Arduino.h”# include“PS2Keyboard.h”# include“binary.h”Typedef uint8_t boolean;Typedef uint8_t字节;/*我知道这很不酷,但我就是不知道该怎么做*前方真的很糟糕**变量用于ISR的内部状态管理。的是*没有保存在对象实例中,因为ISR必须尽可能快*。因此可以避免CPP方法调用的开销。**请不要在代码中引用这些变量,因为它们可能会消失一些快乐的一天。* /int ps2Keyboard_DataPin;字节ps2Keyboard_CurrentBuffer;ps2Keyboard_CharBuffer;volatile字节ps2Keyboard_BufferPos;//用于记住按键信息的变量volatile bool ps2Keyboard_shift;//表示按shift键volatile bool ps2Keyboard_ctrl;//表示按下CTRL键ps2Keyboard_alt;//表示按下Alt键volatile bool ps2Keyboard_extend;//记住接收的键盘扩展字符volatile bool ps2Keyboard_release;//区分按键和释放volatile bool ps2Keyboard_caps_lock;//记住shift锁已按下//用于向键盘发送命令字节的变量,例如caps_lock lightVolatile Boolean cmd_in_progress;Volatile int cmd_count;字节cmd_value;Volatile字节cmd_ack_value;字节cmd_parity;Volatile Boolean cmd_ack_byte_ok;//发送命令字节到键盘需要适当的奇偶校验(否则键盘//只是要求你重复字节)字节odd_parity(字节val) {Int i, count = 1;//从0开始偶数奇偶校验(我= 0;我< 8;我+ +){If (val&1) count++;Val = Val >>1;}返回计数& 1;// count的下位为奇偶校验位}无效kbd_send_command(字节val) {//停止中断例程接收字符,以便我们可以使用它//发送字节Cmd_in_progress = true;Cmd_count = 0;//设置字节移出并初始化ack位Cmd_value = val;Cmd_ack_value = 1;// KBD将在接收字节时清除此位Cmd_parity = odd_parity(val);//设置数据引脚为输出,准备驱动digitalWrite (ps2Keyboard_DataPin、高);pinMode (ps2Keyboard_DataPin、输出);//驱动时钟引脚低-这将生成第一个//移出进程中断pinMode (PS2_INT_PIN、输出);digitalWrite (PS2_INT_PIN、低);//至少等待一个时钟周期(以防KBD是中间传输)delayMicroseconds (60);//设置0起始位digitalWrite (ps2Keyboard_DataPin、低);//放开时钟-大骨节病从这里开始控制时钟digitalWrite (PS2_INT_PIN、高);pinMode (PS2_INT_PIN、输入);//等待中断程序移出字节,奇偶校验和接收ack位While (cmd_ack_value!=0);//切换回接收KBD字符的中断例程Cmd_in_progress = false;}void PS2Keyboard::reset() {kbd_send_command (0 xff);//发送KBD重置代码到KBD: 3灯//应该在KBD上简单闪一下//重置所有全局变量ps2Keyboard_CurrentBuffer = 0;ps2Keyboard_CharBuffer = 0;ps2Keyboard_BufferPos = 0;ps2Keyboard_shift = false;ps2Keyboard_ctrl = false;ps2Keyboard_alt = false;ps2Keyboard_extend = false;ps2Keyboard_release = false;ps2Keyboard_caps_lock = false;Cmd_in_progress = false;Cmd_count = 0;Cmd_value = 0;Cmd_ack_value = 1;}// val: bit_2=caps_lock, bit_1=num_lock, bit_0=scroll_lockVoid kbd_set_lights(字节val) {//当用0xED命令设置灯时,键盘会响应//一个ack字节,0xFA。这和“ack bit”不一样//在成功移动每个命令字节之后。看这个网页//页面来很好地描述这一切:/ / http://www.beyondlogic.org/keyboard/keybrd.htmCmd_ack_byte_ok = false;//初始化ack字节标志kbd_send_command(0中);//发送命令字节(!cmd_ack_byte_ok);// ack字节from keyboard设置此标志kbd_send_command (val);//现在发送数据}//外部中断的ISR//这可能看起来像一个中断例程的很多代码,但是switch//语句是快速的,通过例程的路径只有几个//简单的代码行取消ps2interrupt (Void) {int value = digitalRead(ps2Keyboard_DataPin);//这是发送字节到键盘的代码。实际上是12位://一个起始位,8个数据位,1个奇偶校验位,1个停止位,1个ack位(来自kbd)If (cmd_in_progress) {cmd_count + +;// cmd_count跟踪移位Switch (cmd_count) {Case 1: //起始位digitalWrite (ps2Keyboard_DataPin、低);打破;案例2:案例3:案例4:案例5:案例6:案例7:案例8:案例9://要移位的数据位digitalWrite (ps2Keyboard_DataPin cmd_value&1);>>1;打破;Case 10: //奇偶校验位digitalWrite (ps2Keyboard_DataPin cmd_parity);打破;Case 11: //停止位//释放数据引脚,因此停止位实际上依赖于上拉//但这确保数据引脚已经准备好由KBD驱动//下一个比特。digitalWrite (ps2Keyboard_DataPin、高);pinMode (ps2Keyboard_DataPin、输入);打破;Case 12: // ack位-由kbd驱动,因此我们读取它的值cmd_ack_value = digitalRead(ps2Keyboard_DataPin);Cmd_in_progress = false;//完成移出}返回;//不通过ISR的接收部分}//接收ISR的部分//插入二进制位if(ps2Keyboard_BufferPos > && ps2Keyboard_BufferPos < 11) {ps2Keyboard_CurrentBuffer |= (value << (ps2Keyboard_BufferPos - 1));}ps2Keyboard_BufferPos + +;//记录移动位置if(ps2Keyboard_BufferPos == 11){//接收到一个完整的字符switch (ps2Keyboard_CurrentBuffer) {case 0xF0: {// key释放字符ps2Keyboard_release = true;ps2Keyboard_extend = false;打破;}case 0xFA:{//命令确认字节Cmd_ack_byte_ok = true;打破;}case 0xE0:{//扩展字符集ps2Keyboard_extend = true;打破;}Case 0x12: //左移Case 0x59:{//右移ps2Keyboard_shift = ps2Keyboard_release?假:真;ps2Keyboard_release = false;打破;}Case 0x11: {// Alt键(右Alt扩展为0x11)ps2Keyboard_alt = ps2Keyboard_release?假:真;ps2Keyboard_release = false;打破;}Case 0x14: {// CTRL键(右CTRL扩展为0x14)ps2Keyboard_ctrl = ps2Keyboard_release?假:真;ps2Keyboard_release = false;打破;}Case 0x58:{//大写锁定键if (!ps2Keyboard_release) {ps2Keyboard_caps_lock = ps2Keyboard_caps_lock?假:真;//允许通过大写锁定代码来开启和关闭灯ps2Keyboard_CharBuffer = ps2Keyboard_CurrentBuffer;}其他{ps2Keyboard_release = false;}打破;}默认值:{//实键if (ps2Keyboard_release){//尽管忽略它刚刚释放ps2Keyboard_release = false;}else{//真实键进入CharBufferps2Keyboard_CharBuffer = ps2Keyboard_CurrentBuffer;}}}ps2Keyboard_CurrentBuffer = 0;ps2Keyboard_BufferPos = 0;}}PS2Keyboard: PS2Keyboard () {//这里没什么可做的}void PS2Keyboard::begin(int dataPin) {//准备全局变量ps2Keyboard_DataPin = dataPin;ps2Keyboard_CurrentBuffer = 0;ps2Keyboard_CharBuffer = 0;ps2Keyboard_BufferPos = 0;ps2Keyboard_shift = false;ps2Keyboard_ctrl = false;ps2Keyboard_alt = false;ps2Keyboard_extend = false;ps2Keyboard_release = false;ps2Keyboard_caps_lock = false;Cmd_in_progress = false;Cmd_count = 0;Cmd_value = 0;Cmd_ack_value = 1;//初始化引脚pinMode (PS2_INT_PIN、输入);digitalWrite (PS2_INT_PIN、高);pinMode (dataPin、输入);digitalWrite (dataPin、高);attachInterrupt(1, ps2interrupt, FALLING);#如果0// Global使能INT1中断Eimsk |= (1 << int1);//下降边触发中断Eicra |= (0 << isc10) | (1 << isc11);# endif}bool PS2Keyboard::available() {return ps2Keyboard_CharBuffer != 0;}//这个例程允许调用程序查看是否持有其他键//当接收到一个字符时按下:即
, //注意,这个例程必须在available()返回true后调用,// but BEFORE read()。read()例程清除缓冲区并允许另一个缓冲区//要接收的字符,因此这些位可以在read()后随时改变。PS2Keyboard::read_extra() {return (ps2Keyboard_caps_lock<<3) |(ps2Keyboard_shift < < 2) |(ps2Keyboard_alt < < 1) |ps2Keyboard_ctrl;}PS2Keyboard::read() {字节的结果;//从键盘读取原始数据result = ps2Keyboard_CharBuffer;//使用开关进行代码到字符的转换。//这是快速的,实际上每个简单的行只使用4字节开关(结果){case 0x1C: result = 'a';打破;Case 0x32: result = 'b';打破;Case 0x21: result = 'c';打破;Case 0x23: result = 'd';打破;Case 0x24: result = 'e';打破;case 0x2B: result = 'f';打破;Case 0x34: result = 'g';打破;Case 0x33: result = 'h';打破;Case 0x43: result = 'i';打破;case 0x3B: result = 'j';打破;Case 0x42: result = 'k';打破;case 0x4B: result = 'l';打破;case 0x3A: result = 'm';打破;Case 0x31: result = 'n';打破;Case 0x44: result = 'o';打破;case 0x4D: result = 'p';打破;Case 0x15: result = 'q';打破;case 0x2D: result = 'r';打破;case 0x1B: result = 's';打破;case 0x2C: result = 't';打破;case 0x3C: result = 'u';打破;case 0x2A: result = 'v';打破;case 0x1D: result = 'w';打破;Case 0x22: result = 'x';打破;Case 0x35: result = 'y';打破;case 0x1A: result = 'z';打破;//注意大写锁定只在a-z上使用case 0x41: result = ps2Keyboard_shift?'<': ',';打破;case 0x49: result = ps2Keyboard_shift?'>': '.';打破;case 0x4A: result = ps2Keyboard_shift?”?': '/';打破;case 0x54: result = ps2Keyboard_shift?'{': '[';打破;case 0x5B: result = ps2Keyboard_shift?'}': ']';打破;case 0x4E: result = ps2Keyboard_shift?'_': '-';打破;case 0x55: result = ps2Keyboard_shift?'+': '=';打破;Case 0x29: result = ' ';打破;case 0x45: result = ps2Keyboard_shift?')': '0';打破;case 0x16: result = ps2Keyboard_shift?”!': '1';打破;case 0x1E: result = ps2Keyboard_shift?'@': '2';打破;case 0x26: result = ps2Keyboard_shift?'Ã, £':'3';打破;case 0x25: result = ps2Keyboard_shift?'$': '4';打破;case 0x2E: result = ps2Keyboard_shift?'%': '5';打破;case 0x36: result = ps2Keyboard_shift?'^': '6';打破;case 0x3D: result = ps2Keyboard_shift?'&': '7';打破;case 0x3E: result = ps2Keyboard_shift?'*': '8';打破;case 0x46: result = ps2Keyboard_shift?'(': '9';打破;case 0x0D: result = 't';打破;case 0x5A: result = 'n';打破;case 0x66: result = PS2_KC_BKSP;打破;case 0x69: result = ps2Keyboard_extend?Ps2_kc_end: '1';打破;case 0x6B: result = ps2Keyboard_extend?Ps2_kc_left: '4';打破;case 0x6C: result = ps2Keyboard_extend?Ps2_kc_home: '7';打破;case 0x70: result = ps2Keyboard_extend?Ps2_kc_ins: '0';打破;case 0x71: result = ps2Keyboard_extend?Ps2_kc_del: '.';打破;case 0x72: result = ps2Keyboard_extend?Ps2_kc_down: '2';打破;Case 0x73: result = '5';打破;case 0x74: result = ps2Keyboard_extend?Ps2_kc_right: '6';打破;case 0x75: result = ps2Keyboard_extend?Ps2_kc_up: '8';打破;case 0x76: result = PS2_KC_ESC;打破;Case 0x79: result = '+';打破;case 0x7A: result = ps2Keyboard_extend?Ps2_kc_pgdn: '3';打破;case 0x7B: result = '-';打破;case 0x7C: result = '*';打破;case 0x7D: result = ps2Keyboard_extend?ps2_kc_ppg: '9';打破;例0 x58://设置键盘灯在这里完成。理想情况下,这是可以做到的//在中断例程本身和与th相关的关键代码中// caps锁定按键永远不会作为字符传递。//然而,它会使中断程序非常混乱//与caps_lock控件关联的额外状态//键代码,导致CMD字节传输,导致ack_byte传输//被接收,然后发送一个数据字节。这里做起来容易多了。//然而,缺点是,灯是在//正确的时间取决于要检查的调用程序//字符。如果调用程序停止//在任意点按下大写锁定键轮询字符//在轮询时不会改变caps锁定灯的状态//不发生。result = ps2Keyboard_caps_lock?Ps2_kc_clon: ps2_kc_cloff;if (ps2Keyboard_caps_lock) kbd_set_lights(4);其他kbd_set_lights (0);打破;//重置移位计数器为不期望的值,以回到sink//这允许热插拔键盘默认值:延迟(500);//但要等一会儿,以防中途换班ps2Keyboard_BufferPos = 0;ps2Keyboard_shift = false;ps2Keyboard_ctrl = false;ps2Keyboard_alt = false;ps2Keyboard_extend = false;ps2Keyboard_release = false;ps2Keyboard_caps_lock = false;} //结束开关(result)// shift a-z字符(比switch语句中的代码少)If ((result>='a') && (result<='z')) &&((ps2Keyboard_shift && !ps2Keyboard_caps_lock) ||(!ps2Keyboard_shift && ps2Keyboard_caps_lock)) {result = result + ('A'-' A');}//完成字符ps2Keyboard_CharBuffer = 0;返回(结果);}, 或 # # #
项目源代码
# # #的ifndef PS2Keyboard_h#定义PS2Keyboard_h# include < avr /头文件># include < avr / interrupt.h ># include < avr / pgmspace.h >/** PS2键盘“make”代码检查某些键。* ///给出这些没有被其他任何东西使用的代码//将所有控制键代码设置在0x80以上,便于检查//在调用层打印的字符。#定义PS2_KC_BKSP 0x80#定义PS2_KC_UP 0x81#定义PS2_KC_DOWN 0x82#定义PS2_KC_LEFT 0x83#定义PS2_KC_RIGHT 0x84#定义PS2_KC_PGDN 0x85#定义PS2_KC_PGUP 0x86#定义PS2_KC_END 0x87#定义PS2_KC_HOME 0x88#定义PS2_KC_INS 0x89#定义PS2_KC_DEL 0x8A#定义PS2_KC_ESC 0x8B#定义PS2_KC_CLON 0x8C // caps_lock#define PS2_KC_CLOFF 0x8D // caps_lock off# include“binary.h”Typedef uint8_t boolean;Typedef uint8_t字节;/*这个PIN稍后在init例程中被硬编码。如果你改变这个*确保你也改变了中断初始化。* /#定义PS2_INT_PIN/***用途:方便使用PS2键盘*作者:Christian Weichel* /类PS2Keyboard {私人:int m_dataPin;字节m_charBuffer;公众:/**这个构造函数基本上什么都不做。请调用begin(int)*方法,然后再使用该类的任何其他方法。* /PS2Keyboard ();/***通过注册外部中断启动键盘“服务”。*正确设置引脚模式,并将所需引脚模式调高。*调用此方法的最佳位置可能是在设置例程中。* /void begin(int dataPin);/***如果有字符要读取,则返回true,否则返回false。* /bool可用();/***发送一个重置命令到键盘,并重新初始化所有的控制*变量在PS2Keybaord代码。* /空白重置();/***返回上次从键盘读取的字符。如果用户按了2*键之间调用此方法,只有后一个将是可用的。一次*该字符已被读取,缓冲区将被清除。*如果没有可用的char,则返回0。* /字节读();/***返回键,键,键和 * caps_lock状态。注意shift和caps_lock是在* Ps2Keyboard代码(和返回值从read()已经修改),但是能够在这里阅读它们可能是有用的。*此例程是可选的,但必须在available()返回后读取* true和BEFORE read()被调用来检索字符。读一读*调用read()将返回不可预知的值。* /字节read_extra ();};# endif# # #
与这篇文章有关的问题?
询问和讨论Electro-Tech-Online.com而且EDAboard.com论坛。
告诉我们你的想法!!
你一定是登录发表评论。