一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

ARM Cortex-M學(xué)習(xí)筆記:按鍵

CHANBAEK ? 來源:嵌入式實(shí)驗(yàn)樓 ? 作者:BruceOu ? 2023-05-15 15:12 ? 次閱讀

開發(fā)環(huán)境:

MDK:凱爾 5.30

STM32立方體MX:V6.4.0

單片機(jī):STM32F103ZET6

5.1普通方式

5.1.1 普通方式工作原理

按鍵 GPIO 端口有兩個(gè)方案可以選擇,一是采用上拉輸入模式,因?yàn)榘存I在沒按下的時(shí)候,是默認(rèn)為高電平的,采且內(nèi)部上拉模式正好符合這個(gè)要求。 第二個(gè)方案是直接采用浮空輸入模式,因?yàn)榘凑沼布娐穲D,在芯片外部接了上拉電阻,其實(shí)就沒必要再配置成內(nèi)部上拉輸入模式了,因?yàn)樵谕獠可侠c內(nèi)部上拉效果是一樣的。

5.1.2普通方式實(shí)現(xiàn)-標(biāo)準(zhǔn)庫(kù)

完整代碼請(qǐng)參附件,這里只貼出核心代碼。

  • GPIO 初始化配置
void Key_GPIO_Config(void)
{
       GPIO_InitTypeDefGPIO_InitStructure;
       /*開啟按鍵端口(PA)的時(shí)鐘*/
       RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
       GPIO_InitStructure.GPIO_Pin= GPIO_Pin_0;
       GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IPU;//上拉輸入
       GPIO_Init(GPIOA,&GPIO_InitStructure);
}

Key_GPIO_Config() 與 LED 的 GPIO 初始化函數(shù) LED_GPIO_Config() 類似,區(qū)別只是在這個(gè)函數(shù)中,要開啟的 GPIO 的端口時(shí)鐘不一樣,并且把檢測(cè)按鍵用的引腳 Pin 的模式設(shè)置為適合按鍵應(yīng)用的上拉輸入模式(由于接了外部上拉電阻,也可以使用浮空輸入,讀者可自行修改代碼做實(shí)驗(yàn))。 若 GPIO 被設(shè)置為輸入模式,不需要設(shè)置 GPIO 端口的最大輸出速度,當(dāng)然,如果配置了這個(gè)速度也沒關(guān)系,GPIO_Init() 函數(shù)會(huì)自動(dòng)忽略它。 在 RCC_APB2PeriphClockCmd()和 GPIO_InitStructure.GPIO_Pin 的輸入?yún)?shù)設(shè)置之中,我們可以用符號(hào)“|”,同時(shí)配置多個(gè)參數(shù)。 如:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOG,ENABLE);

輸入?yún)?shù)為RCC_APB2Periph_GPIOB| RCC_APB2Periph_GPIOG ,這樣調(diào)用之后,就把 GPIOB 和 GPIOG 的時(shí)鐘都開啟了。

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;

以上代碼則表示將要同時(shí)配置 GPIOG 端口的 Pin5 和 Pin6。

  • 按鍵消抖
uint8_t Key_Scan(GPIO_TypeDef*GPIOx,u16 GPIO_Pin,uint8_t Down_state)
{                   
       /*檢測(cè)是否有按鍵按下 */
       if(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin)== Down_state )
       {        
              /*延時(shí)消抖*/
              Key_Delay(10000);           
              if(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin)== Down_state ) 
              {       
                     /*等待按鍵釋放 */
                     while(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin)== Down_state);  
                     return  KEY_ON;      
              }
              else
                     returnKEY_OFF;
       }
       else
              returnKEY_OFF;
}

相信延時(shí)消抖的原理大家在學(xué)習(xí)其他單片機(jī)時(shí)就已經(jīng)了解了,本函數(shù)的功能就是掃描輸入?yún)?shù)中指定的引腳,檢測(cè)其電平變化,并作延時(shí)消抖處理,最終對(duì)按鍵消息進(jìn)行確認(rèn)。

  • 利用GPIO_ReadInputDataBit() 讀取輸入數(shù)據(jù),若從相應(yīng)引腳讀取的數(shù)據(jù)等于 0(KEY_ON),低電平,表明可能有按鍵按下,調(diào)用延時(shí)函數(shù)。否則返回 KEY_OFF,表示按鍵沒有被按下。
  • 延時(shí)之后再次利用GPIO_ReadInputDataBit() 讀取輸入數(shù)據(jù),若依然為低電平,表明確實(shí)有按鍵被按下了。否則返回 KEY_OFF,表示按鍵沒有被按下。
  • 循環(huán)調(diào)用 GPIO_ReadInputDataBit()一直檢測(cè)按鍵的電平,直至按鍵被釋放,被釋放后,返回表示按鍵被按下的標(biāo)志 KEY_ON。以上是按鍵消抖的流程,調(diào)用了一個(gè)庫(kù)函數(shù)GPIO_ReadInputDataBit()。輸入?yún)?shù)為要讀取的端口、引腳,返回引腳的輸入電平狀態(tài),高電平為 1,低電平為 0。

5.1.3普通方式實(shí)現(xiàn)-HAL庫(kù)

5.1.3.1 STM32Cube生成工程

關(guān)于如何使用STM32Cube新建工程在前文已經(jīng)講解過了,這里直說配置GPIO部分內(nèi)容。本文要實(shí)現(xiàn)按鍵功能,通過按鍵實(shí)現(xiàn)LED的亮滅。我門在第一個(gè)程序的基礎(chǔ)上進(jìn)行修改即可,不必每次都新建工程。根據(jù)按鍵電路,KEY1的引腳是PA0,我們將PA0的GPIO設(shè)置為下拉的輸入模式,保留3個(gè)LED的GPIO配置。

初始化基本配置后,我們重新生成工程,接下來按鍵編程。

5.1.3.2具體代碼分析

在看代碼前,我們先看看按鍵掃描編程的流程:

1)使能按鍵引腳時(shí)鐘,本文的引腳是PA0;

2)初始化按鍵,即初始化GPIO機(jī)構(gòu)體,在前文已經(jīng)詳細(xì)講解過了;

3)在無限循環(huán)中不斷讀取PA0的電平值,同時(shí)進(jìn)行按鍵消抖;

4)判斷按鍵被按下時(shí),進(jìn)行相應(yīng)的處理。

  • GPIO 初始化配置
