เมนู

วันอังคารที่ 25 ตุลาคม พ.ศ. 2559

Pinguino กับ ADC บน Multifunction Shield


               ในบทความนี้จะเป็นการทดลองอ่านค่าแรงดันตกคร่อมตัวต้านทานปรับค่าได้( POT ) บน Multifunction Shield ผ่านทาง Analog to Digital module ใน PIC18F45K50 แล้วนำค่ามาแสดงผลบน 7-segment โดยแบ่งเป็น 2 โปรแกรม อันแรกจะแสดงค่าตัวแรกจาก 0 - 1023 (ความละเอียด 10-bit, (2^10) - 1 = 1023) ส่วนโปรแกรมที่สอง จะนำค่าที่อ่านได้มาคำนวนกลับเป็นแรงดันตกคร่อมตัว POT แล้วแสดงผลบน 7-segment

รูปที่ 1 POT ของ Multifunction Shield

               จาก schematic ของ multifunction shield จะเห็นว่าตัว POT จะต่ออยู่กับอินพุต AN0 ดังนั้นเราจึงสามารถเรียกใช้ฟังก์ชัน analogRead() เพื่ออ่านค่าแรงดันตกคร่อมที่ขา AN0 ได้เลย โดยค่าที่ได้จะมีค่าระหว่าง 0- 1023
               ในส่วนของการแสดงผลบน 7-segment สามารถย้อนกลับไปดูรายละเอียดได้ในบทความที่ผ่านมาโดยกดที่ลิ้งค์ต่อไปนี้ได้ครับ
pinguino-7-segment-multifunction-shield

               ในส่วนเริ่มต้นจะมีการประกาศตัวแปรที่จะใช้งานในโปรแกรมของเรากันครับ
ตัวแปรอาเรย์ Dis_table[] จะเก็บค่าตัวเลข 0-9 ที่จะแสดงผลบน 7-segment ส่วนตัวแปร Dis_buf[] จะเก็บค่าที่จะ enable เลขแต่ละหลักให้ติดสว่างเอาไว้  โดยทั้งสองตัวแปรนั้นในระหว่างใช้งานไม่ได้มีการเปลี่ยนแปลงค่าดังนั้นเราสามารถประกาศเป็น const เพื่อที่ compiler จะได้เก็บค่าตัวแปรทั้งสองนี้ใน Flash memory จะได้ประหยัด RAM อันมีค่าของเราด้วย
              ส่วนตัวแปร disbuff[] นั้นจะเอาไว้เก็บค่าตัวเลขแต่ละหลักหลังจากที่ได้ decode ออกมาแล้ว และเตรียมพร้อมที่จะแสดงผล

1:  const unsigned char Dis_table[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0X80,0X90};     // ค่าตัวเลขที่ใช้แสดงใน 7 segment  
2:  const unsigned char Dis_buf[]  = {0xF1,0xF2,0xF4,0xF8};                   // ตำแหน่งของ 7 segment ที่ต้องการแสดง  
3:  unsigned char disbuff[] = {0, 0, 0, 0};   

ฟังก์ชัน display() เป็นฟังก์ชันที่จะแสดงตัวเลขที่เก็บอยู่ในตัวแปร disbuff[] ขึ้นไปแสดงผลบน 7-segment

1:  void display()  
2:  {  
3:    char i;  
4:   for(i=0; i<=3; i++)  
5:   {  
6:    digitalWrite(LATCH_DIO,LOW);                                  // เคลียร์ค่าตัวเลข  
7:    shiftOut(DATA_DIO, CLK_DIO, MSBFIRST, Dis_table[disbuff[i]]); // ตัวเลขที่จะแสดง  
8:    shiftOut(DATA_DIO, CLK_DIO, MSBFIRST, Dis_buf[i] );           // ตำแหน่งที่จะแสดง       
9:    digitalWrite(LATCH_DIO,HIGH);                                 // แสดงค่าตัวเลข    
10:   delay(2);                      
11:   }   
12:  }  

โค้ดในส่วน setup() จะเป็นดังนี้ครับ

1:  void setup() {  
2:   pinMode(LATCH_DIO,OUTPUT);  
3:   pinMode(CLK_DIO,OUTPUT);  
4:   pinMode(DATA_DIO,OUTPUT);   
5:   pinMode(25,INPUT);    //turn off buzzer first  
6:  }  

ในฟังก์ชัน loop() จะมีตัวแปรโลคอลชื่อ SUM รับค่าจากฟังก์ชัน analogRead() จากนั้นเราจึงนำมา decode เพื่อแยกตัวเลขแต่ละหลักไปเก็บไว้ในตัวแปร disbuff[] ท้ายสุดจึงเป็นการเรียกฟังกชัน display ให้นำค่าตัวเลขนั้นไปแสดงผล

