首页/新闻资讯/正文详情

基于掌上实验实室V9的按键程序设计

发布时间:2026/11/6 7:47:32 来源:云帆数科 栏目:资讯中心
基于掌上实验实室V9的按键程序设计
从简单的按键电路原理开始到采用软件延时去抖的高级应用一篇全部讲完。1 按键检测原理按键未按下KEY1为高电平按键按下KEY1为低电平。单片机通过检测KEY1状态来判断按键按下与否。2 单片机引脚配置3 简单测试程序下面我们用一个简单程序来测试一下KEY1按下LED4状态翻转。需要注意的是掌上实验室V9按键没有上拉电阻因此单片机对应按键输入的引脚应该配置成上拉输入。#include at32f403a_407_conf.h /** * GPIO初始化按键上拉输入 LED推挽输出 * 按键PD6 PD7 PB3 PB4 * LED PB6 PB7 PB8 PB9 */ void key_led_gpio_init(void) { crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE); crm_periph_clock_enable(CRM_GPIOD_PERIPH_CLOCK, TRUE); gpio_init_type gpio_init_struct; // PB6~PB9 LED推挽输出 gpio_init_struct.gpio_pins GPIO_PINS_6 | GPIO_PINS_7 | GPIO_PINS_8 | GPIO_PINS_9; gpio_init_struct.gpio_mode GPIO_MODE_OUTPUT; gpio_init_struct.gpio_out_type GPIO_OUTPUT_PUSH_PULL; gpio_init_struct.gpio_pull GPIO_PULL_NONE; gpio_init_struct.gpio_drive_strength GPIO_DRIVE_STRENGTH_STRONGER; gpio_init(GPIOB, gpio_init_struct); // PB3 PB4 按键上拉输入 gpio_init_struct.gpio_pins GPIO_PINS_3 | GPIO_PINS_4; gpio_init_struct.gpio_mode GPIO_MODE_INPUT; gpio_init_struct.gpio_pull GPIO_PULL_UP; gpio_init(GPIOB, gpio_init_struct); // PD6 PD7 按键上拉输入 gpio_init_struct.gpio_pins GPIO_PINS_6 | GPIO_PINS_7; gpio_init_struct.gpio_mode GPIO_MODE_INPUT; gpio_init_struct.gpio_pull GPIO_PULL_UP; gpio_init(GPIOD, gpio_init_struct); } /* * 获取按键状态 * 返回非0表示下降沿有效 */ int get_key_down() { static int last_key 1; int cur_key gpio_input_data_bit_read(GPIOD, GPIO_PINS_6); if(cur_key ! last_key){ last_key cur_key; if(cur_key 0) //下降沿有效 return 1; else return 0; }else{ return 0; } } /** * 主函数 */ int main(void) { //按键、LED相关IO初始化 key_led_gpio_init(); //主循环 while(1) { if(get_key_down()){ gpio_bits_toggle(GPIOB, GPIO_PINS_9); } } }上述程序测试时发现按键时灵时不灵问题在哪儿呢这个还是要回到按键的机械特性就是按键的机械抖动。4 按键抖动引起的问题通常的按键所用开关为机械弹性开关当机械触点断开、闭合时由于机械触点的弹性作用一个按键开关在闭合时不会马上稳定地接通在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动为了不产生这种现象而作的措施就是按键消抖。抖动时间的长短由按键的机械特性决定一般为5ms10ms。这是一个很重要的时间参数在很多场合都要用到。按键稳定闭合时间的长短则是由操作人员的按键动作决定的一般为零点几秒至数秒。为确保CPU对键的一次闭合仅作一次处理必须去除键抖动。在键闭合稳定时读取键的状态并且必须判别到键释放稳定后再作处理。5 软件去抖有了上述的波形我们就可以进行针对性的硬件或软件改进。1硬件电路可以增加双稳态触发器或电容滤波来解决。2软件可以通过延时去抖程序来解决。下面我们采用软件去抖每次检测到有键按下后延时20ms再次确认按键状态消除抖动影响。程序如下。#include at32f403a_407_conf.h //系统tick,单位ms volatile uint32_t gSysTick 0; //按键下降沿保存 char key_down[4] {0,0,0,0}; /** * 延时 */ void delay_ms(int ms) { uint32_t tick gSysTick; while(gSysTick - tick ms){} } /** * 系统时钟设置 主频64MHz */ void system_clock_config(void) { crm_reset(); crm_clock_source_enable(CRM_CLOCK_SOURCE_HICK, TRUE); while(crm_flag_get(CRM_HICK_STABLE_FLAG) ! SET); crm_pll_config(CRM_PLL_SOURCE_HICK, CRM_PLL_MULT_16, CRM_PLL_OUTPUT_RANGE_LE72MHZ); crm_clock_source_enable(CRM_CLOCK_SOURCE_PLL, TRUE); while(crm_flag_get(CRM_PLL_STABLE_FLAG) ! SET); crm_sysclk_switch(CRM_SCLK_PLL); while(crm_sysclk_switch_status_get() ! CRM_SCLK_PLL); crm_ahb_div_set(CRM_AHB_DIV_1); crm_apb2_div_set(CRM_APB2_DIV_2); crm_apb1_div_set(CRM_APB1_DIV_2); system_core_clock_update(); } /** * GPIO初始化按键上拉输入 LED推挽输出 * 按键PD6 PD7 PB3 PB4 * LED PB6 PB7 PB8 PB9 */ void key_led_gpio_init(void) { crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE); crm_periph_clock_enable(CRM_GPIOD_PERIPH_CLOCK, TRUE); gpio_init_type gpio_init_struct; // PB6~PB9 LED推挽输出 gpio_init_struct.gpio_pins GPIO_PINS_6 | GPIO_PINS_7 | GPIO_PINS_8 | GPIO_PINS_9; gpio_init_struct.gpio_mode GPIO_MODE_OUTPUT; gpio_init_struct.gpio_out_type GPIO_OUTPUT_PUSH_PULL; gpio_init_struct.gpio_pull GPIO_PULL_NONE; gpio_init_struct.gpio_drive_strength GPIO_DRIVE_STRENGTH_STRONGER; gpio_init(GPIOB, gpio_init_struct); // PB3 PB4 按键上拉输入 gpio_init_struct.gpio_pins GPIO_PINS_3 | GPIO_PINS_4; gpio_init_struct.gpio_mode GPIO_MODE_INPUT; gpio_init_struct.gpio_pull GPIO_PULL_UP; gpio_init(GPIOB, gpio_init_struct); // PD6 PD7 按键上拉输入 gpio_init_struct.gpio_pins GPIO_PINS_6 | GPIO_PINS_7; gpio_init_struct.gpio_mode GPIO_MODE_INPUT; gpio_init_struct.gpio_pull GPIO_PULL_UP; gpio_init(GPIOD, gpio_init_struct); } /** * TMR6初始化 周期为1ms, 开中断 */ void tmr6_init(void) { /* enable tmr6 clock */ crm_periph_clock_enable(CRM_TMR6_PERIPH_CLOCK, TRUE); /* 64000000/64/1000 1000Hz*/ tmr_base_init(TMR6, 1000 - 1, 64 - 1); tmr_cnt_dir_set(TMR6, TMR_COUNT_UP); /* overflow interrupt enable */ tmr_interrupt_enable(TMR6, TMR_OVF_INT, TRUE); /* tmr1 overflow interrupt nvic init */ nvic_priority_group_config(NVIC_PRIORITY_GROUP_4); nvic_irq_enable(TMR6_GLOBAL_IRQn, 0, 0); /* enable tmr1 */ tmr_counter_enable(TMR6, TRUE); } /** * TMR6中断响应函数 */ void TMR6_GLOBAL_IRQHandler(void) { if(tmr_flag_get(TMR6, TMR_OVF_FLAG) ! RESET) { gSysTick; tmr_flag_clear(TMR6, TMR_OVF_FLAG); } } /* * 获取按键状态 * 返回非0表示下降沿有效 */ int get_key_down() { static int last_key 1; int cur_key gpio_input_data_bit_read(GPIOD, GPIO_PINS_6); if(cur_key ! last_key){ delay_ms(20); if(cur_key ! last_key){ last_key cur_key; if(cur_key 0) //下降沿有效 return 1; } } return 0; } /** * 主函数 */ int main(void) { //按键、LED相关IO初始化 key_led_gpio_init(); //定时器初始化 tmr6_init(); //主循环 while(1) { if(get_key_down()){ gpio_bits_toggle(GPIOB, GPIO_PINS_9); } } }现在可以正常工作了但是每次有键按下就会延时去抖会影响程序执行效率。最关键的是当有多个按键的时候在两键同时按下时会导致按键判断错误或延迟。6 后台工作的按键程序在定时器中断进行按键处理是一种高效且准确的按键处理方式。下面的程序给出了以中断方式进行4个按键处理的完整程序。程序中还修复了PB3PB4缺省用于JTAG调试引脚导致的按键问题。#include at32f403a_407_conf.h //系统tick,单位ms volatile uint32_t gSysTick 0; //按键下降沿保存 char key_down[4] {0,0,0,0}; /** * 系统时钟设置 主频64MHz */ void system_clock_config(void) { crm_reset(); crm_clock_source_enable(CRM_CLOCK_SOURCE_HICK, TRUE); while(crm_flag_get(CRM_HICK_STABLE_FLAG) ! SET); crm_pll_config(CRM_PLL_SOURCE_HICK, CRM_PLL_MULT_16, CRM_PLL_OUTPUT_RANGE_LE72MHZ); crm_clock_source_enable(CRM_CLOCK_SOURCE_PLL, TRUE); while(crm_flag_get(CRM_PLL_STABLE_FLAG) ! SET); crm_sysclk_switch(CRM_SCLK_PLL); while(crm_sysclk_switch_status_get() ! CRM_SCLK_PLL); crm_ahb_div_set(CRM_AHB_DIV_1); crm_apb2_div_set(CRM_APB2_DIV_2); crm_apb1_div_set(CRM_APB1_DIV_2); system_core_clock_update(); } /** * GPIO初始化按键上拉输入 LED推挽输出 * 按键PD6 PD7 PB3 PB4 * LED PB6 PB7 PB8 PB9 */ void key_led_gpio_init(void) { crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE); crm_periph_clock_enable(CRM_GPIOD_PERIPH_CLOCK, TRUE); gpio_init_type gpio_init_struct; // PB6~PB9 LED推挽输出 gpio_init_struct.gpio_pins GPIO_PINS_6 | GPIO_PINS_7 | GPIO_PINS_8 | GPIO_PINS_9; gpio_init_struct.gpio_mode GPIO_MODE_OUTPUT; gpio_init_struct.gpio_out_type GPIO_OUTPUT_PUSH_PULL; gpio_init_struct.gpio_pull GPIO_PULL_NONE; gpio_init_struct.gpio_drive_strength GPIO_DRIVE_STRENGTH_STRONGER; gpio_init(GPIOB, gpio_init_struct); // PB3 PB4 按键上拉输入 gpio_init_struct.gpio_pins GPIO_PINS_3 | GPIO_PINS_4; gpio_init_struct.gpio_mode GPIO_MODE_INPUT; gpio_init_struct.gpio_pull GPIO_PULL_UP; gpio_init(GPIOB, gpio_init_struct); // PD6 PD7 按键上拉输入 gpio_init_struct.gpio_pins GPIO_PINS_6 | GPIO_PINS_7; gpio_init_struct.gpio_mode GPIO_MODE_INPUT; gpio_init_struct.gpio_pull GPIO_PULL_UP; gpio_init(GPIOD, gpio_init_struct); } /** * TMR6初始化 周期为1ms, 开中断 */ void tmr6_init(void) { /* enable tmr6 clock */ crm_periph_clock_enable(CRM_TMR6_PERIPH_CLOCK, TRUE); /* 64000000/64/1000 1000Hz*/ tmr_base_init(TMR6, 1000 - 1, 64 - 1); tmr_cnt_dir_set(TMR6, TMR_COUNT_UP); /* overflow interrupt enable */ tmr_interrupt_enable(TMR6, TMR_OVF_INT, TRUE); /* tmr1 overflow interrupt nvic init */ nvic_priority_group_config(NVIC_PRIORITY_GROUP_4); nvic_irq_enable(TMR6_GLOBAL_IRQn, 0, 0); /* enable tmr1 */ tmr_counter_enable(TMR6, TRUE); } /* * 获取按键状态 * 返回非0表示下降沿有效 */ char get_key_down(int which_key) { char key 0; if(which_key0 which_key3){ key key_down[which_key]; } key_down[which_key] 0; return key; } /* * 按键扫描程序 * 建议放在ms中断中 */ void key_scan() { static char last_key[4] {1,1,1,1}; //记录上次按键状态 static uint32_t tickcount_key[4] {0,0,0,0}; //按键状态变化起始时间 char cur_key[4]; //当前按键状态 cur_key[0] gpio_input_data_bit_read(GPIOD, GPIO_PINS_6); cur_key[1] gpio_input_data_bit_read(GPIOD, GPIO_PINS_7); cur_key[2] gpio_input_data_bit_read(GPIOB, GPIO_PINS_3); cur_key[3] gpio_input_data_bit_read(GPIOB, GPIO_PINS_4); for(int i0;i4;i){ if(cur_key[i]!last_key[i]){ //判断按键变化 if(gSysTick - tickcount_key[i] 20){ //20ms 去抖动 if(cur_key[i]!last_key[i]){ //再次确认按键变化 last_key[i] cur_key[i]; if(cur_key[i]0) //下降沿判断 key_down[i] 1; } } }else{ tickcount_key[i] gSysTick; //记录按键变化前时间 } } } /** * TMR6中断响应函数 */ void TMR6_GLOBAL_IRQHandler(void) { if(tmr_flag_get(TMR6, TMR_OVF_FLAG) ! RESET) { gSysTick; tmr_flag_clear(TMR6, TMR_OVF_FLAG); key_scan(); } } /** * 主函数 */ int main(void) { //PB3 PB4 缺省定义为jtag调试口。调试改为SW-JTAG,PB3PB4变成普通IO。 crm_periph_clock_enable(CRM_IOMUX_PERIPH_CLOCK, TRUE); gpio_pin_remap_config(SWJTAG_GMUX_010, TRUE); //设置系统时钟 system_clock_config(); //按键、LED相关IO初始化 key_led_gpio_init(); //1ms定时器初始化 tmr6_init(); //主循环 while(1) { for(int i0;i4;i){ //有键按下对应LED翻转 if(get_key_down(i)){ gpio_bits_toggle(GPIOB, GPIO_PINS_9i); } } } }

