[TH] PIC18F458 Ep.4 GPIO

หลังจากที่ได้สร้างโครงงานและโค้ดโปรแกรมเบื้องต้นจากบทความการใช้ MPLAB X IDE ไปก่อนหน้านี้ บทความนี้เป็นการใช้งานไมโครคอนโทรลเลอร์เพื่อใช้เป็นหน่วยนำออกสัญญาณหรือการ Output และนำเข้าสัญญาณหรือ Input ผ่านทางพอร์ตของ PIC18F458 ทั้งพอร์ต A, B, C และ D ผ่านทางวงจร LED และ Switch

ภาพที่ 1 บอร์ดทดลองแล็บสถาปัตยกรรมกับการทดลองใช้งาน GPIO

GPIO

GPIO หรือ General Purpose Input/Output เป็นขาของไมโครคอนโทรลเลอร์ที่สามารถสั่งงานให้ทำงานได้ทั้งเป็นส่วนนำเข้าสัญญาณ ส่วนนำออกสัญญาณ หรือหน้าที่พิเศษเฉพาะทาง เช่น ขาสำหรับ PWM, ขาสำหรับเป็นตัวนับ, ขาสำหรับเป็นขารอการขัดจังหวะ หรือขาสำหรับอ่าน/ส่งออกสัญญาณแอนาล็อก เป็นต้น

โดยจากผังการจัดวางขาของไมโครคอนโทรลเลอร์ PIC18F458 ในภาพที่ 2 จะพบว่า มีขาหลายขาที่เป็นทั้ง Input/Output และมีบางขาที่นำเข้าอย่างเดียว หรือนำออกเพียงอย่างเดียว นอกจากนี้บางขานั้นมีการทำหน้าที่มากกว่า 1 อย่าง เช่น ขาลำดับที่ 40 เป็นขาแบบทั้ง Input/Output ทำหน้าที่เป็น RB7 หรือขานำเข้าหรือส่งออกของพอร์ต B ลำดับที่ 8 (บิต 7) และทำหน้าที่ PGD ได้ด้วย หรือขาที่ 37 ที่ทำหน้าที่เป็นทั้ง Input และ Output แต่ทำหน้าที่ได้เพียงเป็นบิตที่ลำดับที่ 5 (บิต 4) ของพอร์ต B เป็นต้น

ภาพที่ 2 การจัดวางขาของ PIC18F458
ที่มา : p.4 ของ PIC18F458 Datasheet

จากภาพที่ 3 จะเห็นว่าไมโครคอนโทรลเลอร์มีพอร์ต A, B, C, D และ E ที่เรียกชื่อว่า PORTA, PORTB, PORTC, PORTD และ PORTE โดยขาของพอร์ตเหล่านี้เป็น GPIO และชื่อเรียกของแต่ละขาเป็นดังนี้

  • RA0,RA1,RA2,RA3,RA4,RA5และ RA6 รวมเป็น 7 ขาหรือ 7 บิต เป็นขาของพอร์ต A
  • RB0, RB1, RB2, RB3, RB4, RB5, RB6 และ RB7 รวมเป็น 8 ขาหรือ 8 บิต เป็นขาของพอร์ต B
  • RC0, RC1, RC2, RC3, RC4, RC5, RC6 และ RC7 รวมเป็น 8 ขาหรือ 8 บิต เป็นขาของพอร์ต C
  • RD0, RD1, RD2, RD3, RD4, RD5, RD6 และ RD7 รวมเป็น 8 ขาหรือ 8 บิต เป็นขาของพอร์ต D
  • RE0, RE1 และ RE2 รวมเป็น 3 ขาหรือ 3 บิต เป็นขาของพอร์ต E
ภาพที่ 3 Block Diagram ของ PIC18F458
ที่มา: p.11, Figure 1-2: PIC18F448/458 Block Diagram

การควบคุมทิศทาง

พอร์ต A, B, C, D และ E สามารถกำหนดทิศทางของการทำงานเพื่อให้เป็นขาสำหรับนำเข้าสัญญาณหรือนำออกสัญญาณด้วยการกำหนดค่าในเรจิสเตอร์ต่อไปนี้

  • TRISA สำหรับกำหนดหน้าที่ของขาในพอร์ต A
  • TRISB สำหรับกำหนดหน้าที่ของขาในพอร์ต B
  • TRISC สำหรับกำหนดหน้าที่ของขาในพอร์ต C
  • TRISD สำหรับกำหนดหน้าที่ของขาในพอร์ต D
  • TRISE สำหรับกำหนดหน้าที่ของขาในพอร์ต E

