当前位置: 首页 > wzjs >正文

自贡做网站的公司百度识图网页版 在线

自贡做网站的公司,百度识图网页版 在线,温州网站开发平台,双语网站开发1. 前言 最近在准备16届的蓝桥杯嵌入式赛道的国赛,打算出一个系列的博客,记录STM32G431RBT6这块比赛用板上所有模块可能涉及到的所有考点,如果有错误或者遗漏欢迎各位大佬斧正。 本系列博客会分为以下两大类: 1.1. 单独模块的讲…

 1. 前言

最近在准备16届的蓝桥杯嵌入式赛道的国赛,打算出一个系列的博客,记录STM32G431RBT6这块比赛用板上所有模块可能涉及到的所有考点,如果有错误或者遗漏欢迎各位大佬斧正。

本系列博客会分为以下两大类:

1.1. 单独模块的讲解

在这部分,我会分享自己总结的各个模块的相关配置、代码书写模板,涉及到的大致框架如下:

这个框架后续可能会不断更新,欢迎各位给出建议。

这一大类相关的文章链接如下(持续补充中):

【蓝桥杯嵌入式】【模块】一、系统初始化-CSDN博客

【蓝桥杯嵌入式】【模块】二、LED相关配置及代码模板-CSDN博客

【蓝桥杯嵌入式】【模块】三、LCD相关配置及代码模板-CSDN博客

【蓝桥杯嵌入式】【模块】四、按键相关配置及代码模板-CSDN博客

1.2. 蓝桥杯各届的真题、模拟题复盘及个人答案

在这一部分,我会分享个人练过的所有题的复盘思路及代码,每篇文章结构如下:

这一大类相关的文章链接如下(持续补充中):

【蓝桥杯嵌入式】【复盘】第13届国赛真题-CSDN博客


以下是本篇博客正文内容:

2. 在cubemx中配置按键

在开发板说明书中,案件相关的io口如下所示:

图1

简单分析电路可知,当按键按下时,对应的io口电平会被拉低。

所以,按键功能的实现,归根结底是对gpio口电平状态的扫描,当检测到io口为低电平时,便说明其对应的按键按下了。

需要注意的是,为了保证系统运行的并发性,io状态扫描这一步一般都放在定时器中断里进行。所以,按键的实质可以总结为io读取电平+定时器中断内扫描。

2.1. cubemx配置按键相关io

图2

根据图1,按键1,2,3,4对应的gpio口分别是PB0、PB1、PB2、PA0,所以,在cubemx中讲这几个io配置为gpio input模式即可,用于读取gpio电平。

2.2. cubemx配置定时器中断

图3

图4

我选择了定时器4,需要注意的是配置定时器中断的溢出周期,这里我选择的是80000000 / 80 / 10000 = 100hz,也就是10ms进一次定时器中断,这个频率用于按键测量一般是没什么问题的。注意,最后要记得使能中断。


3. 按键的单击/短按

3.1. 整体模板

按键单机是考察得最为频繁也是必考的一个点,我的整体模板代码如下:

struct keys {uint8_t key_step;bool key_pin_state;bool key_is_down;
};struct keys key_buf[4] = {0};void key_init(void)
{HAL_TIM_Base_Start_IT(&htim4);
}void key_scan(void)
{key_buf[0].key_pin_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);key_buf[1].key_pin_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);key_buf[2].key_pin_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);key_buf[3].key_pin_state = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);for(int i = 0;i < 4;i++){switch(key_buf[i].key_step){case 0:if(key_buf[i].key_pin_state == 0){key_buf[i].key_step = 1;}				else{key_buf[i].key_step = 0;}break;case 1:if(key_buf[i].key_pin_state == 0){key_buf[i].key_step = 2;key_buf[i].key_is_down = 1;}				else{key_buf[i].key_step = 0;key_buf[i].key_is_down = 0;}break;case 2:if(key_buf[i].key_pin_state == 0){
//				key_buf[i].key_step = 1;}				else{key_buf[i].key_step = 0;key_buf[i].key_is_down = 0;}break;}}
}void key_task(void)
{for(int i = 0; i < 4;i++){if(key_buf[i].key_is_down == 1){key_buf[i].key_is_down = 0;}}
}void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM4){key_scan();}
}

以下是一些重要函数设计: 

3.2. 按键的初始化

按键的初始化非常简单,只需要开启定时器的中断就行。

HAL_TIM_Base_Start_IT(&htim4);

使用该函数打开对应计时器的基本定时功能,使得TIM4没10ms就会触发一次中断,进入中断回调函数。

此外,在初始化工作中,还需要声明一个按键结构体:

struct keys {uint8_t key_step;    // 用于标记处于状态机的哪个阶段bool key_pin_state;  // 用于标记按键对应io口的电平bool key_is_down;    // 用于标记按键是否被按下
};struct keys key_buf[4] = {0};    // 有四个按键,所以声明的结构体数组大小为4

