การเขียนโปรแกรม GPIO เบื้องต้น

GPIO (General Purpose Input / Output) คืออินเทอร์เฟซที่ควบคุมด้วยซอฟต์แวร์ซึ่งมักพบในไมโครคอนโทรลเลอร์และไมโครโปรเซสเซอร์ ICs หรือชิปเซ็ตอินเทอร์เฟซบางตัว โดยทั่วไปแล้ว GPIO จะเป็นขาหนึ่งตัวขึ้นไปบน IC ซึ่งไม่มีจุดประสงค์พิเศษในตัวมันเอง แต่ช่วยอำนวยความสะดวกให้กับนักออกแบบอุปกรณ์ในการสร้างอินเทอร์เฟซ / การเชื่อมต่อระหว่าง IC และส่วนประกอบอุปกรณ์ต่อพ่วงโดยการเขียนโปรแกรมการลงทะเบียนฮาร์ดแวร์บางตัว ด้วยเหตุนี้ภายในข้อ จำกัด ขา GPIO สามารถปรับแต่งเพื่อใช้เพื่อให้มีฟังก์ชันหรือวัตถุประสงค์เฉพาะบางอย่างภายในการออกแบบอุปกรณ์ฮาร์ดแวร์

ในบทความนี้เราจะเห็นเทคนิคการเขียนโปรแกรมเพื่อตั้งค่า AVR GPIO เป็น Digital แบบ Input และ Output โดยจะครอบคลุมโหมดทั้งหมดที่สามารถตั้งค่า GPIO ได้และเราจะเห็นการใช้งานจริงสำหรับแต่ละโหมด

  • Output – Push Pull
  • Input
    • Internal Pull Up
    • External Pull Up
    • External Pull Down


GPIO เป็นเอาต์พุต – Push Pull


โปรแกรมแรกของระบบสมองกลฝังตัวมักจะเป็นโปรแกรมกะพริบ เริ่มต้นด้วย LED ใน วงจร ATmega328P ที่เราเคยทดสอบมา สิ่งนี้จะทำให้เริ่มต้นได้ง่ายเนื่องจากไม่ต้องใช้ฮาร์ดแวร์เพิ่มเติม


Registers (รีจิสเตอร์)

Atmel AVR เป็นไมโครคอนโทรลเลอร์ 8 บิต พอร์ตทั้งหมดกว้าง 8 บิต โดยมีพอร์ต 3 พอร์ต ทุกพอร์ตมีรีจิสเตอร์ ที่เชื่อมโยงกัน และแต่ละพอร์ตมี 8 บิต

รีจิสเตอร์ DDxn bit ใน DDRx จะเลือกทิศทางของขานี้ ถ้า DDxn เขียนลอจิกเป็น 1 จะมีการกำหนดค่า pxn เป็นขาเอาต์พุต ถ้า DDxn เขียนลอจิกเป็น 0 จะกำหนดค่า pxn เป็นขาอินพุต

ถ้า PORTxn ถูกเขียนลอจิกเมื่อกำหนดค่าขาเป็นขาเอาต์พุต ขาพอร์ตจะถูกขับเคลื่อนให้เป็น HIGH (1) ถ้า PORTxn เขียนลอจิกเป็น 0 เมื่อกำหนดค่าขาเป็นขาเอาต์พุตขาพอร์ตจะถูกขับเคลื่อนให้เป็น LOW (0)

เรามีพอร์ต 3 พอร์ตในไมโครคอนโทรลเลอร์ Atmega328P คือ พอร์ต B [PB], พอร์ต C [PC], พอร์ต D [PD]


จริงๆแล้ว พอร์ต B มี 8 ขา คือขา PB0 ถึง PB7 แต่ในการทำงานแบบบอร์ด Arduino UNO ขาของ PB6 และ PB7 ใช้สำหรับ Crystal Oscillator คือ PB6 หรือที่เป็นขาตัวเลข 9 เป็น XTAL1 และ PB7 หรือที่เป็นขาตัวเลข 10 เป็น XTAL2 ดังนั้นจึงไม่สามารถใช้ได้ เราจึงเหลือ 6 ขา ในการใช้งานเป็น GPIO คือ PB0 ถึง PB5


พอร์ต C มี 7 ขา คือขา PC0 ถึง PC6 แต่ในการใช้งานแบบบอร์ด Arduino UNO นั้น ขา PC6 หรือที่เป็นขาตัวเลข 1 จะใช้สำหรับ ~ RESET (ขารีเซ็ต) เราจึงสามารถใช้ได้เพียง 6 ขา ที่ เป็น GPIO คือ PC0 ถึง PC5