static void MX_GPIO_Init(void)
{
 GPIO_InitTypeDef GPIO_InitStruct = {0};

 /* GPIO Ports Clock Enable */
 __HAL_RCC_GPIOA_CLK_ENABLE();
 __HAL_RCC_GPIOB_CLK_ENABLE();
 __HAL_RCC_GPIOG_CLK_ENABLE();

 /*Configure GPIO pin Output Level */
 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);

 /*Configure GPIO pin Output Level */
 HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_RESET);

 /*Configure GPIO pin : PA0 */
 GPIO_InitStruct.Pin = GPIO_PIN_0;
 GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

 /*Configure GPIO pin : PB0 */
 GPIO_InitStruct.Pin = GPIO_PIN_0;
 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
 HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

 /*Configure GPIO pins : PG6 PG7 */
 GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
 HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);

}

按鍵與 LED 的 GPIO 初始化函數(shù)類似,區(qū)別只是在這個(gè)函數(shù)中,要開啟的 GPIO 的端口時(shí)鐘不一樣,并且把檢測(cè)按鍵用的引腳 Pin 的模式設(shè)置為適合按鍵應(yīng)用的上拉輸入模式(由于接了外部上拉電阻,也可以使用浮空輸入,讀者可自行修改代碼做實(shí)驗(yàn))。若 GPIO 被設(shè)置為輸入模式,不需要設(shè)置 GPIO 端口的最大輸出速度。

  • 按鍵狀態(tài)監(jiān)測(cè)及按鍵消抖
uint8_t Key_Scan(void)
{                   
       if(HAL_GPIO_ReadPin(KEY_GPIO,GPIO_PIN_0)== KEY_DOWN_LEVEL )
       {        
              HAL_Delay(10);         
              if(HAL_GPIO_ReadPin(KEY_GPIO,GPIO_PIN_0)== KEY_DOWN_LEVEL ) 
              {       
                     while(HAL_GPIO_ReadPin(KEY_GPIO,GPIO_PIN_0)== KEY_DOWN_LEVEL);  
                     return  KEY_DOWN;  
              }
              else
              {
                     returnKEY_UP;
              }
       }
       returnKEY_UP;
}

相信延時(shí)消抖的原理大家在學(xué)習(xí)其他單片機(jī)時(shí)就已經(jīng)了解了,本函數(shù)的功能就是掃描輸入?yún)?shù)中指定的引腳,檢測(cè)其電平變化,并作延時(shí)消抖處理,最終對(duì)按鍵消息進(jìn)行確認(rèn)。

  • 利用HAL_GPIO_ReadPin()函數(shù)讀取輸入數(shù)據(jù),若從相應(yīng)引腳讀取的數(shù)據(jù)等于 0(KEY_DOWN),低電平,表明可能有按鍵按下,調(diào)用延時(shí)函數(shù)。否則返回 KEY_UP,表示按鍵沒有被按下。
  • 延時(shí)之后再次利用 HAL_GPIO_ReadPin()函數(shù)讀取輸入數(shù)據(jù),若依然為低電平,表明確實(shí)有按鍵被按下了。否則返回 KEY_UP,表示按鍵沒有被按下。
  • 循環(huán)調(diào)用HAL_GPIO_ReadPin()函數(shù)一直檢測(cè)按鍵的電平,直至按鍵被釋放,被釋放后,返回表示按鍵被按下的標(biāo)志 KEY_DOWN。以上是按鍵消抖的流程,調(diào)用了一個(gè)庫(kù)函數(shù) HAL_GPIO_ReadPin()函數(shù)。輸入?yún)?shù)為要讀取的端口、引腳,返回引腳的輸入電平狀態(tài),高電平為 1,低電平為 0。

Main函數(shù)如下:

/* USER CODE END Header */
/* Includes------------------------------------------------------------------*/
#include "main.h"

/* Private includes----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef-----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef enum{
       KEY_UP= 0,
       KEY_DOWN= 1,
}KEYState_Type;
/* USER CODE END PTD */

/* Private define------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define KEY_GPIO GPIOA
#define KEY_GPIO_PIN GPIO_PIN_0
#define KEY_DOWN_LEVEL 1
/* USER CODE END PD */

/* Private macro-------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes-----------------------------------------------*/
uint8_t Key_Scan(void);
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
 * @brief  The application entrypoint.
 * @retval int
 */
int main(void)
{
 /* USER CODE BEGIN 1 */

 /* USER CODE END 1 */

 /* MCUConfiguration--------------------------------------------------------*/

 /* Reset of all peripherals, Initializes the Flash interface and theSystick. */
 HAL_Init();

 /* USER CODE BEGIN Init */

 /* USER CODE END Init */

 /* Configure the system clock */
 SystemClock_Config();

 /* USER CODE BEGIN SysInit */

 /* USER CODE END SysInit */

 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 /* USER CODE BEGIN 2 */

 /* USER CODE END 2 */

 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
    /* USER CODE END WHILE */
              if(KEY_DOWN_LEVEL== Key_Scan())
              {
                     HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);

                     HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_6);

                     HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_7);

              }
    /* USER CODE BEGIN 3 */
 }
 /* USER CODE END 3 */
}

/**
 * @brief Key Scan
 * @retval uint8_t
 */
uint8_t Key_Scan(void)
{                   
       if(HAL_GPIO_ReadPin(KEY_GPIO,GPIO_PIN_0)== KEY_DOWN_LEVEL )
       {        
              HAL_Delay(10);         
              if(HAL_GPIO_ReadPin(KEY_GPIO,GPIO_PIN_0)== KEY_DOWN_LEVEL ) 
              {       
                     while(HAL_GPIO_ReadPin(KEY_GPIO,GPIO_PIN_0)== KEY_DOWN_LEVEL);  
                     return  KEY_DOWN;  
              }
              else
              {
                     returnKEY_UP;
              }
       }

       returnKEY_UP;
}

/**
 * @brief System Clock Configuration
 * @retval None
 */
void SystemClock_Config(void)
{
 RCC_OscInitTypeDef RCC_OscInitStruct = {0};
 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

 /** Initializes the RCC Oscillators according to the specifiedparameters
 * in the RCC_OscInitTypeDef structure.
 */
 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
 RCC_OscInitStruct.HSEState = RCC_HSE_ON;
 RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
 RCC_OscInitStruct.HSIState = RCC_HSI_ON;
 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
 RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
 {
    Error_Handler();
 }
 /** Initializes the CPU, AHB and APB buses clocks
 */
 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                             |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) !=HAL_OK)
 {
    Error_Handler();
 }
}

/**
 * @brief GPIO Initialization Function
 * @param None
 * @retval None
 */
