題目:
參照外部中斷的原理和代碼示例,再結合之前已經實現的按鍵切換LED狀態的實驗,用外部中斷改進其實現。
請自行參考文檔《中斷》當中,有關按鍵切換LED狀態的內容, 自行連接電路圖,基于外部中斷機制,實現以下功能:
1.按鍵1,按下和釋放后,點亮LED
2.按鍵2,按下和釋放后,熄滅LED
3.按鍵3,按下和釋放后,使得LED閃爍
具體按鍵的電路接法,可以直接參考文檔的內容去實現。
關鍵點
分析:
按鍵外部中斷實驗
代碼
// 完成實驗代碼
#include "stm32f10x.h"
#include "Delay.h"#define LED_NORMAL 0 // LED 正常狀態
#define LED_BLINK 1 // LED 閃爍狀態uint8_t LED_State = LED_NORMAL; // LED 默認正常狀態
void KEY_GPIO_Init(void);//初始化GPIO引腳
void KEY_AFIO_Map(void);//AFIO映射PA11到EXTI_Line11
void KEY_EXTI_Init(void);//初始化 EXTI 配置(EXTI_Line11)
void KEY_NVIC_Init(void);//初始化NVIC,開啟EXTI9_5_IRQn外部中斷
void EXTI9_5_IRQHandler(void);//中斷服務程序
void EXTI0_IRQHandler(void);
int main(void) {KEY_GPIO_Init();KEY_AFIO_Map();KEY_EXTI_Init();KEY_NVIC_Init();GPIO_SetBits(GPIOA, GPIO_Pin_5);while (1) {if (LED_State == LED_BLINK) {GPIO_SetBits(GPIOA, GPIO_Pin_5);Delay_Ms(100);//當LED閃爍時如果不加入這行代碼,//可能會引發按鍵操作點亮時,LED表現為熄滅狀態//這是因為當GPIO_Pin_6引發中斷時正好處在延遲時候,while循環沒結束//當GPIO_Pin_6中斷執行后,可能會執行GPIO_ResetBits(GPIOA,GPIO_Pin_5);if (LED_State == LED_NORMAL) {continue;}GPIO_ResetBits(GPIOA, GPIO_Pin_5);Delay_Ms(100);}}
}void KEY_GPIO_Init(void) {//開啟GPIO外設時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);//初始化P0引腳GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//上拉輸入模式GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化P6和P8引腳GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_8;//下拉輸入模式GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//推免輸出模式GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);
}void KEY_AFIO_Map(void) {//開啟AFIO外設時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);// 映射 PA0 到 EXTI_Line0GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);// 映射 PB6 到 EXTI_Line6GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource6);// 映射 PB8 到 EXTI_Line8GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource8);}void KEY_EXTI_Init(void) {EXTI_InitTypeDef EXTI_InitStructure;EXTI_InitStructure.EXTI_Line = EXTI_Line0 ;EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中斷模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //采用下降沿觸發EXTI_Init(&EXTI_InitStructure);EXTI_InitStructure.EXTI_Line = EXTI_Line6 | EXTI_Line8 ;EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中斷模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //采用上升沿觸發EXTI_Init(&EXTI_InitStructure);}void EXTI9_5_IRQHandler(void) {if (EXTI_GetITStatus(EXTI_Line6) == SET) {// 清除中斷標志EXTI_ClearITPendingBit(EXTI_Line6);//檢查引腳輸入,如果是高電平說明按下還沒有松開。while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_6) == Bit_SET);
LED_State = LED_NORMAL;//低電平點亮LEDGPIO_ResetBits(GPIOA, GPIO_Pin_5);}if (EXTI_GetITStatus(EXTI_Line8) == SET) {// 清除中斷標志EXTI_ClearITPendingBit(EXTI_Line8);//檢查引腳輸入,如果是高電平說明按下還沒有松開。while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_8) == Bit_SET);
LED_State = LED_NORMAL;//高電平熄滅LEDGPIO_SetBits(GPIOA, GPIO_Pin_5);}}void EXTI0_IRQHandler(void) {if (EXTI_GetITStatus(EXTI_Line0) == SET) {// 清除中斷標志EXTI_ClearITPendingBit(EXTI_Line0);Delay_Ms(20); // 簡單消抖while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET);LED_State = LED_BLINK;}}// 初始化NVIC外設
void KEY_NVIC_Init(void) {//將優先級進行分組NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//初始化NVIC外設NVIC_InitTypeDef NVIC_InitStruct;//設置中斷號NVIC_InitStruct.NVIC_IRQChannel = EXTI9_5_IRQn;//開啟中斷NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;//初始化搶占優先級NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;//初始化子優先級NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;NVIC_Init(&NVIC_InitStruct);NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;//開啟中斷NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;//初始化搶占優先級NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;//初始化子優先級NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;NVIC_Init(&NVIC_InitStruct);}
#include "stm32f10x.h"
#include "Delay.h"#define LED_NORMAL 0 // LED 正常狀態(熄滅或者點亮)
#define LED_BLINK 1 // LED 閃爍狀態uint8_t LED_State = LED_NORMAL; // LED 默認正常狀態/**
* @brief 按鍵初始化,配置 PB6, PB8 和 PA0 為外部中斷
* @param 無
* @retval 無
* 按鍵1控制LED點亮,它的兩腳接入電源正極和PB6引腳,
* 按鍵2控制LED熄滅,它的兩腳接入電源正極和PB8引腳,
* 按鍵3控制LED閃爍,它的兩腳接入電源正極和PA0引腳.
*///按鍵引腳的的初始化一級配件相關的外部中斷
void KEY_Init(void) {// 開啟 GPIOB 和 AFIO 時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;// 配置 PB6 和 PB8 為下拉輸入模式(因為 按鍵 接正極,按下時會變為高電平)GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_8;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 下拉輸入模式GPIO_Init(GPIOB, &GPIO_InitStructure);// 配置 PA0 為上拉輸入模式(因為 按鍵 接負極,按下時會變為低電平)GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉輸入模式GPIO_Init(GPIOA, &GPIO_InitStructure);// 使用AFIO外設將三個按鍵引腳映射到對應達到EXTI線GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource6); // PB6GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource8); // PB8GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); // PA0EXTI_InitTypeDef EXTI_InitStructure;// 配置 EXTI6 和 EXTI8(PB6 和 PB8)為上升沿觸發EXTI_InitStructure.EXTI_Line = EXTI_Line6 | EXTI_Line8;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; // 上升沿觸發EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(&EXTI_InitStructure);// 配置 EXTI0(PA0)為下降沿觸發EXTI_InitStructure.EXTI_Line = EXTI_Line0; // 選擇 PA0 對應的 EXTI0EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿觸發EXTI_Init(&EXTI_InitStructure);// NVIC 配置初始化NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;// 配置 NVIC,處理 EXTI6, EXTI8 和 EXTI0 的中斷// 優先級都設置為相同的優先級NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; // 處理 EXTI6 和 EXTI8 的中斷NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; // 處理 EXTI0 的中斷NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);
}/**
* @brief LED(PA3)初始化
* @param 無
* @retval 無
*/
void LED_Init(void) {// 開啟 GPIOA 時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);// 配置 PA3 為開漏輸出模式GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);// 設定初始狀態為 LED 關閉(PA3 高阻態)GPIO_SetBits(GPIOA, GPIO_Pin_3);
}/**
* @brief 外部中斷服務函數(ISR),處理 PB6 和 PB8 按鍵中斷
* @param 無
* @retval 無
*/
void EXTI9_5_IRQHandler(void) {// 檢查是否是 PB6 觸發的中斷if (EXTI_GetITStatus(EXTI_Line6) == SET) {// 清除 EXTI6 中斷標志EXTI_ClearITPendingBit(EXTI_Line6);// 按鍵持續按下,等待彈起while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_6) == Bit_SET);// 點亮 LED,設置LED為正常模式LED_State = LED_NORMAL;GPIO_ResetBits(GPIOA, GPIO_Pin_3); // 低電平,點亮 LED}// 檢查是否是 PB8 觸發的中斷if (EXTI_GetITStatus(EXTI_Line8) == SET) {// 清除 EXTI8 中斷標志EXTI_ClearITPendingBit(EXTI_Line8);// 按鍵持續按下,等待彈起while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_8) == Bit_SET);// 點亮 LED,設置LED為正常模式LED_State = LED_NORMAL;GPIO_SetBits(GPIOA, GPIO_Pin_3); // 高電平,熄滅 LED}
}/**
* @brief 外部中斷服務函數(ISR),處理 PA0 按鍵中斷,LED閃爍
* @param 無
* @retval 無
*/
void EXTI0_IRQHandler(void) {if (EXTI_GetITStatus(EXTI_Line0) == SET) {// 清除 EXTI0 中斷標志EXTI_ClearITPendingBit(EXTI_Line0);while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET);LED_State = LED_BLINK;}
}int main(void) {LED_Init(); // 初始化 LEDKEY_Init(); // 初始化 PB6, PB8 和 PA0 按鍵while (1) {if (LED_State == LED_BLINK) {// LED 進入閃爍狀態GPIO_ResetBits(GPIOA, GPIO_Pin_3); // 點亮 LEDDelay_Ms(100); // 延時 100ms/*Delay_Ms函數是使用中斷來實現的若LED在閃爍狀態下,按壓點亮LED按鍵恰好進入了上一個Delay_Ms函數等它中斷延時結束就會執行LED熄滅操作這樣點亮LED按鍵的功能就會失效,所以這里要加一個判斷然后跳過后面的熄燈邏輯*/if (LED_State == LED_NORMAL) {continue;}GPIO_SetBits(GPIOA, GPIO_Pin_3); // 熄滅 LEDDelay_Ms(100); // 延時 100ms}}
}
閃爍這里線沒接對,豎著才行,豎著才是聯通的.
修正圖片
解決方案總結:
: