知識點1【中斷的介紹】
單片機的中斷——硬件中斷
Linux操作系統的中斷——軟件中斷
中斷是指計算機運行過程中,出現某種意外情況需要主機干預,機器能自動停止正在運行的程序并轉入處理新情況的程序,處理完畢后有返回原本暫停的程序繼續運行。
1、程序中斷和簡單中斷
程序中斷:系統外部、內部或者現行程序本身若出現緊急事件,處理機立即中止現行程序的運行,自動轉入相應的中斷服務程序,待處理完畢后,再返回原來的程序運行,這整個過程稱為程序中斷
簡單中斷:當處理機接收中斷后,只需暫停一個或幾個周期而不執行對應的中斷服務函數,稱為簡單中斷
2、中斷相關的概念
中斷是一種硬件處理機制——一旦觸發,就不需要程序判斷,CPU內部自動執行。
中斷源:能夠觸發中斷的事件(如按鍵:按壓按鍵會產生一個跳變沿),串口的收發也可以觸發中斷。一旦中斷源被觸發,那么在芯片內部就會產生一個中斷請求。
事件:在中斷部分,我們在使用中斷之前,需要首先確定產生中斷的事件。
在單片機中不是所有的事件都可以觸發中斷的,在手冊中我們可以查到,那些事件可以觸發中斷。我們以串口為例
上圖便是USART能夠觸發中斷的請求。
中斷服務程序:一旦中斷觸發,CPU需要停止當前的任務,去執行其他的任務,我們稱其他任務為中斷服務程序。
優先級:當有多個中斷觸發的時候,中斷的處理順序——數字越大,優先級越低
中斷嵌套,在一個中斷中可以出發另一個中斷。
(1)中斷優先級
搶占優先級:決定一個中斷能否打斷當前正在執行的中斷。
搶占優先級高的可以搶占搶占優先級低的中斷。 如果搶占優先級相同,則無法互相搶占。
次級優先級:當多個中斷共享相同搶占優先級時,決定它們的處理順序。
刺激優先級高的中斷先被處理,但不能打斷正在執行的同搶占優先級的中斷
次級優先級 是不具有打斷功能的。
那么 搶占優先級 和次級優先級是如何管理的呢?
是通過優先級分組 來管理的
(2)優先級分組
在ARM Cortex-M中,優先級數值的位數的分配是由 優先級分組 決定的。通過分組,可以分配搶占優先級和次級優先級的位數。
NVIC(嵌套向量中斷控制器)
NVIC可配置的中斷最大數目是240,每個中斷的中斷優先級 是1-256級
如上圖使用8位來配置中斷優先級的
那么 如果小于八位是如何配置呢?
——通過犧牲低位來配置,如 如果配置4位,就只有4-7位有效
3、中斷注意事項:
終端服務函數中,不能出現死循環,也不能寫入大量的循環語句。
原則:快進快出
知識點2【外部中斷】
1、外部中斷框圖
流程介紹:
1、觸發條件判斷
外部中斷信號 通過輸入線進入,首先經過邊沿檢測電路,判斷是上邊沿,下邊沿還是雙邊沿觸發
判斷方式即是由上邊沿觸發選擇寄存器和下邊沿觸發選擇寄存器決定。
2、經過 或門
軟件和硬件都可以觸發中斷事件
3、事件屏蔽寄存器
用來選擇傳遞的中斷事件。
未被屏蔽的事件可以繼續傳遞到后續模塊
4、脈沖發生器
被篩選后的事件被轉換為單周期脈沖信號,發出 事件請求信號
以上兩個步驟是事件的處理流程,滿足事件不一定會觸發中斷,可能是為了實現其他功能。
5、中斷狀態記錄
請求掛起寄存器:鎖存當前有效的中斷請求狀態,知道處理器響應后清除
6、二次屏蔽
中斷屏蔽寄存器,通過APB總線 過濾請求,避免非使能中斷占用NVIC資源
2、外部中斷請求
外部中斷/事件控制器 是由19個產生事件/中斷請求的邊沿檢測器組成
所以說:外部中斷,其實就是IO口跳變產生的中斷
知識點3【外部中斷配置寄存器介紹】
1、外部中斷配置寄存器
我們從上一張圖片可以看到,每一個外部中斷端口,都對應著很多個引腳如EXTI1對應這A1,B1……,因此我們需要配置其定義關系。
這里我們以外部中斷寄存器2為例
可見上面有EXTI4-EXTI7,那么可以這樣的寄存器一共4個。
注意
我們配置的時候,這個寄存器是按照數組的方式 來操作的。
詳細配置 看下面的代碼演示即可,很易理解,寫代碼的時候細心就好。
2、中斷屏蔽寄存器
這里是配置 哪一個中斷使能的
3、上 下邊沿寄存器
知識點4【外部中斷代碼演示】
exti.c
void Exit1_Key_Config()
{//配置時鐘:按鍵(PE4)和復用按鍵RCC->APB2ENR |= (0x01 << 0);RCC->APB2ENR |= (0x01 << 6);//中斷配置//由于映射到EXIT2的端口有很多,因此需要選擇AFIO->EXTICR[1] |= (0x04 << 0);//下邊沿觸發(線4)EXTI->FTSR |= (0x01 << 4);//中斷屏蔽設置EXTI->IMR |= (0x01 << 4);//NVIC配置NVIC_SetPriority(EXTI4_IRQn,0x06);NVIC_EnableIRQ(EXTI4_IRQn);
}//按鍵中斷服務函數
void EXTI4_IRQHandler(void)
{//中斷觸發條件判斷if(EXTI->PR & (0x01 << 4)){//清除中斷掛起位EXTI->PR &= ~(0x01 << 4);//執行中斷函數while(1){//延時消抖if((GPIOE->IDR & (0x01 << 4)) == 0){Delay_ms(40);if((GPIOE->IDR & (0x01 << 4)) == 0){while((GPIOE->IDR & (0x01 << 4)) == 0);printf("你好\\n");}}}}
}
main.c
#include "stm32f10x.h" // 相當于51單片機中的 #include <reg51.h>
#include "stdio.h"
#include "delay.h"
#include "key.h"
#include "usart.h"
int main(void)
{// 來到這里的時候,系統的時鐘已經被配置成72M?//第五組NVIC_SetPriorityGrouping(0X05);Exit1_Key_Config();Usart1_Config(9600);printf("USART1 is ok!!\\r\\n");while(1){ }}
代碼感悟
1、串口一次只能接收 / 發送一位數據
2、中斷服務函數 需要 軟件清除中斷標志位
3、外部中斷是通過 輔助接口(AFIO) 來設置的
知識點5【事件中斷】
首先我們要清楚,不是所有事件都能觸發中斷的,因此我們要實現知道那些事件能夠產生中斷,我們以串口為例。可以在寄存器手冊中查到。
可見,事件中斷觸發,的前提是 使能對應事件的中斷標志位,如當 接收事件完畢即RXNE被置位 后,并且軟件置為RXNEIE,會產生一個對應的串口中斷
在事件觸發,并且對應的中斷標志位被置位 的情況下,事件中斷就會被觸發,即會執行對應的中斷服務程序。
這里寄存器不過多介紹 我們直接進入實戰
知識點6【事件中斷代碼演示】
邏輯:
只有RXNE 被置位,且RXNEIE 被置位,才會發生中斷
我們知道串口每次接收到一個數據,RXNE會被置位,又由于我們循環軟件置為RXNEIE 是的接收數據中斷持續生效。
那么我們該如何判斷所有位的數據 都被接收呢
我們就需要利用串口的空閑中斷 即 IDLE狀態位,詳細大家看手冊。
這是我們就 引入一個flag位,當進入空閑狀態,flag置1,即接收全部數據完成。
代碼演示
exti.c文件
#include "exti.h"u8 over_flag=0;
u8 rev_data[64]={0};
u8 rev_count=0;
void USART1_IRQHandler(void)
{u8 data=0;if((USART1->SR &(0X01<<5))){USART1->SR &=~(0X01<<5);//進入了接收中斷,可以接受外部傳輸過來的數據rev_data[rev_count++]=USART1->DR;}//當數據接受完成的時候,就會進入空閑中斷if((USART1->SR &(0X01<<4))){USART1->SR &=~(0X01<<4);//清除空閑中斷標志data=USART1->DR;//設置一個接收完成的標志位over_flag=1; }
}//中斷接受完成數據之后,在以下函數中處理數據void Usart1_CtrlLED(void)
{int i=0;if(over_flag==1){over_flag=0;//避免影響下一次的數據接收中斷//處理接收到的數據printf("接收到的數據個數:%d\\r\\n",rev_count);for(i=0;i<rev_count;i++){printf("rev_data[%d]=%x\\r\\n",i,rev_data[i]);}if(rev_data[0]==0xaa && rev_data[2]==0xff){//控制LED1if(rev_data[1]==0x11) GPIOB->ODR &=~(0X01<<5);else GPIOB->ODR |=(0X01<<5);}rev_count=0;memset(rev_data,0,sizeof(rev_data));}
}int fputc(int c, FILE * stream)
{while(!(USART1->SR &(0X01<<7)));USART1->DR=c;return c;
}
main函數
#include "stm32f10x.h" // 相當于51單片機中的 #include <reg51.h>
#include "stdio.h"
#include "delay.h"
#include "led.h"
#include "usart.h"
int main(void)
{// 來到這里的時候,系統的時鐘已經被配置成72M?//第五組NVIC_SetPriorityGrouping(0X05);Led_Config();Usart1_Config(9600);printf("USART1 is ok!!\\r\\n");while(1){Usart1_RevInterrupt();Usart1_CtrlLED();}
}
結束
最近的分析是關于寄存器的,當然后面我會分享標準庫的,因為寄存器是基礎,我們先學習寄存器有利于我們深度理解后面標準庫的原理,我的分享是一個由繁入簡的過程,希望大家能夠理解。
補充:
? ? ? ? 這兩天由于身體狀態原因,沒有正常更新,但學習過程沒有間斷。大家也注意身體,畢竟身體是革命的本錢。
????????望體諒!
最后:
????????代碼重在練習!
????????代碼重在練習!
????????代碼重在練習!
今天的分享就到此結束了,希望對你有所幫助。如果你喜歡我的分享,請點贊收藏加關注,謝謝大家!!!