static void MX_GPIO_Init(void)
{
 GPIO_InitTypeDef GPIO_InitStruct = {0};

 /* GPIO Ports Clock Enable */
 __HAL_RCC_GPIOA_CLK_ENABLE();
 __HAL_RCC_GPIOB_CLK_ENABLE();
 __HAL_RCC_GPIOG_CLK_ENABLE();

 /*Configure GPIO pin Output Level */
 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);

 /*Configure GPIO pin Output Level */
 HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_RESET);

 /*Configure GPIO pin : PA0 */
 GPIO_InitStruct.Pin = GPIO_PIN_0;
 GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

 /*Configure GPIO pin : PB0 */
 GPIO_InitStruct.Pin = GPIO_PIN_0;
 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
 HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

 /*Configure GPIO pins : PG6 PG7 */
 GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
 HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
 * @brief  This function isexecuted in case of error occurrence.
 * @retval None
 */
void Error_Handler(void)
{
 /* USER CODE BEGIN Error_Handler_Debug */
 /* User can add his own implementation to report the HAL error returnstate */

 /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
 * @brief  Reports the name of thesource file and the source line number
 *         where the assert_paramerror has occurred.
 * @param  file: pointer to thesource file name
 * @param  line: assert_param errorline source number
 * @retval None
 */
void assert_failed(uint8_t*file, uint32_t line)
{
  /* USERCODE BEGIN 6 */
 /* User can add his own implementation to report the file name and linenumber,
     tex: printf("Wrong parameters value:file %s on line %d\\r\\n", file, line) */
 /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C)COPYRIGHT STMicroelectronics *****END OF FILE****/

5.2 EXTI方式

5.2.1 EXTI的工作原理

EXTI(ExternalInterrupt)就是指外部中斷,通過 GPIO 檢測(cè)輸入脈沖,引起中斷事件,打斷原來的代碼執(zhí)行流程,進(jìn)入到中斷服務(wù)函數(shù)中進(jìn)行處理,處理完后再返回到中斷之前的代碼中執(zhí)行。

  • STM32 的中斷和異常

Cortex 內(nèi)核具有強(qiáng)大的異常響應(yīng)系統(tǒng),它把能夠打斷當(dāng)前代碼執(zhí)行流程的事件分為異常(exception)和中斷(interrupt),并把它們用一個(gè)表管理起來,編號(hào)為 0 ~ 15 的稱為內(nèi)核異常,而 16 以上的則稱為外部中斷(外是相對(duì)內(nèi)核而言),這個(gè)表就稱為中斷向量表。

而 STM32 對(duì)這個(gè)表重新進(jìn)行了編排,把編號(hào)從–3 至 6 的中斷向量定義為系統(tǒng)異常,編號(hào)為負(fù)的內(nèi)核異常不能被設(shè)置優(yōu)先級(jí),如復(fù)位(Reset)、不可屏蔽中斷(NMI)、硬錯(cuò)誤(Hardfault)。從編號(hào) 7 開始的為外部中斷,這些中斷的優(yōu)先級(jí)都是可以自行設(shè)置的。詳細(xì)的 STM32 中斷向量表見下表。

STM32 的中斷如此之多,配置起來并不容易,因此我們需要一個(gè)強(qiáng)大而方便的中斷控制器 NVIC (Nested VectoredInterrupt Controller)。NVIC 是屬于 Cortex 內(nèi)核的器件,不可屏蔽中斷(NMI)和外部中斷都由它來處理,而 SYSTICK 不是由 NVIC 來控制的。

  • NVIC 結(jié)構(gòu)體成員

當(dāng)我們要使用 NVIC 來配置中斷時(shí),自然想到 ST 庫(kù)肯定也已經(jīng)把它封裝成庫(kù)函數(shù)了。查找?guī)鞄椭臋n,發(fā)現(xiàn)在 Modules->STM32F10x_StdPeriph_Driver->misc 查找到一個(gè)NVIC_Init() 函數(shù)。對(duì) NVIC 初始化,首先要定義并填充一個(gè) NVIC_InitTypeDef類型的結(jié)構(gòu)體。這個(gè)結(jié)構(gòu)體有 4 個(gè)成員,見下表。

結(jié)構(gòu)體成員名稱 描述
NVIC_IRQChannel 需要配置的中斷向量
NVIC_IRQChannelCmd 使能或關(guān)閉相應(yīng)中斷向量的中斷響應(yīng)
NVIC_IRQChannelPreemptionPriority 配置相應(yīng)中斷向量搶占優(yōu)先級(jí)
NVIC_IRQChannelSubPriority 配置相應(yīng)中斷向量的響應(yīng)優(yōu)先級(jí)

前面兩個(gè)結(jié)構(gòu)體成員都很好理解,首先要用 NVIC_IRQChannel 參數(shù)來選擇將要配置的中斷向量,用NVIC_IRQChannelCmd 參數(shù)來進(jìn)行使能(ENABLE)或關(guān)閉(DISABLE)該中斷。在NVIC_IRQChannelPreemptionPriority 成員要配置中斷向量的搶占優(yōu)先級(jí),在NVIC_IRQChannelSubPriority需要配置中斷向量的響應(yīng)優(yōu)先級(jí)。對(duì)于中斷的配置,最重要的便是配置其優(yōu)先級(jí),但 STM32 的同一個(gè)中斷向量為什么需要設(shè)置兩種優(yōu)先級(jí)?這兩種優(yōu)先級(jí)有什么區(qū)別?

  • 搶占優(yōu)先級(jí)和響應(yīng)優(yōu)先級(jí)

STM32 的中斷向量具有兩個(gè)屬性,一個(gè)為搶占屬性,另一個(gè)為響應(yīng)屬性,其屬性編號(hào)越小,表明它的優(yōu)先級(jí)別越高。

搶占,是指打斷其他中斷的屬性,即因?yàn)榫哂羞@個(gè)屬性會(huì)出現(xiàn)嵌套中斷(在執(zhí)行中斷服務(wù)函數(shù) A 的過程中被中斷 B 打斷,執(zhí)行完中斷服務(wù)函數(shù) B 再繼續(xù)執(zhí)行中斷服務(wù)函數(shù)A),搶占屬性由NVIC_IRQChannelPreemptionPriority 的參數(shù)配置。

而響應(yīng)屬性則應(yīng)用在搶占屬性相同的情況下,當(dāng)兩個(gè)中斷向量的搶占優(yōu)先級(jí)相同時(shí),如果兩個(gè)中斷同時(shí)到達(dá),則先處理響應(yīng)優(yōu)先級(jí)高的中斷,響應(yīng)屬性由NVIC_IRQChannelSubPriority參數(shù)配置。例如,現(xiàn)在有三個(gè)中斷向量,見下表。

中斷向量 搶占優(yōu)先級(jí) 響應(yīng)優(yōu)先級(jí)
A 0 0
B 1個(gè) 0
C 1個(gè) 1個(gè)

若內(nèi)核正在執(zhí)行 C 的中斷服務(wù)函數(shù),則它能被搶占優(yōu)先級(jí)更高的中斷 A 打斷,由于 B和 C 的搶占優(yōu)先級(jí)相同,所以 C 不能被 B 打斷。但如果 B 和 C 中斷是同時(shí)到達(dá)的,內(nèi)核就會(huì)首先響應(yīng)響應(yīng)優(yōu)先級(jí)別更高的 B 中斷

  • NVIC 的優(yōu)先級(jí)組

在配置優(yōu)先級(jí)的時(shí)候,還要注意一個(gè)很重要的問題,即中斷種類的數(shù)量。NVIC 只可以配置 16 種中斷向量的優(yōu)先級(jí),也就是說,搶占優(yōu)先級(jí)和響應(yīng)優(yōu)先級(jí)的數(shù)量由一個(gè) 4 位的數(shù)字來決定,把這個(gè) 4 位數(shù)字的位數(shù)分配成搶占優(yōu)先級(jí)部分和響應(yīng)優(yōu)先級(jí)部分。有 5 組分配方式:

  • 第 0 組:所有 4 位用來配置響應(yīng)優(yōu)先級(jí)。即 16 種中斷向量具有都不相同的響應(yīng)優(yōu)先級(jí)。
  • 第 1 組:最高 1 位用來配置搶占優(yōu)先級(jí),低 3 位用來配置響應(yīng)優(yōu)先級(jí)。 表示有 2 ^1^ =2 種級(jí)別的搶占優(yōu)先級(jí)(0 級(jí),1 級(jí)),有 2 ^3^ =8 種響應(yīng)優(yōu)先級(jí),即在 16 種中斷向量之中,有8 種中斷,其搶占優(yōu)先級(jí)都為 0 級(jí),而它們的響應(yīng)優(yōu)先級(jí)分別為 0 ~ 7,其余 8 種中斷向量的搶占優(yōu)先級(jí)則都為 1 級(jí),響應(yīng)優(yōu)先級(jí)別分別為 0~7。
  • 第 2 組:2 位用來配置搶占優(yōu)先級(jí),2 位用來配置響應(yīng)優(yōu)先級(jí)。即 2 ^2^ =4 種搶占優(yōu)先級(jí),2 ^2^ =4 種響應(yīng)優(yōu)先級(jí)。
  • 第 3 組:高 3 位用來配置搶占優(yōu)先級(jí),最低 1 位用來配置響應(yīng)優(yōu)先級(jí)。即有 8 種搶占優(yōu)先級(jí),2 種響應(yīng) 2 優(yōu)先級(jí)。
  • 第 4 組:所有 4 位用來配置搶占優(yōu)先級(jí),即 NVIC 配置的 2 ^4^ =16 種中斷向量都是只有搶占屬性,沒有響應(yīng)屬性。

要配置這些優(yōu)先級(jí)組,可以采用庫(kù)函數(shù)NVIC_PriorityGroupConfi g(),可輸入的參數(shù)為NVIC_PriorityGroup_0 ~NVIC_PriorityGroup_4,分別為以上介紹的 5 種分配組。

于是,有讀者覺得疑惑了,如此強(qiáng)的 STM32,所有GPIO都能夠配置成外部中斷,USART、ADC 等外設(shè)也有中斷,而 NVIC 只能配置 16 種中斷向量,那么在某個(gè)工程中使用超過 16 個(gè)中斷怎么辦呢? 注意 NVIC 能配置的是 16 種中斷向量,而不是16 個(gè),當(dāng)工程中有超過 16 個(gè)中斷向量時(shí),必然有兩個(gè)以上的中斷向量是使用相同的中斷種類,而具有相同中斷種類的中斷向量不能互相嵌套。

STM2 單片機(jī)的所有 I/O 端口都可以配置為 EXTI 中斷模式,用來捕捉外部信號(hào),可以配置為下降沿中斷、上升沿中斷和上升下降沿中斷這三種模式。 它們以圖 3- 2 所示方式連接到 16 個(gè)外部中斷 / 事件線上。

  • EXTI 外部中斷

STM32 的所有 GPIO 都引入到 EXTI 外部中斷線上,使得所有的 GPIO 都能作為外部中斷的輸入源。 GPIO 與 EXTI 的連接方式見下圖。

觀察下圖可知,PA0 ~ PG0 連接到 EXTI0 、PA1 ~ PG1 連接到 EXTI1、……、PA15 ~ PG15 連接到 EXTI15。 這里大家要注意的是:PAx ~ PGx 端口的中斷事件都連接到了 EXTIx,即同一時(shí)刻 EXTIx 只能響應(yīng)一個(gè)端口的事件觸發(fā),不能夠同一時(shí)間響應(yīng)所有GPIO 端口的事件,但可以分時(shí)復(fù)用。 它可以配置為上升沿觸發(fā)、下降沿觸發(fā)或雙邊沿觸發(fā)。 EXTI 最普通的應(yīng)用就是接上一個(gè)按鍵,設(shè)置為下降沿觸發(fā),用中斷來檢測(cè)按鍵。

5.2.2 EXTI的寄存器描述

EXTI 寄存器的寄存器主要有6個(gè),下面分別描述。

  • 中斷屏蔽寄存器(EXTI_IMR)

  • 事件屏蔽寄存器(EXTI_EMR)

  • 上升沿觸發(fā)選擇寄存器(EXTI_RTSR)

注意: 外部喚醒線是邊沿觸發(fā)的,這些線上不能出現(xiàn)毛刺信號(hào)。 在寫EXTI_RTSR寄存器時(shí),在外部中斷線上的上升沿信號(hào)不能被識(shí)別,掛起位也不會(huì)被置位。 在同一中斷線上,可以同時(shí)設(shè)置上升沿和下降沿觸發(fā)。 即任一邊沿都可觸發(fā)中斷

  • 下降沿觸發(fā)選擇寄存器(EXTI_FTSR)

注意: 外部喚醒線是邊沿觸發(fā)的,這些線上不能出現(xiàn)毛刺信號(hào)。 在寫EXTI_FTSR寄存器時(shí),在外部中斷線上的下降沿信號(hào)不能被識(shí)別,掛起位不會(huì)被置位。 在同一中斷線上,可以同時(shí)設(shè)置上升沿和下降沿觸發(fā)。 即任一邊沿都可觸發(fā)中斷。

  • 軟件中斷事件寄存器(EXTI_SWIER)

  • 掛起寄存器(EXTI_PR)

5.2.3 EXTI方式實(shí)現(xiàn)-標(biāo)準(zhǔn)庫(kù)

  • 部署外部掛起

