中斷作為單片機開發(fā)必須掌握的內(nèi)容,它能夠在不搭載操作系統(tǒng)的情況下讓我們體驗多任務(wù)處理的快感,保證了高優(yōu)先級任務(wù)的實時性,同時系統(tǒng)中斷也能夠提供給用戶在核心發(fā)生錯誤之后進(jìn)行處理的機會。STM32F103系列單片機中斷非常強大,每個外設(shè)都可以產(chǎn)生中斷,F(xiàn)103 在內(nèi)核基礎(chǔ)上搭載了一個中斷響應(yīng)系統(tǒng), 支持為數(shù)眾多的系統(tǒng)中斷和外部中斷。
本篇文章介紹了在STM32平臺實現(xiàn)摁鍵中斷控制LED亮滅以及定時器中斷控制LED燈周期亮滅的功能。文章首先系統(tǒng)的介紹了中斷有關(guān)的概念,然后通過摁鍵以及定時器兩個實例帶領(lǐng)讀者直觀的了解中斷的作用。
中斷概念
下面介紹一些中斷的概念,這些概念依托于STM32平臺,不同的芯片平臺會有出入。
-
中斷:程序執(zhí)行過程中CPU會遇到一些特殊情況,正在執(zhí)行的程序被打斷,cpu中止原來正在執(zhí)行的程序,保存現(xiàn)場(保存上下文),轉(zhuǎn)到處理異常情況或特殊事件的程序去執(zhí)行,結(jié)束后恢復(fù)現(xiàn)場(被打斷程序上下文),繼續(xù)執(zhí)行。
-
異常:異常是中斷的一種類型,其中斷源是芯片內(nèi)部,中斷原因為比如執(zhí)行了未定義指令、算術(shù)溢出、除零運算等發(fā)生在CPU內(nèi)部的意外事件,這些異常的發(fā)生,會引起CPU運行相應(yīng)的異常處理程序。下面是Stm32異常中斷。
- Reset:處理器在工作時, 突然收到復(fù)位信號, 就會觸發(fā)該異常。
- NMI(Non Maskable Interrupt):不可屏蔽中斷,產(chǎn)生這個中斷的時候,表示系統(tǒng)發(fā)生了致命的錯誤。
- HardFault:硬件錯誤中斷,數(shù)組越界,野指針,任務(wù)堆棧溢出,未初始化硬件卻開始操作,或無中斷服務(wù)函數(shù)等,都會導(dǎo)致這個中斷產(chǎn)生。(總線地址不可訪問等),Memory Management Fault(在只讀寫的區(qū)域嘗試執(zhí)行代碼),Usage Fault(除零異常等)等異常都會導(dǎo)致硬件錯誤中斷。
- MemManage:訪問了內(nèi)存管理單元(MPU)定義的不合法的內(nèi)存區(qū)域,比如向只讀區(qū)域?qū)懭霐?shù)據(jù)。
- BusFault:在fetch指令、數(shù)據(jù)讀寫、fetch中斷向量或中斷時存儲恢復(fù)寄存器棧情況下,檢測到內(nèi)存訪問錯誤則產(chǎn)生。
- UsageFault:檢測到未定義指令或在存取內(nèi)存時有未對齊,檢測到除數(shù)為0也產(chǎn)生該異常。
- SVC:系統(tǒng)服務(wù)調(diào)用,SVC異常由SVC指令觸發(fā),在很多系統(tǒng)中SVC機制用于實現(xiàn)應(yīng)用任務(wù)訪問系統(tǒng)資源。
- DebugMon:調(diào)試監(jiān)視器(斷電, 數(shù)據(jù)觀察點, 或外部調(diào)試請求),大部分debug的時候就是調(diào)試的時候遇到。
- PendSV:可掛起的系統(tǒng)調(diào)用,由于PendSV在系統(tǒng)中被設(shè)置為最低優(yōu)先級,因此只有當(dāng)沒有其他異?;蛘咧袛嘣趫?zhí)行時才會被執(zhí)行。在一個操作系統(tǒng)環(huán)境中,當(dāng)沒有其他異常正在執(zhí)行時,可以使用PendSV來進(jìn)行上下文的切換。
- SysTick:系統(tǒng)定時中斷,系統(tǒng)定時器中斷 一般用在操作系統(tǒng)的延時?。
-
保存現(xiàn)場:保存現(xiàn)場是當(dāng)發(fā)生異常/中斷時,CPU跳轉(zhuǎn)之前,就需要把r0-r3, r12, lr, psr這幾個寄存器的值保存到棧中。
-
恢復(fù)現(xiàn)場:STM實現(xiàn)了恢復(fù)現(xiàn)場的機制:CPU進(jìn)入異常/中斷服務(wù)程序時,LR寄存器保存的并不是中斷前下一條指令的地址,而是會保存一個特殊的數(shù)值,被稱為EXC_RETURN。異常/中斷返回時,LR寄存器賦值給PC的值是一個被稱為EXC_RETURN的值,而一旦CPU識別到PC的值等于EXC_RETURN的話,那么就會觸發(fā)異常/中斷返回機制,這個機制會幫我們把保存在棧中r0-r3, r12, lr, psr的寄存器的值恢復(fù)回去。
- ?中斷搶占優(yōu)先級和子優(yōu)先級(又稱響應(yīng)優(yōu)先級):所謂搶占式優(yōu)先級和響應(yīng)優(yōu)先級,具有高搶占式優(yōu)先級的中斷可以在低搶占式優(yōu)先級中斷處理過程中被響應(yīng),即中斷嵌套。當(dāng)兩個中斷源的搶占式優(yōu)先級相同時,這兩個中斷將沒有嵌套關(guān)系,當(dāng)一個中斷到來后,如果正在處理另一個中斷,這個后到來的中斷就要等到前一個中斷處理完之后才能被處理。如果這兩個中斷同時到達(dá),則中斷控制器根據(jù)他們的響應(yīng)優(yōu)先級高低來決定先處理哪一個;如果他們的搶占式優(yōu)先級和響應(yīng)優(yōu)先級都相等,則根據(jù)他們在中斷表中的排位順序決定先處理哪一個。每一個中斷源都必須定義2個優(yōu)先級。
-
中斷優(yōu)先級分組:一般情況下,系統(tǒng)代碼執(zhí)行過程中,只設(shè)置一次中斷優(yōu)先級分組,比如分組2,設(shè)置好分組之后一般不會再改變分組。隨意改變分組會導(dǎo)致中斷管理混亂,程序出現(xiàn)意想不到的執(zhí)行結(jié)果。不同分組方式會影響中斷搶占優(yōu)先級和子優(yōu)先級范圍。
實現(xiàn)原理
摁鍵檢測
EXTI(External interrupt/event controller):外部中斷/事件控制器,管理了控制器的 20個中斷/事件線。每個中斷/事件線都對應(yīng)有一個邊沿檢測器,可以實現(xiàn)輸入信號的上升沿檢測和下降沿的檢測。 EXTI 可以實現(xiàn)對每個中斷/事件線進(jìn)行單獨配置,可以單獨配置為中斷或者事件,以及觸發(fā)事件的屬性。
實現(xiàn)流程大體分為以下三個部分:
- 配置GPIO相應(yīng)引腳:通過按鍵產(chǎn)生一個電平信號,然后經(jīng)EXTI處理傳入NVIC產(chǎn)生中斷的,所以要配置連接按鍵的GPIO引腳,主要是設(shè)置相應(yīng)的引腳模式為浮空輸入 。
- 配置EXTI并映射GPIO引腳:打開相關(guān)的時鐘,使用GPIO_EXTILineConfig()函數(shù)映射中斷IO口,然后配置EXIT,包括中斷源,觸發(fā)類型等。
- 編寫中斷服務(wù)函數(shù):stm32f10x_it.c中提前根據(jù)中斷向量表生成了空的中斷函數(shù),我們只需要找到對應(yīng)的中斷函數(shù)編寫中斷服務(wù)內(nèi)容即可。
定時器
定時器(Timer)最基本的功能就是定時了,本例我們使用的是通用定時器,它的時鐘源經(jīng)過以下路徑計算得到:SYSCLK經(jīng)過PLL的得到為72M,AHB時鐘 =SYSCLK,APB1時鐘=72M/2=36M,最終CK_INT的時鐘頻率倍頻兩倍為72M. 計數(shù)器的最終的頻率還需要經(jīng)過PSC預(yù)分頻計算才能得到,基本定時器計數(shù)過程主要涉及到三個寄存器內(nèi)容,分別是計數(shù)器寄存器(TIMx_CNT)、預(yù)分頻器寄存器(TIMx_PSC)、自動重載寄存器(TIMx_ARR),這三個寄存器都是 16 位有效數(shù)字,即可設(shè)置值為 0至 65535。
定時事件生成時間主要由?TIMx_PSC 和 TIMx_ARR兩個寄存器值決定,這個也就是定時器的周期。比如我們需要一個 1s周期的定時器,具體這兩個寄存器值該如何設(shè)置??假設(shè),我們先設(shè)置 TIMx_ARR寄存器值為 9999,即當(dāng) TIMx_CNT從 0開始計算,剛好等于 9999時生成事件,總共計數(shù) 10000次,那么如果此時時鐘源周期為 100us即可得到剛好 1s的定時周期。 接下來問題就是設(shè)置 TIMx_PSC寄存器值使得 CK_CNT 輸出為 100us 周期(10000Hz)的時鐘。預(yù)分頻器的輸入時鐘 CK_PSC為 90MHz,所以設(shè)置預(yù)分頻器值為(9000-1)即可滿足。?
嵌入式程序
摁鍵檢測實現(xiàn)LED燈亮滅
- 中斷通用配置
static void NVIC_Config(void) /* 主要是配置中斷源的優(yōu)先級與打開使能中斷通道 */ { NVIC_InitTypeDef NVIC_InitStruct ; /* 配置中斷優(yōu)先級分組(設(shè)置搶占優(yōu)先級和子優(yōu)先級的分配),在函數(shù)在misc.c */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1) ; /* 配置初始化結(jié)構(gòu)體 在misc.h中 */ /* 配置中斷源 在stm32f10x.h中 */ NVIC_InitStruct.NVIC_IRQChannel = KEY1_EXTI_IRQN ; /* 配置搶占優(yōu)先級 */ NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1 ; /* 配置子優(yōu)先級 */ NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0 ; /* 使能中斷通道 */ NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ; /* 調(diào)用初始化函數(shù) */ NVIC_Init(&NVIC_InitStruct) ; /* 對key2執(zhí)行相同操作 */ NVIC_InitStruct.NVIC_IRQChannel = KEY2_EXTI_IRQN ; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1 ; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1 ; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ; NVIC_Init(&NVIC_InitStruct) ; }
- 外部中斷配置
void EXTI_Config() /* 主要是連接EXTI與GPIO */ { GPIO_InitTypeDef GPIO_InitStruct ; EXTI_InitTypeDef EXTI_InitStruct ; NVIC_Config(); /* 初始化要與EXTI連接的GPIO */ /* 開啟GPIOA與GPIOC的時鐘 */ RCC_APB2PeriphClockCmd(KEY1_EXTI_GPIO_CLK | KEY2_EXTI_GPIO_CLK, ENABLE) ; GPIO_InitStruct.GPIO_Pin = KEY1_EXTI_GPIO_PIN ; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING ; GPIO_Init(KEY1_EXTI_GPIO_PORT , &GPIO_InitStruct) ; GPIO_InitStruct.GPIO_Pin = KEY2_EXTI_GPIO_PIN ; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING ; GPIO_Init(KEY2_EXTI_GPIO_PORT , &GPIO_InitStruct) ; /* 初始化EXTI外設(shè) */ /* EXTI的時鐘要設(shè)置AFIO寄存器 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE) ; /* 選擇作為EXTI線的GPIO引腳 */ GPIO_EXTILineConfig( KEY1_GPIO_PORTSOURCE , KEY1_GPIO_PINSOURCE) ; /* 配置中斷or事件線 */ EXTI_InitStruct.EXTI_Line = KEY1_EXTI_LINE ; /* 使能EXTI線 */ EXTI_InitStruct.EXTI_LineCmd = ENABLE ; /* 配置模式:中斷or事件 */ EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt ; /* 配置邊沿觸發(fā) 上升or下降 */ EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising ; EXTI_Init(&EXTI_InitStruct) ; GPIO_EXTILineConfig( KEY2_GPIO_PORTSOURCE , KEY2_GPIO_PINSOURCE) ; EXTI_InitStruct.EXTI_Line = KEY2_EXTI_LINE ; EXTI_InitStruct.EXTI_LineCmd = ENABLE ; EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt ; EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling ; EXTI_Init(&EXTI_InitStruct); }
- 中斷服務(wù)函數(shù)與主函數(shù)
void EXTI0_IRQHandler(void) { if( EXTI_GetITStatus(KEY1_EXTI_LINE)!=RESET) { LED1_TOGGLE; //LED1的亮滅狀態(tài)反轉(zhuǎn) } EXTI_ClearITPendingBit(KEY1_EXTI_LINE); } void EXTI15_10_IRQHandler(void) { if( EXTI_GetITStatus(KEY2_EXTI_LINE)!=RESET) { LED2_TOGGLE; //LED2的亮滅狀態(tài)反轉(zhuǎn) } EXTI_ClearITPendingBit(KEY2_EXTI_LINE); } / #include "stm32f10x.h" #include "bsp_led.h" #include "bsp_key.h" int main(void) { LED_GPIO_Config(); EXTI_Config(); while(1) { } }
定時器實現(xiàn)LED燈閃爍
代碼主要包含以下三個部分:
- 定時器初始化:TIM3時鐘使能,設(shè)置TIM3_ARR和TIM3_PSC的值,設(shè)置TIM3_DIER允許更新中斷,允許TIM3工作,TIM3中斷分組設(shè)置。
- 編寫中斷服務(wù)函數(shù):實現(xiàn)LED燈亮滅。
- 主函數(shù):調(diào)用相關(guān)接口,完成IO口和定時器啟動。
//定時器初始化
void TIM3_Int_Init()
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //時鐘使能
TIM_TimeBaseStructure.TIM_Prescaler =7199; //設(shè)置用來作為TIMx時鐘頻率除數(shù)的預(yù)分頻值 10Khz的計數(shù)頻率
TIM_TimeBaseStructure.TIM_Period = 4999; //設(shè)置在下一個更新事件裝入活動的自動重裝載寄存器周期的值 計數(shù)到5000為500ms
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設(shè)置時鐘分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數(shù)模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根據(jù)TIM_TimeBaseInitStruct中指定的參數(shù)初始化TIMx的時間基數(shù)單位
TIM_ITConfig(TIM3, TIM_IT_Update,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中斷
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占優(yōu)先級0級
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //從優(yōu)先級3級
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根據(jù)NVIC_InitStruct中指定的參數(shù)初始化外設(shè)NVIC寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIMx外設(shè)
}
//定時器中斷服務(wù)函數(shù)
void TIM3_IRQHandler(void) //TIM3中斷
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //檢查指定的TIM中斷發(fā)生與否:TIM 中斷源
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx的中斷待處理位:TIM 中斷源
LED1=!LED1;
}
}
//主函數(shù)
int main(void)
{
delay_init(); //延時函數(shù)初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 設(shè)置中斷優(yōu)先級分組2
LED_Init(); //初始化與LED連接的硬件接口
TIM3_Int_Init(); //10Khz的計數(shù)頻率,計數(shù)到5000為500ms
while(1)
{
LED0=!LED0;
delay_ms(200);
}
}
十六宿舍 原創(chuàng)作品,轉(zhuǎn)載必須標(biāo)注原文鏈接。
?2023 Yang Li. All rights reserved.文章來源:http://www.zghlxwxcb.cn/news/detail-760606.html
歡迎關(guān)注?『十六宿舍』,大家喜歡的話,給個??,更多關(guān)于嵌入式相關(guān)技術(shù)的內(nèi)容持續(xù)更新中。文章來源地址http://www.zghlxwxcb.cn/news/detail-760606.html
到了這里,關(guān)于STM32+摁鍵與定時器實現(xiàn)Led燈控制(中斷)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!