กำหนดให้ขาใดเป็นขานำเข้าสัญญาณนำออกสัญญาณต้องให้ค่าประจำของขานั้นเป็น 0 และถ้าต้องการให้เป็นขานำเข้าสัญญาณให้กำหนดค่าของบิตหรือขานั้นเป็น 1

  • นำเข้า ให้กำหนดเป็น 1
  • ส่งออก ให้กำหนดเป็น 0

การเข้าถึง

การกำหนดค่า/อ่านค่าหรือการเข้าถึงขาในแต่ละพอร์ตสามารถกระทำได้ 2 แบบ คือ ระบุเป็นลำดับของขา ดังนี้

  • สำหรับพอร์ต A ได้แก่ RA0, RA1, RA2, RA3, RA4, RA5 และ RA6
  • สำหรับพอร์ต B ได้แก่ RB0, RB1, RB2, RB3, RB4, RB5, RB6 และ RB7
  • สำหรับพอร์ต C ได้แก่ RC0, RC1, RC2, RC3, RC4, RC5, RC6 และ RC7
  • สำหรับพอร์ต D ได้แก่ RD0, RD1, RD2, RD3, RD4, RD5, RD6 และ RD7
  • สำหรับพอร์ต E ได้แก่ RE0, RE1 และ RE2

หรืออ้างอิงถึงชื่อพอร์ตดังนี้

  • PORTA สำหรับแทนชื่อพอร์ต A
  • PORTB สำหรับแทนชื่อพอร์ต B
  • PORTC สำหรับแทนชื่อพอร์ต C
  • PORTD สำหรับแทนชื่อพอร์ต D
  • PORTE สำหรับแทนชื่อพอร์ต E

การนำส่งค่าให้ขาหรือพอร์ตมีรูปแบบดังนี้ โดยการนำออก 1 หมายถึงส่งแรงดันที่มีสถานะในระดับ 1 ในสัญญาณดิจิทัลไปยังขานั้น และ 0 หมายถึงให้ขานั้นมีสถานะเป็นกราวด์ (Ground หรือ GND) ส่วนกรณีที่เป็นการส่งข้อมูลไปที่พอร์ตขจะเป็นตัวเลขจำนวนเต็มแบบไม่มีเครื่องหมาย +/-

ชื่อขา = 1

ชื่อขา = 0

ชื่อพอร์ต = ค่าขนาด 8 บิต

และกรณีของการอ่านค่าจากขาหรือพอร์ตมีรูปแบบดังนี้

ขา = ชื่อขา

พอร์ต = ชื่อพอร์ต

วงจร I/O

วงจรสำหรับนำเข้าและส่งออกมีหลักการทำงานแตกต่างกัน คือ

  • การนำเข้าจะเป็นการอ่านค่าจากสถานะที่ปรากฎ ณ ขณะที่ขาที่อ่านค่านั้นเป็นอยู่
  • การนำออกสัญญาณนั้นต้องพิจารณาวงจรที่รับสัญญาณขาออกจากไมโครคอนโทรลเลอร์วาต้องการแรงดันและกระแสเท่าใด
    • ถ้าไมโครคอนโทรลเลอร์สามารถขับแรงดันและกระแสได้โดยตรงให้เชื่อมต่อขาของไมโครคอนโทรลเลอร์เข้ากับวงจรภาคนำออกได้ โดยเรียกการขับแรงดันและกระแสโดยตรงนี้ว่าการทำ Source Current หรือไมโครคอนโทรลเลอร์เป็นแหล่งจ่ายไฟให้กับวงจรภายนอก
    • ส่วนกรณีที่ไมโครคอนโทรลเลอร์มีกระแสไม่มากพอสำหรับขับวงจรภายนอก ให้ออกแบบวงจรภายนอกทำงานเมื่อขาของไมโครคอนโทรลเลอร์เป็น 0 หรือ GND เรียกการทำงานแบบนี้ว่า Sunk Current

ตัวอย่างวงจรภาคนำเข้าและส่งออกที่เลือกใช้เป็นวงจรที่อยู่บนบอร์ดประจำการทำแล็บ คือ Switch และ LED

วงจร LED

วงจรนำออกสัญญาณดิจิทัลของบอร์ดทดลองเป็นวงจรที่ทำงานแบบ Source Current คือขับกระแสมาจากขาของไมโครคอนโทรลเลอร์ เนื่องจาก PIC18F458 สามารถขับกระแสได้มากพอสมควร และวงจรที่เป็นหลอด LED นั้นต้องการกระแสในการทำงานไม่มากจึงไม่ต้องออกแบบให้ทำงานแบบ Sink Current

