วันพฤหัสบดีที่ 15 พฤศจิกายน พ.ศ. 2555

ใช้ Arduino รับค่าจาก PS/2 Keyboard

หลังจากที่ผมได้อ่านตามเว็บต่างๆ พบมีการใช้ไมโครคอนโทรลเลอร์มารับค่าจาก keyboard แบบ PS/2 ที่ใช้ในคอมพิวเตอร์รุ่นเก่าๆ จึงอยากลองเขียนดูบ้าง ตัว keyboard ได้ของเก่าจากห้องเก็บของซึ่งก็เก่ามากยี่ห้อ Sony แต่ยังใช้งานได้ดีอยู่เลย



รูปที่ 1 Keyboard ที่นำมาทดลอง


ในการทดลองผมได้ทำตัวแปลงหัวพอร์ตของ Keyboard เป็นขาแบบ SIP จำนวน 6 ขา เพื่อความสะดวกในการต่อกับบอร์ด Arduino โดยออกแบบวงจรอย่างง่ายด้วยโปรแกรม Protel99se

รูปที่ 2 ตัวแปลงพอร์ต PS/2 มุมมองด้านล่าง

รูปที่ 3 ตัวแปลงพอร์ต PS/2 มุมมองด้านหน้า

บอร์ด Arduino ที่ใช้คือ Arudino Mega 2560 แต่ก็สามารถใช้ Arduino รุ่นอื่นได้เนื่องจาก Code ที่เขียนมีขนาดเล็ก

รูปที่ 4 บอร์ด Arduino Mega2560

จอแสดงผลผมใช้จอของ SILA รุ่น TLCD-164 เป็นบอร์ด Graphic LCD แบบสำเร็จรูปสามารถที่จะแสดงผลภาษาไทย อังกฤษ และรูปภาพต่างๆ ได้ โดยมี interface 3 แบบ คือ TTL Serial, RS-232 Serial, SPI ในการทดลองนี้ได้เลือกใช้ interface แบบ TTL Serial เนื่องจากสามารถต่อกับ Arduino ได้ตรงๆ และง่าย ในการทดลองนี้ได้ใช้งาน 4 คำสั่ง คือ

  • Set charactor Row & Column (:1RCC<CR>) 
  • Set Character (:2XX...X<CR>) 
  • Clear LCD (:5<CR>)
  • Backlight ON (:9<CR>)


รูปที่ 5 บอร์ดจอ SILA TLCD-164

หลักการรับค่าจาก Keyboard จะทำโดยผ่านทางสายสัญญาณ 2 เส้น และไฟเลี้ยง 2 เส้น ดังนี้

  • CLOCK (ขาที่ 5) 
  • DATA (ขาที่ 1) 
  • ไฟเลี้ยง +5V (ขาที่ 4) 
  • Ground (ขาที่ 3) 

รูปที่ 6 ขาของพอร์ต PS/2
ขาแต่ละขาของพอร์ต PS/2 มีหน้าที่ดังนี้

  1. Data
  2. ไม่ใช้งาน
  3. ไฟเลี้ยง +5V
  4. GND
  5. Clock
  6. ไม่ใช้งาน

บัสของ Keyboard จะเป็นมาตรฐาน TTL 5V โหมดของบัส PS/2 มี 3 โหมด ดังนี้

  1. Data = high, Clock = high:  Idle state. บัสว่าง ไม่มีการกดปุ่มที่ Keyboard
  2. Data = high, Clock = low:  Communication Inhibited. เป็นการสั่งหยุดการส่งข้อมูลจาก Keyboard เพื่อที่ทำการส่งคำสั่งควบคุมไปยัง Keyboard เช่น สั่งเปิด LED ปุ่ม Numlock
  3. Data = low, Clock = high:  Host Request-to-Send เป็นการร้องขอการส่งข้อมูลจาก Keyboard
โหมดหลักๆ ที่เราใช้จะเป็นโหมดที่ 3 เพื่อที่จะรอรับค่าที่ Keyboard ส่งมาหา Arduino เมื่อมีการกดปุ่มใดๆ โดย ทั้งสัญญาณ Data และ Clock จะถูกควบควบคุมโดย Keyboard ทั้งหมด เราทำแค่อ่านค่าบิตข้อมูลทุกๆ ขอบขาลงของสัญญาณ Clock