相关推荐

openEuler系统配置使用国密全过程
openEuler系统配置使用国密全过程

背景 最近一直在测试国密,积攒了一定经验,趁着经验全部在记忆中,赶快测试一下其他国产操作系统。今天咱们就一起看看openEuler的国密SSH服务应该如何搭建。 搭建openEuler国密SSH服务 openEuler国密SSH服务,没有类似麒麟操作系… · 2026/11/6 7:47:32

加班到十点的晚上,一杯酒的正确打开方式
加班到十点的晚上,一杯酒的正确打开方式

不是每一次加班后的宵夜都需要热闹。更多的时候,是一个人走出写字楼,在楼下的小馆子要两个菜,给自己倒一杯,让今天翻篇。今天聊聊「一个人的小酌」怎么喝才舒服——是的,独自小酌也需要方法。第一,一个人的… · 2026/11/6 7:46:32

【办公类-200-02】20261102AI撰写人工智能“社会实践报告”
【办公类-200-02】20261102AI撰写人工智能“社会实践报告”

一、背景需求人工智能学习已经第四学期了,开始做社会实践了二、模版内容分析社会实践报告的结构“围绕实习实践工作,不少于1000字。”我手里有很多工作的信息相关的课题但是哪些内容和实习相关呢?让AI来分析三、AI解析和撰文把Word模版里面的… · 2026/11/6 7:46:32

