外部中斷實驗 #STM32F407

外部中斷實驗

此實驗將外部中斷配置為按鍵輸入,通過按鍵輸入觸發外部中斷,在外部中斷里面實施相應的處理,具體功能:

  1. 按下KEY0,翻轉LED0狀態
  2. 按下KEY1,翻轉LED1狀態
  3. 按下KEY2,同時翻轉LED0和LED1狀態
  4. 按下KEY_UP,翻轉BEEP狀態

在中斷回調函數里面使用delay進行消抖,導致中斷是阻塞的,不符合中斷快速執行的原則,linux中的按鍵處理是實驗外部中斷+定時器共同實現的,更具普遍性。

弄清楚:

  1. 中斷在單片機中是如何實現的
  2. 外部中斷處理流程(程序)
  3. 如何配置外部中斷

main函數

main函數代碼:

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/BEEP/beep.h"
#include "./BSP/EXTI/exti.h"int main(void)
{    HAL_Init();                             /* 初始化HAL庫 */sys_stm32_clock_init(336, 8, 2, 7);     /* 設置時鐘,168Mhz */delay_init(168);                        /* 延時初始化 */usart_init(115200);                     /* 串口初始化為115200 */led_init();                             /* 初始化LED */beep_init();                            /* 初始化蜂鳴器 */extix_init();                           /* 初始化外部中斷輸入 */LED0(0);                                /* 先點亮紅燈 */while(1){delay_ms(1000);}
}

在main中主要做的是初始化,然后在while(1)里面死循環等待中斷的到來,HAL_Init是使用HAL庫必須調用的初始化函數;sys_stm32_clock_init、delay_init、usart_init這三個函數是正點原子編寫的SYSTEM初始化函數,配置了系統時鐘,延時函數,串口配置,實現單片機開發常用基本功能;然后調用led_init、beep_init、extix_init,初始化LED引腳、BEEP引腳、EXTI外部中斷,KEY引腳初始化是在extix_init中進行調用的,故未在main中體現,完成所有的硬件初始化后,先將LED0點亮。

本次實驗的核心是extix_init以及中斷處理函數

exti.h

#ifndef __EXTI_H
#define __EXTI_H#include "./SYSTEM/sys/sys.h"/******************************************************************************************/
/* 引腳 和 中斷編號 & 中斷服務函數 定義 */ #define KEY0_INT_GPIO_PORT              GPIOE
#define KEY0_INT_GPIO_PIN               GPIO_PIN_4
#define KEY0_INT_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0)   /* PE口時鐘使能 */
#define KEY0_INT_IRQn                   EXTI4_IRQn
#define KEY0_INT_IRQHandler             EXTI4_IRQHandler#define KEY1_INT_GPIO_PORT              GPIOE
#define KEY1_INT_GPIO_PIN               GPIO_PIN_3
#define KEY1_INT_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0)   /* PE口時鐘使能 */
#define KEY1_INT_IRQn                   EXTI3_IRQn
#define KEY1_INT_IRQHandler             EXTI3_IRQHandler#define KEY2_INT_GPIO_PORT              GPIOE
#define KEY2_INT_GPIO_PIN               GPIO_PIN_2
#define KEY2_INT_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0)   /* PE口時鐘使能 */
#define KEY2_INT_IRQn                   EXTI2_IRQn
#define KEY2_INT_IRQHandler             EXTI2_IRQHandler#define WKUP_INT_GPIO_PORT              GPIOA
#define WKUP_INT_GPIO_PIN               GPIO_PIN_0
#define WKUP_INT_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)   /* PA口時鐘使能 */
#define WKUP_INT_IRQn                   EXTI0_IRQn
#define WKUP_INT_IRQHandler             EXTI0_IRQHandler

.h文件中將引腳相關的量宏定義,方便理解與更改,并且將中斷函數重定義,如將EXTI2_IRQHandler定義成KEY2_INT_IRQHandler,方便自己編寫維護

