資料介紹
描述
概括
本教程介紹如何設(shè)置基于定時器的中斷。具體來說,它使用定時器比較中斷定期閃爍 LED,這與流行的 Blink sketch 使用 delay() 形成對比。編程特定于 Arduino Uno、5V Nano 3.x 和克隆中使用的 16MHz ATmega328P。
我的動力
我有一個瘋狂的想法,即構(gòu)建一個三單元 Raspberry Pi 集群,并使用自定義 8MHz、3.3V ATMega328P(與 Arduino Uno 相同的芯片,但速度和電壓較低)在 Raspberry Pi 控制臺端口之間切換,同時還做其他有用的東西,如測量溫度和電源狀態(tài)。我的第一步是找出控制臺端口,這涉及到 SoftwareSerial 庫。
我有一個 5V Arduino Uno,而 Raspberry Pi 當(dāng)然是 3.3V。因此,為了避免燒壞我的 Pi,我剪了一根電線將軟件串行傳輸和接收引腳連接在一起,創(chuàng)建了一個環(huán)回。這個理論是合理的,無論我在傳輸引腳上發(fā)出什么,都應(yīng)該在接收引腳上返回。但是,它沒有用。由于中斷,它沒有工作。
需要中斷才能知道串行數(shù)據(jù)何時進入特定引腳,但由于關(guān)鍵時序,傳輸時中斷被禁用。這種安排使得無法同時發(fā)送和接收。不用說,這對我連接到多個 Raspberry Pi 控制臺端口的想法來說不是好兆頭。
但是,從好的方面來說,它讓我對 Arduino Uno 上的中斷產(chǎn)生了興趣。
從小做起
眨眼草圖。這可能是每個人的第一個 Arduino 項目。好吧,這里再次作為復(fù)習(xí)。
#define LED 9
void setup() {
pinMode(LED, OUTPUT);
}
void loop() {
digitalWrite(LED, LOW);
delay(500);
digitalWrite(LED, HIGH);
delay(500);
}
請注意熟悉的 delay() 子例程來控制打開和關(guān)閉時間。每個狀態(tài)有 500mS,LED 將以穩(wěn)定的 1Hz 脈沖。
在我的草圖中,我將引腳 9 用于 LED。這需要在引腳 9 和地之間連接一個 LED 和限流電阻。我要大膽猜測,如果您正在閱讀有關(guān)定時器中斷的教程,那么您已經(jīng)連接了幾個 LED,我不會詳細介紹。不過,使用外部 LED 很重要,因為稍后會同時使用內(nèi)置 LED 來比較中斷閃爍和 delay() 閃爍。
如果你想做的只是對著閃光燈發(fā)出嗚嗚聲,那么這幅素描就很完美了。但是,如果您想同時做其他事情怎么辦?也許測量熱敏電阻兩端的電壓并計算溫度。那樣就好了。
問題是那兩個 delay() 函數(shù)調(diào)用。每個人都花半秒鐘什么都不做。您可以在其中一個延遲之前或之后插入代碼。但是,如果該代碼需要足夠長的時間來執(zhí)行,以至于它會錯過眨眼的時間怎么辦?
你可以縮短延遲。也許將其設(shè)為 490 而不是 500。也許這太短了,495 是更好的選擇。在某些時候,您可能會舉手說,“一定有更好的方法!”
有一個更好的辦法。它被稱為定時器中斷。
什么是中斷?
查看本教程標題卡中的插圖。
戴大禮帽的花花公子——他是個干擾者。你幾乎可以想象他在一個古老的維多利亞火車站附近閑逛,說著類似“對不起,我的好先生們”之類的話。
現(xiàn)在長凳上的三位先生——他們是您的 loop() 函數(shù)中的進程。左邊那個坐下,當(dāng) LED 熄滅時,他就是 LED。下一個人是 LED 燈。第三個人,那個戴著帽子遮住眼睛的人,他一定是延遲功能。
“請原諒,我的好先生們,”打斷說。突然,他引起了替補席上三個人的注意。就連昏昏欲睡的家伙也豎起了耳朵。不管他們之前在做什么,他們都停下來讓戴大禮帽的花花公子上臺。
這正是中斷所做的。它會停止 loop() 的正常執(zhí)行并運行它自己的代碼一段時間。
簡潔很重要
如果戴禮帽的人是個體面的人,他會保持簡短的打斷。他可能會說一些重要的話,比如,‘老兄,你的火車就要出發(fā)了,’然后就上路了。或者他可能會一連幾個小時在龐氏騙局的最新趨勢上喋喋不休地自以為是。
前者是好中斷的例子,后者是壞中斷。當(dāng)中斷花費太多時間時,它會使所有其他進程等待。中斷應(yīng)該只做絕對必要的事情,并將控制權(quán)交還給主循環(huán)。
在 LED 閃爍的情況下,最少量的處理歸結(jié)為改變輸出引腳的狀態(tài),以便連接的 LED 打開或關(guān)閉。因此,在我們繼續(xù)之前,請看一下以下代碼行:
PORTB ^= B00100000; // Toggle bit 5, which maps to pin13.
它有什么作用?好吧,閱讀評論,它說它切換了一個映射到引腳的位。該引腳恰好是引腳 13,這是 Arduino Uno 上的內(nèi)置 LED。PORTB
是 Uno 上控制引腳 8 到 13 的寄存器。位 0 控制引腳 8,位 1 控制引腳 9,依此類推。第 5 位控制引腳 13,即內(nèi)置 LED。
該^=
運算符是一個異或 (XOR)。XOR 可用于反轉(zhuǎn)操作數(shù)之一為 1 的任何位。XOR 就像一個帶有扭曲的常規(guī) OR。兩個零位作為輸入給出一個零作為輸出。零和一或一和零給出一的輸出。但是,這里有一個轉(zhuǎn)折……如果你有一個和一個,結(jié)果是零。
這就是為什么上面的代碼行只會翻轉(zhuǎn) one 所在的位。如果寄存器 PORTB 的第 5 位的當(dāng)前值為零,則該零與二進制值第 5 位中的 1 進行異或后B00100000
結(jié)果為 1。如果 PORTB 的第 5 位是 1,則 1 與 1 的異或結(jié)果為 0。每次應(yīng)用 XOR 時,該位都會翻轉(zhuǎn)。PORTB 中的任何其他位將與零進行異或,從而返回原始值并且不受影響。
使用 digitalRead() 確定引腳的當(dāng)前狀態(tài)然后使用 digitalWrite() 應(yīng)用相反狀態(tài)的練習(xí)現(xiàn)在在單個 XOR 中完成。快速高效,就像任何優(yōu)秀的、正直的中斷一樣。
我被賣了,我從哪里得到一個?
有了基礎(chǔ)知識,就該開始設(shè)置中斷了。這涉及兩個部分。
第一個是中斷服務(wù)程序(ISR)。它只是一個執(zhí)行位翻轉(zhuǎn)代碼的子程序。ISR() 子例程存在于 setup() 和 loop() 例程之外,它看起來像這樣:
ISR(TIMER1_COMPA_vect)
{
PORTB ^= B00100000; // Toggle bit 5, which maps to pin13.
}
在最簡單的形式中,ISR() 接受一個參數(shù)。該參數(shù)TIMER1_COMPA_vect
是中斷的向量(或源)。使用像 TIMER1_COMPA_vect 這樣的名稱,您可能會猜到它與 timer1 有關(guān),并且可能正在進行一些比較。你是對的。
但我們還沒有完成。到目前為止,我們所做的只是告訴 ATmega328P 當(dāng)定時器比較中斷發(fā)生時該做什么。我們實際上還沒有設(shè)置任何定時器來產(chǎn)生中斷,所以什么都不會發(fā)生。
設(shè)置的第二部分涉及告訴計時器何時引發(fā)中斷。這涉及更多的控制寄存器和二進制值,但如果你已經(jīng)做到了這一點,你會沒事的。這是代碼:
cli();
TCCR1A = B00000000;
TCCR1B = B00001100;
TIMSK1 = B00000010;
OCR1A = 31250;
sei();
cli() 和 sei() 指令是相關(guān)的。第一個清除中斷標志,第二個設(shè)置它。中斷標志允許中斷發(fā)生。使用 cli() 忽略所有中斷,本質(zhì)上它是一個很大的請勿打擾標志。sei() 做相反的事情并允許中斷。
最初使用 cli() 阻止中斷的原因是因為需要設(shè)置四個寄存器并且所有四個都是相關(guān)的。在中斷開始滾動之前只設(shè)置一兩個將導(dǎo)致一些不可預(yù)測的行為。最好在設(shè)置過程中熄滅請勿打擾標志。
寄存器(TCCR1A、TCCR1B 和 TIMSK1)是指示 timer1 如何工作的標志的集合。在代碼中,所有設(shè)置都使用二進制值,以便更容易查看正在設(shè)置的位以及它是 1 還是 0。
OCR1A 是要與定時器的當(dāng)前計數(shù)進行比較的值。它以十進制表示法顯示,以便于閱讀。按照配置,計時器將從 0 開始并向上計數(shù)。當(dāng)它達到存儲在 OCR1A 中的值時,就會發(fā)生一些事情。如果您猜到某事是中斷,您將贏得獎品。
定時器計數(shù)的速度以及當(dāng)它達到 OCR1A 中的值時它做什么由 TCCR1A 和 TCCR1B 中的標志決定。這些定時器控制寄存器在 ATmega328P 數(shù)據(jù)表的第 15.1 節(jié)中有詳細說明。但這里有一個簡短的描述:
-
TCCR1A = B00000000
是最簡單的。它設(shè)置全零,或所謂的“正常”模式。
-
TCCR1B = B00001100
指示計數(shù)器用位 0..2 的值計數(shù)的速度。在這種情況下,那些較低位中的二進制 100 會將預(yù)分頻器設(shè)置為 256(稍后會詳細介紹)。
-
還包括在 中
TCCR1B = B00001100
,第 3 位中的 1 表示當(dāng)達到 OCR1A 中存儲的值時,計數(shù)器將重置為零。
-
TIMSK1 = B00000010
確保當(dāng)計數(shù)器達到存儲在 OCR1A 中的值時將產(chǎn)生中斷。
最后設(shè)置的寄存器是OCR1A,比較寄存器,但是為什么設(shè)置為31250呢?答案在于以下等式:
interrupts_per_second = clock_speed / 預(yù)分頻器 / OCR1A
也可以表示為
OCR1A = clock_speed / 預(yù)分頻器 / interrupts_per_second
使用 16MHz Arduino Uno 和預(yù)分頻器值為 256(記住 TCCRB1 的最低 3 位設(shè)置該值),等式變得更簡單:
OCR1A = 16MHz / 256 / interrupts_per_second
OCR1A = 62500 / interrupts_per_second
我想每秒切換 LED 兩次以獲得 1Hz 閃爍率,因此我將 62500 除以 2 得到 31250。這就是 OCR1A 值的來源。
讓我們眨眼!
到目前為止,這都是一堆理論。讓我們將所有代碼放在草圖中并證明它確實有效。這是它的樣子:
#define LED 9
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
pinMode(LED, OUTPUT);
cli();
TCCR1A = B00000000;
TCCR1B = B00001100;
TIMSK1 = B00000010;
OCR1A = 31250;
sei();
}
void loop() {
digitalWrite(LED, LOW);
delay(500);
digitalWrite(LED, HIGH);
delay(500);
}
ISR(TIMER1_COMPA_vect)
{
PORTB ^= B00100000;
}
請注意 delay() 閃爍的舊方法如何仍然包括在內(nèi),但現(xiàn)在草圖的新中斷代碼部分也是如此。這就是使用兩個 LED 的原因。外部 LED(引腳 9)將由 loop() 內(nèi)的 digitalWrite() 和 delay() 控制,而內(nèi)置 LED(引腳 13)由 ISR() 和 setup() 內(nèi)的配置控制.
將草圖加載到 Uno,您應(yīng)該會看到兩個 LED 燈同時閃爍。
現(xiàn)在,做一些改變。嘗試以下任何或所有操作:
- 將 OCR1A 的值加倍,使其成為 62500。
- 將預(yù)分頻器位的值設(shè)置為 64 而不是 256。 (TCCR1B = B00001101)
- 將兩個 delay() 值更改為 250 而不是 500。
- 刪除 loop() 中的所有內(nèi)容。
預(yù)測每種情況下會發(fā)生什么,然后上傳草圖以查看您是否正確。在您思考的同時,想象一下使用 8MHz 時鐘晶體代替標準 16MHz 的 Arduino Uno 會產(chǎn)生什么效果。
作為最后的練習(xí),這個練習(xí)需要一些時間,讓原始草圖運行幾個小時甚至一整夜,以查看對 LED 同步的影響。(劇透:它們將明顯不同步。)想出一些關(guān)于為什么會發(fā)生這種情況的理論。哪個LED的時序更準確?
下一步
一開始,閃爍的 LED 很有趣,但中斷可以用于更多用途。例如,有些中斷可以由輸入引腳的變化觸發(fā)。現(xiàn)在您已經(jīng)知道如何編寫用于打開或關(guān)閉 LED 的中斷服務(wù)例程,請嘗試使用分配給輸入引腳的按鈕而不是定時器來觸發(fā)它。
作為介紹,本教程僅展示了一種在 ATmega328p 上配置中斷的方法。還有更高級別的函數(shù),如:attachInterrupt() 和 detachInterrupt,它們涵蓋更廣泛的 Arduino 模型并處理細節(jié),因此您不一定需要閱讀數(shù)據(jù)表和設(shè)置寄存器位。
無論您打算在哪里學(xué)習(xí)新知識,了解中斷的工作原理都將使您能夠構(gòu)建更好、更高效的草圖。因此,放棄 delay() 并開始使用更多中斷。
- S3C2440 開發(fā)板實戰(zhàn)(5):定時器中斷
- 單片機定時器中斷后計數(shù)器和中斷再進入
- 關(guān)于GD32F330單片機定時器中斷初始化后立即進入中斷問題
- 定時器中斷實驗
- 定時器中斷實現(xiàn)步驟及實例筆記
- 51單片機中斷主要關(guān)于計時--定時--計算定時器初值--的簡介 ~~~看到的關(guān)于中斷? 計時器/定時器的介紹,覺得
- STM32定時器與中斷整理
- C51編程15-中斷篇(定時器中斷2)
- "外部中斷,定時器中斷的理解"
- 51單片機:開啟中斷和定時器
- AVR單片機 實驗六 AD轉(zhuǎn)換應(yīng)用實驗
- 51單片機的定時器和計數(shù)器中斷機制的詳細簡介
- 使用定時器需要設(shè)置的寄存器和51單片機定時器的示例代碼說明
- 定時器中斷應(yīng)用 3次下載
- 中斷和定時器/計數(shù)器
- 如何實現(xiàn)一個軟件定時器? 457次閱讀
- 51單片機定時器和中斷的介紹 2032次閱讀
- 利用定時器中斷代替延時函數(shù)(包含例程+原理思想) 3232次閱讀
- 定時器的基本操作 1934次閱讀
- 軟件定時器簡介及程序配置 3651次閱讀
- stm32定時器中斷與誤區(qū) 2.7w次閱讀
- STM32定時器原理與使用 5.7w次閱讀
- 8051單片機定時器溢出中斷與CPU響應(yīng)中斷的時間誤差分析 3971次閱讀
- 51單片機定時器控制LED燈 1w次閱讀
- STM32單片機的系統(tǒng)定時器初始化設(shè)置 7957次閱讀
- LPC210X定時器查詢方式及如何初始化 1220次閱讀
- ARM LPC2103定時器中斷方式寄存器設(shè)置 1716次閱讀
- 51單片機內(nèi)部定時器和中斷系統(tǒng)以及編寫第一個簡單的定時器實驗程序 4w次閱讀
- 詳細介紹定時器和定時器中斷 1.7w次閱讀
- stm32定時器中斷程序 1.3w次閱讀
下載排行
本周
- 1山景DSP芯片AP8248A2數(shù)據(jù)手冊
- 1.06 MB | 532次下載 | 免費
- 2RK3399完整板原理圖(支持平板,盒子VR)
- 3.28 MB | 339次下載 | 免費
- 3TC358743XBG評估板參考手冊
- 1.36 MB | 330次下載 | 免費
- 4DFM軟件使用教程
- 0.84 MB | 295次下載 | 免費
- 5元宇宙深度解析—未來的未來-風(fēng)口還是泡沫
- 6.40 MB | 227次下載 | 免費
- 6迪文DGUS開發(fā)指南
- 31.67 MB | 194次下載 | 免費
- 7元宇宙底層硬件系列報告
- 13.42 MB | 182次下載 | 免費
- 8FP5207XR-G1中文應(yīng)用手冊
- 1.09 MB | 178次下載 | 免費
本月
- 1OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費
- 2555集成電路應(yīng)用800例(新編版)
- 0.00 MB | 33566次下載 | 免費
- 3接口電路圖大全
- 未知 | 30323次下載 | 免費
- 4開關(guān)電源設(shè)計實例指南
- 未知 | 21549次下載 | 免費
- 5電氣工程師手冊免費下載(新編第二版pdf電子書)
- 0.00 MB | 15349次下載 | 免費
- 6數(shù)字電路基礎(chǔ)pdf(下載)
- 未知 | 13750次下載 | 免費
- 7電子制作實例集錦 下載
- 未知 | 8113次下載 | 免費
- 8《LED驅(qū)動電路設(shè)計》 溫德爾著
- 0.00 MB | 6656次下載 | 免費
總榜
- 1matlab軟件下載入口
- 未知 | 935054次下載 | 免費
- 2protel99se軟件下載(可英文版轉(zhuǎn)中文版)
- 78.1 MB | 537798次下載 | 免費
- 3MATLAB 7.1 下載 (含軟件介紹)
- 未知 | 420027次下載 | 免費
- 4OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費
- 5Altium DXP2002下載入口
- 未知 | 233046次下載 | 免費
- 6電路仿真軟件multisim 10.0免費下載
- 340992 | 191187次下載 | 免費
- 7十天學(xué)會AVR單片機與C語言視頻教程 下載
- 158M | 183279次下載 | 免費
- 8proe5.0野火版下載(中文版免費下載)
- 未知 | 138040次下載 | 免費
評論
查看更多