บทความนี้เป็นการประยุกต์ใช้ GPIO ของ PIC18F458 เพื่อสั่งงานวงจรของแอลอีดี 8 หลอดที่จัดเรียงตำแหน่งกันเป็นเหมือนตัวเลขดังภาพที่ 1 โดยใช้หลอด LED จำนวน 8 หลอดมาจัดวางใหม่และเรียกว่า 7-Segment ที่สามารถนำไปประยุกต์แสดงผลตัวเลข และตัวอักษรได้อีกจำนวนหนึ่ง นอกจากนี้ บนตัวบอร์ดทดลองได้ติดตั้ง 7-Segment เอาไว้จำนวน 4 หลัก ทำให้สามารถเขียนโปรแกรมเพื่อควบคุมการแสดงผลข้อมูล 4 หลัก
7-Segment
7-Segment มีแอลอีดีทั้งสิ้น 8 ดวง สำหรับเป็นส่วนต่าง ๆ เรียกชื่อว่า A, B, C, D, E, F, G และ DP ดังภาพที่ 2 และการต่อวงจรของโมดูลแต่ละหลักจะเป็นดังผังวงจรในภาพที่ 3
จากภาพที่ 3 จะพบว่า ขาร่วมเป็น Vdd ซึ่งเป็นขารับแรงดันไฟฟ้ากระแสตรง นั่นหมายความว่า การจะให้หลอดติดตต้องให้ครบวงจรไฟฟ้า มีไฟเข้า ผ่านโหลด และลงกราวด์ โหลดถึงจะทำงานหรือติด ถ้าเราให้ขาที่เชื่อมต่อกับหลอด A ดึง DP เป็น 0 หมายความว่าเปลี่ยนสถานะที่พอร์ตให้เป็นกราวด์ ไฟฟ้าไหลผ่านโหลดได้ หลอดจึงสว่าง และในทางกลับกัน ถ้าเรากำหนดให้ขาของพอร์ตมีสถานะเป็น 1 ซึ่งคือเป็นไฟเหมือนกัน มันก็ไม่มีกราวด์ โหลดไม่ทำงาน หรือ หลอดไม่ติด
วงจรการเชื่อมต่อของโมดูล 7-Segment ของบอร์ดทดลองประจำวิชาเป็นดังภาพที่ 4
การสั่งงาน
จากภาพที่ 4 จะได้ว่า การส่งไปให้หลอด LED แต่ลตำแหน่งอันได้แก่ A, B, C, D, E, F, G และ P (หรือ DP) จะต้องต่อเข้ากับขั้ว D0, D1, D2, D3, D4, D5, D6 และ D7 โดยการส่งค่าเป็นดังนี้
- ส่ง 1 หมายถึงต้องการให้หลอดสว่าง
- ส่ง 0 หมายถึงต้องการให้หลอดดับ
เมื่อดำเนินการส่ง D0 ให้เป็น 1 โดยขาอื่น ๆ เป็น 0 ส่งผลให้เกิดการแสดงผลดังภาพที่ 5
เมื่อดำเนินการส่ง D1 ให้เป็น 1 โดยขาอื่น ๆ เป็น 0 ส่งผลให้เกิดการแสดงผลดังภาพที่ 6
เมื่อดำเนินการส่ง D2 ให้เป็น 1 โดยขาอื่น ๆ เป็น 0 ส่งผลให้เกิดการแสดงผลดังภาพที่ 7
เมื่อดำเนินการส่ง D3 ให้เป็น 1 โดยขาอื่น ๆ เป็น 0 ส่งผลให้เกิดการแสดงผลดังภาพที่ 8
เมื่อดำเนินการส่ง D4 ให้เป็น 1 โดยขาอื่น ๆ เป็น 0 ส่งผลให้เกิดการแสดงผลดังภาพที่ 9
เมื่อดำเนินการส่ง D5 ให้เป็น 1 โดยขาอื่น ๆ เป็น 0 ส่งผลให้เกิดการแสดงผลดังภาพที่ 10
เมื่อดำเนินการส่ง D6 ให้เป็น 1 โดยขาอื่น ๆ เป็น 0 ส่งผลให้เกิดการแสดงผลดังภาพที่ 11
เมื่อดำเนินการส่ง D7 ให้เป็น 1 โดยขาอื่น ๆ เป็น 0 ส่งผลให้เกิดการแสดงผลดังภาพที่ 12
กรณีที่ต้องการแสดงให้เป็นตัวเลข สามารถกำหนดให้ D0, D1, D2, D3, D4, D5, D6 และ D7 มีค่าดังตารางในภาพที่ 13
กรณีมีหลายหลัก
จากภาพที่ 4 จะพบว่าการเลือกให้หลักใดติดนั้นจะกำหนดด้วยการให้ DS1, DS2, DS3 หรือ DS4 มีค่าเป็น 1 ดังนั้นถ้าต้องการแสดงเลข 0 บนหลักที่ 1 และ 3 จะต้องกำหนดค่าของ D0, D1, D2, D3, D4, D5, D6, D7, DS1, DS2, DS3 และ DS4 ดังนี้
- D0 เป็น 1
- D1 เป็น 1
- D2 เป็น 1
- D3 เป็น 1
- D4 เป็น 1
- D5 เป็น 1
- D6 เป็น 0
- D7 เป็น 0
- DS1 เป็น 1
- DS2 เป็น 0
- DS3 เป็น 1
- DS4 เป็น 0
ดังนั้น ได้ข้อสรุปว่า
- ต้องการให้หลอดใดติด (สว่าง) ให้กำหนดค่าเป็น 1
- ต้องการให้หลอดใดดับให้กำหนดค่าเป็น 0
- ต้องการให้หลักใดแสดงผลให้กำหนดค่าของหลักนั้นเป็น 1
- ไม่ต้องการให้หลักใดแสดงผลให้กำหนดค่าของหลักนั้นเป็น 0
ตัวอย่างโปรแกรม
ตัวอย่างโปรแกรมสำหรับการใช้งาน 7-Segment ประกอบด้วยการควบคุมแบบ 1 หลัก และ 4 หลัก โดยแต่ละแบบจะเป็นการสั่งให้หลอดแอลอีดีแสดงผลเพื่อให้การวิ่งวนกับการแสดงเป็นตัวเลข ดังนี้
วิ่งๆๆ
ตัวอย่างแรกเป็นการสั่งให้หลอดแอลอีดีตำแหน่ง A, B, C, D, E, F, G และ DP หรือ P แสดงครั้งละ 1 ตำแหน่งเพื่อทดสอบการถูกต้องของการเชื่อมต่อวงจร และวงจรภายในโมดูล 7-Segment ว่าทำงานได้ถูกต้องหรือไม่ มีหลอดใดขาดหรือเสื่อมสภาพหรือไม่ โดยการเชื่อมต่อระหว่างขาของไมโครคอนโทรลเลอร์และวงจร 7 Segment ได้เชื่อมต่อเอาไว้ดังนี้
- ขา RC0 ต่อเข้ากับ D0
- ขา RC1 ต่อเข้ากับ D1
- ขา RC2 ต่อเข้ากับ D2
- ขา RC3 ต่อเข้ากับ D3
- ขา RC4 ต่อเข้ากับ D4
- ขา RC5 ต่อเข้ากับ D5
- ขา RC6 ต่อเข้ากับ D6
- ขา RC7 ต่อเข้ากับ D7
- ขา RD0 ต่อเข้ากับ DS1
หลักการทำงานของโปรแกรม ประกอบไปด้วย 2 ส่วน คือ
- setup()
- กำหนดให้ค่าที่ส่งออกพอร์ต C เป็น 0b00000001 หรือ 0x01 เพื่อให้หลอดตำแหน่ง A สว่าง
- กำหนดให้ขาของพอร์ต C ทำหน้าที่เป็น Output ทั้งหมด
- กำหนดให้ขาของพอร์ต D เป็น Output ทั้งหมดเหมือนกับพอร์ต C
- loop()
- ปิดการเลือกหลักที่ 1 ของชุดโมดูล 7Segment ด้วยการกำหนดให้ RD0 เป็น 0
- โอนข้อมูลรูปแบบของการแสดงผลไปให้พอร์ต C
- เลื่อนบิตของรูปแบบของการแสดงผลไปทางซ้าย 1 บิต
- ถ้าค่าที่เกิดจากการเลื่อนบิตทำให้ตัวแปรเป็น 0 ให้กำหนดค่าแปรเป็น 0x01 หรือ 0b00000001 เพื่อเริ่มการแสดงผลที่ตำแหน่ง A ใหม่อีกครั้ง
- เปิดการแสดงผลด้วยการกำหนดให้ RD0 เป็น 1
- หน่วงเวลาเพื่อให้มองเห็นผลของการแสดงผล
ตัวอย่างโปรแกรมเป็นดังนี้ และตัวอย่างของการทำงานเป็นดังคลิปที่ 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
#include <xc.h>
#define _XTAL_FREQ 20000000
unsigned char seg_pattern = 0b00000001;
void setup() {
TRISC = 0x00; // 0b00000000
TRISD = 0x00; // 0b00000000
}
void loop() {
// disable
PORTD = 0b00000000;
// transfer
PORTC = seg_pattern;
seg_pattern <<= 1;
if (seg_pattern == 0x00) {
seg_pattern = 0x01;
}
// enable
PORTD = 0b00000001;
// delay
__delay_ms(100);
}
void main(void) {
setup();
while (1) {
loop();
}
return;
}
นับ 0 ถีง 9
จากภาพที่ 13 ซึ่งเป็นตารางค่าที่ส่งให้ D0, D1, D2, D3, D4, D5, D6 และ D7 เพื่อให้การแสดงผลเป็นตัวเลข 0 ถึง 9 เมื่อนำมาเขียนโปรแกรมจะได้ดังนี้ และตัวอย่างของการแสดงผลเป็นตามคลิปที่ 2
#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
#include <xc.h>
#define _XTAL_FREQ 20000000
unsigned char number_patterns[] = {
0x3F,
0x06,
0x5B,
0x4F,
0x66,
0x6D,
0x7D,
0x07,
0x7F,
0x6F
};
void setup() {
TRISC = 0x00; // 0b00000000
TRISD = 0x00; // 0b00000000
}
void loop() {
for (int i=0; i<10; i++) {
// disable
PORTD = 0b00000000;
// transfer
PORTC = number_patterns[i];
// enable
PORTD = 0b00000001;
// delay
__delay_ms(1000);
}
}
void main(void) {
setup();
while (1) {
loop();
}
return;
}
วิ่งข้ามหลัก
ตัวอย่างที่ 3 เป็นการกำหนดให้ค่าของตำแหน่ง G ของหลักที่ 1 ถึง 4 แสดงเรียงกันไปเสมือนเป็นการวิ่งไปมาระหว่างหลัก (ดังคลิปที่ 3) ด้วยการกำหนดให้ PORTC มีค่าเป็น 0b01000000 และ PORTD มีค่าตาม dsx ที่มีการให้ค่าเป็น 0x01, 0x02, 0x04 และ 0x08 เรียงกันไปด้วยการเลื่อนบิตไปทางซ้ายในแต่ละรอบการทำงาน โดยโค้ดของโปรแกรมเป็นดังนี้
#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
#include <xc.h>
#define _XTAL_FREQ 20000000
unsigned char dsx=0x01;
void setup() {
TRISC = 0x00; // 0b00000000
TRISD = 0x00; // 0b00000000
}
void loop() {
// disable
PORTD = 0b00000000;
// transfer
PORTC = 0b01000000;
// enable
PORTD = dsx;
// move next
dsx <<= 1;
if (dsx == 0x10) {
dsx = 0x01;
}
// delay
__delay_ms(100);
}
void main(void) {
setup();
while (1) {
loop();
}
return;
}
แสดงตัวเลขแบบ 4 หลัก
ตัวอย่างสุดท้ายเป็นการแสดงตัวเลขของทั้ง 4 หลัก โดยแบ่งการทำงานเป็นดังนี้
- เริ่มต้นให้ทุกหลักเป็น 0
- แสดงหลักที่ 4 วนจาก 0 ไปถึง 9
- แสดงหลักที่ 3 วนจาก 0 ไปถึง 9
- แสดงหลักที่ 2 วนจาก 0 ไปถึง 9
- แสดงหลักแรกวนจาก 0 ถึง 9
โค้ดโปรแกรมเป็นดังนี้ และตัวอย่างคลิปของการทำงานเป็นดังคลิปที่ 4
#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
#include <xc.h>
#define _XTAL_FREQ 20000000
unsigned char number_patterns[] = {
0x3F,
0x06,
0x5B,
0x4F,
0x66,
0x6D,
0x7D,
0x07,
0x7F,
0x6F
};
void setup() {
TRISC = 0x00; // 0b00000000
TRISD = 0x00; // 0b00000000
}
void loop() {
int idx=0;
/////////////////// step 1 ////////////////////////
// disable
PORTD = 0b00000000;
// transfer
PORTC = number_patterns[0];
// enable
PORTD = 0b00001111;
/////////////////// step 2 ////////////////////////
for (idx=0; idx<10; idx++) {
for (int counter=0; counter<10; counter++) {
// disable
PORTD = 0b00000000;
// transfer
PORTC = number_patterns[0];
// enable
PORTD = 0b0000111;
__delay_ms(10);
// transfer
PORTC = number_patterns[idx];
// enable
PORTD = 0b0001000;
// delay
__delay_ms(10);
}
}
/////////////////// step 3 ////////////////////////
for (idx=0; idx<10; idx++) {
for (int counter=0; counter<10; counter++) {
// disable
PORTD = 0b00000000;
// transfer
PORTC = number_patterns[0];
// enable
PORTD = 0b0001011;
__delay_ms(10);
// transfer
PORTC = number_patterns[idx];
// enable
PORTD = 0b0000100;
// delay
__delay_ms(10);
}
}
/////////////////// step 4 ////////////////////////
for (idx=0; idx<10; idx++) {
for (int counter=0; counter<10; counter++) {
// disable
PORTD = 0b00000000;
// transfer
PORTC = number_patterns[0];
// enable
PORTD = 0b0001101;
__delay_ms(10);
// transfer
PORTC = number_patterns[idx];
// enable
PORTD = 0b0000010;
// delay
__delay_ms(10);
}
}
/////////////////// step 5 ////////////////////////
for (idx=0; idx<10; idx++) {
for (int counter=0; counter<10; counter++) {
// disable
PORTD = 0b00000000;
// transfer
PORTC = number_patterns[0];
// enable
PORTD = 0b0001110;
__delay_ms(10);
// transfer
PORTC = number_patterns[idx];
// enable
PORTD = 0b0000001;
// delay
__delay_ms(10);
}
}
}
void main(void) {
setup();
while (1) {
loop();
}
return;
}
จากโค้ดโปรแกรมจะพบว่าได้มีการวนรอบ counter เพื่อให้แสดงผลซ้ำอีก 10 ครั้ง อันส่งผลให้ตาของคนเราสามารถมองเห็นตัวเลขได้ชัดพอ ทั้งนี้เนื่องจากในการแสดงผลนั้นได้แสดงค่า 0 ของ 3 หลักเป้นเวลา 20 มิลลิวินาที และแสดงค่าตัวเลขที่วนรอบตาม idx อีก 20 มิลลิวินาที รวมระยะเวลาการแสดงผลเป้นรอบละ 40 มิลลิวินาที (ให้นักศึกษาลองเปลี่ยนค่านี้ หรือตัดออก แล้วไปเพิ่มจำนวนรอบของการทำงานของ counter เพื่อสังเกตความเปลี่ยนแปลง และบันทึกผลสำหรับเป็นประสบการณ์ในการนำไปใช้ต่อไป)
สรุป
จากบทความนี้ผู้อ่านได้เรียนรู้ วิธีการควบคุมโมดูล 7 Segment ทั้งในแบบ 1 หลักและหลายหลัก โดยจะพบว่าหลักการทำงานเหมือนกับการส่งข้อมูล 0 หรือ 1 ไปยังขาของไมโครคอนโทรลเลอร์ แต่ด้วยวิธีที่เหมาะสมกับวงจรกลับส่งผลให้การแสดงผลเปลี่ยนแตกต่างกันดันไป ด้วยเหตุนี้ การเขียนโปรแกรมจึงต้องมีความเข้าใจถึงหลักการทำงานของวงจรนั้น จึงจะสามารถสั่งงานการทำงานให้ได้อย่างที่ต้องการ และที่สำคัญ การคิดจะให้ผลลัพธ์เป็นอย่างไรแล้วสามารถทำได้หรือไม่นั้นล่วนต้องอาศัยประสบการณ์การได้ทดลองทำมาก่อนจึงจะตัดสินได้ว่าแนวคิดที่สร้างขึ้นมานั้นสามารถทำได้จริงด้วยวิธีการใด หรือไม่สามารถทำได้ด้วยเหตุผลใด สุดท้าย ขอให้สนุกกับการเขียนโปรแกรมครับ
(C) 2022, โดย อ.อนุชาติ บุญมาก, อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ
ปรับปรุงเมื่อ 2022-02-18, 2022-03-15