現(xiàn)在我們重點(diǎn)分析 EXTI_PA0_Config() 這個(gè)函數(shù),它完成了配置一個(gè) I/O 為 EXTI 中斷的一般步驟,主要有以下功能:

1)使能 EXTIx 線的時(shí)鐘和第二功能 AFIO 時(shí)鐘。

2)配置 EXTIx 線的中斷優(yōu)先級(jí)。

3)配置 EXTI 中斷線 I/O。

4)選定要配置為 EXTI 的 I/O 口線和 I/O 口的工作模式。

5)EXTI 中斷線工作模式配置。

void EXTI_PA0_Config(void)
{
       GPIO_InitTypeDefGPIO_InitStructure;
       EXTI_InitTypeDefEXTI_InitStructure;

       /*config the extiline clock and AFIO clock */
       RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA| RCC_APB2Periph_AFIO,ENABLE);

       /*config the NVIC */
       NVIC_Configuration();

       /*EXTI line gpio config*/ 
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;      
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;    // 下拉輸入
 GPIO_Init(GPIOA, &GPIO_InitStructure);

       /*EXTI line mode config */
 GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
 EXTI_InitStructure.EXTI_Line = EXTI_Line0;
 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿觸發(fā)中斷
 EXTI_InitStructure.EXTI_LineCmd = ENABLE;
 EXTI_Init(&EXTI_InitStructure);
}

EXTI_PA0_Config()代碼中,配置好 NVIC 后,還要對(duì) GPIOA 進(jìn)行初始化,這部分和按鍵輪詢的設(shè)置類似。

接下來,調(diào)用GPIO_EXTILineConfi g() 函數(shù)把 GPIOA、Pin0 設(shè)置為 EXTI 輸入線。 選擇好了 GPIO,開始填寫 EXTI 的初始化結(jié)構(gòu)體。 從這些參數(shù)的名字,相信讀者已經(jīng)知道如何把它應(yīng)用到按鍵檢測(cè)中。

1). EXTI_Line =EXTI_Line0 :給 EXTI_Line 成員賦值。 選擇 EXTI_Line0 線進(jìn)行配置,因?yàn)榘存I的 PA0 連接到了 EXTI_Line0。

2). EXTI_Mode =EXTI_Mode_Interrupt :給 EXTI_Mode 成員賦值。 把 EXTI_Line0的模式設(shè)置為中斷模式(EXTI_Mode_Interrupt)。 這個(gè)結(jié)構(gòu)體成員也可以賦值為事件模式EXTI_Mode_Event ,這個(gè)模式不會(huì)立刻觸發(fā)中斷,而只是在寄存器上把相應(yīng)的事件標(biāo)志位置 1,應(yīng)用這個(gè)模式需要不停地查詢相應(yīng)的寄存器。

3). EXTI_Trigger =EXTI_Trigger_Falling :給 EXTI_Trigger 成員賦值。 把觸發(fā)方式(EXTI_Trigger)設(shè)置為下降沿觸發(fā)(EXTI_Trigger_Falling)。

4) . EXTI_LineCmd =ENABLE :給 EXTI_LineCmd 成員賦值。 把 EXTI_LineCmd 設(shè)置為使能。

5)最后調(diào)用 EXTI_Init() 把 EXTI 初始化結(jié)構(gòu)體的參數(shù)寫入寄存器。

  • AFIO 時(shí)鐘

代碼中調(diào)用RCC_APB2PeriphClockCmd()時(shí)還輸入了參數(shù)RCC_APB2Periph_AFIO,表示開啟 AFIO的時(shí)鐘。

AFIO (alternate-functionI/O),指 GPIO 端口的復(fù)用功能,GPIO 除了用作普通的輸入輸出(主功能),還可以作為片上外設(shè)的復(fù)用輸入輸出,如串口、ADC,這些就是復(fù)用功能。 大多數(shù) GPIO 都有一個(gè)默認(rèn)復(fù)用功能,有的 GPIO 還有重映射功能。 重映射功能是指把原來屬于 A 引腳的默認(rèn)復(fù)用功能,轉(zhuǎn)移到 B 引腳進(jìn)行使用,前提是 B 引腳具有這個(gè)重映射功能。

當(dāng)把 GPIO 用作 EXTI 外部中斷或使用重映射功能的時(shí)候,必須開啟 AFIO 時(shí)鐘,而在使用默認(rèn)復(fù)用功能的時(shí)候,就不必開啟 AFIO 時(shí)鐘了。

  • NVIC 初始化配置
static voidNVIC_Configuration(void)
{
 NVIC_InitTypeDef NVIC_InitStructure;

 /* Configure one bit for preemption priority */
 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

 /* 配置中斷源 */
 NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
 NVIC_Init(&NVIC_InitStructure);
}

本代碼中調(diào)用了NVIC_PriorityGroupConfi g() 庫(kù)函數(shù),把 NVIC 中斷優(yōu)先級(jí)分組設(shè)置為第 1 組。 接下來開始向 NVIC 初始化結(jié)構(gòu)體寫入?yún)?shù) . NVIC_IRQChannel=EXTI0_IRQn,表示要配置的為 EXTI 第 1 線的中斷向量。 因?yàn)榘存I PA0 對(duì)應(yīng)的 EXTI 線為EXTI0。 這些可寫入的參數(shù)可以在 stm32f10x.h 文件的 IRQn 類型定義中查找到。 然后配置搶占優(yōu)先級(jí)和響應(yīng)優(yōu)先級(jí),因?yàn)檫@個(gè)工程簡(jiǎn)單,就直接把它設(shè)置為最高級(jí)中斷。 填充完結(jié)構(gòu)體,別忘記最后要調(diào)用 NVIC_Init() 函數(shù)來向寄存器寫入?yún)?shù)。 這里要注意的是,如果用的 IO 口是 IO0 ~ IO4,那么對(duì)應(yīng)的中斷向量是 EXTI0_IRQn ~ EXTI4_IRQn,如果用的 IO 是I05 ~ IO9 中的一個(gè)的話,對(duì)應(yīng)的中斷向量只能是 EXTI9_5_IRQn, 如果用的 IO 是 I010~IO15中的一個(gè)的話,對(duì)應(yīng)的中斷向量只能是 EXTI15_10_IRQn。 舉例:如果 PE5 或者 PE6 作為EXTI 中斷口,那么對(duì)應(yīng)的中斷向量都是 EXTI9_5_IRQn,在同一時(shí)刻只能相應(yīng)來自一個(gè)IO 的 EXTI 中斷。

  • 編寫中斷服務(wù)函數(shù)