ภาพที่ 4 วงจร LED บนบอร์ด PCK-1000

จากภาพที่ 4 จะพบว่าฝั่งขาด้านหนึ่งของ LED ต่อเข้ากับตัวต่านทานที่ต่อตรงจากขาไมโครคอนโทรลเลอร์ และอรกด้านต่อเข้ากับ GND ของระบบ ดังนั้น เมื่อส่งสัญญาณดิจิทัล 1 จึงทำให้วงจรครบการทำงานส่งผลให้หลอดแอลอีดีสว่าง ในทางตรงกันข้ามเมื่อส่ง 0 ทำให้ไม่มีแรงดันไหลผ่านหลอดแอลอีดีจึงทำให้หลอดแอลอีดีไม่สว่าง

วงจร Switch

สวิตช์เป็นอุปกรณ์นำเข้าสัญญาณประเภทหนึ่งที่ใช้กับระบบคอมพิวเตอร์ โดยบนบอร์ดทดลองมีสวิตช์จำนวน 5 ตัว ซึ่งเป็นสวิตช์แบบดิป (Dip Switch) จำนวน 1 ตัว (ตัวถังสีฟ้า และคันโยกเป็นสีขาว) กับสวิตช์แบบปุ่ม (Push Button) แบบกดติดปล่อยดับจำนวน 4 ตัว

ภาพที่ 5 วงจร Switch บนบอร์ด PCK-1000

จากภาพที่ 5 จะพบว่า S2 หรือ SW DIP-8 ที่เป็นสวิตช์ของตัวดิปสวิตช์นั้นต่อเข้ากับขา SW1,SW2, SW3, SW4, SW5, SW6, SW7 และ SW8 ส่วนสวิตช์แบบกดติดปล่อยดับ S3, S4, S5 และ S6 ต่อเข้ากับ SW5, SW6, SW7 และ SW8

เมื่อพิจารณาวงจรจะพบว่า มีการต่อไฟเข้ากับขาด้านหนึ่งของสวิตช์และเป็นด้านเดียวกันกับที่ต่อเข้ากับขานำเข้าสัญญาณของไมโครคอนโทรลเลอร์ ส่วนอีกด้านของสวิตช์ต่อเข้ากับ GND ของระบบ ดังนั้น

  • ถ้า SW DIP-8, หรือ S3, S4, S5, S6 มีสถานะเป็นปิดวงจร (หรือกดสวิตช์) จะทำให้แรงดันไหลผ่านจากแหล่งจ่ายไฟไปลง GND อย่างรวดเร็วทำให้ไม่มีแรงดันไปโผล่ที่ขาของไมโครคอนโทรลเลอร์ ส่งผลให้ได้รับค่าแรงดันเป็นสัญญาณดิจิทัล 0
  • ถ้าสถานะของสวิตช์เป็นแบบเปิดหรือไม่ถูกกดจะมีแรงดันอยู่ที่ขาของไมโครคอนโทรลเลอร์ทำให้อ่านค่าแรงดันที่ขาได้เป็นสัญญาณดิจิทัล 1

ตัวอย่างโปรแกรม

จากหลักการควบคุมทิศทางการทำงานของพอร์ต การเข้าถึงพอร์ต และตัวอย่างวงจร LED และ Switch เมื่อนำมาประยุกต์เขียนโปรแกรมเพื่อเป็นแนวทางได้ดังนี้

กระพริบหลอด LED จาก RA0, RB0, RC0, RD0

ตัวอย่างแรกเป็นการต่อขา RA0, RB0, RC0 และ RD0 เข้ากับวงจรหลอด LED ที่หลอด 0, 1, 2 และ 3 โดยต้องกำหนดให้พอร์ต A, B, C และ D ทั้งหมดเป็น OUTPUT ด้วยคำสั่งต่อไปนี้

การส่งแรงดันไปที่ขาใช้คำสั่งต่อไปนี้

การเปลี่ยนสถานะของขาให้เป็น GND เพื่อให้หลอดดับสังงานดังนี้

และการหน่วงเวลาจะต้องกำหนดความเร็วของ XTal เป็น 20MHz พร้อมทั้งกำหนด OSC เป็น HS ส่วนคำสั่งหน่วงเวลาคือ __delay_ms( จำนวนมิลลิวินาที )