如何可以考高压电工证报名费多少钱
如何可以考高压电工证报名费多少钱

高压电工证怎么报名?怕考不过白交培训费的看这篇 怕考不过白交培训费?高压电工证怎么报名?别急,下面告诉你怎么避坑、怎么准备,看完你就知道怎么花小钱办大事。… · 2026/11/6 18:25:13

璧山 电工证呀报名费多少钱
璧山 电工证呀报名费多少钱

璧山电工证呀过期了?考前押题帮你顺利复审 证书过期了不知道怎么复审延期,这是很多电工朋友遇到的难题,尤其是像璧山电工证呀这类操作证,关系到日常工作是否合规、能否继续上岗。别慌,本文教你一招搞定, 考前押题… · 2026/11/6 18:25:13

雅安高低压电工证怎么转异地,怕考不过白交培训费?官方报名入口在这里!
雅安高低压电工证怎么转异地,怕考不过白交培训费?官方报名入口在这里!

雅安高低压电工证怎么转异地,怕考不过白交培训费?官方报名入口在这里! 怕考不过白交培训费?雅安高低压电工证转异地流程你知道吗?官方报名入口其实早就开通了,但很多人却因为不了解流程、政策变化而白白浪费时间和钱。本文从政策变化、办理流程、避坑建… · 2026/11/6 18:25:13