exti.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/BEEP/beep.h"
#include "./BSP/KEY/key.h"
#include "./BSP/EXTI/exti.h"/*** @brief       KEY0 外部中斷服務程序* @param       無* @retval      無*/
void KEY0_INT_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(KEY0_INT_GPIO_PIN);         /* 調用中斷處理公用函數 清除KEY0所在中斷線 的中斷標志位 */__HAL_GPIO_EXTI_CLEAR_IT(KEY0_INT_GPIO_PIN);         /* HAL庫默認先清中斷再處理回調,退出時再清一次中斷,避免按鍵抖動誤觸發 */
}/*** @brief       KEY1 外部中斷服務程序* @param       無* @retval      無*/
void KEY1_INT_IRQHandler(void)
{ HAL_GPIO_EXTI_IRQHandler(KEY1_INT_GPIO_PIN);         /* 調用中斷處理公用函數 清除KEY1所在中斷線 的中斷標志位,中斷下半部在HAL_GPIO_EXTI_Callback執行 */__HAL_GPIO_EXTI_CLEAR_IT(KEY1_INT_GPIO_PIN);         /* HAL庫默認先清中斷再處理回調,退出時再清一次中斷,避免按鍵抖動誤觸發 */
}/*** @brief       KEY2 外部中斷服務程序* @param       無* @retval      無*/
void KEY2_INT_IRQHandler(void)
{ HAL_GPIO_EXTI_IRQHandler(KEY2_INT_GPIO_PIN);        /* 調用中斷處理公用函數 清除KEY2所在中斷線 的中斷標志位,中斷下半部在HAL_GPIO_EXTI_Callback執行 */__HAL_GPIO_EXTI_CLEAR_IT(KEY2_INT_GPIO_PIN);        /* HAL庫默認先清中斷再處理回調,退出時再清一次中斷,避免按鍵抖動誤觸發 */
}/*** @brief       WK_UP 外部中斷服務程序* @param       無* @retval      無*/
void WKUP_INT_IRQHandler(void)
{ HAL_GPIO_EXTI_IRQHandler(WKUP_INT_GPIO_PIN);        /* 調用中斷處理公用函數 清除KEY_UP所在中斷線 的中斷標志位,中斷下半部在HAL_GPIO_EXTI_Callback執行 */__HAL_GPIO_EXTI_CLEAR_IT(WKUP_INT_GPIO_PIN);        /* HAL庫默認先清中斷再處理回調,退出時再清一次中斷,避免按鍵抖動誤觸發 */
}/*** @brief       中斷服務程序中需要做的事情*              在HAL庫中所有的外部中斷服務函數都會調用此函數* @param       GPIO_Pin:中斷引腳號* @retval      無*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{delay_ms(20);      /* 消抖 */switch(GPIO_Pin){case KEY0_INT_GPIO_PIN:if (KEY0 == 0){LED0_TOGGLE();  /* LED0 狀態取反 */ }break;case KEY1_INT_GPIO_PIN:if (KEY1 == 0){LED1_TOGGLE();  /* LED1 狀態取反 */ }break;case KEY2_INT_GPIO_PIN:if (KEY2 == 0){LED1_TOGGLE();  /* LED1 狀態取反 */LED0_TOGGLE();  /* LED0 狀態取反 */ }break;case WKUP_INT_GPIO_PIN:if (WK_UP == 1){BEEP_TOGGLE();  /* 蜂鳴器狀態取反 */ }break;default : break;}
}/*** @brief       外部中斷初始化程序* @param       無* @retval      無*/
void extix_init(void)
{GPIO_InitTypeDef gpio_init_struct;key_init();gpio_init_struct.Pin = KEY0_INT_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_IT_FALLING;            /* 下降沿觸發 */gpio_init_struct.Pull = GPIO_PULLUP;HAL_GPIO_Init(KEY0_INT_GPIO_PORT, &gpio_init_struct);    /* KEY0配置為下降沿觸發中斷 */gpio_init_struct.Pin = KEY1_INT_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_IT_FALLING;            /* 下降沿觸發 */gpio_init_struct.Pull = GPIO_PULLUP;HAL_GPIO_Init(KEY1_INT_GPIO_PORT, &gpio_init_struct);    /* KEY1配置為下降沿觸發中斷 */gpio_init_struct.Pin = KEY2_INT_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_IT_FALLING;            /* 下降沿觸發 */gpio_init_struct.Pull = GPIO_PULLUP;HAL_GPIO_Init(KEY2_INT_GPIO_PORT, &gpio_init_struct);    /* KEY2配置為下降沿觸發中斷 */gpio_init_struct.Pin = WKUP_INT_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_IT_RISING;             /* 上升沿觸發 */gpio_init_struct.Pull = GPIO_PULLDOWN;HAL_GPIO_Init(WKUP_GPIO_PORT, &gpio_init_struct);        /* WKUP配置為上升沿觸發中斷 */HAL_NVIC_SetPriority(KEY0_INT_IRQn, 0, 2);               /* 搶占0,子優先級2 */HAL_NVIC_EnableIRQ(KEY0_INT_IRQn);                       /* 使能中斷線4 */HAL_NVIC_SetPriority(KEY1_INT_IRQn, 1, 2);               /* 搶占1,子優先級2 */HAL_NVIC_EnableIRQ(KEY1_INT_IRQn);                       /* 使能中斷線3 */HAL_NVIC_SetPriority(KEY2_INT_IRQn, 2, 2);               /* 搶占2,子優先級2 */HAL_NVIC_EnableIRQ(KEY2_INT_IRQn);                       /* 使能中斷線2 */HAL_NVIC_SetPriority(WKUP_INT_IRQn, 3, 2);               /* 搶占3,子優先級2 */HAL_NVIC_EnableIRQ(WKUP_INT_IRQn);                       /* 使能中斷線0 */}

