原理圖
什么是中斷?
為微控制器編寫(xiě)的簡(jiǎn)單程序通常都可以在主函數(shù)內(nèi)部完成,并且?guī)缀醪恍枰褂猛庠O(shè)。但是,大多數(shù)其他微控制器程序更復(fù)雜,需要大量代碼。當(dāng)發(fā)生這種情況時(shí),中斷會(huì)變得非常有用,但究竟什么是中斷?
想象一下,我們的微控制器需要同時(shí)做兩件事:準(zhǔn)確跟蹤時(shí)間并使LED閃爍。我們的程序可以通過(guò)重置計(jì)時(shí)器,遞增計(jì)數(shù)器,然后等待計(jì)時(shí)器溢出來(lái)開(kāi)始。完成后,我們的代碼可以使LED閃爍。雖然這有點(diǎn)完成工作,但是有兩個(gè)問(wèn)題。 CPU花費(fèi)大部分時(shí)間坐在延遲循環(huán)中,這浪費(fèi)了CPU時(shí)間,并且LED的執(zhí)行時(shí)間很難計(jì)算。
那么,我們?nèi)绾谓鉀Q這個(gè)問(wèn)題呢?我們可以在計(jì)時(shí)器上使用中斷!因此,我們不是在主代碼中遞增計(jì)數(shù)器,而是將代碼轉(zhuǎn)換為處理時(shí)序的中斷服務(wù)程序。
通常,微控制器將運(yùn)行LED閃爍代碼,但是一旦定時(shí)器生成中斷請(qǐng)求,微控制器停止LED閃爍代碼,執(zhí)行定時(shí)器中斷服務(wù)程序,然后返回到LED閃爍代碼。這樣,LED閃爍代碼不會(huì)干擾我們的定時(shí)器代碼,它可以更準(zhǔn)確(并且更容易)跟蹤時(shí)間。
AVR Core上的中斷
AVR有一個(gè)向量表,每個(gè)中斷源都跳轉(zhuǎn)到一個(gè)唯一的地址。這是非常有利的,因?yàn)槲覀儾辉傩枰獔?zhí)行比較來(lái)查看觸發(fā)了哪個(gè)中斷,這可能需要一些時(shí)間。
下表顯示了Atmega168上可用的不同中斷以及它們跳轉(zhuǎn)到的地址。程序記憶。但是,在我們使用它們之前必須配置幾個(gè)中斷選項(xiàng)。
從ATmega168數(shù)據(jù)表中提取
表位置
Atmega168具有允許的引導(dǎo)加載程序區(qū)域它可以動(dòng)態(tài)地重寫(xiě)自己的程序存儲(chǔ)器,這對(duì)固件更新很有用。因此,ISR向量表將位于內(nèi)存中很重要。如果表位于引導(dǎo)加載程序區(qū)域中,則在啟用引導(dǎo)加載程序時(shí)永遠(yuǎn)不會(huì)更新(不推薦)。
因此,如果沒(méi)有引導(dǎo)加載程序,則應(yīng)將向量表放在內(nèi)存的底部(接近地址0x0000),但如果使用引導(dǎo)加載程序,則應(yīng)將向量表移動(dòng)到引導(dǎo)加載程序上方。這可以通過(guò)改變MCUCR寄存器中的幾個(gè)位來(lái)輕松完成。
如果IVSEL = 0,則ISR位于向量表的起始,否則ISR駐留在引導(dǎo)加載程序中。現(xiàn)在,將其保留為0,因?yàn)槲覀儧](méi)有使用引導(dǎo)加載程序
如果IVCE = 1,則執(zhí)行ISR切換。暫時(shí)保留為0
中斷啟用位
每個(gè)中斷源(I/O引腳,外設(shè)等)都有關(guān)聯(lián)中斷使能位。與PIC類似,STATUS寄存器中有一個(gè)全局中斷使能位,需要將其設(shè)置為允許中斷工作。要找出這些中斷標(biāo)志所在的位置,需要參考數(shù)據(jù)手冊(cè)中的特定外設(shè)章節(jié)。
例如,我們將在定時(shí)器0上使用溢出中斷,所以如果我們看一下定時(shí)器0在章節(jié)中,我們發(fā)現(xiàn)中斷使能位位于TIMSK0寄存器(第89頁(yè))中,稱為TOIE0。需要將此位設(shè)置為1才能觸發(fā)定時(shí)器溢出。該寄存器還有另外兩個(gè)中斷源,A匹配溢出和B匹配溢出,這對(duì)PWM功能很有用(將來(lái)會(huì)介紹)。
注意,設(shè)置我在SREG中的位不是使用SREG本身,而是使用函數(shù)sei();設(shè)置I位和cei();清除I位。
在WinAVR中編寫(xiě)ISR
所以我們現(xiàn)在明白需要啟用中斷才能啟動(dòng),但我們?nèi)绾问褂肅和WINAVR編譯器編寫(xiě)?答案很簡(jiǎn)單:我們使用特殊保留字ISR并傳遞中斷名稱參數(shù)來(lái)告訴編譯器哪個(gè)中斷函數(shù)處理。注意我們需要包含中斷頭文件,否則中斷函數(shù)將不起作用!
#include
ISR(TIMER0_OVF_vect)
{
// Interestingly, the AVR automatically clears interrupt flags.。。.unlike the PIC
// Put your code here
}
簡(jiǎn)單閃爍示例
在這個(gè)例子中,ATmega168會(huì)使連接到PD0的LED頻繁閃爍,其中閃爍的速率受到控制通過(guò)定時(shí)器0但是,您可能會(huì)注意到主功能為空,并且LED在定時(shí)器溢出中斷服務(wù)程序(ISR)內(nèi)閃爍。這意味著我們可以在while循環(huán)中放入我們想要的任何代碼,并且該代碼不會(huì)阻止中斷運(yùn)行。
/*
* AVR Interrupt.c
*
* Created: 09/01/2018
* Author : RobinLaptop
*/
// These are really useful macros that help to get rid of unreadable bit masking code
#define setBit(reg, bit) (reg = reg | (1 《《 bit))
#define clearBit(reg, bit) (reg = reg & ~(1 《《 bit))
#define toggleBit(reg, bit) (reg = reg ^ (1 《《 bit))
#define clearFlag(reg, bit) (reg = reg | (1 《《 bit))
#include
#include
ISR(TIMER0_OVF_vect)
{
// Interestingly, the AVR automatically clears interrupt flags =) 。。..unlike the PIC =(
// Toggle the LED (PD0 , Pin 2)
toggleBit(PORTD, PD0);
}
int main(void)
{
// Initialize Registers
clearBit(TCCR0A, WGM00); // Configure WGM to be 0x00 for normal mode
clearBit(TCCR0A, WGM01);
clearBit(TCCR0B, WGM02);
setBit(TCCR0B, CS00); // Configure clock source to be clock io at 1024 pre-scale
clearBit(TCCR0B, CS01);
setBit(TCCR0B, CS02);
DDRD = 0xFF; // Make PORT D and output
sei(); // Enable interrupts
setBit(TIMSK0, TOIE0); // Enable the timer interrupt
while (1)
{
// Put any code you want here
// It should not affect the interrupt service routine!
}
}
結(jié)論
本教程僅涵蓋單個(gè)中斷,即定時(shí)器0溢出中斷,但它清楚地表明中斷是非常強(qiáng)大。如果使用得當(dāng),您可以擁有一個(gè)系統(tǒng),它可以在信號(hào)到達(dá)時(shí)立即響應(yīng)并暫停主代碼。這可以用來(lái)做很多事情,包括多任務(wù)處理,不同外圍設(shè)備的多重處理,以及創(chuàng)建實(shí)時(shí)代碼!
-
中斷
+關(guān)注
關(guān)注
5文章
899瀏覽量
41535
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論