基於STM32的八路搶答器仿真
一、硬件說明:
最小系統采用了STM32F4xx系列的,PB8~15分別連接了八個按鈕,八個按鈕模擬八位選手搶答,PE8~15連接了八盞LED燈,按鈕采用上拉電阻的,將按鈕作為中斷源,所有中斷全設置為同一優先級,設置的低電平觸發模式;觸發中斷后,對應的燈會閃爍。
二Proteus仿真原理圖:

三、代碼部分:
3.1主函數:
1 #include "stm32f4xx.h" 2 #include "delay.h" 3 #include "led.h" 4 #include "exti.h" 5 char led_status = 0; //聲明一個表示LED燈狀態的變量 6 int led_count = 0; //聲明一個表示LED按下次數的變量 7 unsigned char led_now = 0; //聲明一個當前按下的LED燈變量 8 9 void main(void) 10 { 11 led_init(); //初始化LED控制管腳 12 exti_init(); //初始化按鍵檢測管腳 13 while(1){ //循環體 14 if (led_count > 1){ //判斷按鍵按下次數 15 continue; 16 }else if (led_count == 1){ //第一名 17 flash_4Hz(led_now); 18 } 19 20 } 21 }
3.2 led函數:
1 #include "led.h" 2 #include "delay.h" 3 /********************************************************************************************* 4 * 名稱:led_init 5 * 功能:初始化LED對應的GPIO 6 * 參數:無 7 * 返回:無 8 * 修改: 9 * 注釋: 10 *********************************************************************************************/ 11 void led_init(void) //初始化led 12 { 13 GPIO_InitTypeDef GPIO_InitStructure; 14 15 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); //使能GPIO時鍾 16 17 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_8 | GPIO_Pin_9 | 18 GPIO_Pin_14 | GPIO_Pin_15 | GPIO_Pin_10 | GPIO_Pin_11; //選中引腳 19 20 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //輸出模式 21 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽輸出 22 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; //輸出速度2MHz 23 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //無上下拉 24 GPIO_Init(GPIOE, &GPIO_InitStructure); //根據上述參數配置GPIOE0、GPIOE1、GPIOE2、GPIOE3 25 GPIO_SetBits(GPIOE, GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_8 | GPIO_Pin_9 | 26 GPIO_Pin_14 | GPIO_Pin_15 | GPIO_Pin_10 | GPIO_Pin_11); 27 } 28 29 /********************************************************************************************* 30 * 名稱:turn_off 31 * 功能:置引腳高電平,關閉LED 32 * 參數:led 33 * 返回:無 34 * 修改: 35 * 注釋: 36 *********************************************************************************************/ 37 void turn_off(unsigned char led){ //關閉led 38 if(led & D1) //判斷LED選擇 39 GPIO_SetBits(GPIOE, GPIO_Pin_8); //PE0置引腳高電平,關閉D1 40 if(led & D2) 41 GPIO_SetBits(GPIOE, GPIO_Pin_9); //PE1置引腳高電平,關閉D2 42 if(led & D3) 43 GPIO_SetBits(GPIOE, GPIO_Pin_10); //PE2置引腳高電平,關閉D3 44 if(led & D4) 45 GPIO_SetBits(GPIOE, GPIO_Pin_11); //PE3置引腳高電平,關閉D4 46 47 if(led & D5) //判斷LED選擇 48 GPIO_SetBits(GPIOE, GPIO_Pin_12); //PE0置引腳高電平,關閉D1 49 if(led & D6) 50 GPIO_SetBits(GPIOE, GPIO_Pin_13); //PE1置引腳高電平,關閉D2 51 if(led & D7) 52 GPIO_SetBits(GPIOE, GPIO_Pin_14); //PE2置引腳高電平,關閉D3 53 if(led & D8) 54 GPIO_SetBits(GPIOE, GPIO_Pin_15); //PE3置引腳高電平,關閉D4 55 } 56 57 /********************************************************************************************* 58 * 名稱:turn_on 59 * 功能:置引腳低電平,打開LED 60 * 參數:led 61 * 返回:無 62 * 修改: 63 * 注釋: 64 *********************************************************************************************/ 65 void turn_on(unsigned char led){ //打開led 66 if(led & D1) //判斷LED選擇 67 GPIO_ResetBits(GPIOE, GPIO_Pin_8); //PE0置引腳低電平,打開D1 68 if(led & D2) 69 GPIO_ResetBits(GPIOE, GPIO_Pin_9); //PE1置引腳低電平,打開D2 70 if(led & D3) 71 GPIO_ResetBits(GPIOE, GPIO_Pin_10); //PE2置引腳低電平,打開D3 72 if(led & D4) 73 GPIO_ResetBits(GPIOE, GPIO_Pin_11); //PE3置引腳低電平,打開D4 74 75 if(led & D5) //判斷LED選擇 76 GPIO_ResetBits(GPIOE, GPIO_Pin_12); //PE0置引腳低電平,打開D1 77 if(led & D6) 78 GPIO_ResetBits(GPIOE, GPIO_Pin_13); //PE1置引腳低電平,打開D2 79 if(led & D7) 80 GPIO_ResetBits(GPIOE, GPIO_Pin_14); //PE2置引腳低電平,打開D3 81 if(led & D8) 82 GPIO_ResetBits(GPIOE, GPIO_Pin_15); 83 } 84 /********************************************************************************************* 85 * 名稱:get_led_status 86 * 功能:獲取LED狀態 87 * 參數: 88 * 返回:led_status--bit0-bit3分別表示4路LED燈的狀態,bit4-bit6分別表示RGB燈的狀態 89 * 修改: 90 * 注釋: 91 *********************************************************************************************/ 92 unsigned char get_led_status(void){ //獲取led狀態 93 unsigned char led_status = 0; 94 if(GPIO_ReadOutputDataBit(GPIOE, GPIO_Pin_8)) //判斷PE0引腳電平 95 led_status |= D1; //高電平將led_status bit0置1 96 else 97 led_status &= ~D1; //低電平將led_status bit0置0 98 99 if(GPIO_ReadOutputDataBit(GPIOE, GPIO_Pin_9)) //判斷PE1引腳電平 100 led_status |= D2; //高電平將led_status bit1置1 101 else 102 led_status &= ~D2; //低電平將led_status bit1置0 103 104 if(GPIO_ReadOutputDataBit(GPIOE, GPIO_Pin_10)) //判斷PE2引腳電平 105 led_status |= D3; //高電平將led_status bit2置1 106 else 107 led_status &= ~D3; //低電平將led_status bit2置0 108 109 if(GPIO_ReadOutputDataBit(GPIOE, GPIO_Pin_11)) //判斷PE3引腳電平 110 led_status |= D4; //高電平將led_status bit3置1 111 else 112 led_status &= ~D4; //低電平將led_status bit3置0 113 114 if(GPIO_ReadOutputDataBit(GPIOE, GPIO_Pin_12)) //判斷PE0引腳電平 115 led_status |= D5; //高電平將led_status bit0置1 116 else 117 led_status &= ~D5; //低電平將led_status bit0置0 118 119 if(GPIO_ReadOutputDataBit(GPIOE, GPIO_Pin_13)) //判斷PE1引腳電平 120 led_status |= D6; //高電平將led_status bit1置1 121 else 122 led_status &= ~D6; //低電平將led_status bit1置0 123 124 if(GPIO_ReadOutputDataBit(GPIOE, GPIO_Pin_14)) //判斷PE2引腳電平 125 led_status |= D7; //高電平將led_status bit2置1 126 else 127 led_status &= ~D7; //低電平將led_status bit2置0 128 129 if(GPIO_ReadOutputDataBit(GPIOE, GPIO_Pin_15)) //判斷PE3引腳電平 130 led_status |= D8; //高電平將led_status bit3置1 131 else 132 led_status &= ~D8; //低電平將led_status bit3置0 133 return led_status; //返回led_status 134 } 135 136 137 void flash_4Hz(unsigned char led){ //4Hz的閃爍 138 turn_on(led); 139 delay_count(900); //由計算可得,delay_count內的值為7200時,延時為1秒 140 turn_off(led); 141 delay_count(900); 142 }
3.3 key函數:
1 #include "key.h" 2 3 /********************************************************************************************* 4 * 名稱:key_init 5 * 功能:按鍵管腳初始化 6 * 參數:無 7 * 返回:無 8 * 修改:無 9 *********************************************************************************************/ 10 void key_init(void) //初始化按鍵 11 { 12 GPIO_InitTypeDef GPIO_InitStructure; //定義一個GPIO_InitTypeDef類型的結構體 13 RCC_AHB1PeriphClockCmd( K1_CLK | K2_CLK |K3_CLK | K4_CLK | 14 K5_CLK | K6_CLK |K7_CLK | K8_CLK , ENABLE); //開啟KEY相關的GPIO外設時鍾 15 16 GPIO_InitStructure.GPIO_Pin = K1_CLK | K2_CLK |K3_CLK | K4_CLK | 17 K5_CLK | K6_CLK |K7_CLK | K8_CLK; //選擇要控制的GPIO引腳 18 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //設置引腳的輸出類型為推挽輸出 19 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; //設置引腳模式為輸入模式 20 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //設置引腳為上拉模式 21 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; //設置引腳速率為2MHz 22 23 GPIO_Init(K1_PORT, &GPIO_InitStructure); //初始化GPIO配置 24 GPIO_Init(K2_PORT, &GPIO_InitStructure); //初始化GPIO配置 25 GPIO_Init(K3_PORT, &GPIO_InitStructure); //初始化GPIO配置 26 GPIO_Init(K4_PORT, &GPIO_InitStructure); //初始化GPIO配置 27 28 GPIO_Init(K5_PORT, &GPIO_InitStructure); //初始化GPIO配置 29 GPIO_Init(K6_PORT, &GPIO_InitStructure); //初始化GPIO配置 30 GPIO_Init(K7_PORT, &GPIO_InitStructure); //初始化GPIO配置 31 GPIO_Init(K8_PORT, &GPIO_InitStructure); //初始化GPIO配置 32 } 33 34 /********************************************************************************************* 35 * 名稱:get_key_status 36 * 功能:按鍵管腳初始化 37 * 參數:無 38 * 返回:key_status 39 * 修改: 40 *********************************************************************************************/ 41 char get_key_status(void) //獲取按鍵狀態 42 { 43 char key_status = 0; 44 if(GPIO_ReadInputDataBit(K1_PORT,K1_PIN) == 0) //判斷PB12引腳電平狀態 45 key_status |= K1_PREESED; //低電平key_status bit0位置1 46 if(GPIO_ReadInputDataBit(K2_PORT,K2_PIN) == 0) //判斷PB13引腳電平狀態 47 key_status |= K2_PREESED; //低電平key_status bit1位置1 48 if(GPIO_ReadInputDataBit(K3_PORT,K3_PIN) == 0) //判斷PB14引腳電平狀態 49 key_status |= K3_PREESED; //低電平key_status bit2位置1 50 if(GPIO_ReadInputDataBit(K4_PORT,K4_PIN) == 0) //判斷PB15引腳電平狀態 51 key_status |= K4_PREESED; //低電平key_status bit3位置1 52 53 if(GPIO_ReadInputDataBit(K5_PORT,K5_PIN) == 0) //判斷PB12引腳電平狀態 54 key_status |= K5_PREESED; //低電平key_status bit0位置1 55 if(GPIO_ReadInputDataBit(K6_PORT,K6_PIN) == 0) //判斷PB13引腳電平狀態 56 key_status |= K6_PREESED; //低電平key_status bit1位置1 57 if(GPIO_ReadInputDataBit(K7_PORT,K7_PIN) == 0) //判斷PB14引腳電平狀態 58 key_status |= K7_PREESED; //低電平key_status bit2位置1 59 if(GPIO_ReadInputDataBit(K8_PORT,K8_PIN) == 0) //判斷PB15引腳電平狀態 60 key_status |= K8_PREESED; 61 return key_status; 62 }
3.4 exit函數:
1 #include "exti.h" 2 #include "key.h" 3 #include "delay.h" 4 #include "led.h" 5 extern char led_status; 6 extern int led_count; 7 extern unsigned char led_now; 8 /********************************************************************************************* 9 * 名稱:exti_init 10 * 功能:外部中斷初始化 11 * 參數:無 12 * 返回:無 13 * 修改:無 14 *********************************************************************************************/ 15 void exti_init(void) //中斷初始化 16 { 17 NVIC_InitTypeDef NVIC_InitStructure; 18 EXTI_InitTypeDef EXTI_InitStructure; 19 key_init(); //按鍵引腳初始化 20 21 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); //使能SYSCFG時鍾 22 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource8); //PB12 連接到中斷線12 23 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource9); //PB13 連接到中斷線13 24 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource10); //PB14 連接到中斷線14 25 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource11); //PB15 連接到中斷線15 26 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource12); //PB12 連接到中斷線12 27 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource13); //PB13 連接到中斷線13 28 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource14); //PB14 連接到中斷線14 29 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource15); //PB15 連接到中斷線15 30 31 EXTI_InitStructure.EXTI_Line = EXTI_Line8 | EXTI_Line9 | EXTI_Line10 | EXTI_Line11 | 32 EXTI_Line12 | EXTI_Line13 | EXTI_Line14 | EXTI_Line15; //LINE14、LINE15 33 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中斷事件 34 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿觸發 35 EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能LINE14、LINE15 36 EXTI_Init(&EXTI_InitStructure); //按上述參數配置 37 38 NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //外部中斷15-10 39 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //搶占優先級0 40 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子優先級1 41 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中斷通道 42 NVIC_Init(&NVIC_InitStructure); //按上述配置初始化 43 44 NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; //外部中斷15-10 45 NVIC_Init(&NVIC_InitStructure); 46 47 } 48 /********************************************************************************************* 49 * 名稱:EXTI15_10_IRQHandler 50 * 功能:外部中斷15-10中斷處理函數 51 * 參數:無 52 * 返回:無 53 * 修改: 54 * 注釋: 55 *********************************************************************************************/ 56 57 void EXTI9_5_IRQHandler(void) //中斷程序 58 { 59 if(get_key_status() == K1_PREESED){ //檢測K1被按下 60 delay_count(500); //延時消抖 61 if(get_key_status() == K1_PREESED){ //確認K1被按下 62 while(get_key_status() == K1_PREESED); //等待按鍵松開 63 ++led_count; 64 led_now = D1; 65 } 66 } 67 if(get_key_status() == K2_PREESED){ //檢測K2被按下 68 delay_count(500); //延時消抖 69 if(get_key_status() == K2_PREESED){ //確認K2被按下 70 while(get_key_status() == K2_PREESED); //等待按鍵松開 71 ++led_count; 72 led_now = D2; 73 } 74 } 75 76 if(EXTI_GetITStatus(EXTI_Line8)!=RESET) 77 EXTI_ClearITPendingBit(EXTI_Line8); //清除LINE12上的中斷標志位 78 if(EXTI_GetITStatus(EXTI_Line9)!=RESET) 79 EXTI_ClearITPendingBit(EXTI_Line9); //清除LINE13上的中斷標志位 80 81 } 82 83 84 85 void EXTI15_10_IRQHandler(void) //中斷程序 86 { 87 88 if(get_key_status() == K3_PREESED){ //檢測K3被按下 89 delay_count(500); //延時消抖 90 if(get_key_status() == K3_PREESED){ //確認K3被按下 91 while(get_key_status() == K3_PREESED); //等待按鍵松開 92 ++led_count; 93 led_now = D3; 94 } 95 } 96 if(get_key_status() == K4_PREESED){ //檢測K4被按下 97 delay_count(500); //延時消抖 98 if(get_key_status() == K4_PREESED){ //確認K4被按下 99 while(get_key_status() == K4_PREESED); //等待按鍵松開 100 ++led_count; 101 led_now = D4; 102 } 103 } 104 if(get_key_status() == K5_PREESED){ //檢測K1被按下 105 delay_count(500); //延時消抖 106 if(get_key_status() == K5_PREESED){ //確認K1被按下 107 while(get_key_status() == K5_PREESED); //等待按鍵松開 108 ++led_count; 109 led_now = D5; 110 } 111 } 112 if(get_key_status() == K6_PREESED){ //檢測K2被按下 113 delay_count(500); //延時消抖 114 if(get_key_status() == K6_PREESED){ //確認K2被按下 115 while(get_key_status() == K6_PREESED); //等待按鍵松開 116 ++led_count; 117 led_now = D6; 118 } 119 } 120 if(get_key_status() == K7_PREESED){ //檢測K1被按下 121 delay_count(500); //延時消抖 122 if(get_key_status() == K7_PREESED){ //確認K1被按下 123 while(get_key_status() == K7_PREESED); //等待按鍵松開 124 ++led_count; 125 led_now = D7; 126 } 127 } 128 if(get_key_status() == K8_PREESED){ //檢測K2被按下 129 delay_count(500); //延時消抖 130 if(get_key_status() == K8_PREESED){ //確認K2被按下 131 while(get_key_status() == K8_PREESED); //等待按鍵松開 132 ++led_count; 133 led_now = D8; 134 } 135 } 136 if(EXTI_GetITStatus(EXTI_Line10)!=RESET) 137 EXTI_ClearITPendingBit(EXTI_Line10); //清除LINE12上的中斷標志位 138 if(EXTI_GetITStatus(EXTI_Line11)!=RESET) 139 EXTI_ClearITPendingBit(EXTI_Line11); //清除LINE13上的中斷標志位 140 if(EXTI_GetITStatus(EXTI_Line12)!=RESET) 141 EXTI_ClearITPendingBit(EXTI_Line12); //清除LINE12上的中斷標志位 142 if(EXTI_GetITStatus(EXTI_Line13)!=RESET) 143 EXTI_ClearITPendingBit(EXTI_Line13); //清除LINE13上的中斷標志位 144 if(EXTI_GetITStatus(EXTI_Line14)!=RESET) 145 EXTI_ClearITPendingBit(EXTI_Line14); //清除LINE14上的中斷標志位 146 if(EXTI_GetITStatus(EXTI_Line15)!=RESET) 147 EXTI_ClearITPendingBit(EXTI_Line15); //清除LINE15上的中斷標志位 148 }
3.5 延時函數:
1 #include "delay.h" 2 3 /********************************************************************************************* 4 * 名稱:delay_count 5 * 功能:計數延時 6 * 參數:times------計數數值 7 * 返回:無 8 * 修改:無 9 *********************************************************************************************/ 10 void delay_count(uint32_t times) 11 { 12 uint32_t temp = 0; //定義臨時變量並初始化 13 14 while(times --){ //times自減並判斷是否為空 15 temp = 1000; //賦值1000 16 while(temp --); //temp自減並判斷是否為空 17 } 18 }
總結:
本實驗作為最基本的搶答器,只實現了最基本的搶答,沒有擴展復位按鍵,LCD液晶顯示,還有定時器計時等等;這只涉及最簡單的外部中斷。