extix_init函數,先調用key_init將各按鍵IO的時鐘打開,然后再將按鍵IO模式配置為中斷模式,最后配置各中斷優先級及使能。

中斷觸發流程:(以KEY0為例)

當KEY0變為低電平時,會執行EXTI4_IRQHandler,不過此函數在exit.h里面被重定義成KEY0_INT_IRQHandler。

/*** @brief       KEY0 外部中斷服務程序* @param       無* @retval      無*/
void KEY0_INT_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(KEY0_INT_GPIO_PIN);         /* 調用中斷處理公用函數 清除KEY0所在中斷線 的中斷標志位 */__HAL_GPIO_EXTI_CLEAR_IT(KEY0_INT_GPIO_PIN);         /* HAL庫默認先清中斷再處理回調,退出時再清一次中斷,避免按鍵抖動誤觸發 */
}

在函數內先調用HAL_GPIO_EXTI_IRQHandler,這是HAL庫內的公用處理函數,在其中會先進行一次中斷標志位清除,然后調用回調函數(實際的處理函數)。
最后在KEY0_INT_IRQHandler再執行一次中斷標志位清除,也就是說一次中斷處理內總共執行了兩次中斷標志位清除,第一次是在HAL庫的公用處理函數內,第二層是在自己編寫的KEY0_INT_IRQHandler內,HAL庫官方推薦這么寫:在中斷處理首末各執行一次清標志位,以防止誤觸發。

/*** @brief  This function handles EXTI interrupt request.* @param  GPIO_Pin Specifies the pins connected EXTI line* @retval None*/
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{/* EXTI line interrupt detected */if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET){__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);HAL_GPIO_EXTI_Callback(GPIO_Pin);}
}

先清除中斷標志位,這是第一次清中斷,然后執行回調函數HAL_GPIO_EXTI_Callback,這個回調函數是HAL庫內的虛函數,需要自己重寫,具體如下,正式開始執行邏輯處理

/*** @brief       中斷服務程序中需要做的事情*              在HAL庫中所有的外部中斷服務函數都會調用此函數* @param       GPIO_Pin:中斷引腳號* @retval      無*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{delay_ms(20);      /* 消抖 */switch(GPIO_Pin){case KEY0_INT_GPIO_PIN:if (KEY0 == 0){LED0_TOGGLE();  /* LED0 狀態取反 */ }break;case KEY1_INT_GPIO_PIN:if (KEY1 == 0){LED1_TOGGLE();  /* LED1 狀態取反 */ }break;case KEY2_INT_GPIO_PIN:if (KEY2 == 0){LED1_TOGGLE();  /* LED1 狀態取反 */LED0_TOGGLE();  /* LED0 狀態取反 */ }break;case WKUP_INT_GPIO_PIN:if (WK_UP == 1){BEEP_TOGGLE();  /* 蜂鳴器狀態取反 */ }break;default : break;}
}

在回調函數中先延時20毫秒以消抖,根據不同的觸發引腳在switch中執行相應的操作。

執行完這個回調函數后,就回到了KEY0_INT_IRQHandler內,然后執行第二次清中斷標志位,一次外部中斷處理就完成了。

實際上外部中斷處理并不復雜,硬件初始化后,程序就等待中斷的到來,IO電平變低,中斷寄存器中的標志位被置高,程序會立刻跳轉到EXTI4_IRQHandler這個函數中,這個函數其實就是一個地址,其在啟動文件內定義,是一個固定的地址,用戶需要在自己的代碼中重寫這個函數,在里面加上業務處理函數,若最后加上一個清中斷標志位的函數,那該中斷可以被重復觸發,反之則只能觸發一次,這個函數被執行完后,程序又立即跳轉到觸發中斷前執行的位置,這就是STM32的外部中斷處理。
HAL庫對EXTI4_IRQHandler這個函數進行多次封裝,所以變得復雜
在這里插入圖片描述

