2.1 实验内容
通过本实验主要学习以下内容:
• GPIO输入功能原理;
• 按键查询输入检测原理;
2.2 实验原理
2.2.1 GPIO输入功能原理
GD32H7XX系列MCU GPIO输入配置结构如下图所示,输入可配置上下拉电阻,通过施密特触发器后可通过备用功能输入或者通过输入状态寄存器进行读取。
输入状态寄存器为GPIOx_ISTAT,其状态位定义如下图所示,每个控制位对应相应引脚的输入电平状态。
GPIO引脚输入电平判断阈值如下图所示,当输入电平小于0.3VDD时,可被内部有效识别为低电平;当输入电平大于0.7VDD时,可被内部有效识别为高电平。
2.3 硬件设计
GD32H7海棠派派开发板具有两个按键,对应电路图如下图所示,其中,唤醒按键未按下时为低电平,按下后唤醒按键为高电平;用户按键未按下时为高电平,按下后用户按键为低电平,通过读取按键对应GPIO引脚的电平状态可检测对应按键是否被按下。
注意:机械按键在按下或者松开时具有抖动,建议可增加硬件消抖或者软件消抖,以避免按键检测被多次触发。
2.4代码解析
本例程实现通过查询的方式可查询按键按下的时间,进而可实现短时间按下和长时间按下的检测。
主函数代码如下,首先进行驱动初始化/按键初始化/LED初始化/串口初始化,并打印Example of key scan detection.之后进入主循环,通过key_scan函数实现对按键的扫描并检测按键按下时间。
C
int main(void)
{
/* 系统延时初始化 */
driver_init();
//注册按键扫描
driver_tick_handle[0].tick_value=10;
driver_tick_handle[0].tick_task_callback=key_scan_10ms_callhandle;
/* 按键组初始化 */
bsp_key_group_init();
/* LED组初始化 */
bsp_led_group_init();
/* 板载UART初始化 */
bsp_uart_init(&BOARD_UART);
delay_ms(1000);
printf_log("Example of key scan detection.\r\n");
while (1)
{
delay_ms(1);
/* 按键扫描结果检查:检测任意按键和多按键组合按下时间,所有按键弹起后有效 */
if(SET==all_key_up)
{
/* 检测按键组合按下时长 */
if(USER_KEY.press_timerms >= PRESS_3000MS && WKUP_KEY.press_timerms >= PRESS_3000MS)
{
printf_log("USER_KEY/KEY2/WKUP_KEY pressed together for more than 3000ms.\r\n");
USER_KEY.press_timerms=PRESS_NONE;
WKUP_KEY.press_timerms=PRESS_NONE;
}
else if(USER_KEY.press_timerms >= PRESS_50MS && WKUP_KEY.press_timerms >= PRESS_50MS)
{
printf_log("USER_KEY/KEY2/WKUP_KEY pressed together for more than 50ms.\r\n");
USER_KEY.press_timerms=PRESS_NONE;
WKUP_KEY.press_timerms=PRESS_NONE;
}
if(USER_KEY.press_timerms >= PRESS_200MS && USER_KEY.press_timerms < PRESS_5000MS)
{
USER_KEY.press_timerms=PRESS_NONE;
printf_log("USER_KEY press more than 200ms, less than 5000ms .\r\n");
}
else if(USER_KEY.press_timerms >= PRESS_5000MS)
{
USER_KEY.press_timerms=PRESS_NONE;
printf_log("USER_KEY press more than 5000ms.\r\n");
}
else if(USER_KEY.press_timerms >= PRESS_DOWN)
{
USER_KEY.press_timerms=PRESS_NONE;
printf_log("USER_KEY press briefly.\r\n");
}
if(WKUP_KEY.press_timerms >= PRESS_200MS && WKUP_KEY.press_timerms < PRESS_5000MS)
{
WKUP_KEY.press_timerms=PRESS_NONE;
printf_log("WKUP_KEY press more than 200ms, less than 5000ms .\r\n");
}
else if(WKUP_KEY.press_timerms >= PRESS_5000MS)
{
WKUP_KEY.press_timerms=PRESS_NONE;
printf_log("WKUP_KEY press more than 5000ms.\r\n");
}
else if(WKUP_KEY.press_timerms >= PRESS_DOWN)
{
WKUP_KEY.press_timerms=PRESS_NONE;
printf_log("WKUP_KEY press briefly.\r\n");
}
all_key_up=RESET;
}
//非弹起检查
if(USER_KEY.press_cycle_count >= PRESS_50MS)
{
bsp_led_toggle(&LED2);
}
else if(WKUP_KEY.press_cycle_count >= PRESS_50MS)
{
bsp_led_toggle(&LED2);
}
else
{
bsp_led_off(&LED2);
}
//直接读取
if(bsp_key_state_get(&USER_KEY)==SET)
{
bsp_led_toggle(&LED1);
}
else if(bsp_key_state_get(&WKUP_KEY)==SET)
{
bsp_led_toggle(&LED1);
}
else
{
bsp_led_off(&LED1);
}
}
}
按键初始化函数如下,通过KEY_DEF定义相关按键参数,之后调用bsp_key_init对按键进行分别初始化。
C
void bsp_key_group_init(void)
{
uint8_t i;
for(i=0;i<KEY_INIT_SIZE;i++)
{
bsp_key_init(((typdef_bsp_key *)KEY_INIT_GROUP[i]));
}
}
KEY_DEF(USER_KEY,A,1,IN_PU,SET,NULL); // PA1定义为USER_KEY非中断模式,默认状态高
KEY_DEF(WKUP_KEY,A,0,IN_PD,RESET,NULL); // PA0定义为KEY2非中断模式,默认状态高
通过key_scan进行按键扫描,实现对按键按下时间长度范围的检测。
C
bit_status key_scan(uint16_t scan_ms_cycle)
{
uint8_t i;
bit_status press_flag=RESET;
for(i=0;i<KEY_INIT_SIZE;i++)
{
// ((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_timerms=press_none;
if(bsp_key_state_get((typdef_bsp_key *)KEY_INIT_GROUP[i])==SET && ((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_cycle_count<0xffff){
((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_timerms =PRESS_DOWN;
((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_cycle_count+=scan_ms_cycle;
}
}
for(i=0;i<KEY_INIT_SIZE;i++)
{
if(bsp_key_state_get((typdef_bsp_key *)KEY_INIT_GROUP[i])==SET && ((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_cycle_count < 15000) //持续15s被按下标记为按键可能损坏
{
return press_flag;
}
}
for(i=0;i<KEY_INIT_SIZE;i++)
{
if( (((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_cycle_count > PRESS_50MS) && (((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_cycle_count < 15000) )
{
press_flag=SET;
all_key_up=SET;
if(((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_cycle_count > PRESS_5000MS){
((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_timerms=PRESS_5000MS;
}
else if(((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_cycle_count>PRESS_4000MS){
((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_timerms=PRESS_4000MS;
}
else if(((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_cycle_count>PRESS_3000MS){
((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_timerms=PRESS_3000MS;
}
else if(((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_cycle_count>PRESS_2000MS){
((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_timerms=PRESS_2000MS;
}
else if(((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_cycle_count>PRESS_1000MS){
((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_timerms=PRESS_1000MS;
}
else if(((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_cycle_count>PRESS_500MS){
((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_timerms=PRESS_500MS;
}
else if(((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_cycle_count>PRESS_200MS){
((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_timerms=PRESS_200MS;
}
else{
((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_timerms=PRESS_50MS;
}
}
if(bsp_key_state_get((typdef_bsp_key *)KEY_INIT_GROUP[i])==RESET){
((typdef_bsp_key *)KEY_INIT_GROUP[i])->press_cycle_count=0;
}
}
return press_flag;
}
key_scan通过10ms扫描中断回调函数进行调用。
C
void key_scan_10ms_callhandle(void)
{
key_scan(10);
}
2.5实验结果
将本例程烧录到开发板中,在液晶屏上首先打印Example of key scan detection.
之后短按或者长按WKUP或USER按键,将在液晶上显示按下的状态。