在這個(gè) EXTI 設(shè)置中我們把 PA0 連接到內(nèi)部的 EXTI0,GPIO 配置為上拉輸入,工作在下降沿中斷。在外圍電路上我們將 PA0 接到了 key上。當(dāng)按鍵沒有按下時(shí),PA0 始終為高,當(dāng)按鍵按下時(shí) PA0 變?yōu)榈?,從?PA0 上產(chǎn)生一個(gè)下降沿跳變,EXTI0 會(huì)捕捉到這一跳變,并產(chǎn)生相應(yīng)的中斷,中斷服務(wù)程序在 stm32f10x_it.c 中實(shí)現(xiàn)。stm32f10x_it.c 文件是專門用來存放中斷服務(wù)函數(shù)的。文件中默認(rèn)只有幾個(gè)關(guān)于系統(tǒng)異常的中斷服務(wù)函數(shù),而且都是空函數(shù),在需要的時(shí)候自行編寫。那么中斷服務(wù)函數(shù)名是不是可以自己定義呢?不可以。中斷服務(wù)函數(shù)的名字必須要與啟動(dòng)文件startup_stm32f10x_hd.s中的中斷向量表定義一致。

EXTI0_. IRQHandler表示為 EXTI0 中斷向量的服務(wù)函數(shù)名。 于是,我們就可以在 stm32f10x_it.c 文件中加入名為EXTI0_IRQHandler() 的函數(shù)。

void EXTI0_IRQHandler(void)
{
       if(EXTI_GetITStatus(EXTI_Line0)!= RESET) //確保是否產(chǎn)生了 EXTI Line 中斷
       {
              //LED取反
              LED_TOGGLE;
              EXTI_ClearITPendingBit(EXTI_Line0);    //清除中斷標(biāo)志位
       } 
}

其內(nèi)容比較容易理解,進(jìn)入中斷后,調(diào)用庫(kù)函數(shù)EXTI_GetITStatus() 來重新檢查是否產(chǎn)生了 EXTI_Line 中斷,接下來把 LED 取反,操作完畢后,調(diào)用EXTI_ClearITPendingBit()清除中斷標(biāo)志位再退出中斷服務(wù)函數(shù)。

5.2.4 EXTI方式實(shí)現(xiàn)-HAL庫(kù)

5.2.4.1 STM32Cube生成工程

根據(jù)按鍵電路,KEY1的引腳是PA0,我們將PA0的GPIO設(shè)置為上升沿觸發(fā)的外部中斷模式,保留3個(gè)LED的GPIO配置。

如上圖中對(duì)KEY的GPIO進(jìn)行了初始化配置,接下來就要進(jìn)行NVIC配置。 NVIC選項(xiàng)用于設(shè)置中斷的優(yōu)先級(jí),這里先設(shè)置優(yōu)先級(jí)組為4位搶占式優(yōu)先級(jí)為1,響應(yīng)式優(yōu)先級(jí)為0; EXTI[15:10]; EXTI10- EXTI15中短線在中斷向量表中占用同一個(gè)優(yōu)先級(jí),所以EXTI10- EXTI15中斷線優(yōu)先級(jí)都是一樣的,同意配置。 EXTI5 EXTI9情況也是一樣。

最后生成工程文件即可。

5.2.4.2 EXTI代碼分析

在看代碼前,我們先看看按鍵中斷編程的流程:

1)使能AFIO時(shí)鐘,設(shè)置NVIC優(yōu)先級(jí)NVIC_PRIORITYGROUP_4;

2)使能按鍵引腳PA0,將其設(shè)置為上升沿觸發(fā)中斷模式并使能下拉;

3)配置按鍵引腳中斷優(yōu)先級(jí)并使能中斷;

4)編寫中斷回調(diào)函數(shù),同時(shí)進(jìn)行消抖處理。

  • 配置GPIO
static void MX_GPIO_Init(void)
{
 GPIO_InitTypeDef GPIO_InitStruct = {0};

 /* GPIO Ports Clock Enable */
 __HAL_RCC_GPIOA_CLK_ENABLE();
 __HAL_RCC_GPIOB_CLK_ENABLE();
 __HAL_RCC_GPIOG_CLK_ENABLE();

 /*Configure GPIO pin Output Level */
 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);

 /*Configure GPIO pin Output Level */
 HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_RESET);

 /*Configure GPIO pin : PA0 */
 GPIO_InitStruct.Pin = GPIO_PIN_0;
 GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
 GPIO_InitStruct.Pull = GPIO_PULLDOWN;
 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

 /*Configure GPIO pin : PB0 */
 GPIO_InitStruct.Pin = GPIO_PIN_0;
 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
 HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

 /*Configure GPIO pins : PG6 PG7 */
 GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
 HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);

 /* EXTI interrupt init*/
 HAL_NVIC_SetPriority(EXTI0_IRQn, 1, 0);
 HAL_NVIC_EnableIRQ(EXTI0_IRQn);

}

這部分和按鍵輪詢的設(shè)置類似,需要對(duì)GPIO進(jìn)行初始化設(shè)置。

  • AFIO 時(shí)鐘

代碼中調(diào)用__HAL_RCC_AFIO_CLK_ENABLE()函數(shù),表示開啟 AFIO的時(shí)鐘。 這個(gè)函數(shù)HAL_Init函數(shù)調(diào)用HAL_MspInit()函數(shù)實(shí)現(xiàn)的。

AFIO (alternate-functionI/O),指 GPIO 端口的復(fù)用功能,GPIO 除了用作普通的輸入輸出(主功能),還可以作為片上外設(shè)的復(fù)用輸入輸出,如串口、ADC,這些就是復(fù)用功能。 大多數(shù) GPIO 都有一個(gè)默認(rèn)復(fù)用功能,有的 GPIO 還有重映射功能。 重映射功能是指把原來屬于 A 引腳的默認(rèn)復(fù)用功能,轉(zhuǎn)移到 B 引腳進(jìn)行使用,前提是 B 引腳具有這個(gè)重映射功能。

當(dāng)把 GPIO 用作 EXTI 外部中斷或使用重映射功能的時(shí)候,必須開啟 AFIO 時(shí)鐘,而在使用默認(rèn)復(fù)用功能的時(shí)候,就不必開啟 AFIO 時(shí)鐘了。

  • NVIC 初始化配置
HAL_StatusTypeDef HAL_Init(void)
{
…
 /* Set Interrupt Group Priority */
 HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
…
}

static void MX_GPIO_Init(void)
{
….
 /* EXTI interrupt init*/
 HAL_NVIC_SetPriority(EXTI0_IRQn, 1, 0);
 HAL_NVIC_EnableIRQ(EXTI0_IRQn);

}