1:  void loop() {  
2:    uint16_t SUM;  
3:    SUM=analogRead(POT);          //อ่านค่าจาก POT(AN0) 
4:    disbuff[0]=SUM/1000;          // คำนวณค่าที่จะแสดงในหลักพัน  
5:    disbuff[1]=SUM%1000/100;      // คำนวณค่าที่จะแสดงในหลักร้อย  
6:    disbuff[2]=SUM%100/10;        // คำนวณค่าที่จะแสดงในหลักสิบ  
7:    disbuff[3]=SUM%10;            // คำนวณค่าที่จะแสดงในหลักหน่วย  
8:    display();  
9:  }  

โค้ดตัวเต็มของโปรแกรมแรกครับ

1:  /*-----------------------------------------------------  
2:  Author: --<Ekkachai Muangrodpai>  
3:  Date: 2016-10-25  
4:  Description: Read the voltage drop on POT of Multifunction shield, then   
5:           Display on 7-Segment.  
6:  -----------------------------------------------------*/  
7:  /* Define shift register pins used for seven segment display */  
8:  #define LATCH_DIO   24  
9:  #define CLK_DIO     16  
10:  #define DATA_DIO    5  
11:  #define POT   A0  
12:  const unsigned char Dis_table[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0X80,0X90};     // ค่าตัวเลขที่ใช้แสดงใน 7 segment  
13:  const unsigned char Dis_buf[]  = {0xF1,0xF2,0xF4,0xF8};                   // ตำแหน่งของ 7 segment ที่ต้องการแสดง  
14:  unsigned char disbuff[] = {0, 0, 0, 0};  
15:  void setup() {  
16:   pinMode(LATCH_DIO,OUTPUT);  
17:   pinMode(CLK_DIO,OUTPUT);  
18:   pinMode(DATA_DIO,OUTPUT);   
19:   pinMode(25,INPUT);    //turn off buzzer first  
20:  }  
21:  void display()  
22:  {  
23:    char i;  
24:   for(i=0; i<=3; i++)  
25:   {  
26:    digitalWrite(LATCH_DIO,LOW);                         // เคลียร์ค่าตัวเลข  
27:    shiftOut(DATA_DIO, CLK_DIO, MSBFIRST, Dis_table[disbuff[i]]); // ตัวเลขที่จะแสดง  
28:    shiftOut(DATA_DIO, CLK_DIO, MSBFIRST, Dis_buf[i] );        // ตำแหน่งที่จะแสดง       
29:    digitalWrite(LATCH_DIO,HIGH);                        // แสดงค่าตัวเลข    
30:    delay(2);                      
31:   }   
32:  }  
33:  void loop() {  
34:    uint16_t SUM;  
35:    SUM=analogRead(POT);          //อ่านค่าจาก POT(AN0)  
36:    disbuff[0]=SUM/1000;          // คำนวณค่าที่จะแสดงในหลักพัน  
37:    disbuff[1]=SUM%1000/100;     // คำนวณค่าที่จะแสดงในหลักร้อย  
38:    disbuff[2]=SUM%100/10;     // คำนวณค่าที่จะแสดงในหลักสิบ  
39:    disbuff[3]=SUM%10;          // คำนวณค่าที่จะแสดงในหลักหน่วย  
40:    display();  
41:  }  


               โปรแกรมที่ 2 นั้นจะคล้ายกับโปรแกรมแรก เพียงแต่หลังจากที่อ่านค่า ADC เข้ามาแล้วนั้นก่อนที่จะแสดงผล เราจะนำค่าที่ได้มาเข้าสมการเพื่อคำนวณกลับไปเป็นแรงดันตกคร่อมซะก่อน แล้วจึงนำไปแสดงผลบน 7-segment
               โดยค่าที่อ่านเข้ามาได้จะนำมาเข้าสมการดังนี้ (ADCread * 5)/1023 โดยเลข 5 คือแรงดันสูงสุดที่ 5 V. และ 1023 คือ (2^10 - 1 = 1023) ครับ แต่คราวนี้ค่าที่ได้จะเป็นเลขทศนิยมหรือ floating poing ซึ่ง mcu จะใช้ทรัพยากร เวลา และ พลังงานมากในการคำนวณ ดังนั้นเราจะทำการปรับซักเล็กน้อยให้การคำนวณเป็นเลขจำนวนเต็ม ซึ่งเราต้องการให้แสดงตัวเลขหลังจุดทศนิยมเพียง 2 ตำแหน่งเท่านั้น ดังนั้นเราสามารถตัวเลข 5 ไปทางซ้าย 2 หลัก จะได้เป็น (ADCread * 500)/1023 ผลลัพธ์ที่ได้ก็จะนำมา decode แยกเป็นตัวเลขหน้าจุดทศนิยม 1 ตำแหน่ง และตัวเลขหลังจุดทศนิยม 2 ตำแหน่ง ในที่สุดก็จะได้โค้ดดังนี้ครับ

