หลังจากที่ผมได้อ่านตามเว็บต่างๆ พบมีการใช้ไมโครคอนโทรลเลอร์มารับค่าจาก 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 มีหน้าที่ดังนี้
- Data
- ไม่ใช้งาน
- ไฟเลี้ยง +5V
- GND
- Clock
- ไม่ใช้งาน
บัสของ Keyboard จะเป็นมาตรฐาน TTL 5V โหมดของบัส PS/2 มี 3 โหมด ดังนี้
- Data = high, Clock = high: Idle state. บัสว่าง ไม่มีการกดปุ่มที่ Keyboard
- Data = high, Clock = low: Communication Inhibited. เป็นการสั่งหยุดการส่งข้อมูลจาก Keyboard เพื่อที่ทำการส่งคำสั่งควบคุมไปยัง Keyboard เช่น สั่งเปิด LED ปุ่ม Numlock
- 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 ส่วนค่าประจำตัวของตัวอื่นๆ สามารถดูได้จากลิงค์ต่อไปนี้
เมื่อมีการกดปุ่มจะมีค่าส่งออกมา 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