設置
對于本指南中的所有示例,將使用以下接線:
什么是中斷?
中斷是一個信號,告訴處理器立即停止其正在執行的操作并處理一些高優先級的處理。這種高優先級處理稱為中斷處理程序。
中斷處理程序與任何其他void函數一樣。如果編寫一個并將其附加到中斷,則在觸發該中斷信號時將調用該中斷。當您從中斷處理程序返回時,處理器將返回以繼續之前的操作。
它們來自何處?
可以生成中斷
外部中斷由于外部中斷引腳之一的狀態改變而引起的外部中斷。
Pin-change中斷是由于一組引腳中任何一個引腳的狀態改變。
它們有什么用?
使用中斷,您無需編寫循環代碼即可連續檢查高優先級中斷條件。您不必擔心由于長時間運行的子例程而反應遲鈍或錯過了按鍵操作。
當中斷發生時,處理器將自動停止正在執行的操作并調用中斷處理程序。您只需編寫代碼以響應中斷。
定時器中斷
請勿致電給我們,我們會打電話給您
在本系列的第1部分中,我們學習了如何使用millis()進行計時。但是為了進行這項工作,我們每次都要在循環中調用millis()來查看是否該做某事了。一毫秒多次調用millis()只是發現時間沒有改變,這是一種浪費。如果只需要每毫秒檢查一次,那會不會很好?
計時器和計時器中斷讓我們可以做到這一點。我們可以設置一個計時器來每毫秒中斷一次。計時器實際上會打電話給我們,讓我們知道現在該檢查時鐘了!
Arduino計時器
Arduino Uno有3個計時器:Timer0,Timer1和Timer2。已經將Timer0設置為生成毫秒中斷,以更新millis()報告的毫秒計數器。既然這就是我們要尋找的東西,我們也將得到Timer0來為我們生成一個中斷!
頻率和計數
定時器是簡單的計數器,它們以從16MHz系統時鐘。您可以配置時鐘分頻器以更改頻率和各種不同的計數模式。您還可以將它們配置為在計時器達到特定計數時生成中斷。
Timer0是8位,從0到255計數,并在溢出時生成中斷。默認情況下,它使用64的時鐘分頻比為我們提供976.5625 Hz的中斷率(就我們的目的而言,足夠接近1KHz)。我們不會弄亂Timer0的頻率,因為那樣會破壞breakmillis()!
比較寄存器
Arduino定時器具有許多配置寄存器??梢允褂肁rduino IDE中定義的特殊符號來讀取或寫入這些符號。 有關所有這些寄存器及其功能的詳細說明,請參見下面的“ 進一步閱讀”中的鏈接。
我們將建立一個《計時器0的strong》比較寄存器(該寄存器稱為OCR0A),在該計數中間的某個位置產生另一個中斷。在每次滴答時,計時器計數器都會與比較寄存器進行比較,并且當它們相等時,將產生一個中斷。
只要計數器值超過0xAF,下面的代碼就會產生一個“ TIMER0_COMPA”中斷。
下載:文件
復制代碼
// Timer0 is already used for millis() - we‘ll just interrupt somewhere
// in the middle and call the “Compare A” function below
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A); // Timer0 is already used for millis() - we’ll just interrupt somewhere
// in the middle and call the “Compare A” function below
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
然后,我們將為定時器中斷向量(稱為“ TIMER0_COMPA_vect”)定義一個中斷處理程序。在此中斷處理程序中,我們將完成循環中所有的工作。
下載:文件
復制代碼
// Interrupt is called once a millisecond,
SIGNAL(TIMER0_COMPA_vect)
{
unsigned long currentMillis = millis();
sweeper1.Update(currentMillis);
//if(digitalRead(2) == HIGH)
{
sweeper2.Update(currentMillis);
led1.Update(currentMillis);
}
led2.Update(currentMillis);
led3.Update(currentMillis);
} // Interrupt is called once a millisecond,
SIGNAL(TIMER0_COMPA_vect)
{
unsigned long currentMillis = millis();
sweeper1.Update(currentMillis);
//if(digitalRead(2) == HIGH)
{
sweeper2.Update(currentMillis);
led1.Update(currentMillis);
}
led2.Update(currentMillis);
led3.Update(currentMillis);
}
這給我們留下了一個完全空的循環。
下載:文件
復制代碼
void loop()
{
} void loop()
{
}
您現在可以在循環中做任何您想做的事情。您甚至可以decade廢并使用delay()!閃光燈和掃地機將不在乎。無論如何,它們仍然會每毫秒被調用一次!
進一步閱讀:
這只是計時器可以執行的簡單示例。有關不同類型的計時器及其配置方式的更多詳細信息,請查看“庫和鏈接”頁面。
源代碼:
以下是整個代碼,包括閃光燈和掃地機:
下載:文件
復制代碼
#include
class Flasher
{
// Class Member Variables
// These are initialized at startup
int ledPin; // the number of the LED pin
long OnTime; // milliseconds of on-time
long OffTime; // milliseconds of off-time
// These maintain the current state
int ledState; // ledState used to set the LED
unsigned long previousMillis; // will store last time LED was updated
// Constructor - creates a Flasher
// and initializes the member variables and state
public:
Flasher(int pin, long on, long off)
{
ledPin = pin;
pinMode(ledPin, OUTPUT);
OnTime = on;
OffTime = off;
ledState = LOW;
previousMillis = 0;
}
void Update(unsigned long currentMillis)
{
if((ledState == HIGH) && (currentMillis - previousMillis 》= OnTime))
{
ledState = LOW; // Turn it off
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis 》= OffTime))
{
ledState = HIGH; // turn it on
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
}
};
class Sweeper
{
Servo servo; // the servo
int pos; // current servo position
int increment; // increment to move for each interval
int updateInterval; // interval between updates
unsigned long lastUpdate; // last update of position
public:
Sweeper(int interval)
{
updateInterval = interval;
increment = 1;
}
void Attach(int pin)
{
servo.attach(pin);
}
void Detach()
{
servo.detach();
}
void Update(unsigned long currentMillis)
{
if((currentMillis - lastUpdate) 》 updateInterval) // time to update
{
lastUpdate = millis();
pos += increment;
servo.write(pos);
if ((pos 》= 180) || (pos 《= 0)) // end of sweep
{
// reverse direction
increment = -increment;
}
}
}
};
Flasher led1(11, 123, 400);
Flasher led2(12, 350, 350);
Flasher led3(13, 200, 222);
Sweeper sweeper1(25);
Sweeper sweeper2(35);
void setup()
{
sweeper1.Attach(9);
sweeper2.Attach(10);
// Timer0 is already used for millis() - we‘ll just interrupt somewhere
// in the middle and call the “Compare A” function below
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
}
// Interrupt is called once a millisecond, to update the LEDs
// Sweeper2 s not updated if the button on digital 2 is pressed.
SIGNAL(TIMER0_COMPA_vect)
{
unsigned long currentMillis = millis();
sweeper1.Update(currentMillis);
if(digitalRead(2) == HIGH)
{
sweeper2.Update(currentMillis);
led1.Update(currentMillis);
}
led2.Update(currentMillis);
led3.Update(currentMillis);
}
void loop()
{
} #include
class Flasher
{
// Class Member Variables
// These are initialized at startup
int ledPin; // the number of the LED pin
long OnTime; // milliseconds of on-time
long OffTime; // milliseconds of off-time
// These maintain the current state
int ledState; // ledState used to set the LED
unsigned long previousMillis; // will store last time LED was updated
// Constructor - creates a Flasher
// and initializes the member variables and state
public:
Flasher(int pin, long on, long off)
{
ledPin = pin;
pinMode(ledPin, OUTPUT);
OnTime = on;
OffTime = off;
ledState = LOW;
previousMillis = 0;
}
void Update(unsigned long currentMillis)
{
if((ledState == HIGH) && (currentMillis - previousMillis 》= OnTime))
{
ledState = LOW; // Turn it off
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis 》= OffTime))
{
ledState = HIGH; // turn it on
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
}
};
class Sweeper
{
Servo servo; // the servo
int pos; // current servo position
int increment; // increment to move for each interval
int updateInterval; // interval between updates
unsigned long lastUpdate; // last update of position
public:
Sweeper(int interval)
{
updateInterval = interval;
increment = 1;
}
void Attach(int pin)
{
servo.attach(pin);
}
void Detach()
{
servo.detach();
}
void Update(unsigned long currentMillis)
{
if((currentMillis - lastUpdate) 》 updateInterval) // time to update
{
lastUpdate = millis();
pos += increment;
servo.write(pos);
if ((pos 》= 180) || (pos 《= 0)) // end of sweep
{
// reverse direction
increment = -increment;
}
}
}
};
Flasher led1(11, 123, 400);
Flasher led2(12, 350, 350);
Flasher led3(13, 200, 222);
Sweeper sweeper1(25);
Sweeper sweeper2(35);
void setup()
{
sweeper1.Attach(9);
sweeper2.Attach(10);
// Timer0 is already used for millis() - we’ll just interrupt somewhere
// in the middle and call the “Compare A” function below
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
}
// Interrupt is called once a millisecond, to update the LEDs
// Sweeper2 s not updated if the button on digital 2 is pressed.
SIGNAL(TIMER0_COMPA_vect)
{
unsigned long currentMillis = millis();
sweeper1.Update(currentMillis);
if(digitalRead(2) == HIGH)
{
sweeper2.Update(currentMillis);
led1.Update(currentMillis);
}
led2.Update(currentMillis);
led3.Update(currentMillis);
}
void loop()
{
}
外部中斷
最好是退出循環
與定時器中斷不同的是,外部事件會觸發外部中斷。例如,當按下按鈕或從旋轉編碼器接收到脈沖時。但是,就像計時器中斷一樣,您不需要繼續輪詢GPIO引腳以進行更改。
Arduino UNO有2個外部中斷引腳。在此示例中,我們將按鈕附加到其中一個按鈕,并使用它來重置我們的清掃器。首先,在我們的清除程序類中添加一個“ reset()”函數。 reset()函數將位置設置為0,并立即將伺服器放置在此處:
下載:文件
復制代碼
void reset()
{
pos = 0;
servo.write(pos);
increment = abs(increment);
} void reset()
{
pos = 0;
servo.write(pos);
increment = abs(increment);
}
接下來,我們將添加對AttachInterrupt()的調用以連接外部中斷
在UNO上,中斷0與數字引腳2相關聯。我們告訴它在該引腳上尋找信號的“ FALLING”沿。當按下按鈕時,信號從HIGH降到LOW,并調用“重置”中斷處理程序。
下載:文件
復制代碼
pinMode(2, INPUT_PULLUP);
attachInterrupt(0, Reset, FALLING); pinMode(2, INPUT_PULLUP);
attachInterrupt(0, Reset, FALLING);
這是“重置”中斷處理程序。它僅調用清除程序重置功能:
下載:文件
復制代碼
void Reset()
{
sweeper1.reset();
sweeper2.reset();
} void Reset()
{
sweeper1.reset();
sweeper2.reset();
}
現在,每當您按下按鈕時,伺服器就會停止其正在執行的操作,并立即尋找到零位置。
源代碼:
這是帶有計時器和外部中斷的完整草圖:
下載:文件
復制代碼
#include
class Flasher
{
// Class Member Variables
// These are initialized at startup
int ledPin; // the number of the LED pin
long OnTime; // milliseconds of on-time
long OffTime; // milliseconds of off-time
// These maintain the current state
volatile int ledState; // ledState used to set the LED
volatile unsigned long previousMillis; // will store last time LED was updated
// Constructor - creates a Flasher
// and initializes the member variables and state
public:
Flasher(int pin, long on, long off)
{
ledPin = pin;
pinMode(ledPin, OUTPUT);
OnTime = on;
OffTime = off;
ledState = LOW;
previousMillis = 0;
}
void Update(unsigned long currentMillis)
{
if((ledState == HIGH) && (currentMillis - previousMillis 》= OnTime))
{
ledState = LOW; // Turn it off
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis 》= OffTime))
{
ledState = HIGH; // turn it on
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
}
};
class Sweeper
{
Servo servo; // the servo
int updateInterval; // interval between updates
volatile int pos; // current servo position
volatile unsigned long lastUpdate; // last update of position
volatile int increment; // increment to move for each interval
public:
Sweeper(int interval)
{
updateInterval = interval;
increment = 1;
}
void Attach(int pin)
{
servo.attach(pin);
}
void Detach()
{
servo.detach();
}
void reset()
{
pos = 0;
servo.write(pos);
increment = abs(increment);
}
void Update(unsigned long currentMillis)
{
if((currentMillis - lastUpdate) 》 updateInterval) // time to update
{
lastUpdate = currentMillis;
pos += increment;
servo.write(pos);
if ((pos 》= 180) || (pos 《= 0)) // end of sweep
{
// reverse direction
increment = -increment;
}
}
}
};
Flasher led1(11, 123, 400);
Flasher led2(12, 350, 350);
Flasher led3(13, 200, 222);
Sweeper sweeper1(25);
Sweeper sweeper2(35);
void setup()
{
sweeper1.Attach(9);
sweeper2.Attach(10);
// Timer0 is already used for millis() - we‘ll just interrupt somewhere
// in the middle and call the “Compare A” function below
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
pinMode(2, INPUT_PULLUP);
attachInterrupt(0, Reset, FALLING);
}
void Reset()
{
sweeper1.reset();
sweeper2.reset();
}
// Interrupt is called once a millisecond,
SIGNAL(TIMER0_COMPA_vect)
{
unsigned long currentMillis = millis();
sweeper1.Update(currentMillis);
//if(digitalRead(2) == HIGH)
{
sweeper2.Update(currentMillis);
led1.Update(currentMillis);
}
led2.Update(currentMillis);
led3.Update(currentMillis);
}
void loop()
{
} #include
class Flasher
{
// Class Member Variables
// These are initialized at startup
int ledPin; // the number of the LED pin
long OnTime; // milliseconds of on-time
long OffTime; // milliseconds of off-time
// These maintain the current state
volatile int ledState; // ledState used to set the LED
volatile unsigned long previousMillis; // will store last time LED was updated
// Constructor - creates a Flasher
// and initializes the member variables and state
public:
Flasher(int pin, long on, long off)
{
ledPin = pin;
pinMode(ledPin, OUTPUT);
OnTime = on;
OffTime = off;
ledState = LOW;
previousMillis = 0;
}
void Update(unsigned long currentMillis)
{
if((ledState == HIGH) && (currentMillis - previousMillis 》= OnTime))
{
ledState = LOW; // Turn it off
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis 》= OffTime))
{
ledState = HIGH; // turn it on
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
}
};
class Sweeper
{
Servo servo; // the servo
int updateInterval; // interval between updates
volatile int pos; // current servo position
volatile unsigned long lastUpdate; // last update of position
volatile int increment; // increment to move for each interval
public:
Sweeper(int interval)
{
updateInterval = interval;
increment = 1;
}
void Attach(int pin)
{
servo.attach(pin);
}
void Detach()
{
servo.detach();
}
void reset()
{
pos = 0;
servo.write(pos);
increment = abs(increment);
}
void Update(unsigned long currentMillis)
{
if((currentMillis - lastUpdate) 》 updateInterval) // time to update
{
lastUpdate = currentMillis;
pos += increment;
servo.write(pos);
if ((pos 》= 180) || (pos 《= 0)) // end of sweep
{
// reverse direction
increment = -increment;
}
}
}
};
Flasher led1(11, 123, 400);
Flasher led2(12, 350, 350);
Flasher led3(13, 200, 222);
Sweeper sweeper1(25);
Sweeper sweeper2(35);
void setup()
{
sweeper1.Attach(9);
sweeper2.Attach(10);
// Timer0 is already used for millis() - we’ll just interrupt somewhere
// in the middle and call the “Compare A” function below
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
pinMode(2, INPUT_PULLUP);
attachInterrupt(0, Reset, FALLING);
}
void Reset()
{
sweeper1.reset();
sweeper2.reset();
}
// Interrupt is called once a millisecond,
SIGNAL(TIMER0_COMPA_vect)
{
unsigned long currentMillis = millis();
sweeper1.Update(currentMillis);
//if(digitalRead(2) == HIGH)
{
sweeper2.Update(currentMillis);
led1.Update(currentMillis);
}
led2.Update(currentMillis);
led3.Update(currentMillis);
}
void loop()
{
}
庫和鏈接
有關計時器的更多信息
可以將計時器配置為以各種頻率運行并以不同的模式運行。除了產生中斷,它們還用于控制PWM引腳。以下鏈接是了解如何配置和使用計時器的出色資源:
Arduino PWM的機密
計時器/PWM速查表
計時器庫
網上有許多Arduino``計時器‘’庫可用與本系列第1部分中所做的一樣,許多人僅監視millis()并要求進行持續輪詢。但是實際上有一些配置可以讓您配置定時器以生成中斷。
Paul Stoffregan出色的TimerOne和TimerThree庫處理了許多定時器中斷配置的低級細節。 (請注意,TimerThree不適用于UNO。它可以與Leonardo,Mega和某些Teensy板一起使用)
TimerOne和Timer 3庫
引腳更改中斷
對于2個不足的情況
Arduino UNO只有2個外部中斷引腳。但是,如果您需要兩個以上的中斷,該怎么辦?幸運的是,Arduino UNO在所有引腳上都支持“引腳更改”中斷。
引腳更改中斷類似于外部中斷。區別在于,在8個相關引腳中的任何一個引腳上都會因狀態變化而產生一個中斷。這些操作要稍微復雜一點,因為您必須跟蹤所有8個引腳的最后一個已知狀態,以找出8個引腳中的哪個導致了中斷。
Arduino Playground的PinChangeInt庫實現了一個方便的引腳更改中斷接口:http://playground.arduino.cc/Main/PinChangeInt
PinChangeInt庫
計時器和中斷禮儀
中斷就像超市的快速通道??紤]周全,將其保持在10件以下,一切都會順利進行。
如果一切都是高水平的,那么沒有什么是高度優先的。
中斷處理程序應僅用于處理高優先級,對時間敏感的事件。請記住,在中斷處理程序中時,禁用了中斷。如果您嘗試在中斷級別執行過多操作,則會降低對其他中斷的響應。
一次僅一個中斷。
ISR,中斷被禁用。這有兩個非常重要的含義:
在ISR中完成的工作應保持簡短,以免丟失任何中斷。
在ISR中的代碼不應調用任何需要激活中斷(例如delay()或使用i2c總線的任何中斷)。這將導致程序掛起。
將冗長的處理推遲到循環中。
如果您需要進行大量處理以響應中斷,請使用中斷處理程序僅執行必要的操作,然后設置易失性狀態變量(參見下文)以指示需要進一步處理。當您從循環中調用更新功能時,請檢查狀態變量以查看是否需要任何后續處理。
重新配置計時器之前需要檢查 《計時器是一種有限的資源。 UNO上只有3個,它們用于許多用途。如果您弄亂了計時器配置,則其他某些功能可能不再起作用。例如,在Arduino UNO上:
Timer0 -用于引腳5和6上的millis(),micros(),delay()和PWM
》
Timer1 -用于Servos,WaveHC庫和引腳9和10上的PWM
Timer2 -由Tone和PWM引腳上使用11&13
安全共享數據
因為中斷將暫停,無論處理器正在處理該中斷如何,我們必須小心在中斷處理程序和循環中的代碼之間共享數據。
易失性變量
有時,編譯器會嘗試優化代碼以提高速度。有時,這些優化會將常用變量的副本保留在寄存器中以便快速訪問。問題是,如果這些變量之一在中斷處理程序和循環代碼之間共享,則其中一個變量最終可能會查看陳舊的副本而不是真實的副本。將變量標記為易失性會告訴編譯器不要對優化進行潛在的危險操作。
保護較大的變量
Evan標記變量 volatile 是不夠的如果它的變量大于整數(例如字符串,數組,結構等)。較大的變量需要幾個指令周期來更新,并且如果在更新的中間發生中斷,則數據可能會被破壞。如果您具有與中斷處理程序共享的較大變量或結構,則在從循環中更新中斷時應禁用中斷。 (默認情況下,已在中斷處理程序中禁用了中斷。)
責任編輯:wv
-
中斷
+關注
關注
5文章
899瀏覽量
41535 -
計時器
+關注
關注
1文章
421瀏覽量
32735 -
Arduino
+關注
關注
188文章
6471瀏覽量
187244
發布評論請先 登錄
相關推薦
評論