ในบทความนี้จะเป็นการทดลองอ่านค่าแรงดันตกคร่อมตัวต้านทานปรับค่าได้( 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: }
และด้านล่างนี้คือวิดีโอแสดงการทำงานครับ
ขอบคุณที่ติดตามครับ :>