高压电工证必须复审吗?在哪儿考才能快速上岗?
高压电工证必须复审吗?在哪儿考才能快速上岗?

高压电工证必须复审吗?在哪儿考才能快速上岗? 你正愁没证找不到工作?高压电工证必须复审吗?这事儿直接影响你能不能顺利拿到证书,顺利上岗。特别是在濮阳这样的建筑工地,电工岗位需求大,但门槛也高, 高压电工证必须复审吗… · 2026/11/6 18:25:13

工地太忙根本没时间复习考试?乐安电工证考试费用+全国通用怎么搞定了?
工地太忙根本没时间复习考试?乐安电工证考试费用+全国通用怎么搞定了?

工地太忙根本没时间复习考试?乐安电工证考试费用+全国通用怎么搞定了? 工地太忙根本没时间复习考试,你是真的没时间,但不是不能考!乐安电工证考试费用+全国通用,听起来好像很难搞定,但其实现在有一套成熟的流程和资源,能帮你轻松应对。… · 2026/11/6 18:25:13

郑州电工证报考避坑指南:假电工证是什么怎么识别
郑州电工证报考避坑指南:假电工证是什么怎么识别

郑州电工证报考避坑指南:假电工证是什么怎么识别 怕考不过白交培训费?郑州电工证报考避坑指南来了,教你一眼识破假电工证陷阱。… · 2026/11/6 18:24:10

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 10:29:23

三菱PLC数据采集到数据库的时序设计:Python+MC协议实战
三菱PLC数据采集到数据库的时序设计:Python+MC协议实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 1:02:11

ASIC主模式RTL设计:状态机与三态驱动的流片级实现
ASIC主模式RTL设计:状态机与三态驱动的流片级实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 1:02:11

STM32嵌入式C++开发:GDB调试与Renode仿真实战指南
STM32嵌入式C++开发:GDB调试与Renode仿真实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 1:02:11

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 10:29:23

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码