พอร์ต D มี 8 ขา คือ PD0 ถึง PD7 ในการใช้งานแบบบอร์ด Arduino UNO เราสามารถใช้ขาทั้ง 8 เป็น GPIO ได้อย่างมีประสิทธิภาพ

ให้เราดูแผนผังของการใช้งานแบบบอร์ด Arduino UNO และดูว่าความสามารถการใช้งานของขาต่างๆ

Arduino UNO Schematics Port Pin Mapping
Atmega328p Pin Mapping with Arduino UNO Board

BIT OPERANDS:



ฟังก์ชั่น BIT SHIFT (<< และ >>)


ฟังก์ชั่น Bit Shift ทำตามที่มันบอกมันจะเลื่อนข้อมูลไปทางซ้ายหรือทางขวาทีละนิด

ฟังก์ชัน Bit Shift เป็นฟังก์ชันคณิตศาสตร์บิตที่ใช้มากที่สุดเมื่อจัดการกับ AVR

ดังนั้นลองดูการดำเนินการนี้ในรูปแบบ mathematical โดยจะเว้นวรรคทุกๆอักขระที่ 4 เพื่อให้ตัวเลขอ่านง่ายดังนั้น 00011000 จะอ่านเป็น 0001 1000

เลื่อนบิตไปทางซ้าย:

ตอนนี้เราได้เห็นการขยับเล็กน้อยในแบบ mathematical มาดูในโค้ดกัน

i = 1;// i = 0000 0001
i = i << 2;    // i is now = 0000 0100
i = i >> 2;// i is now = 0000 0001


NOT (~)


โดยพื้นฐานแล้วมันจะพลิกบิตไปตรงข้าม

~ 0 = 1
~ 1 = 0

หากใช้ใน big register  จะเปลี่ยนแต่ละบิตให้อยู่ในสถานะตรงกันข้าม

~ 0000 1111 = 1111 0000

i = 0b10101010;
i = ~i;// i is now 01010101
i = 0b11111111;
i = ~i;// i is now 0


AND ( & )


ในทางคณิตศาสตร์และค่อนข้างง่ายคุณเปรียบเทียบตัวเลข 2 ตัวเข้าด้วยกันและถ้าทั้งคู่เป็น TRUE (1) คำตอบของคุณก็คือ TRUE (1)

0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1


OR and XOR ( | and ^ )


อาจทำให้สับสนเล็กน้อยมีฟังก์ชัน OR มี 2 ประเภทคือ INCLUSIVE หรือ (|) และ EXCLUSIVE OR (^)

OR (INCLUSIVE):

Inclusive หรือมักเรียกง่ายๆว่า OR

ฟังก์ชัน OR จะตรวจสอบว่า ether bit เป็น 1 หรือไม่

0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1


1: การเขียนโปรแกรม กำหนดค่าเป็นเอาต์พุต


ตัวอย่างที่ 1: การกำหนดค่าขา PB0 ถึง PB5 ของพอร์ต B ของ Atmega328P ให้เป็นเอาต์พุตทั้งหมด

DDRB = 0x3F; // ใช้ระบบเลขฐานสิบหก


หรือ

DDRB = 0b00111111; // ใช้ระบบเลขฐานสอง




ตัวอย่างที่ 2: การกำหนดค่า PB4 ขาเดียวให้เป็นเอาต์พุตโดยไม่ทำให้ขาอื่นๆเปลี่ยนแปลง โดยการ Bit Shift

DDRB |= 1 << 4;   // เลื่อนบิตไปทางซ้าย 4 ตำแหน่ง


หรือ

DDRB |= 0b00010000;  // ใช้ระบบเลขฐานสอง


หรือ

DDRB |= 0x10;  // ใช้ระบบเลขฐานสิบหก



ตัวอย่างที่ 3: การกำหนดค่าขาหลายขาเช่น PB2, PB3, PB4 เป็นเอาต์พุตด้วยการ Bit Shift

DDRB |= 1<<4 | 1<<3 | 1<<2;


หรือ

DDRB |= ‭0b00011100‬;


หรือ

DDRB |= 0x‭1C‬;


2: การเขียนโปรแกรม ขับเคลื่อนผลลัพธ์

ตัวอย่างที่ 1: การกำหนดขาของพอร์ต B ทั้งหมด คือขา PB0 ถึง PB5 ของ Atmega328P ให้เป็น HIGH

PORTB = 0x3F; // ใช้ระบบเลขฐานสิบหก


หรือ

PORTB = 0b00111111; // ใช้ระบบเลขฐานสอง