เมื่อมีการกดปุ่มที่ Keyboard บัสของระบบจากที่อยู่สถานะ Idle จะเปลี่ยนเป็นสถานะ Host-Request-to_Send โดย Keyboard จะให้สัญญาณ Data เป็น LOW บ่งบอกถึงการเริ่มการส่งข้อมูล รูปแบบของข้อมูลที่ Keyboard ส่งไปจะมีขนาด 11 บิต ดังรูปที่ 7

รูปที่ 7 รูปแบบการส่งข้อมูลของ Keyboard
ข้อมูลทั้ง 11 บิต ประกอบด้วย
  • Start bit มีขนาด 1 บิต
  • Data bit มีขนาด 8 บิต
  • Parity bit มีขนาด 1 บิต (Odd Parity)
  • Stop bit มีขนาด 1 บิต

เมื่อ Data = 0 เป็นการเริ่มการส่งข้อมูล ให้รอจน Clock = 0 ต่อไปก็ให้รอขอบขาลงของ Clock ลูกถัดไป แล้วทำการอ่านค่าสัญญาณของ Data เก้บไว้ในตัวแปร เก็บจนครบ 8 บิตข้อมูล 2 บิตสุดท้ายคือ Parity และ Stop bit ไม่ต้องสนใจก็ได้

ข้อมูลที่ได้จะเป็นค่า Scan code ของแต่ละปุ่มซึ่งต้องนำมาแปลค่าอีกที


รูปที่ 8 รูปสัญญาณที่ได้จากการดักจับสัญญาณ
จากรูปที่ 8 เราค่าที่ส่งมาคือ 0x15 เมื่อเปิดตารางแปลจะมีความหมายเป็นตัว Q ส่วนค่าประจำตัวของตัวอื่นๆ สามารถดูได้จากลิงค์ต่อไปนี้
1 2

เมื่อมีการกดปุ่มจะมีค่าส่งออกมา 2 แบบ คือ ค่าเมื่อกดปุ่มลงไป เช่น ตัว Q คือ 0x15 อีกค่าที่ออกมาจะออกเมื่อปล่อยปุ่ม Q คือ 0xF0 0x15 ทำให้เราสามารถรู้ได้ว่าผู้ใช้ที่กดปุ่มได้ปล่อยปุ่มหรือยัง ตัวค่าที่ออกมาเมื่อปล่อยปุ่มส่วนใหญ่มีขนาด 2 byte โดย byte แรกจะเป็นค่า 0xF0 ตัวถัดมาเป็นค่าประจำตำแหน่งของปุ่มนั้นๆ

ในส่วนของ Sourcecode เขียนด้วย Arduino version 1.0 โดยต่อขา Data เข้ากับขา Digital 2, Clock เข้ากับขา Digital 3 ของ Arduino มีการแสดงผลออก 2 ทาง คือ จอแอลซีดี และทาง Serial Port รองรับการพิมพ์อักษรภาษาอังกฤษทั้งพิมฑ์เล็ก พิมพ์ใหญ่ อักขระพิเศษ ปุ่ม Tab, Caps lock, Shift 


