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