参考资料:
蓝桥杯嵌入式备赛手册
蓝桥杯嵌入式开发经验分享(4.按键的配置)
总目录:目录
按键硬件连接
- 首先,先看电路原理图
- 其中K1-K4的连接如图
- 可以看到连接了PA0/PA8/PB1/PB2,即需要配置GPIOA和GPIOB
利用库函数实现按键中断
- 按键库函数在比赛提供的V3.5库的"Project->STM32F10x_StdPeriph_Examples->EXTI->EXTI_Config"文件夹下
- 打开"main.c",如果仅按键的话,只需配置GPIO,后面的代码会在EXIT中断中用到,这里就先写普通的按键
button.c
- 上述库函数只配置了GPIOA的PA0,但我们看的原理图,四个按键分别对应着PA0/PA8/PB1/PB2,因此我们需要把库函数复制过来以后,进行改动,写出key_Init函数
- 改后的key_Init函数代码如下:
void key_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//配置GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
//例程中仅有PA0,因此加上PA8
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_8;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//加上PB1和PB2
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
- 其中
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
,表示浮空输入,也可用GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
,表示上拉输入。因为电路中有接上拉电阻。默认为高电平,按下则为低电平。
- 为了实现按键的循环扫描,需要利用函数
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
- 这个函数的作用是可以用来获取某个引脚的电平,返回值就是它的电平状态(高电平返回1,低电平返回0)。我们进行一个宏定义可以使我们接下来更方便的获取引脚的电平值(在button.h中详述)
- 接下来我们可以写一个扫描函数了,主要利用这个宏定义,写这个函数主要是为了主函数中循环扫描判断
u8 key_Scan()
{
static u8 key = 1;//°´¼üËÉ¿ª
if(key==1 && (!BTN1||!BTN2||!BTN3||!BTN4))
{
Delay_Ms(10);
key = 0;
if(!BTN1)
return 1;
else if(!BTN2)
return 2;
else if(!BTN3)
return 3;
else if(!BTN4)
return 4;
}
else if(BTN1&&BTN2&&BTN3&&BTN4)
{
key = 1;
}
return 0;
}
- button.c代码如下:
#include "button.h"
extern void Delay_Ms(u32 nTime);
void key_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
//
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
u8 key_Scan()
{
static u8 key = 1;//°´¼üËÉ¿ª
if(key==1 && (!BTN1||!BTN2||!BTN3||!BTN4))
{
Delay_Ms(10);
key = 0;
if(!BTN1)
return 1;
else if(!BTN2)
return 2;
else if(!BTN3)
return 3;
else if(!BTN4)
return 4;
}
else if(BTN1&&BTN2&&BTN3&&BTN4)
{
key = 1;
}
return 0;
}
button.h
- .h文件主要是之前的宏定义
#ifndef __BUTTON_H
#define __BUTTON_H
#include "stm32f10x.h"
#define BTN1 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)
#define BTN2 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8)
#define BTN3 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)
#define BTN4 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_2)
void Key_Init(void);
u8 Key_Scan(void);
#endif
按键消抖
- 从主函数中,我们可以看到其延时函数的写法
- 考虑到我们想利用延时函数进行消抖(尤其后面使用中断方法,消抖非常必要!)
- 由于比赛规定不能更改内核文件,因此我们可以复制来写一个新的SysTick_Config
- 右击去找函数原型
- 把函数直接复制到main.c中,并改名为SysTick_Config1
- 修改其中设置优先级的函数
- 注意这里的定义
- 因此需要在main.c中也加上这样的声明
static __INLINE uint32_t SysTick_Config1(uint32_t ticks);
主函数编写
- 我们需要利用key_Scan函数和ed_Control函数,实现循环扫描按键来控制灯亮灭
- 为了实现灯自动取反,我们需要更改之前的led_Control函数
void led_Control2(u16 led)
{
u8 state;
if(GPIO_ReadInputDataBit(GPIOC,led))
state = 0;
else
state = 1;
//注意这里的状态,最开始忘了光加了前面的state,没细想具体改对应的状态,导致百思不得其解为啥按键没有QWQ
if(state==0)
{
GPIO_ResetBits(GPIOC,led);
GPIO_SetBits(GPIOD,GPIO_Pin_2);
GPIO_ResetBits(GPIOD,GPIO_Pin_2);
}
else if(state==1)
{
GPIO_SetBits(GPIOC,led);
GPIO_SetBits(GPIOD,GPIO_Pin_2);
GPIO_ResetBits(GPIOD,GPIO_Pin_2);
}
}
- 然后利用switch即可控制
while(1)
{
if(key)
{
switch(key)
{
case 1:led_Control2(LED1);break;
case 2:led_Control2(LED2);break;
case 3:led_Control2(LED3);break;
case 4:led_Control2(LED4);break;
}
}
else
Delay_Ms(10);
}
- 综上,main.c如下
#include "stm32f10x.h"
#include "button.h"
#include "led.h"
u32 TimingDelay = 0;
void Delay_Ms(u32 nTime);
static __INLINE uint32_t SysTick_Config1(uint32_t ticks);
//Main Body
int main(void)
{
u8 key = key_Scan();
SysTick_Config1(SystemCoreClock/1000);
key_Init();
led_Init();
while(1)
{
if(key)
{
switch(key)
{
case 1:led_Control2(LED1);break;
case 2:led_Control2(LED2);break;
case 3:led_Control2(LED3);break;
case 4:led_Control2(LED4);break;
}
}
else
Delay_Ms(10);
}
}
__STATIC_INLINE uint32_t SysTick_Config1(uint32_t ticks)
{
if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
SysTick->LOAD = ticks - 1; /* set reload register */
//NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Systick Interrupt */
NVIC_SetPriority (SysTick_IRQn, 1);
SysTick->VAL = 0; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}
void Delay_Ms(u32 nTime)
{
TimingDelay = nTime;
while(TimingDelay != 0);
}