这个结构体会用于之后的按键判断及消抖状态机。

3.3. 按键的扫描及消抖

 这部分的核心函数是key_scan,在该函数中大致做了以下几步:

1. 读取四个按键各自io口的引脚电平。

2. 构造状态机,实现按键的判定及消抖。

首先,读取引脚电平,通过HAL_GPIO_ReadPin函数便可实现:

    key_buf[0].key_pin_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);key_buf[1].key_pin_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);key_buf[2].key_pin_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);key_buf[3].key_pin_state = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);

接着,便是最为重要的步骤,基于一个三步骤的状态机,实现按键的状态判断及消抖:

	for(int i = 0;i < 4;i++){switch(key_buf[i].key_step){case 0:if(key_buf[i].key_pin_state == 0){key_buf[i].key_step = 1;}				else{key_buf[i].key_step = 0;}break;case 1:if(key_buf[i].key_pin_state == 0){key_buf[i].key_step = 2;key_buf[i].key_is_down = 1;}				else{key_buf[i].key_step = 0;key_buf[i].key_is_down = 0;}break;case 2:if(key_buf[i].key_pin_state == 0){
//				key_buf[i].key_step = 1;}				else{key_buf[i].key_step = 0;key_buf[i].key_is_down = 0;}break;}}

在一个4次的循环中,我们检测每一个按键对应的io口状态,如果为0,说明此时按键是按下了的,但是我们并不选择在此时便判定按键的状态为“按下”,而是让其进入状态机的第二层,在第二层中会再次判断一遍io口的状态,如果依旧为0,便可确认按键确实是按下了,将按键的状态更新为“按下”(key_buf[i].key_is_down = 1;)。

这个过程实现了消抖,因为按键在按下时可能会有一定的抖动,导致其电平并不稳定,采用双层状态机的设计,便可在抖动发生后进行第二步的判断,如果判定为抖动,则第二次测量的电平不会为0,此时便回到状态机的第一步,实现消抖。   

那么为什么状态机还有第三层呢?实际上,这个第三层一般用来进行长按判断,后续会提到。

之后,将该key_scan函数放入定时器中断中,之后便可以没10ms运行一次该函数,使得按键状态的判断并不影响其他逻辑的进行:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM4){key_scan();}
}

3.4. 按键业务函数

在进行代码编写时,我们一定要有一种异步处理的思想,即判断逻辑在中断中进行,只负责一些标志位的改变,复杂的业务逻辑放在主循环中进行,因此,设计一个key_task任务,将对应按键按下后的处理逻辑放在里面进行:

void key_task(void)
{for(int i = 0; i < 4;i++){if(key_buf[i].key_is_down == 1){key_buf[i].key_is_down = 0;// 根据题目编写该按键对应的业务逻辑}}
}

 4. 按键的长按

与之前的代码相比,按键长按的基本框架不变,核心模板代码如下:

struct keys {uint8_t key_step;bool key_pin_state;bool key_is_down;uint32_t key_time;bool key_is_long;
};void key_scan(void)
{key_buf[0].key_pin_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);key_buf[1].key_pin_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);key_buf[2].key_pin_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);key_buf[3].key_pin_state = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);for(int i = 0;i < 4;i++){switch(key_buf[i].key_step){case 0:if(key_buf[i].key_pin_state == 0){key_buf[i].key_step = 1;}				else{key_buf[i].key_step = 0;key_buf[i].key_is_long = 0;key_buf[i].key_time = 0;}break;case 1:if(key_buf[i].key_pin_state == 0){key_buf[i].key_step = 2;key_buf[i].key_is_down = 1;}				else{key_buf[i].key_step = 0;key_buf[i].key_is_down = 0;key_buf[i].key_is_long = 0;key_buf[i].key_time = 0;}break;case 2:if(key_buf[i].key_pin_state == 0){
//				key_buf[i].key_step = 1;key_buf[i].key_time++;if(key_buf[i].key_time >= 100){key_buf[i].key_is_long = 1;}}				else{key_buf[i].key_step = 0;key_buf[i].key_is_down = 0;key_buf[i].key_is_long = 0;key_buf[i].key_time = 0;}break;}}
}

主要变动如下:

1. 在按键结构体中新增key_time用于计数按键按下的时间,用key_is_long用于标记长按标志位

2. 在状态机的第三层中,如果案件长按,则计数时间会累加,当时间超过题目要求的时间时(模板上是100 * 10ms = 1000ms = 1s),长按标志位便会置1,之后在业务函数中便可根据该标志位做判断。

这里的核心是key_time,实际在考试时,所运用的整体模板可能不同,具体见下面的易错点。

4.1. 易错点

在考试时,常常遇到的要求是按键长按后松开时,才触发对应的长按逻辑,此时模板代码中关于计数时间和计数标志的清0便可能发生改变,如下面的例子所示:

void key_scan(void)
{key_buffer[0].key_pin_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);key_buffer[1].key_pin_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);key_buffer[2].key_pin_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);key_buffer[3].key_pin_state = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);for(uint8_t i = 0; i < 4; i++){switch(key_buffer[i].key_step){case 0:if(key_buffer[i].key_pin_state == 0){key_buffer[i].key_step = 1;}	else{key_buffer[i].key_step = 0;}break;case 1:if(key_buffer[i].key_pin_state == 0){key_buffer[i].key_step = 2;key_buffer[i].key_is_down = 1;}	else{key_buffer[i].key_step = 0;key_buffer[i].key_is_down = 0;}break;case 2:if(key_buffer[i].key_pin_state == 0){key_buffer[i].key_time++;if(key_buffer[i].key_time >= 100){key_buffer[i].key_is_long = 1;}}	else{key_buffer[i].key_step = 0;key_buffer[i].key_is_down = 0;}break;}}
}void key_task(void)
{for(uint8_t i = 0;i < 4;i++){if(key_buffer[i].key_is_down == 1){switch(i){case 0:// 按键1的业务逻辑break;case 1:// 按键2的业务逻辑	break;case 2:// 按键3的业务逻辑	break;case 3:if(show_num == 0){// 使用key4_flag 来标记按键4是否被按下key4_flag = 1;break;}}if(key_buffer[3].key_is_down == 0){if(key4_flag == 1){key4_flag = 0;if(key_buffer[3].key_is_long == 1){// 长按逻辑}else{// 短按逻辑}}// 相关标志位清0key_buffer[3].key_is_long = 0;key_buffer[3].key_time = 0;}}
}

上面是13届国赛的按键核心代码,可以发现的是,其与之前的模板代码的核心区别是将key_time和key_is_long的清零逻辑放到了key_task中进行, 并且使用了一个key4_flag标志位,这个标志位执行这样的作用:当按键4按下之后,该标志位会置1,但此时不进行任何的逻辑,之后等按键4松开之后,会判断该标志位,如果为1,说明是按下后松开,此时再判断长按标志位,实现对应的功能;如果此时key4_flag本身为0,说明此时按键4只是单纯的没有被按下,直接跳过即可。

5. 按键的双击

按键双击定义为在0.7s内连续按下按键两次,所以我的思路是在按键第一次按下之后开始计时,若700ms内按键再次按下,为双击;否则为单击。

我采用的模板代码如下:

void key_task(void)
{for(int i = 0; i < 4;i++){if(key_buf[i].key_is_down == 1){key_buf[i].key_is_down = 0;switch(i){case 0:key1_flag++;if(key1_flag == 2){printf("key1 double click\n");}break;}}}
}uint32_t key1_cnt = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM4){key_scan();if(key1_flag >= 1){key1_cnt++;if(key1_cnt >= 70){key1_cnt = 0;key1_flag--;}}}
}

使用key1_flag标志位,当按键1按下时,其值增加,同时在定时器中断中,会判断计数器,计数器每超过700ms都会让key1_flag减1,只有当0.7s内连续按下两次按键1,才能触发双击,printf打印日志。


总结

本文主要介绍了按键相关操作的模板代码,主要是按键的cubemx配置、单机、长按、双击。

http://www.dtcms.com/wzjs/362642.html

相关文章:

  • 怎么创建游戏软件seo门户网站建设方案
  • 浙江网站搭建百度框架户开户渠道
  • 手机怎么样做网站站长之家新网址
  • 政府找网站开发商要求王通seo
  • 樟木头网站仿做简述网站建设的基本流程
  • 做定制型网站电子商务网站推广策略
  • 网站开发论文答辩问题近期国际新闻20条
  • 营销型企业网站建设的基本原则是seo发帖网站
  • 虹口专业做网站百度app客服人工电话
  • 中英文网站英文任务放单平台
  • 如何进行网站网站调试站长是什么级别
  • 宝鸡营销型网站建设win7优化大师免安装版
  • 做设计都有什么网站推广代理平台登录
  • 仙居县建设规划局网站社交媒体营销案例
  • 网站的seo优化怎么做一键seo提交收录
  • wordpress设置主题404模板宁波seo推广如何收费
  • 网站建设商业计划书线上推广是什么意思
  • 第一ppt模板免费下载网站关键词推广软件
  • 巴青网站制作百度销售
  • 陕西网站建设哪家强百度识图软件
  • 怎么做视频解析的网站百度云登录首页
  • 电商网站建设最好的公司阿森纳英超积分
  • 建筑工程招投标网站长沙网站优化推广
  • 国家高新技术企业认定机构崇左seo
  • 外包加工网真的假的网站关键词排名优化系统
  • 网站产品类别顺序如果修改淘宝优化标题都是用什么软件
  • wordpress和帝国cms百度收录seo问答
  • 上海网站建设的报价郑州中原区最新消息
  • 平面设计手绘网站google chrome官网
  • 河南响应式建站磁力猫引擎