การใช้งานอินเตอร์รัพท์ ภาษาซี

อินเตอร์รัพท์ (Interrupt) คือการขัดจังหวะการทำงานของโปรแกรมปกติ เมื่อเกิดเหตุการณ์บางอย่างขึ้น ทำให้ซีพียูไปทำงานที่กำหนดไว้เมื่อเกิดอินเตอร์รัพท์ คำสั่ง Interrupt ใน ATmega328P คือ การขัดจังหวะ คำสั่งนี้มีประโยชน์มาก โดยเมื่อ ATmega328P ได้รับสัญญาณ Interrupt แจ้งเข้ามา จะหยุดพักงานที่ทำอยู่ แล้วกระโดดมาทำในคำสั่ง Interrupt ทำให้เราไม่ต้องใช้ if เช็คเงื่อนไขต่าง ๆ ซึ่งจะทำให้เสียเวลา และบางครั้งอาจติดทำคำสั่งอื่นอยู่ ทำให้ไม่ทำงานทันที

ชนิดของอินเตอร์รัพท์


แบ่งตามชนิดของการเกิดได้ดังนี้

  • อินเตอร์รัพท์จากภายนอก เช่น การเปลี่ยนสถานะลอจิกของพอร์ตใดพอร์ตหนึ่ง
  • อินเตอร์รัพท์จากภายใน เช่น อินเตอร์รัพท์ที่เกิดจากทามเมอร์


 รายการอุปกรณ์


ขั้นตอนการทํางาน

1 : ทดสอบโปรแกรมแรก กับ ATmega328P


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


2: การใช้งานอินเตอร์รัพท์ใน ATmega328P



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

ใน ATmega328P มีแหล่งอินเทอร์รัปต์ภายนอก 2 แหล่งคือ INT0, INT1 แต่ละแหล่งที่มาจะอยู่ที่ขาต่างกัน

  • INT0 ที่ PD2
  • INT1 ที่ PD3



การขัดจังหวะภายนอกมีประสิทธิภาพพอสมควรสามารถกำหนดค่าให้ทริกเกอร์หนึ่งใน 4 สถานะ ระดับต่ำจะเริ่มทำงานเมื่อใดก็ตามที่ขาตรวจจับสัญญาณ LOW (GND) ทริกเกอร์ Logic Change ใด ๆ ในขณะที่เปลี่ยนจาก HIGH (Vcc) เป็น LOW (GND) หรือจาก LOW (GND) เป็น HIGH (Vcc) บน ขา Falling Edge จะทริกเกอร์ในขณะที่เปลี่ยนจาก HIGH (Vcc) เป็น LOW (GND) บน Rising Edge จะทริกเกอร์ทันทีเปลี่ยนจาก LOW (GND) เป็น HIGH (Vcc) ส่วนที่ดีที่สุดคือคุณสามารถกำหนดค่า INTx แต่ละรายการได้อย่างอิสระ

การขัดจังหวะภายนอกใช้ 3 registers ดังรายการด้านล่าง ซึ่งจะพบได้ในส่วน “External Interrupts section of the datasheet.”

 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit 
EICRA– – – – ISC11 ISC10 ISC01 ISC00 

External Interrupt Control Register A

 ISCx1 ISCx0 DESCRIPTION 
00 Low level of INTx generates an interrupt request
01 Any logic change on INTx generates an interrupt request 
10 The falling edge of INTx generates an interrupt request 
11 The rising edge of INTx generates an interrupt request 

ISC Bit Settings

  7 bit6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit 
EIMSK –– – – – – INT1 INT0 

External Interrupt Mask Register

  7 bit6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit 
EIFR –– – – – – INTF1 INTF0 

External Interrupt Flag Register


3: การใช้งานขาอินเตอร์รัพท์ พื้นฐาน ขา PD2 หรือ ขา PD3


วิธีการต่อวงจรทำได้โดยต่อขาด้านหนึ่งของสวิตซ์เข้ากับ Input ขา PD2 ส่วนขาอีกด้านหนึ่งต่อเข้ากับ GND

การใช้งานอินเตอร์รัพท์ ภาษาซี

ตัวอย่างโค้ดด้านล่าง เมื่อเริ่มทำงาน LED สีแดง ที่เชื่อมต่อที่ขา PB5 จะติด แต่เมื่อมีการ กดสวิตช์ ที่เชื่อมต่อกับขา PD2 ซึ่งเป็นการการขัดจังหวะการทำงานของโปรแกรมปกติ จึงทำให้ LED สีแดง ที่เชื่อมต่อที่ขา PB5 นั้นดับลง ตามที่เราเขียนโค้ดไว้ ในส่วนของฟังก์ชัน ISR

