基礎教程14 Arduino內部中斷和庫使用

基礎教程14 Arduino內部中斷和庫使用

來自專欄 Arduino 魔法書5 人贊了文章

我是潘,曾經是個工程師。這是為 Ardui.Co 製作的 「Arduino 公開課」 系列的入門教程。前面幾課講解了外部中斷,現在開始介紹內部中斷,往後還會深入 Arduino 的內核來分析內部中斷的機制,並做一些很酷的實驗。有任何疑問請在評論區提出,我會逐一回答。

如果我在寫稿,收到外界指令,比如電話聲響去接電話、聽到門鈴聲去開門,那叫做外部中斷。內部中斷呢?我在規定的時間點上,去做一些事情,不管此刻正在幹什麼。儘管我沒日沒夜地寫稿,但我每天肯定會在12點、18點兩個時間段去吃飯,吃完繼續寫,就像定了一個鬧鐘。

Arduino 已經內置了鬧鐘,它們叫做定時器,可以設定 Arduino 隔多長時間干一件其他事情。不過,中斷時間不能太長,否則會影響正常的工作。

其實,在第12課 利用霍爾感測器測速的過程中,我們已經用到了定時器,只不過是通過程序調用millis() 的方式來實現,但程序中斷有一個問題:佔用CPU資源、效率不高、而且不準確(millis()會被外部中斷打斷)。而Arduino 內置的三個硬體定時器:timer0、timer1、timer2,不會佔用CPU資源、而且非常精確。

我們要感謝 Arduino 平台完善的開發環境,要調用硬體定時器並不困難,因為 Arduino 社區已經有不少庫函數供使用(否則,就要非常複雜的編程)。常用的有三個:TimerOne、MsTimer2、和 FlexiTimer2(打包下載),使用方法都很類似。先看看第一個演示程序:

/* 作者:Ardui.Co 效果:使用定時器讓板載LED每0.5s切換一下狀態 版本:1.0 更新時間:2017年2月21日*/#include "TimerOne.h"void setup(){ pinMode(13, OUTPUT); Timer1.initialize(500000); // 初始化 Timer1 ,定時器每間隔 0.5s(500000us = 500ms = 0.5s)執行中斷函數一次 Timer1.pwm(9, 512); // 設置D9 PWM 占空比為50% Timer1.attachInterrupt(Flash); // 設定 callback 為 Timer 的中斷函數 }void Flash(){ digitalWrite(13, digitalRead(13) ^ 1);// 「^」異或符,如果為HIGH,輸出 LOW,反之亦然}void loop(){ //這傢伙很輕鬆,啥都不用做}

顧名思義 TimerOne 庫函數調用的是 Timer1 定時器。

注意 Arduino 的 PWM 輸出是依靠內置的3個 Timer 來控制的,所以 Timer1 會同時影響到 D9、D10 兩個埠的 analogWrite() 方法,但可以通過調用 Timer1.pwm(pin, duty, period) 來設定,duty 是占空比(解析度為10bits,取值0~1023),period 是可選參數,設定周期,如果不設定則為默認值,範圍為 1 ~ 8388480us,即最大可產生1MHz方波 。

再看看 MsTimer2 :

/* 作者:Ardui.Co 效果:使用定時器讓D13 LED 每0.5s 切換一下狀態 版本:1.0 更新時間:2017年2月22日*/ #include <MsTimer2.h>void Flash() { digitalWrite(13, digitalRead(13) ^ 1);}void setup() { pinMode(13, OUTPUT); MsTimer2::set(500, Flash); // 定時器間隔 0.5s (500ms = 0.5s) MsTimer2::start();//開始計時}void loop() { //這傢伙很輕鬆,啥都不用做}

程序實在很簡單,但 TimerOne 和 MsTimer2 的區別非常大,前者調用的是Timer1 是 16bit 定時器,解析度可以達到1us,而後者調用Timer2 是8bit 定時器解析度只能達到1ms。而且 TimerOne 的函數方法要比 MsTimer2 豐富,更多用法可參考官方文檔。

MsTimer2 目前已經更新為 FlexiTimer2,用法完全是一樣的,但後者增加了「解析度「參數:

/* 作者:Ardui.Co 效果:使用定時器讓D13 LED 每0.5s 切換一下狀態 版本:1.0 更新時間:2017年2月22日*/#include <FlexiTimer2.h>void Flash() { digitalWrite(13, digitalRead(13) ^ 1);}void setup() { pinMode(13, OUTPUT); FlexiTimer2::set(500, 1.0 / 1000, Flash); FlexiTimer2::start();}void loop() { //這傢伙很輕鬆,啥都不用做}

解析度是個 double 參數,如果將 1/1000 設置為 1/1280,即每秒會調用中斷函數Flash() 1280次,也可以理解為 1/1280 秒,那麼中斷周期就是 500 * 1/1280。

現在有了硬體定時器,霍爾感測器測速程序就可以不用 millis() 來計時,提高效率和精確度:

/* 作者:Ardui.Co 效果:霍爾感測器測試方法3 版本:1.0 更新時間:2017年2月22日*/#include <MsTimer2.h>const byte interruptPin = 3;float Val = 0; //設置變數Val,計數void setup( ) { Serial.begin(9600); attachInterrupt(digitalPinToInterrupt(interruptPin), count, FALLING);//觸發信號必須是變化的,上升或下降皆可 MsTimer2::set(1000, Print);//每秒列印一次 MsTimer2::start();}void loop( ) { //這傢伙很輕鬆,啥都不用做}void count() { Val += 1;}void Print() { Serial.println(Val * 60); Val = 0;}

其實,關於中斷我們只是入了門,介紹了最基礎的用法,但已經滿足現階段,應用層面的開發需求了。進階教程裡面,我們將繼續深入探討,各種問題,比如中斷的優先順序別、利用定時器產生更頻率的 PWM、中斷能做與不能做的事(先提醒一下:中斷程序內不能使用I2C、SPI、串口等通信協議)等一系列問題。

推薦閱讀:

基礎教程7 Arduino 測量電壓與ADC精度
Arduino和Genuio產品入門指南(譯)
使用Arduino製作自動寵物餵食機
手動搭建你的高擴展C++神經網路框架
mBlock & Arduino(16)控制步進電機

TAG:單片機 | Arduino | 開源硬體 |