บทความนี้เป็นการใช้งาน GPIO ของไมโครคอนโทรลเลอร์ PIC18F458 ที่เชื่อมต่อกับโมดูล ADC หรือโมดูลแปลงสัญญาณแอนาล็อกให้เป็นสัญญาณดิจิทัลเพื่อใช้ในการอ่านค่าระดับแรงดันที่อยู่ในช่วง 0 ถึง 5V จากขานำเข้าสัญญาณ ทำให้ผู้ออกแบบระบบสามารถพิจารณารายละเอียดของแรงดันจากวงจร เช่น จากตัวต้านทานเปลี่ยนแปลงค่าได้ ตัวต้านทานเปลี่ยนแปลงค่าตามค่าความสว่าง หรือไมโครโฟน เป็นต้น เพื่อนำค่าเหล่านี้ไปประมวลผล หรือเข้าสู่เงื่อนไขการทำงานต่อไป เช่น การอ่านค่าแรงดันเพื่อรายงานผลว่าเป็นแรงดันในระดับ Lo, Hi หรือไม่เสถียร เป็นต้น โดยบนบอร์ดทดลองประจำวิชาสถาปัตยกรรมคอมพิวเตอร์มีวงจรของตัวต้านทานปรับค่าได้อยู่ 4 ชุดดังภาพที่ 1 ทำให้สามารถศึกษาการเขียนโปรแกรมเพื่อใช้งานโมดูล ADC ได้และสามารถนำไปประยุกต์ใช้ต่อไป
ADC
ADC หรือ Analog-to-Digital Convertor เป็นวิธีการแปลงค่าที่นำเข้าจากสัญญาณแอนาล็อกให้อยู่ในรูปของตัวเลขแบบดิจิทัล โดยค่าตัวเลขที่ได้จะอยู่ในช่วงของความละเอียดจากโมดูลแปลงสัญญาณ ซึ่งไมโครคอนโทรลเลอร์ PIC18F458 มีความละเอียดในการแปลงอยู่ที่ระดับ 10 บิต หรือ 210 ทำให้สามารถแปลงแรงดันที่อ่านจากขานำเข้าที่อยู่ในช่วง 0V ถึง 5V ให้เป็นค่าตัวเลข 0 ถึง 1023 หรือ 1024 ค่า
ขานำเข้าสัญญาณแอนาล็อก
ขานำเข้าสัญญาณที่เชื่อมต่อโมดูลการแปลงสัญญาณแอนาล็อกเป็นดิจิทัล มี 8 ขานำเข้าชื่อ AN0, AN1, AN2, AN3, NA4, AN5, AN6 และ AN7 ซึ่งกระจายอยู่ในพอร์ต A และ E ดังนี้
- RA0 เป็นขานำเข้าสัญญาณ AN0
- RA1 เป็นขานำเข้าสัญญาณ AN1
- RA2 เป็นขานำเข้าสัญญาณ AN2
- RA3 เป็นขานำเข้าสัญญาณ AN3
- RA5 เป็นขานำเข้าสัญญาณ AN4
- RE0 เป็นขานำเข้าสัญญาณ AN5
- RE1 เป็นขานำเข้าสัญญาณ AN6
- RE2 เป็นขานำเข้าสัญญาณ AN7
และผังการทำงานของโมดูลเป็นดังภาพที่ 2
การควบคุมภาค ADC
การทำงานของโมดูล ADC ถูกควบคุมโดยเรจิสเตอร์ ADCON0 และ ADCON1 ส่วนค่าที่อ่านได้นั้นอยู่ในเรจิสเตอร์ ADRESH และ ADRESL เพื่อเก็บค่าไบต์สูลและไบต์ต่ำของค่าที่อ่านได้ ซึ่งทั้ง 4 ตัวเป็นเรจิสเตอร์ขนาด 8 บิต
ADCON0
เรจิสเตอร์ ADCON0 อยู่ที่หน่วยความจำแรมของไมโครคอนโทรลเลอร์ตำแหน่ง 0xFC2 โดยค่าของแต่ละบิตของเรจิสเตอร์เป็นดังนี้
บิต7 | บิต6 | บิต5 | บิต4 | บิต3 | บิต2 | บิต1 | บิต0 |
ADCS1 | ADCS0 | CHS2 | CHS1 | CHS0 | GO/DONE’ | – | ADON |
เมื่อไมโครคอนโทรลเลอร์เริ่มทำงานหรือถูกรีเซ็ตจะมีค่าเป็น 0000 00-0 แต่ถ้าเกิดการขัดจังหวะจาก WDT (Watch Dog Timer เป็นตัวตั้งเวลาเพื่อให้ฝั่งซอฟต์แวร์ส่งสัญญาณไปให้ตัวจับเวลาเพื่อให้ทราบว่าซอฟต์แวร์ยังทำงานได้อย่างถูกต้อง ทั้งนี้เพื่อใช้สำหรับการทวนสอบว่าซอฟต์แวร์แฮงค์หรือไม่ และถ้าแฮงค์จะทำการรีเซ็ตตัวเอง) และกลับมาสู่การทำงานในโปรแกรม ค่าของเรจิสเตอร์ตัวนี้จะไม่ถูกเปลี่ยนแปลงค่า
ADCON1
เรจิสเตอร์ ADCON1 อยู่ที่หน่วยความจำแรมของไมโครคอนโทรลเลอร์ตำแหน่ง 0xFC1 และมีรายละเอียดของแต่ละบิตดังนี้
บิต7 | บิต6 | บิต5 | บิต4 | บิต3 | บิต2 | บิต1 | บิต0 |
ADFM | ADCS2 | – | – | PCFG3 | PCFG2 | PCFG1 | PCFG0 |
สถานะเริ่มต้นของ ADCON1 เมื่อเริ่มทำงานหรือถูกรีเซ็ตจะมีค่าเป็น 00== 0000 และหลังจากผ่านการทวนสอบของ WDT ที่ตั้งไว้ยังคงมีค่าเหมือนกับที่เคยตั้งค่าไว้ก่อนที่ WDT จะทำงาน
ความหมายของการตั้งค่าในเรจิสเตอร์ ADCON0 และ ADCON1 เป็นดังนี้
- การกำหนดความถี่ของการทำงานของโมดูลแปลงสัญญาณจะกำหนดจากค่าของ ADCS2, ADCS1 และ ADCS0 ตามตาราง ep6-3
ADCON1.ADCS2 | ADCON0.ADCS1 | ADCON0.ADCS0 | ความหมาย |
0 | 0 | 0 | FOSC/2 |
0 | 0 | 1 | FOSC/8 |
0 | 1 | 0 | FOSC/32 |
0 | 1 | 1 | FRC |
1 | 0 | 0 | FOSC/4 |
1 | 0 | 1 | FOSC/16 |
1 | 1 | 0 | FOSC/64 |
1 | 1 | 1 | FRC |
- การเลือกช่องสัญญาณที่กำลังตั้งค่า กำหนดด้วย CHS0, CHS1 และ CHS2 ใน ADCON0 ดังตาราง ep6-4
CHS2 | CHS1 | CHS0 | ความหมาย |
0 | 0 | 0 | เลือกตั้งค่า AN0 |
0 | 0 | 1 | เลือกตั้งค่า AN1 |
0 | 1 | 0 | เลือกตั้งค่า AN2 |
0 | 1 | 1 | เลือกตั้งค่า AN3 |
1 | 0 | 0 | เลือกตั้งค่า AN4 |
1 | 0 | 1 | เลือกตั้งค่า AN5 |
1 | 1 | 0 | เลือกตั้งค่า AN6 |
1 | 1 | 1 | เลือกตั้งค่า AN7 |
- การกำหนดให้ ADC ของช่องสัญญาณที่ระบุไว้ทำงาน ให้กำหนดบิต ADON โดยที่
- 0 หมายถึง ปิดหรือหยุดการทำงาน
- 1 หมายถึง เปิดการทำงานหรือทำงานต่อจากที่หยุดไว้
- การตรวจสอบว่าารแปลงสัญญาณทำงานเสร็จสิ้นแล้วหรือไม่นั้น สามารถตรวจสอบได้จากบิต GO/DONE’ โดยถ้ามีค่าเป็น 0 หมายถึงทำงานเสร็จสิ้น แต่ถ้าเป็น 1 หมายถึงโมดูลแปลงสัญญาณกำลังทำงาน
- การกำหนดรูปแบบของการเรียงบิตของผลลัพธ์ที่ได้ทำด้วยการกำหนดค่าแก่ ADFM ดังภาพที่ 3
- 1 หมายถึง เป็นแบบ Right Justified จะเก็บค่าเรียงกจากขวาไปซ้าย คือ บิต 9,8,7,6,5,4,3,2,1 และ 0 โดยบิต 0 ถึง 7 เก็บใน ADRESL และ 8 ถึง 9 เก็บในบิตที่ 0 และ 1 ของ ADRESH
- 0 หมายถึง เป็นแบบ Left Justified เพื่อเก็บเรียงจากซ้ายไปขวา หรือ บิตลำดับ 0,1,2,3,4,5,6,7,8 และ 9 ทำให้บิตที่ 0 ถึง 7 เก็บใน ADRESH และบิตที่ 8 และ 9 เก็บในบิตที่ 7 และ 6 ของ ADRESL
- การกำหนดโหมดของขาต่าง ๆ ในการทำ ADC จะกำหนดจากบิตด PCFG3, PCFG2, PCFG1 และ PCFG0 โดยให้ค่าตามตารางที่ ep6-5 และความหมายต่าง ๆ ดังนี้
- A หมายถึง ทำงานเป็นขานำเข้าสัญญาณแอนาล็อก
- D หมายถึง ทำงานเป็นขานำเข้าสัญญาณดิจิทัล
- VREF+ หมายถึง ทำงานเป็นขานำเข้าแรงดันอ้างอิง REF+
- VREF- หมายถึง ทำงานเป็นขานำเข้าแรงดันอ้างอิง REF-
- VDD หมายถึง ใช้แรงดันอ้างอิงตามที่ใช้กับขา VDD
- VSS หมายถึง ใช้แรงดันอ้างอิงตามที่ใช้กับขา VSS
- C/R เป็นจำนวนขาที่เป็นขานำเข้าสัญญาณแอนาล็อกกับจำนวนของขาที่ถูกใช้เป็นแหบ่งนำเข้าแรงดันสำหรับการอ้างอิง
PCFG | AN7 | AN6 | AN5 | AN4 | AN3 | AN2 | AN1 | AN0 | VREF+ | VREF- | C/R |
0000 | A | A | A | A | A | A | A | A | VDD | VSS | 8/0 |
0001 | A | A | A | A | VREF+ | A | A | A | AN3 | VSS | 7/1 |
0010 | D | D | D | A | A | A | A | A | VDD | VSS | 5/0 |
0011 | D | D | D | A | VREF+ | A | A | A | AN3 | VSS | 4/1 |
0100 | D | D | D | D | A | D | A | A | VDD | VSS | 3/0 |
0101 | D | D | D | D | VREF+ | D | A | A | AN3 | VSS | 2/1 |
0110 | D | D | D | D | D | D | D | D | – | – | 0/0 |
0111 | D | D | D | D | D | D | D | D | – | – | 0/0 |
1000 | A | A | A | A | VREF+ | VREF- | A | A | AN3 | AN2 | 6/2 |
1001 | D | D | A | A | A | A | A | A | VDD | VSS | 6/0 |
1010 | D | D | A | A | VREF+ | A | A | A | AN3 | VSS | 5/1 |
1011 | D | D | A | A | VREF+ | VREF- | A | A | AN3 | AN2 | 4/2 |
1100 | D | D | D | A | VREF+ | VREF- | A | A | AN3 | AN2 | 3/2 |
1101 | D | D | D | D | VREF+ | VREF- | A | A | AN3 | AN2 | 2/2 |
1110 | D | D | D | D | D | D | D | A | VDD | VSS | 1/0 |
1111 | D | D | D | D | VREF+ | VREF- | D | A | AN3 | AN2 | 1/2 |
ดังนั้น ได้ข้อสรุปว่า
- ต้องตั้งค่าการทำงานของ TRISA หรือ TRISE ของช่องสัญญาณที่ต้องการนำเข้าสัญญาณแอนาล็อกเป็น 1
- การตั้งค่าการทำงานต้องกำหนดในเรจิสเตอร์ ADCON0 และ ADCON1
- ผลลัพธ์ที่ได้จากการแปลงสัญญาณจะถูกเก็บในเรจิสเตอร์ ADRESH และ ADRESL ตามรูปแบบที่กำหนดในบิต ADFM
ตัวอย่างโปรแกรม
ตัวอย่างโปรแกรมของบทความนี้เป้นการอ่านค่าจากตัวต้านทานปรับค่าได้บนบอร์ดทดลองที่ถูกเชื่อมต่อกับ AN0 หรือขา RA0 ดังภาพที่ 4 หลังจากนั้นทำการแปลงค่าที่ได้ให้ถูกแบ่งเป็น 9 ระดับ เพื่อนำไปแสดงผลที่หลอด LED จำนวน 8 หลอดที่ถูกเชื่อมต่อกับพอร์ต D ดังภาพที่ 5 โดยแต่ละระดับมีผลดังนี้
- ระดับ 0 ค่า 0 ถึง 112 หมายถึง หลอด 0, 1, 2, 3, 4, 5, 6 และ 7 ดับ
- ระดับ 1 ค่า 113 ถึง 225 หมายถึง หลอด 0 ติด
- ระดับ 2 ค่า 226 ถึง 338 หมายถึง หลอด 0, และ 1 ติด
- ระดับ 3 ค่า 339 ถึง 451 หมายถึง หลอด 0, 1 และ 2 ติด
- ระดับ 4 ค่า 452 ถึง 564 หมายถึง หลอด 0, 1, 2 และ 3 ติด
- ระดับ 5 ค่า 565 ถึง 677 หมายถึง หลอด 0, 1, 2, 3 และ 4 ติด
- ระดับ 6 ค่า 678 ถึง 790 หมายถึง หลอด 0, 1, 2, 3, 4 และ 5 ติด
- ระดับ 7 ค่า 791 ถึง 903 หมายถึง หลอด 0, 1, 2, 3, 4, 5 และ 6 ติด
- ระดับ 8 ค่า 904 ถึง 1023หมายถึง หลอด 0, 1, 2, 3, 4, 5, 6 และ 7 ติด
การตั้งค่าแก่ ADCON0 และ ADCON1 เพื่อให้ AN0 ทำงาน โดยขาอื่นเป็นขาดิจิทัลเหมือนปกติ และเก็บข้อมูลแบบ Right Justified จึงทำได้ดังนี้
- การกำหนดค่าแก่ ADCON0 จึงเป็นดังนี้
- ADCS1, ADCS0 เป็น 0 กับ 0
- CHS2, CHS1, CHS0 เป็น 0, 0 และ 0
- สำหรับ ADCON1 ได้ค่าดังต่อไปนี้
- ADFM เป็น 1
- ADCS2 เป็น 0
- PCFG3, PCFG2, PCFG1, PCFG0 เป็น 1,1,1 และ 0
ภาพวงจรของตัวต้านทานปรับค่าได้เป็นดังภาพที่ 6 และตัวอย่างผลลัพธ์เป็นดังคลิปที่ 1
โค้ดโปรแกรมเป็นดังต่อไปนี้
#pragma config OSC = HS
#pragma config OSCS = ON
#pragma config PWRT = OFF
#pragma config BOR = ON
#pragma config BORV = 25
#pragma config WDT = OFF
#pragma config WDTPS = 128
#pragma config STVR = ON
#pragma config LVP = ON
#pragma config CP0 = OFF
#pragma config CP1 = OFF
#pragma config CP2 = OFF
#pragma config CP3 = OFF
#pragma config CPB = OFF
#pragma config CPD = OFF
#pragma config WRT0 = OFF
#pragma config WRT1 = OFF
#pragma config WRT2 = OFF
#pragma config WRT3 = OFF
#pragma config WRTC = OFF
#pragma config WRTB = OFF
#pragma config WRTD = OFF
#pragma config EBTR0 = OFF
#pragma config EBTR1 = OFF
#pragma config EBTR2 = OFF
#pragma config EBTR3 = OFF
#pragma config EBTRB = OFF
#define _XTAL_FREQ 20000000
#include <xc.h>
void setup() {
TRISA = 0b00000001; // AN0
TRISD = 0x00; // input
ADCON0 = 0B00000001; // ADC Configurations bits - ADC on
ADCON1 = 0B10001110; // AN0 as AD, Internal REF V
}
unsigned int result;
void loop() {
__delay_us(10);
ADCON0bits.GO = 1;
while (ADCON0bits.GO_nDONE); // wait
result = ((ADRESH<<8)|ADRESL);
if (result<113) {
PORTD = 0b00000000;
}
else if (result < 226) {
PORTD = 0b00000001;
}
else if (result < 339) {
PORTD = 0b00000011;
}
else if (result < 452) {
PORTD = 0b00000111;
}
else if (result < 565) {
PORTD = 0b00001111;
}
else if (result < 678) {
PORTD = 0b00011111;
}
else if (result < 791) {
PORTD = 0b00111111;
}
else if (result < 904) {
PORTD = 0b01111111;
}
else {
PORTD = 0b11111111;
}
}
void main(void) {
setup();
while (1) {
loop();
}
return;
}
สรุป
จากบทความนี้ผู้อ่านได้เรียนรู้หลักการทำงานของ ADC พร้อมทั้งวิธีการควบคุมการทำงานโมดูลแปลงสัญญาณแอนาล็อกมาเป็นสัญญาณดิจิทัลของไมครคอนโทรลเลอร์ PIC18F458 ที่เชื่อมต่อ AN0 หรือขา RA0 เข้ากับ V0 บนบอร์ดทดลแง และแสดงผลที่หลอดแอลอีดีทั้ง 8 หลอดที่เชื่อมต่อกับ PORTD จะพบว่าอ่านอ่านค่าจากสัญญาณแอนาล็อกนั้นไม่ยุ่งยาก แต่การตั้งค่าแก่ PCFG มีความสำคัญต่อการทำงานของภาคแปลงสัญญาณภายในชิพไมโครคอนโทรลเลอร์ และที่สำคัญ คือ ต้องกำหนด ADON ให้เป็น 1 และตั้งค่าหมายเลขช่องสัญญาณให้ตรงกับขาที่เลือกใช้เพื่อเปิดการทำงานของขาที่ต้องการใช้งานด้วย สุดท้าย ขอให้สนุกกับการเขียนโปรแกรมครับ
(C) 2022, โดย อ.อนุชาติ บุญมาก, อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ
ปรับปรุงเมื่อ 2022-02-22, 2022-03-19