1:  void loop() {  
2:    ADCread=analogRead(POT);  
3:     volts=0;  
4:     decivolts = 0;  
5:     millivolts = (int)((((unsigned long)ADCread)*500)/1023);  
6:     while(millivolts>=100)          //hunderd digit  
7:      {  
8:          millivolts-=100;  
9:          volts++;  
10:      }  
11:      while(millivolts>=10)          //tens digit  
12:      {  
13:          millivolts-=10;  
14:          decivolts++;  
15:      }  
16:    disbuff[0]= volts;          // คำนวณค่าที่จะแสดงในหลักพัน  
17:    disbuff[1]= decivolts;               // คำนวณค่าที่จะแสดงในหลักร้อย  
18:    disbuff[2]= millivolts;          // คำนวณค่าที่จะแสดงในหลักสิบ  
19:    display();  
20:    delay(10);  
21:  }  

               คราวนี้มีปัญหาตามมานิดหน่อยคือเราต้องแสดงจุดทศนิยมหลังตัวเลขหลักแรกด้วย แต่ค่าในตัวแปร Dis_table[] เป็นค่าสำหรับแสดงตัวเลข 0 - 9 โดยที่ไม่มีจุดซึ่งเรายังจำเป็นต้องใช้ในการแสดงผลตัวเลขหลังจุดทศนิยมสองตำแหน่งดังนั้นไม่ควรไปเปลี่ยนแก้มัน ผมเลยประกาศตัวแปรใหม่ชื่อ Dis_table_DOT[] ให้เก็บค่าคงที่สำหรับแสดงตัวเลข 0 - 5 พร้อมจุดทศนิยม( เราใช้แค่ 0 - 5 เพราะแรงดันสูงสุดเพียงแค่ 5 โวลต์ครับ ) นอกจากนี้ก็ยังมีการประกาศตัวแปรเพิ่มอีก 3 ตัวเพื่อใช้เก็บค่าตัวเลขหลังจาก decode แล้ว ดังนั้นตัวแปรทั้งหมดจึงเป็นดังนี้ครับ

1:  const byte Dis_table[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0X80,0X90};      // ค่าตัวเลขที่ใช้แสดงใน 7 segment  
2:  const byte Dis_table_DOT[] = {0x40,0x79,0x24,0x30,0x19,0x12};             // ค่าตัวเลขที่ใช้แสดงใน 7 segment พร้อมจุดทศนิยม   
3:  const byte Dis_buf[]  = {0xF1,0xF2,0xF4,0xF8};                         // ตำแหน่งของ 7 segment ที่ต้องการแสดง  
4:  unsigned char disbuff[] = {0, 0, 0};  
5:  uint16_t millivolts,ADCread;  
6:  uint8_t volts,decivolts;  

               และสุดท้ายในฟังก์ชัน display() ก็ต้องมีการปรับแก้กันเล็กน้อยโดยในการแสดงตัวเลขหลักแรกให้ใช้ค่าคงที่จาก Dis_table_DOT ส่วนสองหลักถัดมาก็ให้ใช้ค่าคงที่จาก Dis_table แทน เท่านี้เราก็จะสามารถแสดงค่าแรงดันตกคร่อม POT บน 7-segment ได้แล้วครับ
โค้ดส่วนฟังกชัน display() ครับ

1:  void display()  
2:  {  
3:    char i;  
4:   for(i=0; i<3; i++)  
5:   {  
6:     if(i==0){  
7:        digitalWrite(LATCH_DIO,LOW);                             // เคลียร์ค่าตัวเลข  
8:        shiftOut(DATA_DIO, CLK_DIO, MSBFIRST, Dis_table_DOT[disbuff[i]]); // ตัวเลขที่จะแสดง  
9:        shiftOut(DATA_DIO, CLK_DIO, MSBFIRST, Dis_buf[i] );            // ตำแหน่งที่จะแสดง       
10:        digitalWrite(LATCH_DIO,HIGH);                            // แสดงค่าตัวเลข         
11:     }  
12:     else{  
13:      digitalWrite(LATCH_DIO,LOW);                         // เคลียร์ค่าตัวเลข  
14:      shiftOut(DATA_DIO, CLK_DIO, MSBFIRST, Dis_table[disbuff[i]]); // ตัวเลขที่จะแสดง  
15:      shiftOut(DATA_DIO, CLK_DIO, MSBFIRST, Dis_buf[i] );        // ตำแหน่งที่จะแสดง       
16:      digitalWrite(LATCH_DIO,HIGH);                        // แสดงค่าตัวเลข            
17:     }  
18:     delay(2);                 
19:   }   
20:  }  