#include <avr/io.h>
#include <avr/interrupt.h>

int main(void)
{
    
    DDRB |= 1<<5; // Configuring PB5 as Output
    PORTB |= 1<<5; // Writing HIGH to PB5

    DDRD &= ~(1 << DDD2);     // Clear the PD2 pin
    // PD2 (PCINT0 pin) is now an input

    PORTD |= (1 << PORTD2);    // turn On the Pull-up
    // PD2 is now an input with pull-up enabled


    EICRA |= (1 << ISC00);    // set INT0 to trigger on ANY logic change

    EIMSK |= (1 << INT0); // Turns on INT0


    sei();            // turn on interrupts

    while(1)

    {

    /*main program loop here */

    }
}


ISR (INT0_vect)
{
    /* interrupt code here */
    PORTB &= ~(1<<5); // Writing LOW to PB5

}
 



4: การเปลี่ยนขาใช้งานอินเตอร์รัพท์



สิ่งสำคัญอย่างหนึ่งที่ควรทราบใน ATmega8 รุ่นเก่าไม่มีไพน์ PCINT ดังนั้นส่วนของบทความนี้ ใช้กับ ATmega328 เท่านั้น

  7 bit6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit 
 PCICR– – – – – PCIE2 PCIE1 PCIE0 

Pin Change Interrupt Control Register

  7 bit6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit 
 PCIFR– – – – – PCIF2 PCIF1 PCIF0 

Pin Change Interrupt Flag Register

  7 bit6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit 
PCMSK0PCINT7PCINT6PCINT5PCINT4PCINT3PCINT2PCINT1PCINT0

Pin Change Mask Register 0

  7 bit6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit 
PCMSK2PCINT23PCINT22PCINT21PCINT20PCINT19PCINT18PCINT17PCINT16

Pin Change Mask Register 2

  7 bit6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit 
PCMSK1PCINT14PCINT13PCINT12PCINT11PCINT10PCINT9PCINT8

Pin Change Mask Register 1

  7 bit6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit 
PCMSK0PCINT7PCINT6PCINT5PCINT4PCINT3PCINT2PCINT1PCINT0

Pin Change Mask Register 0


บิต PCIEx ในการลงทะเบียน PCICR เปิดใช้งานการขัดจังหวะภายนอกและบอกให้ MCU ตรวจสอบ PCMSKx ในการเปลี่ยนสถานะ เมื่อขาเปลี่ยนสถานะ (HIGH เป็น LOW หรือLOW เป็น HIGH) และบิต PCINTx ที่สอดคล้องกันในทะเบียน PCMSKx คือสูงบิต PCIFx ที่สอดคล้องกันในทะเบียน PCIFR จะถูกตั้งค่าเป็นสูงและ MCU จะข้ามไปยังเวกเตอร์ขัดจังหวะที่เกี่ยวข้อง

4: การใช้งานขาอื่นๆใช้งานอินเตอร์รัพท์

วิธีการต่อวงจรทำได้โดยต่อขาด้านหนึ่งของสวิตซ์เข้ากับ Input ขา PB0 ส่วนขาอีกด้านหนึ่งต่อเข้ากับ GND

การใช้งานอินเตอร์รัพท์ ภาษาซี

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

#include <avr/io.h>
#include <avr/interrupt.h>


int main(void)
{  
    
    DDRB |= 1<<5; // Configuring PB5 as Output
    PORTB |= 1<<5; // Writing HIGH to PB5

    DDRB &= ~(1 << DDB0);     // Clear the PB0 pin
    // PB0 (PCINT0 pin) is now an input

    PORTB |= (1 << PORTB0);    // turn On the Pull-up
    // PB0 is now an input with pull-up enabled


    PCICR |= (1 << PCIE0);    // set PCIE0 to enable PCMSK0 scan

    PCMSK0 |= (1 << PCINT0);// set PCINT0 to trigger an interrupt on state change 

    sei();// turn on interrupts

    while(1)

    {

    /*main program loop here */

    }
}


ISR (PCINT0_vect)
{
    /* interrupt code here */
    PORTB &= ~(1<<5); // Writing LOW to PB5

}



<<<  C4: Push Button กดติดปล่อยดับ บทความก่อนหน้า | ภาษาซี C6: การใช้งาน PWM บทความต่อไป >>>

Leave a Reply

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

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

Privacy Preferences

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

Allow All
Manage Consent Preferences
  • Always Active

Save