การใช้งานอินเตอร์รัพท์ ภาษาซี
อินเตอร์รัพท์ (Interrupt) คือการขัดจังหวะการทำงานของโปรแกรมปกติ เมื่อเกิดเหตุการณ์บางอย่างขึ้น ทำให้ซีพียูไปทำงานที่กำหนดไว้เมื่อเกิดอินเตอร์รัพท์ คำสั่ง Interrupt ใน ATmega328P คือ การขัดจังหวะ คำสั่งนี้มีประโยชน์มาก โดยเมื่อ ATmega328P ได้รับสัญญาณ Interrupt แจ้งเข้ามา จะหยุดพักงานที่ทำอยู่ แล้วกระโดดมาทำในคำสั่ง Interrupt ทำให้เราไม่ต้องใช้ if เช็คเงื่อนไขต่าง ๆ ซึ่งจะทำให้เสียเวลา และบางครั้งอาจติดทำคำสั่งอื่นอยู่ ทำให้ไม่ทำงานทันที
ชนิดของอินเตอร์รัพท์
แบ่งตามชนิดของการเกิดได้ดังนี้
- อินเตอร์รัพท์จากภายนอก เช่น การเปลี่ยนสถานะลอจิกของพอร์ตใดพอร์ตหนึ่ง
- อินเตอร์รัพท์จากภายใน เช่น อินเตอร์รัพท์ที่เกิดจากทามเมอร์
รายการอุปกรณ์
- 1. ชุดคิทต่อวงจร Minimum ATmega328P Circuit
- 2. Breadboard 700 Points SYB-120
- 3. Jumper (M2M) cable 10cm Male to Male
- 4. FT232RL FTDI USB To TTL Serial Converter Module
- 5. USB Male to Mini USB B 5pin Data Cable
- 6. Push Button Switch สวิตช์กดติดปล่อยดับ 2 ขา
ขั้นตอนการทํางาน
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 |
0 | 0 | Low level of INTx generates an interrupt request |
0 | 1 | Any logic change on INTx generates an interrupt request |
1 | 0 | The falling edge of INTx generates an interrupt request |
1 | 1 | The rising edge of INTx generates an interrupt request |
ISC Bit Settings
7 bit | 6 bit | 5 bit | 4 bit | 3 bit | 2 bit | 1 bit | 0 bit | |
EIMSK | – | – | – | – | – | – | INT1 | INT0 |
External Interrupt Mask Register
7 bit | 6 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 bit | 6 bit | 5 bit | 4 bit | 3 bit | 2 bit | 1 bit | 0 bit | |
PCICR | – | – | – | – | – | PCIE2 | PCIE1 | PCIE0 |
Pin Change Interrupt Control Register
7 bit | 6 bit | 5 bit | 4 bit | 3 bit | 2 bit | 1 bit | 0 bit | |
PCIFR | – | – | – | – | – | PCIF2 | PCIF1 | PCIF0 |
Pin Change Interrupt Flag Register
7 bit | 6 bit | 5 bit | 4 bit | 3 bit | 2 bit | 1 bit | 0 bit | |
PCMSK0 | PCINT7 | PCINT6 | PCINT5 | PCINT4 | PCINT3 | PCINT2 | PCINT1 | PCINT0 |
Pin Change Mask Register 0
7 bit | 6 bit | 5 bit | 4 bit | 3 bit | 2 bit | 1 bit | 0 bit | |
PCMSK2 | PCINT23 | PCINT22 | PCINT21 | PCINT20 | PCINT19 | PCINT18 | PCINT17 | PCINT16 |
Pin Change Mask Register 2
7 bit | 6 bit | 5 bit | 4 bit | 3 bit | 2 bit | 1 bit | 0 bit | |
PCMSK1 | – | PCINT14 | PCINT13 | PCINT12 | PCINT11 | PCINT10 | PCINT9 | PCINT8 |
Pin Change Mask Register 1
7 bit | 6 bit | 5 bit | 4 bit | 3 bit | 2 bit | 1 bit | 0 bit | |
PCMSK0 | PCINT7 | PCINT6 | PCINT5 | PCINT4 | PCINT3 | PCINT2 | PCINT1 | PCINT0 |
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 บทความต่อไป >>>