基于STM32的八路抢答器仿真

一、硬件说明:

  最小系统采用了STM32F4xx系列的,PB8~15分别连接了八个按钮,八个按钮模拟八位选手抢答,PE8~15连接了八盏LED灯,按钮采用上拉电阻的,将按钮作为中断源,所有中断全设置为同一优先级,设置的低电平触发模式;触发中断后,对应的灯会闪烁。

二Proteus仿真原理图:

中断实现ymodem 中断实现八路抢答器_引脚

 

 三、代码部分:

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