1:  #define dat 2  
2:  #define clk 3  
3:  #define led 13  
4:  #define clk_low()  digitalWrite(clk,LOW)  
5:  #define clk_high() digitalWrite(clk,HIGH)  
6:  #define read_dat() digitalRead(dat)  
7:  #define read_clk() digitalRead(clk)  
8:  #define ps2_delay() delayMicroseconds(39)  
9:  unsigned char shift, caplock;  
10:  int idx=0;  
11:  unsigned char arr[10] = {0};  
12:  unsigned long wait_time;  
13:  char in_transmit = 0,timeout=0;  
14:  void setup()  
15:  {  
16:   Serial.begin(9600);  
17:   Serial.print(":5\r");  
18:   delay(100);  
19:   Serial.print(":9\r");  
20:   delay(10);  
21:   Serial.print(":2PS2 Keyboard\r");  
22:   delay(100);  
23:   Serial.print(":1100\r");  
24:   delay(10);  
25:   pinMode(dat,INPUT);  
26:   pinMode(clk,INPUT);  
27:   pinMode(led,OUTPUT);  
28:   digitalWrite(clk,HIGH);  
29:   digitalWrite(dat,HIGH);  
30:  }  
31:  void print_arr(unsigned char *p,int len)  
32:  {  
33:   int i;  
34:   for(i=0;i < len;i++)  
35:   {  
36:    print_hex(p[i]);  
37:    Serial.write(' ');  
38:   }  
39:  }  
40:  void decode_key(char c)  
41:  {  
42:   Serial.print(":2");  
43:   switch(c)  
44:   {  
45:    case 0x76: Serial.write(0x1b);break; //esc  
46:    case 0x05: Serial.write(0x1b);break; //f1  
47:    case 0x06: Serial.write(0x1b);break; //f2  
48:    case 0x04: Serial.write(0x1b);break; //f3  
49:    case 0x0c: Serial.write(0x1b);break; //f4  
50:    case 0x03: Serial.write(0x1b);break; //f5  
51:    case 0x0b: Serial.write(0x1b);break; //f6  
52:    case 0x83: Serial.write(0x1b);break; //f7  
53:    case 0x0a: Serial.write(0x1b);break; //f8  
54:    case 0x01: Serial.write(0x1b);break; //f9  
55:    case 0x09: Serial.write(0x1b);break; //f10  
56:    case 0x78: Serial.write(0x1b);break; //f11  
57:    case 0x07: Serial.write(0x1b);break; //f12  
58:    case 0x0e:   
59:          if(shift == 0)Serial.write('`');  
60:          else Serial.write('~');  
61:          break;  
62:    case 0x16:   
63:          if(shift == 0)Serial.write('1');  
64:          else Serial.write('!');  
65:          break;  
66:    case 0x1e:   
67:          if(shift == 0)Serial.write('2');  
68:          else Serial.write('@');  
69:          break;  
70:    case 0x26:   
71:          if(shift == 0)Serial.write('3');  
72:          else Serial.write('#');  
73:          break;  
74:    case 0x25:   
75:          if(shift == 0)Serial.write('4');  
76:          else Serial.write('$');  
77:          break;  
78:    case 0x2e:   
79:          if(shift == 0)Serial.write('5');  
80:          else Serial.write('%');  
81:          break;  
82:    case 0x36:   
83:          if(shift == 0)Serial.write('6');  
84:          else Serial.write('^');  
85:          break;  
86:    case 0x3d:   
87:          if(shift == 0)Serial.write('7');  
88:          else Serial.write('&');  
89:          break;  
90:    case 0x3e:   
91:          if(shift == 0)Serial.write('8');  
92:          else Serial.write('*');  
93:          break;  
94:    case 0x46:   
95:          if(shift == 0)Serial.write('9');  
96:          else Serial.write('(');  
97:          break;  
98:    case 0x45:   
99:          if(shift == 0)Serial.write('0');  
100:          else Serial.write(')');  
101:          break;  
102:    case 0x4e:   
103:          if(shift == 0)Serial.write('-');  
104:          else Serial.write('_');  
105:          break;  
106:    case 0x55:   
107:          if(shift == 0)Serial.write('=');  
108:          else Serial.write('+');  
109:          break;  
110:    case 0x66:   
111:          Serial.write(0x08); //backspace  
112:          Serial.write(' ' );  
113:          Serial.write(0x08);  
114:          break;  
115:    case 0x0d:   
116:          if(shift == 0)Serial.write(0x09); //tab  
117:          break;  
118:    case 0x15:   
119:          if(shift == 0 && caplock == 0)Serial.write('q');  
120:          else Serial.write('Q');  
121:          break;  
122:    case 0x1d:   
123:          if(shift == 0 && caplock == 0)Serial.write('w');  
124:          else Serial.write('W');  
125:          break;  
126:    case 0x24:   
127:          if(shift == 0 && caplock == 0)Serial.write('e');  
128:          else Serial.write('E');  
129:          break;  
130:    case 0x2d:   
131:          if(shift == 0 && caplock == 0)Serial.write('r');  
132:          else Serial.write('R');  
133:          break;  
134:    case 0x2c:   
135:          if(shift == 0 && caplock == 0)Serial.write('t');  
136:          else Serial.write('T');  
137:          break;  
138:    case 0x35:   
139:          if(shift == 0 && caplock == 0)Serial.write('y');  
140:          else Serial.write('Y');  
141:          break;  
142:    case 0x3c:   
143:          if(shift == 0 && caplock == 0)Serial.write('u');  
144:          else Serial.write('U');  
145:          break;  
146:    case 0x43:   
147:          if(shift == 0 && caplock == 0)Serial.write('i');  
148:          else Serial.write('I');  
149:          break;  
150:    case 0x44:   
151:          if(shift == 0 && caplock == 0)Serial.write('o');  
152:          else Serial.write('O');  
153:          break;  
154:    case 0x4d:   
155:          if(shift == 0 && caplock == 0)Serial.write('p');  
156:          else Serial.write('P');  
157:          break;  
158:    case 0x54:   
159:          if(shift == 0)Serial.write('[');  
160:          else Serial.write('{');  
161:          break;  
162:    case 0x5b:   
163:          if(shift == 0)Serial.write(']');  
164:          else Serial.write('}');  
165:          break;  
166:    case 0x5d:   
167:          if(shift == 0)Serial.write('\\');  
168:          else Serial.write('|');  
169:          break;  
170:    case 0x1c:   
171:          if(shift == 0 && caplock == 0)Serial.write('a');  
172:          else Serial.write('A');  
173:          break;  
174:    case 0x1b:   
175:          if(shift == 0 && caplock == 0)Serial.write('s');  
176:          else Serial.write('S');  
177:          break;  
178:    case 0x23:   
179:          if(shift == 0 && caplock == 0)Serial.write('d');  
180:          else Serial.write('D');  
181:          break;  
182:    case 0x2b:   
183:          if(shift == 0 && caplock == 0)Serial.write('f');  
184:          else Serial.write('F');  
185:          break;  
186:    case 0x34:   
187:          if(shift == 0 && caplock == 0)Serial.write('g');  
188:          else Serial.write('G');  
189:          break;  
190:    case 0x33:   
191:          if(shift == 0 && caplock == 0)Serial.write('h');  
192:          else Serial.write('H');  
193:          break;  
194:    case 0x3b:   
195:          if(shift == 0 && caplock == 0)Serial.write('j');  
196:          else Serial.write('J');  
197:          break;  
198:    case 0x42:   
199:          if(shift == 0 && caplock == 0)Serial.write('k');  
200:          else Serial.write('K');  
201:          break;  
202:    case 0x4b:   
203:          if(shift == 0 && caplock == 0)Serial.write('l');  
204:          else Serial.write('L');  
205:          break;  
206:    case 0x4c:   
207:          if(shift == 0)Serial.write(':');  
208:          else Serial.write(';');  
209:          break;  
210:    case 0x52:   
211:          if(shift == 0)Serial.write('\'');  
212:          else Serial.write('"');  
213:          break;  
214:    case 0x5a:   
215:          Serial.println("");  
216:          break;  
217:    case 0x1a:   
218:          if(shift == 0 && caplock == 0)Serial.write('z');  
219:          else Serial.write('Z');  
220:          break;  
221:    case 0x22:   
222:          if(shift == 0 && caplock == 0)Serial.write('x');  
223:          else Serial.write('X');  
224:          break;  
225:    case 0x21:   
226:          if(shift == 0 && caplock == 0)Serial.write('c');  
227:          else Serial.write('C');  
228:          break;  
229:    case 0x2a:   
230:          if(shift == 0 && caplock == 0)Serial.write('v');  
231:          else Serial.write('V');  
232:          break;  
233:    case 0x32:   
234:          if(shift == 0 && caplock == 0)Serial.write('b');  
235:          else Serial.write('B');  
236:          break;  
237:    case 0x31:   
238:          if(shift == 0 && caplock == 0)Serial.write('n');  
239:          else Serial.write('N');  
240:          break;  
241:    case 0x3a:   
242:          if(shift == 0 && caplock == 0)Serial.write('m');  
243:          else Serial.write('M');  
244:          break;  
245:    case 0x41:   
246:          if(shift == 0)Serial.write(',');  
247:          else Serial.write('<');  
248:          break;  
249:    case 0x49:   
250:          if(shift == 0)Serial.write('.');  
251:          else Serial.write('>');  
252:          break;  
253:    case 0x4a:   
254:          if(shift == 0)Serial.write('/');  
255:          else Serial.write('?');  
256:          break;  
257:    case 0x29:   
258:          Serial.write(' '); //space  
259:          break;  
260:    //default: print_hex(c);  
261:   }  
262:   Serial.print("\r");  
263:  }  
264:  void print_hex(unsigned char a)  
265:  {  
266:   unsigned char t;  
267:   t = (a >> 4) & 0x0f;  
268:   if(t <10)Serial.write(t + '0');  
269:   else   
270:   {  
271:    t = t - 10;  
272:    Serial.write(t + 'A');  
273:   }  
274:   t = a & 0x0f;  
275:   if(t <10)Serial.write(t + '0');  
276:   else   
277:   {  
278:    t = t - 10;  
279:    Serial.write(t + 'A');  
280:   }  
281:   //Serial.println("");  
282:  }  
283:  void loop()  
284:  {  
285:   if((millis() - wait_time > 10) && in_transmit == 1)  
286:   {  
287:    in_transmit = 0;  
288:    timeout = 1;  
289:   }  
290:   if(timeout == 1)  
291:   {  
292:    timeout = 0;  
293:    if((idx == 1) && ((arr[0] == 0x12) || (arr[0] == 0x59)))  
294:    {  
295:     shift = 1;  
296:     //Serial.println("Shift key DOWN");  
297:    }  
298:    if((idx == 2) && (arr[0] == 0xf0) && ((arr[1] == 0x12) || (arr[1] == 0x59)))  
299:    {  
300:     shift = 0;  
301:     //Serial.println("Shift key UP");  
302:    }  
303:    if((idx == 1) && (arr[0] == 0x58))  
304:    {  
305:     caplock = ~caplock;  
306:    }    
307:    if(idx == 1)decode_key(arr[0]);  
308:    else{  }  
309:    /*Serial.print(" [");  
310:    print_arr(arr,idx);  
311:    Serial.print(", ");  
312:    Serial.print(idx);  
313:    Serial.print("] ");  
314:   */  
315:    //Serial.print(arr[0],HEX);  
316:    idx = 0;  
317:   }  
318:   if(read_dat() == LOW)  
319:   {  
320:    in_transmit = 1;  
321:    //Start bit  
322:    while(read_clk() == HIGH);  
323:    while(read_clk() == LOW);  
324:    //Read 8 bit data  
325:    int i=0;  
326:    char mdat=0;  
327:    for(i = 0;i <= 7;i++)  
328:    {  
329:     while(read_clk() == HIGH);  
330:     if(read_dat() == HIGH)mdat = (mdat | (1 << i));  
331:     while(read_clk() == LOW);  
332:    }  
333:    //Parity bit  
334:    while(read_clk() == HIGH);  
335:    while(read_clk() == LOW);  
336:    //Stop bit   
337:    while(read_clk() == HIGH);  
338:    while(read_clk() == LOW);  
339:    //print_hex(mdat);  
340:    arr[idx] = mdat;  
341:    idx++;  
342:    in_transmit = 1;  
343:    wait_time = millis();  
344:   }  
345:  }  

Source code File ครับ

หวังบทความนี้คงพอช่วยให้ท่านสนุกกับการเล่นนะครับ

แหล่งข้อมูล

http://en.wikipedia.org/wiki/PS/2_connector
http://www.computer-engineering.org/ps2protocol/
http://www.pyroelectro.com/tutorials/ps2_keyboard_interface/theory.html

4 ความคิดเห็น:

  1. อัพเยอะๆนะคะ รอติดตามผลงานอยู่ค่ะ

    ตอบลบ
  2. ถ้าว่างก็จะเขียนเรื่อยๆครับผม

    ตอบลบ