ตัวอย่างที่ 2: การกำหนดขา PB4 ขาเดียวให้เป็น HIGH โดยที่ขาอื่นๆไม่เปลี่ยนแปลง

PORTB | = 1 << 4;  // เลื่อนบิตไปทางซ้าย 4 ตำแหน่ง


หรือ

PORTB | = 0b00010000;  // ใช้ระบบเลขฐานสอง


หรือ

PORTB | = 0x10;  // ใช้ระบบเลขฐานสิบหก



ตัวอย่างที่ 3: การกำหนดหลายๆขา เช่น PB2, PB3, PB4 ให้เป็น HIGH

PORTB |= 1<<4 | 1<<3 | 1<<2;


หรือ

PORTB |= ‭0b00011100‬;



หรือ

PORTB |= 0x‭1C‬;



ตัวอย่างที่ 4: การกำหนดหลายๆขา เช่น PB1, PB2, PB5 ให้เป็น LOW

PORTB &= ~(1<<5 | 1<<2 | 1<<1);


หรือ

PORTB &= 0b‭11011001‬;


หรือ

PORTB &= 0x‭D9;



ตัวอย่างที่ 5: การกำหนดขา PB5 ขาเดียวให้เป็น LOW โดยไม่ทำให้ขาอื่นๆเปลี่ยนแปลง

PORTB &= ~(1 << 5);  // เลื่อนบิตไปทางซ้าย 5 ตำแหน่ง


หรือ

PORTB &= 0b11011111;  // ใช้ระบบเลขฐานสอง


หรือ

PORTB &= 0xDF;  // ใช้ระบบเลขฐานสิบหก


3: การเขียนโปรแกรม Binky LED


โค้ดตัวอย่างเป็นการทำให้ LED ที่อยู่เชื่อมต่ออยู่ที่ ขา PB5 ของ ATmega328P กระพริบได้ ด้วยการหน่วงเวลา หรือเว้นระยะ จะใช้วิธีที่เรียกว่า Software Delay Loop

ตัวอย่าง : ใช้ระบบเลขฐานสอง


 #define F_CPU 16000000UL // ให้นำค่า 16MHz ไปไปคำนวณร่วมกับฟังก์ชั่น delay.h ในการหน่วงเวลา
  
 #include <avr/io.h> // ใช้งานเกี่ยวกับควบคุมอินพุท/เอาท์พุท (เช่น PORTB, DDRB)
  
 #include <util/delay.h> // ใช้งานเกี่ยวกับการหน่วงเวลา
  
  int main(void)  // เป็นฟังก์ชั่นแรกที่โปแกรมเริ่มทำงาน 
  
  {  // เริ่มต้นบล็อกฟังก์ชั่น main()
  
      DDRB |= 0B100000; // กำหนดให้ขา PB5 ทำงานแบบเอาท์พุท
  
      while (1)  // วนลูปไปเรื่อยๆ
  
      { // เริ่มต้นบล็อกในส่วนของ while (1)
  
          PORTB |= 0B100000; // ให้ที่ขา PB5 ทำงานเป็น HIGH
  
          _delay_ms(1000); // หน่วงเวลารอ 1 วินาที
  
          PORTB &= ~ 0B100000; // ให้ที่ขา PB5 ทำงานเป็น LOW
  
          _delay_ms(1000); // หน่วงเวลารอ 1 วินาที
  
      }  // สิ้นสุดบล็อกในส่วนของ while (1)
  
 } //  สิ้นสุดบล็อกฟังก์ชั่น main()

หรือ

ตัวอย่าง : เลื่อนบิตไปทางซ้าย Bit Shift

#define F_CPU 16000000UL // ให้นำค่า 16MHz ไปไปคำนวณร่วมกับฟังก์ชั่น delay.h ในการหน่วงเวลา
 
#include <avr/io.h> // ใช้งานเกี่ยวกับควบคุมอินพุท/เอาท์พุท (เช่น PORTB, DDRB)

#include <util/delay.h> // ใช้งานเกี่ยวกับการหน่วงเวลา

 int main(void)  // เป็นฟังก์ชั่นแรกที่โปแกรมเริ่มทำงาน 

 {  // เริ่มต้นบล็อกฟังก์ชั่น main()

     DDRB |= 1<<5; // กำหนดให้ขา PB5 ทำงานแบบเอาท์พุท

     while (1)  // วนลูปไปเรื่อยๆ

     { // เริ่มต้นบล็อกในส่วนของ while (1)

         PORTB |= 1<<5; // ให้ที่ขา PB5 ทำงานเป็น HIGH

         _delay_ms(1000); // หน่วงเวลารอ 1 วินาที

         PORTB &= ~(1<<5); // ให้ที่ขา PB5 ทำงานเป็น LOW

         _delay_ms(1000); // หน่วงเวลารอ 1 วินาที

     }  // สิ้นสุดบล็อกในส่วนของ while (1)

}  //  สิ้นสุดบล็อกฟังก์ชั่น main()


