在了解中斷子系統之前,首先要了解中斷的概念。你正在看書,這時電話響了,你會怎麼做呢?相信大多數人會這樣:先标記看到的位置,接完電話回來後繼續閱讀。這就是一個現實生活中中斷的例子,我們把“電話響了”成為中斷源。Arduino UNO R3的主處理器ATMega328P擁有26個中斷源,如下表所示:
向量号 | 程式位址 | 中斷源 | 中斷定義 | 中斷服務程式名稱 |
1 | 0x0000 | RESET | 外部電平複位,上電複位,掉電檢測複位,看門狗複位 | |
2 | 0x0002 | INT0 | 外部中斷請求0 | INT0_vect |
3 | 0x0004 | INT1 | 外部中斷請求1 | INT1_vect |
4 | 0x0006 | PCINT0 | 引腳電平變化中斷請求0 | PCINT0_vect |
5 | 0x0008 | PCINT1 | 引腳電平變化中斷請求1 | PCINT1_vect |
6 | 0x000A | PCINT2 | 引腳電平變化中斷請求2 | PCINT2_vect |
7 | 0x000C | WDT | 看門狗溢出中斷 | WDT_vect |
8 | 0x000E | TIMER2 COMPA | 定時/計數器2比較比對A | TIMER2_COMPA_vect |
9 | 0x0010 | TIMER2 COMPB | 定時/計數器2比較比對B | TIMER2_COMPB_vect |
10 | 0x0012 | TIMER2 OVF | 定時/計數器2溢出 | TIMER2_OVF_vect |
11 | 0x0014 | TIMER1 CAPT | 定時/計數器1事件捕捉 | TIMER1_CAPT_vect |
12 | 0x0016 | TIMER1 COMPA | 定時/計數器1比較比對A | TIMER1_COMPA_vect |
13 | 0x0018 | TIMER1 COMPB | 定時/計數器1比較比對B | TIMER1_COMPB_vect |
14 | 0x001A | TIMER1 OVF | 定時/計數器1溢出 | TIMER1_OVF_vect |
15 | 0x001C | TIMER0 COMPA | 定時/計數器0比較比對A | TIMER0_COMPA_vect |
16 | 0x001E | TIMER0 COMPB | 定時/計數器0比較比對B | TIMER0_COMPB_vect |
17 | 0x0020 | TIMER0 OVF | 定時/計數器0溢出 | TIMER0_OVF_vect |
18 | 0x0022 | SPI STC | SPI串行傳輸結束 | SPI_STC_vect |
19 | 0x0024 | USART RX | USART接收結束 | USART_RX_vect |
20 | 0x0026 | USART UDRE | USART資料寄存器空 | USART_UDRE_vect |
21 | 0x0028 | USART TX | USART,發送結束 | USART_TX_vect |
22 | 0x002A | ADC | 模數轉換結束 | ADC_vect |
23 | 0x002C | EE READY | EEPROM準備好 | EE_READY_vect |
24 | 0x002E | ANALOG COMP | 模拟比較器 | ANALOG_COMP_vect |
25 | 0x0030 | TWI | 兩線串行接口 | TWI_vect |
26 | 0x0032 | SPM READY | 儲存程式存儲器内容就緒 | SPM_ready_vect |
這裡以外部中斷0為例了解對中斷子系統的程式設計,沿用上一章中用于數字輸入示例的電路,這個示例使得按鍵在按下時LED的狀态取反:
1 // Interrupt.ino
2 const byte ledPin = 13;
3 const byte interruptPin = 2;
4 volatile byte state = LOW;
5
6 void setup() {
7 pinMode(ledPin, OUTPUT);
8 pinMode(interruptPin, INPUT_PULLUP);
9 attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE);
10 }
11
12 void loop() {
13 digitalWrite(ledPin, state);
14 }
15
16 void blink() {
17 state = !state;
18 }
與外部中斷相關的Arduino庫函數有:
attachInterrupt(digitalPinToInterrupt(pin), ISR, mode):啟用指定引腳的外部中斷并連接配接到指定中斷服務程式
pin:指定外部中斷的引腳
ISR:指定中斷服務程式的名稱
mode:LOW(低電平觸發中斷),CHANG(邏輯電平變化觸發中斷),RISING(上升沿觸發中斷)或FALLING(下降沿觸發中斷)
detachInterrupt(digitalPinToInterrupt(pin)):禁用指定中斷
pin:指定取消外部中斷的引腳
interrupts():開啟總中斷
noInterrupts():禁用總中斷
ATMega328P的外部中斷由2個相關寄存器控制,外部中斷控制寄存器EICRA的結構如下圖所示:
INT1 | INT0 | ||
ISC11 | ISC10 | ISC01 | ISC00 |
ISCx[1:0](x = 0, 1)位用于設定外部中斷的觸發方式,如下表所示:
ISCx[1:0] (x = 0, 1) | 外部中斷觸發方式 |
00 | 低電平 |
01 | 邏輯電平變化 |
10 | 下降沿 |
11 | 上升沿 |
外部中斷屏蔽寄存器EIMSK用于設定是否屏蔽外部中斷,它的結構如下圖所示:
INT1 | INT0 |
若向其中某位寫入1,則該位控制的外部中斷啟用;寫入0則禁用。
通過直接通路寄存器改寫以上程式為:
1 // Interrupt_reg.ino
2 volatile byte state = LOW;
3
4 void setup() {
5 DDRB |= (1 << PB5);
6
7 DDRD &= ~(1 << PD2);
8 PORTD |= (1 << PD2);
9 EICRA &= ~(1 << ISC01) & ~(1 << ISC00);
10 EIMSK |= (1 << INT0);
11 sei(); // 啟用總中斷
12 }
13
14 void loop() {
15 if (state == HIGH) {
16 PORTB |= (1 << PB5);
17 } else {
18 PORTB &= ~(1 << PB5);
19 }
20 }
21
22 // 外部中斷0中斷處理函數
23 ISR(INT0_vect) {
24 state = !state;
25 }