【蓝桥杯嵌入式学习G431】模块三:KEY

原理图:

STM32CubeMX配置:

打开软件,根据原理图,选中所需的引脚:PB0、PB1、PB2、PA0,选择GPIO Input。

GPIO -> 选中按键的引脚 -> Pull-up。

然后选一个没有被占用的定时器,例如TIM3/TIM4,这里以TIM4作为例子:

关于PSC、ARR的设置,定时器每隔Tms进入中断一次:

F=主频/((PSC+1) * (ARR+1))

T=F/10;

例如,此处PSC=80-1,ARR=10000-1,F=80000000/(80*10000)=100Hz

T=10ms,定时器每隔10ms进入中断一次。

打开中断。

配置完成。

Keil5:

打开软件,新建两个文件,命名为interrupt.c interrupt.h,保存至自己项目对应的文件夹中。

添加至Group中:

在interrupt.c中,实现中断回调服务函数:

interrupt.c:

//interrupt.c

#include "interrupt.h"

struct key keys[4];

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM4)//判断中断是哪个定时器触发的
	{
        //读取每个按键的状态,0是被按下
		keys[0].key_status=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);//PB0
		keys[1].key_status=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);//PB1
		keys[2].key_status=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);//PB2
		keys[3].key_status=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);//PA0
		
        //利用状态机消抖按键
		for(int i=0;i<4;i++)
		{
			switch(keys[i].judge_status)
			{
				case 0:
				{
					if(keys[i].key_status==0)//如果现在被按下,进入第二步
					{
						keys[i].judge_status=1;
                        keys[i].key_time=0;
					}
				}
				break;
				case 1:
				{
					if(keys[i].key_status==0)//如果现在还是被按下,进入第三步
					{
						keys[i].judge_status=2;
					}else                    //否则回到第一步
					{
						keys[i].judge_status=0;
					}
				}
				break;
				case 2:
				{
					if(keys[i].key_status==0)//如果现在还是被按下,记录按下的时长,如果大于70次,判定为长按
					{
						keys[i].key_time++;
						if(keys[i].key_time>=70)
						{
							keys[i].long_flag=1;
						}
					}else    //当松开时,判断按下的时长,根据时长判断长按和短按的情况
					{
                        keys[i].judge_status=0;
						if(keys[i].key_tim<70)
						{
							keys[i].single_flag=1;
						}
					}
				}
				break;
			}
		}
	}
}

interrupt.h:;

//interrupt.h

#ifndef _INTERRUPT_H_
#define _INTERRUPT_H_

#include "main.h"
#include "stdbool.h"
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);

struct key
{
	bool single_flag;//短按标志位
	bool long_flag;//长按标志位
	uint key_time;//按下时间
	uint key_status;//当前状态是否被按下
	uint judge_status;//进行到第几步?
};

#endif

在使用按键前,要先开启定时器TIM4,在main函数初始化位置添加如下代码:

HAL_TIM_Base_Start_IT(&htim4);

在main函数中添加头文件,利用extern关键字,将刚刚定义的keys,引到main.c中。

#include "interrupt.h"
extern struct key keys[4];

 现在我们根据题目要求,编写代码实现界面切换的功能:

我们定义一个变量view,来表示当前的界面是“车位显示界面”还是“费率设置界面”,通过按键B1,改变变量view的值,再根据view的值来显示对应的界面:

定义一个函数key_process(),来判断按键的情况:

void key_process(void)
{
	if(keys[0].single_flag==1)
	{
		view=(view+1)%2;
		keys[0].single_flag=0;
	}
}

定义一个函数lcd_process(),来根据view的值来显示对应界面:

void lcd_process(void)
{
	char text[30];
	if(view==0)
	{
		Clear();
		sprintf(text,"        Data    ");
		LCD_DisplayStringLine(Line1,(unsigned char*)text);
		sprintf(text,"   CNBR:%d    ",CNBR);
		LCD_DisplayStringLine(Line3,(unsigned char*)text);
		sprintf(text,"   VNBR:%d    ",VNBR);
		LCD_DisplayStringLine(Line5,(unsigned char*)text);
		sprintf(text,"   IDLE:%d    ",IDLE);
		LCD_DisplayStringLine(Line7,(unsigned char*)text);
	}else if(view==1)
	{
		Clear();
		sprintf(text,"        Para    ");
		LCD_DisplayStringLine(Line1,(unsigned char*)text);
		sprintf(text,"   CNBR:%.2f    ",P_CNBR);
		LCD_DisplayStringLine(Line3,(unsigned char*)text);
		sprintf(text,"   VNBR:%.2f    ",P_VNBR);
		LCD_DisplayStringLine(Line5,(unsigned char*)text);
	}
}

Clear()函数的作用是在切换显示界面的时候,进行清屏,并且不闪烁:

void Clear(void)
{
	if(keys[0].single_flag==1)
	{
		LCD_Clear(Black);
		keys[0].single_flag=0;
	}
}

在while循环中调用: 

while (1)
{
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
    lcd_process();
	key_process();
}

编译,下载。

下载完成后,显示的初始界面是Data界面,当我按下B1按键的时候就跳转到了Para界面:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值