Arduino成长日记6 - 中断机制
中斷是在程序運(yùn)行中經(jīng)常用到的功能,用于處理一下實(shí)時(shí)性比較高的事件,首先來了解一下中斷的概念。
中斷
當(dāng)出現(xiàn)需要及時(shí)處理的事件(中斷請(qǐng)求)時(shí),CPU暫停當(dāng)前工作的執(zhí)行轉(zhuǎn)而處理應(yīng)急事件(中斷)的過程。即在程序運(yùn)行過程中,系統(tǒng)出現(xiàn)了一個(gè)必須由CPU立即處理的情況,此時(shí),CPU暫停當(dāng)前程序去處理這個(gè)新事件的過程就就是中斷。
舉個(gè)栗子:你正在看著一本Arduino程序設(shè)計(jì)的書,看到95頁時(shí)肚子餓了,你記下來頁碼跑去吃東西,吃完東西后繼續(xù)從書的95頁往下看;這里面看書就類似CPU在正常工作,肚子餓了吃東西就像處理中斷,中斷事件處理(吃東西)結(jié)束后(CPU)又從停下來的地方繼續(xù)運(yùn)行(看書)。
Arduino外部中斷
外部中斷是單片機(jī)實(shí)時(shí)地處理外部事件的一種內(nèi)部機(jī)制。當(dāng)某種外部事件發(fā)生時(shí),單片機(jī)的中斷系統(tǒng)將迫使CPU暫停正在執(zhí)行的程序,轉(zhuǎn)而去進(jìn)行中斷事件的處理;中斷處理完畢后.又返回被中斷的程序處,繼續(xù)執(zhí)行下去。
大多數(shù)的Arduino板至少擁有兩個(gè)外部中斷引腳:0號(hào)中斷(引腳2)和1號(hào)中斷(引腳3)。不同的Arduino控制板的中斷引腳還有所區(qū)別,如下表所示。
| Uno,Ethernet | 2 | 3 | X | X | X | X |
| Mega | 2 | 3 | 21 | 20 | 19 | 18 |
| Leonardo | 3 | 2 | 0 | 1 | X | X |
| Due | 所 | 有 | IO | 口 | 均 | 可 |
表中int表示Arduino板的中斷號(hào)對(duì)應(yīng)的引腳,X 則表示該板沒有對(duì)應(yīng)的中斷引腳, 在所有的Arduino控制板中,Arduino Due比較強(qiáng)大的中斷功能,允許在所有IO腳觸發(fā)外部中斷。
Arduino編程中的中斷均已函數(shù)調(diào)用的形式來配置及使用,相對(duì)來說比較簡單,下面是外部中斷的常用函數(shù)。
1、中斷函數(shù)
attachInterrupt(interrupt,function,mode)
描述:當(dāng)發(fā)生外部中斷時(shí),調(diào)用一個(gè)指定的函數(shù)。在程序中再次調(diào)用時(shí)可以指定新的中斷調(diào)用函數(shù);如在setup函數(shù)中初始化函數(shù)A為中斷0的中斷調(diào)用函數(shù),再次調(diào)用 attachInterrupt 函數(shù)時(shí),可以指定函數(shù)B為中斷0的中斷調(diào)用函數(shù),在程序中有需要可多次調(diào)用更新中斷函數(shù)。
語法:attachInterrupt(interrupt,function,mode)
attachInterrupt(pin,function,mode)(Due專用)
參數(shù):
interrupt,中斷編號(hào);pin,引腳編號(hào)(Due專用);
function:中斷發(fā)生時(shí)調(diào)用的函數(shù),此函數(shù)必須不帶任何參數(shù),不返回任何值。
mode:定義什么情況下觸發(fā)中斷,以下四個(gè)常數(shù)為mode的有效值:
- LOW: 當(dāng)引腳為低電平時(shí),觸發(fā)中斷;
- CHANGE:當(dāng)引腳電平發(fā)生變化時(shí),觸發(fā)中斷;
- RISING:當(dāng)引腳電平由低變高時(shí),觸發(fā)中斷(上升沿中斷);
- FALLING:當(dāng)引腳電平由高變低時(shí),觸發(fā)中斷(下降沿中斷);
對(duì)于Due而言,多了一個(gè)專用參數(shù) – HIGH,即當(dāng)引腳為高電平時(shí),觸發(fā)中斷;
注意:不要妄想在中斷函數(shù)中加延時(shí),在Arduino中斷函數(shù)中,delay()不會(huì)生效,millis()不會(huì)持續(xù)累加;當(dāng)中斷發(fā)生時(shí),串口數(shù)據(jù)可能會(huì)出現(xiàn)丟包;在中斷函數(shù)里面使用到的全局變量應(yīng)該聲明為volatile變量。
示例:
const byte ledPin = 13; const byte interruptpin = 2; volatile byte state = LOW;void setup(void) {pinMode(ledPin,OUTPUT);pinMode(interruptPin,INPUT_PULLUP);attachInterrupt(digitalPinToInterrupt(interruptPin),blink,CHANGE); }void loop() {digitalWrite(ledPin,state); }void blink() {state = !state; }detachInterrupt(interrupt)
描述:關(guān)閉對(duì)應(yīng)的中斷。
參數(shù):interrupt,禁用中斷的編號(hào)(0或1)。
2、中斷使能函數(shù)
interrupts()
描述:啟用中斷/重新啟用中斷(在被禁用中斷過后)。
noInterrupts()
描述:禁用中斷。在程序中如果有一些函數(shù)的運(yùn)行不希望被中斷打斷,可以調(diào)用noInterrupts函數(shù)來禁用中斷的發(fā)生,再配合interrupts函數(shù)恢復(fù)中斷使能。
示例:
Arduino定時(shí)器中斷
外部中斷是通過檢測(cè)輸入電平的變化,而產(chǎn)生中斷信號(hào)。除了外部中斷方式外,Arduino控制板還可以按時(shí)間變化產(chǎn)生中斷,這里使用到定時(shí)器(Timer),而對(duì)應(yīng)產(chǎn)生的中斷被稱為定時(shí)器中斷。
定時(shí)器是嵌入式系統(tǒng)中的一個(gè)特殊的計(jì)數(shù)器。它可以對(duì)分頻后時(shí)鐘信號(hào)的進(jìn)行計(jì)數(shù),當(dāng)計(jì)數(shù)值達(dá)到設(shè)定值,即會(huì)產(chǎn)生定時(shí)器中斷。且通過時(shí)鐘頻率和計(jì)數(shù)值可以計(jì)算出時(shí)間,所以可以達(dá)到以時(shí)間觸發(fā)中斷的效果。
即當(dāng)需要按一定的時(shí)間間隔執(zhí)行某個(gè)操作時(shí),就需要用到定時(shí)器中斷了。
以UNO為例,一共有3個(gè)定時(shí)器可以使用:Timer0,Timer1,Timer2。
每個(gè)定時(shí)器都有自己的函數(shù)庫,通過使用硬件內(nèi)部計(jì)時(shí)器中斷來實(shí)現(xiàn)中斷效果,下面以Timer1庫的程序?yàn)槔f明~
首先需要安裝TimeOne庫,也就是Timer1的對(duì)應(yīng)庫。下面提供兩種安裝方式,可以點(diǎn)擊鏈接跳到對(duì)應(yīng)的安裝教程:
第一種:Arduino IDE安裝TimerOne庫
第二種:Sublime Text3環(huán)境安裝TimerOne庫
安裝完成后,大概了解一下Timer1的庫函數(shù),打開.h文件TimerOne.h,除了包含 Arduino.h 外,還有一個(gè)宏定義聲明了Timer1是一個(gè)16位定時(shí)器。
隨后在TimerOne類中定義了定時(shí)器相關(guān)函數(shù),在配置函數(shù)中包含了初始化函數(shù)initialize()函數(shù),參數(shù)默認(rèn)為1000000毫秒,也就是1秒;setPeriod()函數(shù)較長就不發(fā)出來了,主要作用是設(shè)置定時(shí)器的觸發(fā)周期,在initialize()函數(shù)中調(diào)用。
//****************************// Configuration//****************************void initialize(unsigned long microseconds=1000000) __attribute__((always_inline)) {TCCR1B = _BV(WGM13); // set mode as phase and frequency correct pwm, stop the timerTCCR1A = 0; // clear control register A setPeriod(microseconds);}void setPeriod(unsigned long microseconds) __attribute__((always_inline)) {...}定時(shí)器狀態(tài)控制函數(shù)
//****************************// Run Control//****************************void start() __attribute__((always_inline)) {TCCR1B = 0;TCNT1 = 0; // TODO: does this cause an undesired interrupt?resume();}void stop() __attribute__((always_inline)) {TCCR1B = _BV(WGM13);}void restart() __attribute__((always_inline)) {start();}void resume() __attribute__((always_inline)) {TCCR1B = _BV(WGM13) | clockSelectBits;}PWM控制相關(guān)函數(shù)(函數(shù)體省略)
//****************************// PWM outputs//****************************void setPwmDuty(char pin, unsigned int duty) __attribute__((always_inline)) {...}void pwm(char pin, unsigned int duty) __attribute__((always_inline)) {...}void pwm(char pin, unsigned int duty, unsigned long microseconds) __attribute__((always_inline)) {...}void disablePwm(char pin) __attribute__((always_inline)) {...}最重要的來了,定時(shí)器中斷相關(guān)函數(shù)
//****************************// Interrupt Function//****************************void attachInterrupt(void (*isr)()) __attribute__((always_inline)) {isrCallback = isr;TIMSK1 = _BV(TOIE1);}void attachInterrupt(void (*isr)(), unsigned long microseconds) __attribute__((always_inline)) {if(microseconds > 0) setPeriod(microseconds);attachInterrupt(isr);}void detachInterrupt() __attribute__((always_inline)) {TIMSK1 = 0;}大概了解過定時(shí)器相關(guān)函數(shù)后,可以開始編寫定時(shí)器中斷程序了。
1、添加TimerOne頭文件(安裝庫之后);
2、初始化定時(shí)器,開啟定時(shí)器中斷;
3、編寫定時(shí)器中斷操作;
簡單程序如下
使用定時(shí)器中斷可以很好的解決Arduino的單任務(wù)機(jī)制,但是UNO的3個(gè)定時(shí)器資源同樣跟引腳PWM會(huì)有沖突,比如Timer0定時(shí)器負(fù)責(zé) millis()函數(shù) 和 delay()函數(shù) 的計(jì)時(shí)工作,同時(shí)控制著數(shù)字引腳 pin5,pin6 的PWM功能;Timer1定時(shí)器負(fù)責(zé)數(shù)字引腳 pin9、pin10 的PWM功能,也影響 Servo.h庫 的使用;Timer2定時(shí)器負(fù)責(zé)數(shù)字引腳 pin11,pin3 的PWM功能,也和蜂鳴器的 tone()函數(shù)有關(guān)。
綜上所述,定時(shí)器的使用會(huì)影響對(duì)應(yīng)引腳的PWM功能,和某些時(shí)序函數(shù)的運(yùn)行,比如Timer0負(fù)責(zé)delay()函數(shù)延時(shí)操作的,使用pin5、pin6的PWM輸出可能會(huì)有些微誤差,像analogWrite(5,0)和analogWrite(5,6)可能是一樣的效果
除了對(duì)應(yīng)的硬件定時(shí)器,還可以用Timer庫和SimpleTimer庫來實(shí)現(xiàn)定時(shí)效果,這兩個(gè)庫不用到硬件定時(shí)器,也是用到 millis()函數(shù) 來實(shí)現(xiàn)延時(shí),所以對(duì) delay函數(shù) 也是會(huì)有點(diǎn)影響。
串口中斷(沒這回事兒)
關(guān)于Arduino串口中斷的也是找了一段時(shí)間沒結(jié)果,后來發(fā)現(xiàn)了Arduino沒有串口中斷這回事,但是卻機(jī)緣巧合的在main函數(shù)中找到了串口的端倪。
以下是Arduino中main函數(shù)的內(nèi)容~
即是Arduino沒有串口中斷,但是卻用for循環(huán)來檢測(cè)串口事件,與loop函數(shù)在同個(gè)循環(huán)中,相當(dāng)于loop函數(shù)運(yùn)行一次順帶檢測(cè)一次串口事件。
總結(jié)
以上是生活随笔為你收集整理的Arduino成长日记6 - 中断机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 高斯白噪声-python高
- 下一篇: 函数指针实现回调函数