#蓝桥杯嵌入式#按键配置(循环扫描)

参考资料:
      蓝桥杯嵌入式备赛手册
      蓝桥杯嵌入式开发经验分享(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);	
}

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值