本代碼中調(diào)用了HAL_NVIC_SetPriorityGrouping()庫(kù)函數(shù),把 NVIC 中斷優(yōu)先級(jí)分組設(shè)置為4組。 MX_GPIO_Init()函數(shù)的最后兩個(gè)函數(shù)是關(guān)于中斷優(yōu)先級(jí)分組和使能中斷的。

HAL_NVIC_SetPriority(),共有三個(gè)參數(shù):

1.中斷向量號(hào)

中斷向量號(hào)在stm32f103xe.h中定義的。

2.搶占優(yōu)先級(jí):設(shè)置了兩位搶占優(yōu)先級(jí),那么搶占優(yōu)先級(jí)可以是00-11,即0-3。

3.響應(yīng)優(yōu)先級(jí):同樣是兩位。

HAL_NVIC_EnableIRQ()函數(shù)用于使能外部中斷線,外部中斷線10-15是共用一個(gè)中斷向量的。

這里要注意的是,如果用的 IO 口是 IO0 ~ IO4,那么對(duì)應(yīng)的中斷向量是 EXTI0_IRQn ~EXTI4_IRQn,如果用的 IO 是I05 ~ IO9 中的一個(gè)的話,對(duì)應(yīng)的中斷向量只能是 EXTI9_5_IRQn, 如果用的 IO 是 I010 ~ IO15中的一個(gè)的話,對(duì)應(yīng)的中斷向量只能是 EXTI15_10_IRQn。 舉例:如果 PE5 或者 PE6 作為EXTI 中斷口,那么對(duì)應(yīng)的中斷向量都是 EXTI9_5_IRQn,在同一時(shí)刻只能相應(yīng)來自一個(gè)IO 的 EXTI 中斷。

  • 編寫中斷服務(wù)函數(shù)

在這個(gè) EXTI 設(shè)置中我們把 PA0 連接到內(nèi)部的 EXTI0,GPIO 配置為上拉輸入,工作在下降沿中斷。在外圍電路上我們將 PA0 接到了 key上。當(dāng)按鍵沒有按下時(shí),PA0 始終為高,當(dāng)按鍵按下時(shí) PA0 變?yōu)榈停瑥亩?PA0 上產(chǎn)生一個(gè)下降沿跳變,EXTI0 會(huì)捕捉到這一跳變,并產(chǎn)生相應(yīng)的中斷,中斷服務(wù)程序在 stm32f10x_it.c 中實(shí)現(xiàn)。stm32f10x_it.c 文件是專門用來存放中斷服務(wù)函數(shù)的。文件中默認(rèn)只有幾個(gè)關(guān)于系統(tǒng)異常的中斷服務(wù)函數(shù),而且都是空函數(shù),在需要的時(shí)候自行編寫。那么中斷服務(wù)函數(shù)名是不是可以自己定義呢?不可以。中斷服務(wù)函數(shù)的名字必須要與啟動(dòng)文件startup_stm32f10x_hd.s中的中斷向量表定義一致。

EXTI0_IRQHandler 表示為 EXTI0 中斷向量的服務(wù)函數(shù)名。于是,我們就可以在 stm32f10x_it.c 文件中加入名為EXTI0_IRQHandler() 的函數(shù)。

void EXTI0_IRQHandler(void)
{
 /* USER CODE BEGIN EXTI0_IRQn 0 */

 /* USER CODE END EXTI0_IRQn 0 */
 HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
 /* USER CODE BEGIN EXTI0_IRQn 1 */

 /* USER CODE END EXTI0_IRQn 1 */
}

EXTI0_IRQHandler()函數(shù)調(diào)用HAL_GPIO_EXTI_IRQHandler()函數(shù),我們進(jìn)入HAL_GPIO_EXTI_IRQHandler()函數(shù),發(fā)現(xiàn)又調(diào)用了函數(shù)HAL_GPIO_EXTI_Callback(),再此進(jìn)入HAL_GPIO_EXTI_Callback()函數(shù),HAL_GPIO_EXTI_Callback()就是回調(diào)函數(shù)。

__weak 是一個(gè)弱化標(biāo)識(shí),帶有這個(gè)的函數(shù)就是一個(gè)弱化函數(shù),就是你可以在其他地方寫一個(gè)名稱和參數(shù)都一模一樣的函數(shù),編譯器就會(huì)忽略這一個(gè)函數(shù),而去執(zhí)行你寫的那個(gè)函數(shù); 而UNUSED(GPIO_Pin) ,這就是一個(gè)防報(bào)錯(cuò)的定義,當(dāng)傳進(jìn)來的GPIO端口號(hào)沒有做任何處理的時(shí)候,編譯器也不會(huì)報(bào)出警告。 其實(shí)我們?cè)陂_發(fā)的時(shí)候已經(jīng)不需要去理會(huì)中斷服務(wù)函數(shù)了,只需要找到這個(gè)中斷回調(diào)函數(shù)并將其重寫即可而這個(gè)回調(diào)函數(shù)還有一點(diǎn)非常便利的地方這里沒有體現(xiàn)出來,就是當(dāng)同時(shí)有多個(gè)中斷使能的時(shí)候,STM32CubeMX會(huì)自動(dòng)地將幾個(gè)中斷的服務(wù)函數(shù)規(guī)整到一起并調(diào)用一個(gè)回調(diào)函數(shù),也就是無論幾個(gè)中斷,我們只需要重寫一個(gè)回調(diào)函并判斷傳進(jìn)來的端口號(hào)即可。

那么接下來我們就在stm32f4xx_it.c這個(gè)文件的最下面添加以下代碼:

voidHAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
       if(GPIO_Pin==KEY_GPIO_PIN)
       {
              HAL_Delay(100);
              if(HAL_GPIO_ReadPin(KEY_GPIO,KEY_GPIO_PIN)== KEY_DOWN_LEVEL)
              {
                     HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
                     HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_6);
                     HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_7);
              }
       }
}

其內(nèi)容比較容易理解,進(jìn)入中斷后,調(diào)用庫(kù)函數(shù) HAL_GPIO_ReadPin來重新檢查是否產(chǎn)生了中斷,接下來把 LED 取反。

5.3實(shí)驗(yàn)現(xiàn)象

編譯好程序后,下載到板子上,不管是普通方式還是中斷方式,當(dāng)按在按鍵S1時(shí),LED1或亮或滅。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 電路圖
    +關(guān)注

    關(guān)注

    10388

    文章

    10732

    瀏覽量

    539164
  • mcu
    mcu
    +關(guān)注

    關(guān)注

    146

    文章

    17734

    瀏覽量

    358547
  • ARM
    ARM
    +關(guān)注

    關(guān)注

    134

    文章

    9273

    瀏覽量

    373750
  • 按鍵
    +關(guān)注

    關(guān)注

    4

    文章

    225

    瀏覽量

    57895
  • GPIO
    +關(guān)注

    關(guān)注

    16

    文章

    1243

    瀏覽量

    53344
