1. 程式人生 > 其它 >外部方法呼叫內部_基礎教程14 Arduino內部中斷和庫使用

外部方法呼叫內部_基礎教程14 Arduino內部中斷和庫使用

技術標籤:外部方法呼叫內部

本專欄內容經修訂後,已在豆瓣閱讀集結成書出版:

e4bd5f5f9a9f5d4cb2c3906cbd06766b.png https://read.douban.com/ebook/106875966/​read.douban.com

我是潘,曾經是個工程師。這是為 http://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、串列埠等通訊協議)等一系列問題。