【嵌入式学习-STM32F103-EXTI外部中断】
生活随笔
收集整理的這篇文章主要介紹了
【嵌入式学习-STM32F103-EXTI外部中断】
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
目錄
1、EXTI基礎知識補充
2、編程要點
3、對射式紅外傳感器計次完整代碼(注釋)
4、旋轉編碼器計次完整代碼(注釋)
參考江科大32單片機教學視頻!
1、EXTI基礎知識補充
對于旋轉編碼器,正向旋轉時,A、B相輸出的波形為下圖(上),反向旋轉時,A、B相輸出的波形為下圖(下)。
1、如果把一相的的下降沿用作觸發中斷,在中斷時刻讀取另一相的電平,此時正轉就是高電平,反轉就是低電平,這樣就能區分旋轉方向。
(小瑕疵:正轉時,由于A相先出現下降沿,旋轉編碼器剛開始動,就進入中斷,而反轉時,A相后出現下降沿,只有轉到位了,才能進入中斷)
2、改進方案:只有在B相下降沿和A相低電平時,才判斷為正轉,在A相下降沿和B相低電平時,才判斷為反轉。這樣能夠保證正轉和反轉都是轉到位了,才執行數字的加減操作。
上圖對應的中斷服務函數為中斷0服務函數和中斷1服務函數
//中斷0服務函數 void EXTI0_IRQHandler(void) {//檢查函數標志位if (EXTI_GetITStatus(EXTI_Line0) == SET){/* 如果出現數據亂跳的現象,可再次判斷引腳電平,以避免抖動 */if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0){if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0){Encoder_Count --;}}//清除中斷標志位EXTI_ClearITPendingBit(EXTI_Line0);} } //中斷1服務函數 void EXTI1_IRQHandler(void) {//檢查函數標志位if (EXTI_GetITStatus(EXTI_Line1) == SET){/* 如果出現數據亂跳的現象,可再次判斷引腳電平,以避免抖動 */if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0){if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0){Encoder_Count ++;}}//清除中斷標志位EXTI_ClearITPendingBit(EXTI_Line1);} }2、編程要點
1)初始化用來產生中斷的GPIO
2)初始化EXTI
3)配置NVIC
4)編寫中斷服務函數
3、對射式紅外傳感器計次完整代碼(注釋)
CountSensor.c
#include "stm32f10x.h" // Device header//我們想要一個數字來統計中斷觸發的次數 uint16_t CountSensor_Count;//設計的外設包括RCC GPIO AFIO EXTI NVIC void CountSensor_Init(void) {RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//EXTI(原因不詳) 和 NVIC(內核外設都不需要開啟時鐘)的時鐘一直都打開,不需要我們開啟時鐘GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉輸入,默認為高電平GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);/* 與AFIO相關的重要的函數配置 *///GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState); //作用是可用來進行引腳重映射,第一個參數可以選擇重映射的方式 第二個參數是新的狀態GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14); //配置AFIO的數據選擇器,來選擇我們想要的中斷引腳//將EXTI的第14個線路配置為中斷模式,下降沿觸發,開啟中斷,這樣PB14的電平信號就能夠通過EXTI通向下一級的NVICEXTI_InitTypeDef EXTI_InitStructure;EXTI_InitStructure.EXTI_Line = EXTI_Line14; //指定我們需要配置的中斷線EXTI_InitStructure.EXTI_LineCmd = ENABLE; //指定選擇的中斷線的新狀態EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中斷模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿觸發EXTI_Init(&EXTI_InitStructure);//配置NVIC//中斷分組NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //這個分組方式整個芯片只能用一種,因此這個分組的代碼整個工程只需要執行一次即可//中斷初始化NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //優先級是在多個中斷源同時申請,產生擁擠時才有作用,這只有一個中斷,隨機匹配即可NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStructure); }//返回變量CountSensor_Count uint16_t CountSensor_Get(void) {return CountSensor_Count; } //中斷函數都是無參無返回值 //中斷函數不需要調用,它是自動執行的 void EXTI15_10_IRQHandler(void) {//中斷標志位的判斷,確保是我們想要的中斷源觸發的這個函數,因為這個函數EXTI10到15都能進來,因此需要判斷是否是EXTI14 if (EXTI_GetITStatus(EXTI_Line14) == SET){//如果是,我們就執行中斷程序/*如果出現數據亂跳的現象,可再次判斷引腳電平,以避免抖動*/if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0){CountSensor_Count ++;}//中斷程序結束后,一定要再調用一下清除中斷標志位的函數,因為只要中斷標志位置1,程序就會跳轉到中斷函數//如果你不清除中斷標志位,那他就會一直申請中斷,程序就會不斷響應中斷,執行中斷函數,程序就會卡死在中斷函數里EXTI_ClearITPendingBit(EXTI_Line14);} }CountSensor.h
#ifndef __COUNT_SENSOR_H #define __COUNT_SENSOR_Hvoid CountSensor_Init(void); uint16_t CountSensor_Get(void);#endifmain.c
#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "CountSensor.h"int main(void) {OLED_Init();CountSensor_Init();OLED_ShowString(1, 1, "Count:");while (1){//顯示計次的數據OLED_ShowNum(1, 7, CountSensor_Get(), 5);} }4、旋轉編碼器計次完整代碼(注釋)
Encoder.c
#include "stm32f10x.h" // Device header//由于需要正反轉,因此定義一個帶符號的變量 int16_t Encoder_Count;//初始化PB0和PB1兩個GPIO口的外部中斷 void Encoder_Init(void) { //初始化GPIO和AFIO時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//初始化GPIO,PB0和PB1GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);//AFIO中斷引腳選擇GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0); //將第0個線路撥到GPIOB上GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1); //將第1個線路撥到GPIOB上//指定中斷線,同時將第0條線路和第1條線路初始化為中斷模式,下降沿觸發EXTI_InitTypeDef EXTI_InitStructure;EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1; EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;EXTI_Init(&EXTI_InitStructure);//中斷分組NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中斷優先級,對兩個通道分別設置優先級NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;NVIC_Init(&NVIC_InitStructure); }//定義函數,返回變量,在這里不直接返回Encoder_Count變量,而是返回每次調用這個get函數后的Count變化值 //因為用count返回的話,return直接跳出函數,count就無法清零 int16_t Encoder_Get(void) {int16_t Temp;Temp = Encoder_Count;Encoder_Count = 0;return Temp; }//中斷0服務函數 void EXTI0_IRQHandler(void) {//檢查函數標志位if (EXTI_GetITStatus(EXTI_Line0) == SET){/* 如果出現數據亂跳的現象,可再次判斷引腳電平,以避免抖動 */if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0){if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0){Encoder_Count --;}}//清除中斷標志位EXTI_ClearITPendingBit(EXTI_Line0);} } //中斷1服務函數 void EXTI1_IRQHandler(void) {//檢查函數標志位if (EXTI_GetITStatus(EXTI_Line1) == SET){/* 如果出現數據亂跳的現象,可再次判斷引腳電平,以避免抖動 */if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0){if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0){Encoder_Count ++;}}//清除中斷標志位EXTI_ClearITPendingBit(EXTI_Line1);} }/* 中斷編程的建議 1、在中斷函數里,最好不要執行耗時過長的代碼,中斷函數要簡短快速,別剛進中斷就執行一個Delay多少毫秒的代碼 因為中斷時處理突發的事情,如果你為了一個突發的事情呆在中斷里不出來,主程序就會收到嚴重的阻塞 2、最好不要在中斷函數和主函數調用相同的函數或者操作同一個硬件,如OLED顯示函數,如果既在主函數里調用OLED函數 又在中斷里調用OLED,OLED就會顯示錯誤 為了避免問題,最好不要在主程序和中斷程序里操作可能產生沖突的硬件 在實現功能的時候,可以在中斷里操作變量或者標志位 當中斷返回時,我在對這個變量進行顯示和操作 這樣既能保證中斷函數的簡短快速,又能保證不產生沖突的硬件操作EXTI_GetITStatus() 函數用于獲取指定的中斷標志位的狀態,返回值為標志位的狀態,即是否被觸發。如果返回值為 SET,則表示對應的中斷已經被觸發;如果返回值為 RESET,則表示對應的中斷還未被觸發。EXTI_ClearITPendingBit() 函數用于清除指定的中斷標志位。當某個中斷被觸發時,對應的中斷標志位會被置位。在處理完中斷后,需要通過調用 EXTI_ClearITPendingBit() 函數來清除該標志位,否則會一直保持置位狀態,導致下一次中斷無法正常觸發。因此,這兩個函數常常一起使用,以確保外部中斷能夠正常工作。 */Encoder.h
#ifndef __ENCODER_H #define __ENCODER_Hvoid Encoder_Init(void); int16_t Encoder_Get(void);#endifmain.c
#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "Encoder.h"int16_t Num;int main(void) {OLED_Init();Encoder_Init();OLED_ShowString(1, 1, "Num:");while (1){//因為Get函數返回的是調用這個函數的間隔里旋轉編碼器產生的正負脈沖數,所以返回值直接+=給Num,就能夠對Num進行加減操作Num += Encoder_Get();OLED_ShowSignedNum(1, 5, Num, 5);} }總結
以上是生活随笔為你收集整理的【嵌入式学习-STM32F103-EXTI外部中断】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 钢琴家软件里曲谱的数据为什么更新不了_王
- 下一篇: server多笔记录拼接字符串 sql_