// CONFIG1H
#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 // กำหนดค่าความถี่ของ XTal

void main(void) {
    // กำหนดให้ทุกขาของพอร์ต A, B, C และ D เป็น Output
    TRISA = 0x00;
    TRISB = 0x00;
    TRISC = 0x00;
    TRISD = 0x00;
    // วนรอบเรื่อย ๆ
    while (1) {
        RA0 = 1;  // LED ON
        RB0 = 1;  // LED ON
        RC0 = 1;  // LED ON
        RD0 = 1;  // LED ON
        __delay_ms(500); // 1 Second Delay
        RA0 = 0;  // LED OFF
        RB0 = 0;  // LED OFF
        RC0 = 0;  // LED OFF
        RD0 = 0;  // LED OFF
        __delay_ms(500); // 1 Second Delay
    }
    return;
}

ตัวอย่างการทำงานเป็นดังคลิป 1

ไฟวิ่งด้วย PORTC

ตัวอย่างนี้ใช้พอร์ต C ทั้งหมดเป็นขา OUTPUT และเริ่มต้นให้มีค่าเป็น 000000012 หรือ 0x01 หลังจากนั้นทำการเลื่อนบิตไปทางซ้าย หลังจากเลื่อนไปจนเป็นตำแหน่ง 100000002 หรือ 0x80 ให้กลับค่ามาเป็น 0x01 ใหม่่อีกครั้ง และทำซ้ำต่อไปเรื่อย ๆ

// CONFIG1H
#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 // กำหนดค่าความถี่ของ XTal

void main(void) {
    unsigned char patt = 0x01;
    TRISC = 0x00;
    while (1) {
        PORTC = patt;
        __delay_ms(100);
        patt <<= 1;
        if (patt == 0) {
            patt = 0x01;
        }
    }
    return;
}

ตัวอย่างการทำงานเป็นดังคลิป 2

กดติดปล่อยดับ

ตัวอย่างนี้เป็นการใช้พอร์ต C จำนวน 4 บิตสำหรับเป็นขา OUTPUT และอีก 4 บิตสำหรับ INPUT ดังนั้น

  • RC0 ต่อกับ SW5
  • RC1 ต่อกับ SW3
  • RC2 ต่อกับ SW7
  • RC3 ต่อกับ SW8
  • RC4 ต่อกับ LED0
  • RC5 ต่อกับ LED1
  • RC6 ต่อกับ LED2
  • RC7 ต่อกับ LED3

ทำให้ต้องกำหนดค่าของ TRISC เป็น 0x0F หรือ 000011112 โดยความสัมพันธ์ของการทำงานเป็นดังนี้

  • นำค่าจาก RC0 ไปลบ 1 แล้วนำผลลัพธ์ ส่งไปที่ RC4
  • นำค่าจาก RC1 ไปลบ 1 แล้วนำผลลัพธ์ ส่งไปที่ RC5
  • นำค่าจาก RC2 ไปลบ 1 แล้วนำผลลัพธ์ ส่งไปที่ RC6
  • นำค่าจาก RC3 ไปลบ 1 แล้วนำผลลัพธ์ ส่งไปที่ RC7
// CONFIG1H
#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 // กำหนดค่าความถี่ของ XTal

void main(void) {
    TRISC = 0x0F;
    while (1) {
        RC4 = 1-RC0;
        RC5 = 1-RC1;
        RC6 = 1-RC2;
        RC7 = 1-RC3;
        __delay_ms(100);
    }
    return;
}

ตัวอย่างการทำงานเป็นดังคลิป 3

สรุป

จากบทความนี้ผู้อ่านได้เรียนรู้ขั้นตอนของการกำหนดทิศทางการทำงานของพอร์ต การเข้าถึงพอร์ต การเข้าถึงขาของพอร์ต และประยุกต์หลักการเข้ากับวงจรของภาค LED และ Switch บนบอร์ดทดลองประจำวิชาอันเป้นพื้นฐานสำหรับการนำไปประยุกต์ใช้ต่อไป เช่น เปลี่ยนจากวงจร LED เป็นวงจรรีเลย์สำหรับเปิดปิดไฟ หรือโซลินอยด์ เพื่อเปิดปิดน้ำ เป็นต้น สุดท้าย ขอให้สนุกกับการเขียนโปรแกรมครับ

(C) 2022, โดย อ.อนุชาติ บุญมาก, อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ

ปรับปรุงเมื่อ 2022-02-11, 2022-02-18, 2022-03-14