基于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液晶显示,还有定时器计时等等;这只涉及最简单的外部中断。