在STM32中,幾乎每個引腳都可以被配置為外部中斷,那么問題來了,我怎么哪個引腳會執行哪個中斷處理函數?這需要去查詢單片機的參考手冊
在這里插入圖片描述

原來STM32是依靠引腳位來分割中斷處理函數的,在啟動匯編文件里面也一一對應
在這里插入圖片描述
在這里插入圖片描述

當中斷引腳為0~4時,就使用:
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
當中斷引腳為5~9時,就使用:
EXTI9_5_IRQHandler
當中斷引腳為10~15時,就使用:
EXTI15_10_IRQHandler

在HAL庫中如何編寫外部中斷

  1. 初始化IO引腳,將其配置為中斷上升沿觸發或下降沿觸發
  2. 使用HAL_NVIC_SetPriority配置中斷優先級并使用HAL_NVIC_EnableIRQ使能其中斷號
  3. 重寫EXTIx_IRQHandler,在內部加上HAL_GPIO_EXTI_IRQHandler和__HAL_GPIO_EXTI_CLEAR_IT
  4. 重寫回調函數HAL_GPIO_EXTI_Callback,在內部編寫具體業務處理代碼

Linux中的外部中斷+定時器實現按鍵大概處理思路

當按鍵按下,觸發該按鍵對應的中斷,在其中斷處理函數內啟動一個10ms的定時器中斷,當10ms結束,定時器中斷內再判斷該按鍵電平是否按下,如按下,則將事件標志位置高,在正常程序內查詢事件標志位以做出響應,如判斷為 沒按下,則說明可能是抖動電平,忽略此次中斷觸發。

中斷會打斷單片機當前執行的程序,因此不應在中斷內過多耗費時間,使用中斷來置各種標志位是常用的做法。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/895004.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/895004.shtml
英文地址,請注明出處:http://en.pswp.cn/news/895004.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java中如何給內部類的屬性賦值

在 Java 中,內部類的屬性賦值方式取決于該屬性的訪問修飾符、內部類的類型(非靜態或靜態),以及賦值的時機。以下是幾種常見的方式: 1. 通過構造方法賦值 class Outer {class Inner {private String name;// 構造方法賦…

機器學習8-卷積和卷積核1

機器學習8-卷積和卷積核1 卷積與圖像去噪卷積的定義與性質定義性質卷積的原理卷積步驟卷積的示例與應用卷積的優缺點優點缺點 總結 高斯卷積核卷積核尺寸的設置依據任務類型考慮數據特性實驗與調優 高斯函數標準差的設置依據平滑需求結合卷積核尺寸實際應用場景 總結 圖像噪聲與…

SVN 提交與原有文件類型不一樣的文件時的操作

SVN 提交與原有文件類型不一樣的文件時的操作 背景 SVN 服務器上原本的文件是軟鏈接類型的,但是我將它改成普通文件再上傳。出現了以下提示: 解決過程 本來想著通過 svn rm 和 svn add 來解決,但是行不通。 最終解決方案 svn rm --keep-…

阿里云專有云網絡架構學習

阿里云專有云網絡架構 葉脊(spine-leaf)網絡和傳統三層網絡拓撲對比 阿里云網絡架構V3拓撲角色介紹推薦設備設備組網舉例帶外管理網絡帶外網和帶內網對比設備介紹 安全網絡設備介紹 參考 后續更新流量分析葉脊(spine-leaf)網絡和傳…

Deepseek本地部署指南:在linux服務器部署,在mac遠程web-ui訪問

1. 在Linux服務器上部署DeepSeek模型 要在 Linux 上通過 Ollama 安裝和使用模型,您可以按照以下步驟進行操作: 步驟 1:安裝 Ollama 安裝 Ollama: 使用以下命令安裝 Ollama: curl -sSfL https://ollama.com/download.…

3D數字化營銷:重塑家居電商新生態

隨著電商的蓬勃發展,網上訂購家具已成為眾多消費者的首選。然而,線上選購家具的諸多挑戰,如風格不匹配、尺寸不合適、定制效果不如預期以及退換貨不便等,一直困擾著消費者。為解決這些問題,家居行業急需一種全新的展示…

重塑“景區+商業”模式,打造特色文旅新體驗

重塑“景區商業”模式,打造特色文旅新體驗 近年來,旅游業蓬勃發展,旅游熱潮不斷升溫,游客消費觀念也隨之升級。為順應這一趨勢,各大景區紛紛探索打造特色文旅項目,以期吸引更多游客。然而,“景…

在亞馬遜云科技上云原生部署DeepSeek-R1模型(下)

在本系列的上篇中,我們介紹了如何通過Amazon Bedrock部署并測試使用了DeepSeek模型。在接下來的下篇中小李哥將繼續介紹,如何利用亞馬遜的AI模型訓練平臺SageMaker AI中的,Amazon Sagemaker JumpStart通過腳本輕松一鍵式部署DeepSeek預訓練模…

Kubernetes是什么?為什么它是云原生的基石

從“手工時代”到“自動化工廠” 想象一下,你正在經營一家工廠。在傳統模式下,每個工人(服務器)需要手動組裝產品(應用),效率低下且容易出錯。而Kubernetes(k8s)就像一個…

Transformer 詳解:了解 GPT、BERT 和 T5 背后的模型

目錄 什么是 Transformer? Transformer如何工作? Transformer 為何有用? 常見問題解答:機器學習中的 Transformer 在技??術領域,突破通常來自于修復損壞的東西。制造第一架飛機的人研究過鳥類。萊特兄弟觀察了禿鷲如何在氣流中保持平衡,意識到穩定性比動力更重要。…

圖片webp格式動圖圖片

這是一個webp動圖1 這是一個webp動圖2 webp 圖像由gif 轉換 3

Spring(26) spring-security-oauth2 官方表結構解析

目錄 一、什么是 spring-security-oauth2?二、spring-security-oauth2 的表結構2.1 oauth_client_details 客戶端詳細信息表2.2 oauth_access_token 認證授權Token記錄表2.3 oauth_refresh_token 刷新授權Token記錄表2.4 oauth_code 授權Code記錄表 一、什么是 spri…

【R語言】plyr包和dplyr包

一、plyr包 plyr擴展包主要是實現數據處理中的“分割-應用-組合”(split-apply-combine)策略。此策略是指將一個問題分割成更容易操作的部分,再對每一部分進行獨立的操作,最后將各部分的操作結果組合起來。 plyr擴展包中的主要函…

【DeepSeek】DeepSeek小模型蒸餾與本地部署深度解析DeepSeek小模型蒸餾與本地部署深度解析

一、引言與背景 在人工智能領域,大型語言模型(LLM)如DeepSeek以其卓越的自然語言理解和生成能力,推動了眾多應用場景的發展。然而,大型模型的高昂計算和存儲成本,以及潛在的數據隱私風險,限制了…

程序員也可以這樣賺錢

最近有朋友和我交流了關于程序員副業的想法,我想借這個機會對目前軟件開發常用的兼職平臺做一個梳理。 以下是程序員接副業的靠譜平臺推薦,結合政策合規性、平臺口碑及實際操作性整理,覆蓋國內外主流選擇: 一、國內綜合型平臺 程序…

【AI】在Ubuntu中使用docker對DeepSeek的部署與使用

這篇文章前言是我基于部署好的deepseek-r1:8b模型跑出來的 關于部署DeepSeek的前言與介紹 在當今快速發展的技術環境中,有效地利用機器學習工具來解決問題變得越來越重要。今天,我將引入一個名為DeepSeek 的工具,它作為一種強大的搜索引擎&a…

代碼隨想錄算法【Day39】

Day39 198.打家劫舍 class Solution { public:int rob(vector<int>& nums) {if (nums.size() 0) return 0;if (nums.size() 1) return nums[0];vector<int> dp(nums.size());dp[0] nums[0];dp[1] max(nums[0], nums[1]);for (int i 2; i < nums.size…

TCP三次握手全方面詳解

文章目錄 (1) 三次握手各狀態CLOSE狀態SYN_SENT狀態SYN_RECV狀態ESTABLISHED狀態 (2) 為什么握手時的seqnum是隨機值&#xff0c;以及acknum的功能(3) 三次握手中的半連接隊列&#xff08;SYN隊列&#xff09;和全連接隊列&#xff08;ACCEPT隊列&#xff09;半連接隊列全連接隊…

數據結構與算法-遞歸

單路遞歸 二分查找 /*** 主函數&#xff1a;執行二分查找。* * param a 要搜索的數組&#xff08;必須是已排序的&#xff09;* param target 目標值* return 返回目標值在數組中的索引&#xff1b;如果未找到&#xff0c;則返回 -1*/ public static int binarySearch(int[] …

軟中斷和tasklet的區別是什么?

軟中斷和 tasklet 都是 Linux 內核中用于實現異步事件處理的機制&#xff0c;它們的主要區別如下&#xff1a; 實現機制 軟中斷&#xff1a;是一種基于軟件觸發的中斷機制&#xff0c;在內核中是一組靜態定義的、預先分配好的軟中斷向量。每個軟中斷都有一個唯一的編號和對應…