收藏 人收藏

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    ARM Cortex-M的音頻性能解析

    ARM Cortex-M3和Cortex-M4處理器內(nèi)核的功能和能力可以實(shí)現(xiàn)高效的音頻處理。由于低功耗、高性能,Cortex-M處理器非常適合音頻應(yīng)用。這些處理器超強(qiáng)的音頻能力可用于低
    發(fā)表于 08-09 18:07 ?5757次閱讀
    <b class='flag-5'>ARM</b> <b class='flag-5'>Cortex-M</b>的音頻性能解析

    ARM Cortex-M學(xué)習(xí)筆記:初識(shí)Systick定時(shí)器

    Cortex-M的內(nèi)核中包含Systick定時(shí)器了,只要是Cortex-M系列的MCU就會(huì)有Systick,因此這是通用的,下面詳細(xì)分析。
    的頭像 發(fā)表于 05-15 15:01 ?3848次閱讀
    <b class='flag-5'>ARM</b> <b class='flag-5'>Cortex-M</b><b class='flag-5'>學(xué)習(xí)</b><b class='flag-5'>筆記</b>:初識(shí)Systick定時(shí)器

    ARM Cortex-M處理器詳解 精選資料分享

    ARM Cortex-M處理器家族現(xiàn)在有8款處理器成員。在本文中,我們會(huì)比較Cortex-M系列處理器之間的產(chǎn)品特性,重點(diǎn)講述如何根據(jù)產(chǎn)品應(yīng)用選擇正確的Cortex-M處理器。本文中會(huì)
    發(fā)表于 07-16 07:57

    ARM Cortex-M堆棧機(jī)制介紹

      大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家介紹的是ARM Cortex-M堆棧機(jī)制?! 〗裉旖o大家分享的這篇依舊是2016年之前痞子衡寫的技術(shù)文檔,花了點(diǎn)時(shí)間重新編排了一下
    發(fā)表于 12-16 06:26

    ARM Cortex-M內(nèi)核的相關(guān)資料推薦

      大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家介紹的是ARM Cortex-M功能模塊,不過側(cè)重點(diǎn)是三款安全特性處理器?! ?b class='flag-5'>ARM Cortex-M處理器家族發(fā)展至今(2
    發(fā)表于 12-27 07:21

    ARM Cortex-M 系列微控制器(ST)

    ARM Cortex-M 系列微控制器(ST) 意法半導(dǎo)體(ST)宣布在基于ARM Cortex-M系列處理器內(nèi)核的微控制器研發(fā)項(xiàng)目上取得突破,推出全球業(yè)內(nèi)首款采用90nm技術(shù)嵌入式
    發(fā)表于 11-02 09:29 ?1005次閱讀

    ARM白皮書】ARM Cortex-M處理器入門

    ARM Cortex-M處理器家族現(xiàn)在有8款處理器成員。在本文中,會(huì)比較Cortex-M系列處理器之間的產(chǎn)品特性,重點(diǎn)講述如何根據(jù)產(chǎn)品應(yīng)用選擇正確的Cortex-M處理器。本文中會(huì)詳細(xì)
    發(fā)表于 04-20 15:34 ?39次下載

    spmt284 Tiva C Series ARM Cortex-M Microcontrollers 新舊型號(hào)對(duì)應(yīng)

    spmt284 Tiva C Series ARM Cortex-M Microcontrollers 新舊型號(hào)對(duì)應(yīng)
    發(fā)表于 10-09 11:13 ?8次下載

    傳統(tǒng)的單片機(jī)和ARM較量 助推MCU踏上高端Cortex-M市場(chǎng)

    據(jù)有關(guān)市場(chǎng)調(diào)研機(jī)構(gòu)稱,基于ARM Cortex-M內(nèi)核的MCU在2010年創(chuàng)紀(jì)錄地實(shí)現(xiàn)了100%的出貨量增長(zhǎng)。而整個(gè)MCU市場(chǎng)才增長(zhǎng)了37%。MCU市場(chǎng)的增長(zhǎng)也幾乎是來自于ARM Cortex
    發(fā)表于 04-28 10:00 ?1729次閱讀

    Atmel Studio 6軟件中如何調(diào)試ARM Cortex-M

    Atmel Studio 6軟件中如何調(diào)試ARM Cortex-M
    的頭像 發(fā)表于 07-04 10:49 ?4406次閱讀

    米爾科技Cortex-M Prototyping System +介紹

    ARM? Cortex?-M原型系統(tǒng) MPS2+,為Cortex-M 系列微處理器設(shè)計(jì)的原型驗(yàn)證評(píng)估系統(tǒng),包含最新的Cortex-M7 及
    的頭像 發(fā)表于 11-14 10:45 ?2097次閱讀
    米爾科技<b class='flag-5'>Cortex-M</b> Prototyping System +介紹

    Cortex-MCortex-A認(rèn)識(shí)ARM處理器

    Cortex-MCortex-A認(rèn)識(shí)ARM處理器
    的頭像 發(fā)表于 03-08 11:34 ?3723次閱讀

    MCU學(xué)習(xí)筆記_ARM Cortex M0_簡(jiǎn)介

    MCU學(xué)習(xí)筆記ARM Cortex M01. RM的CPU core 的基本概念2. ARM
    發(fā)表于 10-28 11:21 ?15次下載
    MCU<b class='flag-5'>學(xué)習(xí)</b><b class='flag-5'>筆記</b>_<b class='flag-5'>ARM</b> <b class='flag-5'>Cortex</b> <b class='flag-5'>M</b>0_簡(jiǎn)介

    mcookie與單片機(jī)的關(guān)系_使用ARM Cortex-M MCU拓展單片機(jī)教學(xué)

    mcookie與單片機(jī)的關(guān)系_使用ARM Cortex-M MCU拓展單片機(jī)教學(xué)
    發(fā)表于 11-30 19:36 ?9次下載
    mcookie與單片機(jī)的關(guān)系_使用<b class='flag-5'>ARM</b> <b class='flag-5'>Cortex-M</b> MCU拓展單片機(jī)教學(xué)

    分析ARM Cortex-M內(nèi)核復(fù)位啟動(dòng)過程

    ARM Cortex-M內(nèi)核的復(fù)位啟動(dòng)過程也被稱為復(fù)位序列(Reset sequence),下面就來簡(jiǎn)要總結(jié)分析下這一過程。
    的頭像 發(fā)表于 03-20 09:58 ?2851次閱讀