และนี่คือโค้ดตัวเต็มของโปรแกรมที่สองครับ

1:  /*-----------------------------------------------------  
2:  Author: --<Ekkachai Muangrodpai>  
3:  Date: 2016-10-25  
4:  Description: Read the voltage drop on POT of Multifunction shield, then   
5:           calculate the voltage and Display on 7-Segment.  
6:  -----------------------------------------------------*/  
7:  /* Define shift register pins used for seven segment display */  
8:  #define LATCH_DIO   24  
9:  #define CLK_DIO     16  
10:  #define DATA_DIO    5  
11:  #define POT   A0  
12:  const byte Dis_table[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0X80,0X90};      // ค่าตัวเลขที่ใช้แสดงใน 7 segment  
13:  const byte Dis_table_DOT[] = {0x40,0x79,0x24,0x30,0x19,0x12};             // ค่าตัวเลขที่ใช้แสดงใน 7 segment พร้อมจุดทศนิยม   
14:  const byte Dis_buf[]  = {0xF1,0xF2,0xF4,0xF8};                         // ตำแหน่งของ 7 segment ที่ต้องการแสดง  
15:  unsigned char disbuff[] = {0, 0, 0};  
16:  uint16_t millivolts,ADCread;  
17:  uint8_t volts,decivolts;  
18:  void setup() {  
19:   pinMode(LATCH_DIO,OUTPUT);  
20:   pinMode(CLK_DIO,OUTPUT);  
21:   pinMode(DATA_DIO,OUTPUT);   
22:   pinMode(25,INPUT);    //turn off buzzer first  
23:  }  
24:  void display()  
25:  {  
26:    char i;  
27:   for(i=0; i<3; i++)  
28:   {  
29:     if(i==0){  
30:        digitalWrite(LATCH_DIO,LOW);                             // เคลียร์ค่าตัวเลข  
31:        shiftOut(DATA_DIO, CLK_DIO, MSBFIRST, Dis_table_DOT[disbuff[i]]); // ตัวเลขที่จะแสดง  
32:        shiftOut(DATA_DIO, CLK_DIO, MSBFIRST, Dis_buf[i] );            // ตำแหน่งที่จะแสดง       
33:        digitalWrite(LATCH_DIO,HIGH);                            // แสดงค่าตัวเลข         
34:     }  
35:     else{  
36:      digitalWrite(LATCH_DIO,LOW);                         // เคลียร์ค่าตัวเลข  
37:      shiftOut(DATA_DIO, CLK_DIO, MSBFIRST, Dis_table[disbuff[i]]); // ตัวเลขที่จะแสดง  
38:      shiftOut(DATA_DIO, CLK_DIO, MSBFIRST, Dis_buf[i] );        // ตำแหน่งที่จะแสดง       
39:      digitalWrite(LATCH_DIO,HIGH);                        // แสดงค่าตัวเลข            
40:     }  
41:     delay(2);                 
42:   }   
43:  }  
44:  void loop() {  
45:    ADCread=analogRead(POT);  
46:     volts=0;  
47:     decivolts = 0;  
48:     millivolts = (int)((((unsigned long)ADCread)*500)/1023);  
49:     while(millivolts>=100)          //hunderd digit  
50:      {  
51:          millivolts-=100;  
52:          volts++;  
53:      }  
54:      while(millivolts>=10)          //tens digit  
55:      {  
56:          millivolts-=10;  
57:          decivolts++;  
58:      }  
59:    disbuff[0]= volts;          // คำนวณค่าที่จะแสดงในหลักพัน  
60:    disbuff[1]= decivolts;               // คำนวณค่าที่จะแสดงในหลักร้อย  
61:    disbuff[2]= millivolts;          // คำนวณค่าที่จะแสดงในหลักสิบ  
62:    display();  
63:    delay(10);  
64:  }  

และด้านล่างนี้คือวิดีโอแสดงการทำงานครับ


ขอบคุณที่ติดตามครับ :>

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

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