GPIO as Input – Internal Pull Up


ถ้า DDxn เขียนลอจิกเป็น 0 จะกำหนดค่า pxn เป็นขาอินพุต

ถ้า PORTxn ถูกเขียนลอจิกเมื่อกำหนดค่าขาเป็นพินอินพุตตัวต้านทานแบบ Pull Up จะเปิดใช้งาน ในการปิดตัวต้านทานแบบ Pull Up นั้น PORTxn จะต้องเขียนลอจิกเป็น 0 หรือต้องกำหนดค่าขาเป็นขาเอาต์พุต port pins are tri-stated when reset จะใช้งานได้แม้ว่าจะไม่มีนาฬิกาทำงานอยู่ก็ตาม


4: การเขียนโปรแกรม กำหนดค่าเป็นอินพุต


ตัวอย่างที่ 1: การกำหนดค่าขาทั้งหมดของพอร์ต B คือ ขา PB0 ถึง PB5 ของ ATmega328P เป็นอินพุต

DDRB = 0xC0; // ใช้ระบบเลขฐานสิบหก


หรือ

DDRB = 0b11000000; // ใช้ระบบเลขฐานสอง



ตัวอย่างที่ 2: การกำหนดค่า PB4 ขาเดียวให้เป็นอินพุต โดยไม่ทำให้ขาอื่นๆเปลี่ยนแปลง

PINB |= (1 << 4); // เลื่อนบิตไปทางซ้าย 4 ตำแหน่ง


หรือ

DDRB &= ~(1 << 4);  // เลื่อนบิตไปทางซ้าย 4 ตำแหน่ง


หรือ

DDRB &= 0b11101111;   // ใช้ระบบเลขฐานสอง


หรือ

DDRB &= 0xEF; // ใช้ระบบเลขฐานสิบหก



ตัวอย่างที่ 3: การกำหนดค่าหลายๆขา เช่นขา PB2, PB3, PB4 ให้เป็นอินพุต

PINB |= (1<<4 | 1<<3 | 1<<2);


หรือ

DDRB &= ~(1<<4 | 1<<3 | 1<<2);


หรือ

DDRB &= ‭0b11100011‬;


หรือ

DDRB &= 0x‭E3‬;


5: การเขียนโปรแกรม เปิดใช้งาน Pull Up ภายใน


ตัวอย่างที่ 1: การเปิดใช้งาน Internal Pull Up ขาที่ใช้งานทั้งหมดของพอร์ต B คือขา PB0 ถึง PB5

PORTB = 0xC0;  // ใช้ระบบเลขฐานสิบหก


หรือ

PORTB = 0b11000000;  // ใช้ระบบเลขฐานสอง


ตัวอย่างที่ 2: การเปิดใช้งาน Internal Pull Up ขา PB4 ขาเดียว

PORTB |= 1 << 4;


หรือ

PORTB |= 0b00010000;


หรือ

PORTB |= 0x10;


6: การเขียนโปรแกรม รับค่าจากสวิตช์


ตัวอย่างที่ 1: การการรับค่าจากอินพุตของขาทั้งหมดของพอร์ต B คือขา PB0 ถึง PB5

uint8_t port_value = 0;
 port_value = PINB;  //  ใช้ระบบเลขฐานสิบหก

<<< C2: อัพโหลดโค้ด ด้วย External Tools บทความก่อนหน้า | บทความต่อไป C4: Push Button กดติดปล่อยดับ >>>

Leave a Reply

Your email address will not be published. Required fields are marked *

เราใช้คุกกี้เพื่อพัฒนาประสิทธิภาพ และประสบการณ์ที่ดีในการใช้เว็บไซต์ของคุณ คุณสามารถศึกษารายละเอียดได้ที่ นโยบายความเป็นส่วนตัว และสามารถจัดการความเป็นส่วนตัวเองได้ของคุณได้เองโดยคลิกที่ ตั้งค่า

Privacy Preferences

คุณสามารถเลือกการตั้งค่าคุกกี้โดยเปิด/ปิด คุกกี้ในแต่ละประเภทได้ตามความต้องการ ยกเว้น คุกกี้ที่จำเป็น

Allow All
Manage Consent Preferences
  • Always Active

Save