ในบทความก่อนหน้านี้ผมได้เขียนถึงการใช้บอร์ด
Pinguino
8 เชื่อมต่อกับ LCD key pad shield, อันนี้ครับ
ซึ่งหลังเขียนเสร็จแล้วผมก็นึกขึ้นมาได้ว่ายังมี LCD key pad shield อยู่อีกอันหนึ่ง( ยี่ห้อ ElecFreaks , shield สีขาวในรูปด้านบน )ซึ่งแตกต่างจากอันแรกตรงที่ใช้ปุ่มกดแบบจอยสติ้ก 5 ทิศทางแทน แต่ยังคงเชื่อมต่อกับไมโครคอนโทรลเลอร์ที่ขา A0 เหมือนเดิมทำให้สามารถใช้โค้ดเดิมได้ และที่เพิ่มขึ้นมาอีกอันก็คือตัว rotary encoder ที่ถ้าดูผิวเผินแล้วเหมือนตัวโวลุ่มเลย โดยเราสามารถหมุนได้ทั้งสองทิศทางซึ่งจะให้เอ้าพุตออกมาสองแชลแนล และมีพัลล์เหลื่อมกันดังแสดงในรูปที่ 2 นอกจากนี้ยังสามารถกดลงคล้ายกับการกดปุ่ม enter ได้อีกด้วย โดยในบทความนี้จะมุ่งเน้นที่การเขียนโค้ดเพิ่อตรวจจับการหมุนหรือเปลี่ยนตำแหน่งของ encoder โดยอาศัยอินเตอร์รับออนเชนต์( interrupt on change ) และมีแก้ไขค่าคงที่ของ keys ต่างๆเพื่อรองรับการกดปุ่มของ encoder ที่เพิ่มเติมขึ้นมา โดยผู้อ่านสามารถหาข้อมูลเพิ่มเติมเกี่ยวกับ shield ตัวนี้ได้จากลิ้งค์ต่อไปนี้ ซึ่งจะมี schematic, datasheet, และโค้ดตัวอย่างของ Arduino ที่เราสามารถนำมาใช้ได้กับ Pinguino ของเรา
ซึ่งหลังเขียนเสร็จแล้วผมก็นึกขึ้นมาได้ว่ายังมี LCD key pad shield อยู่อีกอันหนึ่ง( ยี่ห้อ ElecFreaks , shield สีขาวในรูปด้านบน )ซึ่งแตกต่างจากอันแรกตรงที่ใช้ปุ่มกดแบบจอยสติ้ก 5 ทิศทางแทน แต่ยังคงเชื่อมต่อกับไมโครคอนโทรลเลอร์ที่ขา A0 เหมือนเดิมทำให้สามารถใช้โค้ดเดิมได้ และที่เพิ่มขึ้นมาอีกอันก็คือตัว rotary encoder ที่ถ้าดูผิวเผินแล้วเหมือนตัวโวลุ่มเลย โดยเราสามารถหมุนได้ทั้งสองทิศทางซึ่งจะให้เอ้าพุตออกมาสองแชลแนล และมีพัลล์เหลื่อมกันดังแสดงในรูปที่ 2 นอกจากนี้ยังสามารถกดลงคล้ายกับการกดปุ่ม enter ได้อีกด้วย โดยในบทความนี้จะมุ่งเน้นที่การเขียนโค้ดเพิ่อตรวจจับการหมุนหรือเปลี่ยนตำแหน่งของ encoder โดยอาศัยอินเตอร์รับออนเชนต์( interrupt on change ) และมีแก้ไขค่าคงที่ของ keys ต่างๆเพื่อรองรับการกดปุ่มของ encoder ที่เพิ่มเติมขึ้นมา โดยผู้อ่านสามารถหาข้อมูลเพิ่มเติมเกี่ยวกับ shield ตัวนี้ได้จากลิ้งค์ต่อไปนี้ ซึ่งจะมี schematic, datasheet, และโค้ดตัวอย่างของ Arduino ที่เราสามารถนำมาใช้ได้กับ Pinguino ของเรา
รูปที่ 2 เอ้าพุตของ rotary encoder |
วิธีการอ่านค่าของ rotary encoder นั้นสามารถหาอ่านเพิ่มเติมได้จากฟอร์รั่มของ Arduino เช่นลิ้งค์ต่อไปนี้
หรือลิ้งค์อันนี้ก็ให้ข้อมูลดีครับและใช้ C18 ในการเขียนโค้ดด้วย
จาก schematic ที่สามารถโหลดได้จากลิ้งค์แรกนั้นจะเห็นว่าเอ้าพุตของ rotary encoder นั้นจะต่ออยู่กับ pin 2 และ 3 ของ Arduino ซึ่งทั้งสองพินนี้นั้นสามารถ config ให้เป็น external interrupt และสามารถเลือกได้ว่าจะให้เกิด interrupt เมื่อเกิดสัญญานขอบขาขึ้นหรือขอบขาลง คราวนี้เมื่อผมจะใช้บอร์ด Pinguino 8 มาแทน Arduino พอเช็คขาที่ตำแหน่งเดียวกันนี้พบว่าบอร์ด Pinguino 8 ที่ตำแหน่ง D2 กับ D3 นี้ไม่รองรับ external interrupt ทำให้ผมต้องมาทำการโมดิฟายตัว shield โดยบัดกรีสายจากตำแหน่ง D2 กับ D3 ไปที่ตำแหน่ง D12 และ D13 ตามลำดับดังแสดงในรูปที่ 3 ครับ
รูปที่ 3 บัดกรีสายจาก D2, D3 ไป D12, D13 |
โดยที่ตำแหน่งใหม่นี้จะเป็นพินหมายเลข 0 และ 1 บนบอร์ด Pinguino 8 ดังแสดงในรูปที่ 4 ซึ้่งทั้งสองพินนี้รองรับ interrupt on change ตามที่เราต้องการ
รูปที่ 4 หมายเลขพินบน Pinguino 8 |
เมื่อ hardware ของเราพร้อมแล้วทีนี้ก็มาถึงขั้นตอนการเขียนโค้ด จากในรูปที่ 2 เมื่อพิจารณาให้ดีจะเห็นว่าในจังหวะที่พัลล์เอ้าพุตแชลแนล A ของ rotary encoder เปลี่ยนลอจิกจาก HIGH เป็น LOW ( Falling Edge ) แล้วถ้าเราอ่านค่าของเอ้าพุตแชลแนล B ได้ค่าเป็น HIGH นั่นแสดงว่า rotary encoder ถูกหมุนไปในทิศทางตามเข็มนาฬิกา ( Clockwise ) เราก็สามาถเพิ่มค่าในตัวแปรที่เราเอาไว้เก็บค่าการหมุนได้
ในทางตรงกันข้ามหากจังหวะที่เอ้าพุตแชลแนล A ของ rotary encoder เปลี่ยนลอจิกจาก HIGH เป็น LOW ( Falling Edge ) แล้วถ้าเราอ่านค่าของเอ้าพุตแชลแนล B ได้ค่าเป็น LOW นั่นแสดงว่า rotary encoder ถูกหมุนไปในทิศทางทวนเข็มนาฬิกา ( Counter-Clockwise ) เราก็สามาถบดค่าในตัวแปรที่เราเอาไว้เก็บค่าการหมุนได้
เมื่อเรารู้แนวคิดในการเขียนโค้ดแล้วเราก็สามารถเริ่มเขียนได้โดยเริ่มจากการ define ชื่อขาของ rotary encoder เพื่อให้ง่ายในการอ่านและเขียนโค้ด และประกาศตัวแปรที่เอาไว้เก็บค่าการหมุน พร้อมทั้งตัวแปรที่จำเป็นดังนี้
1: #define Encoder_A 0
2: #define Encoder_B 1
3: volatile int Encoder_number=0;
4: volatile u8 state=0;
จากนั้นในฟังก์ชัน setup() เราก็ config ให้ขา encoder เป็น input ซะ แต่อย่าลืมที่ตำแหน่ง D2 กับ D3 ของ shield นั้นเมื่อเสียบลงบนบอร์ด Pinguino 8 แล้วจะเป็นพินหมายเลข 25 กับ 26 ( ดูได้จากรูปที่ 4 ) ซึ่งโดย default ของตัวไมโครคอนโทรลเลอร์(PIC18F45K50)เองหลังจาก power on or reset ขึ้นมามันจะเป็น output ซึ่งจะทำให้ feature อินเตอร์รัพที่ขา 0 กับ 1 ไม่ทำงานดังนั้นเราจำเป็นต้อง config พิน 25 กับ 26 ให้เป็น input (high impedance) ด้วย interrupt on change ถึงจะทำงานได้ถูกต้อง (หรือไม่งั้นก็ต้องตัดขา D2 กับ D3 ของ shield ทิ้งซะก่อน) จากนั้นจึงจะ enable ความสามารถ Interrupt On Change บน pin 0 โดยให้เกิด interrupt ทุกๆขอบขาลง ( INT_FALLING_EDGE ) โดยเมื่อดักจับขอบขาลงได้ให้กระโดดไปทำงานที่ฟังก์ชั่นชื่อ Encode() ดังตัวอย่างโค้ดต่อไปนี้ครับ
ในส่วนของฟังก์ชั่น Encode() นั้นเราก็เพียงอ่านค่าของ Encoder_B หากอ่านได้เป็น 1 (HIGH) ก็ให้เพิ่มค่าตัวแปร แต่หากอ่านค่าได้เป็น 0 (LOW) ก็ลดค่าตัวแปรลง จากนั้นจึงบอกให้ฟังก์ชัน loop รู้ว่า rotary encoder มีการหมุน( เปลี่ยนแปลงค่า ) โดยการเซ็ทให้ตัวแปร state เป็น 1 เพื่อที่ในฟังก์ชั่น loop จะได้อัพเดตค่าบนจอ LCD ต่อไป ตัวอย่างโค้ดก็จะเป็นตามนี้นะครับ
และในส่วนของฟังก์ชั่น loop() เราก็จะเพิ่มโค้ดเพื่ออัพเดตค่าของ Encoder_number บนจอ LCD โดยจะทำการอัพเดตก็ต่อเมื่อมีการหมุน rotary encoder เท่านั้น ดังโค้ดตัวอย่างต่อไปนี้ครับ
สุดท้ายเราก็จะมาแก้ไขค่าการเปรียบเทียบค่าที่อ่านได้จาก ADC จากตัวอย่างในบทความเกี่ยวกับ LCD keys pad shield ที่ผ่านมา โดยแก้ไขค่าเป็นดังนี้ครับ
และต้องเพิ่มสเตรทในฟังก์ชั่น loop ขึ้นอีกอันหนึ่งด้วย สำหรับปุ่ม Encode OK ซึ่งโค้ดตัวเต็มก็จะเป็นดังนี้ครับ
และข้างล่างนี้คือวิดีโอผลการทำงานของ Pinguino 8 กับ LCD key pad shield V1.2 ครับ
1: //make the pin 2 and 3(which is pin 25, and 26 on Pinguino 8) of LCD keypad shield as input
2: //so that the pin 0 and 1 of Pinguino 8 that we use for interrupt on change can work correctly
3: pinMode(25, INPUT);
4: pinMode(26, INPUT);
5: pinMode(Encoder_A, INPUT);
6: pinMode(Encoder_B, INPUT);
7: digitalWrite(Encoder_A, HIGH);
8: digitalWrite(Encoder_B, HIGH);
9: OnChangePin0(Encode, INT_FALLING_EDGE);
ในส่วนของฟังก์ชั่น Encode() นั้นเราก็เพียงอ่านค่าของ Encoder_B หากอ่านได้เป็น 1 (HIGH) ก็ให้เพิ่มค่าตัวแปร แต่หากอ่านค่าได้เป็น 0 (LOW) ก็ลดค่าตัวแปรลง จากนั้นจึงบอกให้ฟังก์ชัน loop รู้ว่า rotary encoder มีการหมุน( เปลี่ยนแปลงค่า ) โดยการเซ็ทให้ตัวแปร state เป็น 1 เพื่อที่ในฟังก์ชั่น loop จะได้อัพเดตค่าบนจอ LCD ต่อไป ตัวอย่างโค้ดก็จะเป็นตามนี้นะครับ
1: void Encode(){
2: if(digitalRead(Encoder_B))
3: {
4: Encoder_number++;
5: }
6: else
7: {
8: Encoder_number--;
9: }
10: state=1;
11: }
และในส่วนของฟังก์ชั่น loop() เราก็จะเพิ่มโค้ดเพื่ออัพเดตค่าของ Encoder_number บนจอ LCD โดยจะทำการอัพเดตก็ต่อเมื่อมีการหมุน rotary encoder เท่านั้น ดังโค้ดตัวอย่างต่อไปนี้ครับ
1: if(state==1)
2: {
3: lcd.clear();
4: lcd.setCursor(9,1); // move cursor to second line "1" and 9 spaces over
5: lcd.printf("%d",Encoder_number);
6: lcd.setCursor(0,0); // move cursor to second line "1" and 9 spaces over
7: lcd.printf("Push the buttons"); // print a simple message
8: state=0;
9: }
สุดท้ายเราก็จะมาแก้ไขค่าการเปรียบเทียบค่าที่อ่านได้จาก ADC จากตัวอย่างในบทความเกี่ยวกับ LCD keys pad shield ที่ผ่านมา โดยแก้ไขค่าเป็นดังนี้ครับ
1: if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result
2: // For V1.1 us this threshold
3: if (adc_key_in < 50) return btnLEFT;
4: if (adc_key_in < 150) return btnUP;
5: if (adc_key_in < 250) return btnRIGHT;
6: if (adc_key_in < 450) return btnSELECT;
7: if (adc_key_in < 700) return btnDOWN;
8: if (adc_key_in < 850) return btnEncodeOK;
และต้องเพิ่มสเตรทในฟังก์ชั่น loop ขึ้นอีกอันหนึ่งด้วย สำหรับปุ่ม Encode OK ซึ่งโค้ดตัวเต็มก็จะเป็นดังนี้ครับ
1: /*-----------------------------------------------------
2: Author: --<Ekkachai Muangrodpai>
3: www.picgetstart.blogspot.com
4: Date: 2016-03-18
5: Board: Pinguino8 by Jimmy + LCD Keypads Shield V.1
6: Description:
7: -Display a message on LCD Keypads Shield, and also read a keys through analog AN0.
8: -At the same time, display the second time too.
9: * LCD RS pin to digital pin 5
10: * LCD E pin to digital pin 4
11: * LCD D4 pin to digital pin 24
12: * LCD D5 pin to digital pin 18
13: * LCD D6 pin to digital pin 17
14: * LCD D7 pin to digital pin 16
15: * LCD R/W pin to ground
16: note:
17: - don't use lcd.print() and lcd.printf() in the same program.
18: -----------------------------------------------------*/
19: #define BACK_LIGHT 2 //LCD back light control by pin2
20: // define some values used by the panel and buttons
21: int lcd_key = 0;
22: int adc_key_in = 0;
23: #define btnRIGHT 0
24: #define btnUP 1
25: #define btnDOWN 2
26: #define btnLEFT 3
27: #define btnSELECT 4
28: #define btnNONE 5
29: #define btnEncodeOK 6
30: #define Encoder_A 0
31: #define Encoder_B 1
32: volatile int Encoder_number=0;
33: volatile u8 state=0;
34: // read the buttons
35: int read_LCD_buttons()
36: {
37: adc_key_in = analogRead(0); // read the value from the sensor
38: // my buttons when read are centered at these valies: 0, 144, 329, 504, 741
39: // we add approx 50 to those values and check to see if we are close
40: if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result
41: // For V1.1 us this threshold
42: if (adc_key_in < 50) return btnLEFT;
43: if (adc_key_in < 150) return btnUP;
44: if (adc_key_in < 250) return btnRIGHT;
45: if (adc_key_in < 450) return btnSELECT;
46: if (adc_key_in < 700) return btnDOWN;
47: if (adc_key_in < 850) return btnEncodeOK;
48: return btnNONE; // when all others fail, return this...
49: }
50: void Encode(){
51: if(digitalRead(Encoder_B))
52: {
53: Encoder_number++;
54: }
55: else
56: {
57: Encoder_number--;
58: }
59: state=1;
60: }
61: void setup( void) {
62: // put your setup code here, to run once:
63: pinMode(BACK_LIGHT, OUTPUT);
64: digitalWrite(BACK_LIGHT, HIGH);
65: // initialize the library with the numbers of the interface pins
66: lcd.pins(5, 4, 24, 18, 17, 16, 0, 0, 0, 0); // RS, E, D4 ~ D8
67: // set up the LCD's number of columns and rows:
68: lcd.begin(16, 2);
69: // Print a message to the LCD.
70: lcd.clear();
71: lcd.setCursor(0,0);
72: lcd.printf("Push the buttons"); // print a simple message
73: //make the pin 2 and 3(which is pin 25, and 26 on Pinguino 8) of LCD keypad shield as input
74: //so that the pin 0 and 1 of Pinguino 8 that we use for interrupt on change can work correctly
75: pinMode(25, INPUT);
76: pinMode(26, INPUT);
77: pinMode(Encoder_A, INPUT);
78: pinMode(Encoder_B, INPUT);
79: digitalWrite(Encoder_A, HIGH);
80: digitalWrite(Encoder_B, HIGH);
81: OnChangePin0(Encode, INT_FALLING_EDGE);
82: }
83: void loop( void ) {
84: if(state==1)
85: {
86: lcd.clear();
87: lcd.setCursor(9,1); // move cursor to second line "1" and 9 spaces over
88: lcd.printf("%d",Encoder_number);
89: lcd.setCursor(0,0); // move cursor to second line "1" and 9 spaces over
90: lcd.printf("Push the buttons"); // print a simple message
91: state=0;
92: }
93: lcd.setCursor(0,1); // move to the begining of the second line
94: lcd_key = read_LCD_buttons(); // read the buttons
95: switch (lcd_key) // depending on which button was pushed, we perform an action
96: {
97: case btnRIGHT:
98: {
99: lcd.printf("RIGHT ");
100: break;
101: }
102: case btnLEFT:
103: {
104: lcd.printf("LEFT ");
105: break;
106: }
107: case btnUP:
108: {
109: lcd.printf("UP ");
110: break;
111: }
112: case btnDOWN:
113: {
114: lcd.printf("DOWN ");
115: break;
116: }
117: case btnSELECT:
118: {
119: lcd.printf("SELECT ");
120: break;
121: }
122: case btnNONE:
123: {
124: lcd.printf("NONE ");
125: break;
126: }
127: case btnEncodeOK:
128: {
129: lcd.printf("EncdOK ");
130: break;
131: }
132: }
133: }
และข้างล่างนี้คือวิดีโอผลการทำงานของ Pinguino 8 กับ LCD key pad shield V1.2 ครับ
ไม่มีความคิดเห็น:
แสดงความคิดเห็น