เมนู

วันอังคารที่ 2 สิงหาคม พ.ศ. 2559

Pinguino กับ LCD Keys pad shield V1.2 (another shield)


              ในบทความก่อนหน้านี้ผมได้เขียนถึงการใช้บอร์ด 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 ของเรา
รูปที่ 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() ดังตัวอย่างโค้ดต่อไปนี้ครับ


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 ครับ




ไม่มีความคิดเห็น:

แสดงความคิดเห็น