正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-13-按键实验

 前言:

本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM(MX6U)裸机篇”视频的学习笔记,在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。

引用:

正点原子IMX6U仓库 (GuangzhouXingyi) - Gitee.com

《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.5.2.pdf》

正点原子资料下载中心 — 正点原子资料下载中心 1.0.0 文档

正文:

本文是 “正点原子[第二期]Linux之ARM(MX6U)裸机篇--第13.1, 13.2, 13.3 讲” 的读书笔记。第13.1, 13.2, 13.3 讲介绍如何使用通过GPIO 输入模式(input)来获取按键的输入。本节的示例程序是一个最简单的例子,它使用轮询的方法,在循环中每隔 10ms 检查一次按键输入引脚是低电平还是高电平来判断按键是否按下。

使用轮询的方法来检测按键的输入时,处理器将会一直忙运行造成处理器资源的浪费,但是作为本节入门实验的最简单例子来说,学习如何检测按键是否被按下已经足够了,后续的课程中将会学习如何改进按键检测的机制。

1. 查看电路原理图中按键使用的GPIO引脚

参考正点原子I.MX6ULL Mini 核心开发板的电路原理图,找到按键 'KEY0' ,并找到按键 KEY0 接在了I.MX6ULL 处理器的 'UART1_CTS'  IO 引脚。

 I.MX6ULL 处理器的 'UART1_CTS' 引脚作为按键的输入引脚,需要将UART1_CTS IO接口复用为 'gpio’ 功能并作为 'gpio input' 接口。和之前几节‘LED灯驱动程序’中将I.MX6ULL处理器引脚作为 gpio output 模式使用类似,将 io 引脚作为 gpio output 模式使用需要如下几步:

  1. 设置 MUX_CTL_UART1_CTS_B 寄存器,复用为 GPIO 模式,GPIO1_IO18。
  2. 设置 MUX_CTL_UART1_CTS_B 寄存器,配置io接口电气特性(速率,上拉电阻,压摆率,等)
  3. 设置 GPIO1 寄存器组 DR,GDIR 寄存器配置,GPIO1_IO18 位 gpio input 模式

2. 编写 bsp_key 源码实现按键引脚 gpio input 高/低电平的读取

从电路原理图中可以看到按键 KEY0 接到I.MX6ULL处理器 gpio1_io18 引脚,gpio1_io18有一个 10K 的上拉电阻,默认情况下按键打开 gpio 引脚读取到高电平,当按下按键后 gpio 引脚读取到低电平。通过读取 gpio1_io18 的电平输入,当读取到低电平时可以判断出按键被按下。

2.1 按键消抖

理想型按键电压变换过程如图 15.3.1 所示:

在15.3.1中,按键没有按下的时候按键值为1,当按键在 t1 时刻按下以后按键值就变为0,这是最理性的状态。但是实际上按键是机械结构,加上刚按下去的一瞬间人手可能也有抖动,实际电压变换过程如图 15.3.2 所示

在图15.3.2 中,t1 时刻按键被按下,但是由于抖动原因,知道 t2 时刻才稳定下来,t1 到 t2 这段时间就是抖动。一般这段时间就是十几 ms 左右,从图 15.3.2 可以看出在抖动期间会有多次触发,如果不消除这段抖动的话软件就会误判,本来按键就按下了一次,结果软件读取IO值发现电平多次跳变以为按下多次。所以我们需要跳过这段抖动时间再去读取按键的 io 值,也就是至少要在 t2 时刻以后再去读取IO值。在示例源码中,就是延时了大约10ms 后再去读取 gpio1_io18的值,如果此时按键的值依然是0,那么就表示这是一次有效的触发。

2.2 bsp/bsp_key.c 源码

根据上面按键KEY0使用的的分析,已经知道本次按键实验使用 KEY0 GPIO1_IO18 引脚作为 input 输入,当读取到gpio1_io18 引脚低电平时按键被按下,读取到高电平时按键松开,因为物理按键不是理想型的按键,在按键按下后的十几 ms 内会有多次的电平高低跳变如果不对按键读取掉的电平进行软件消抖可能会把一次按键按下错误的判断为多次按键输入,本节实验使用时延函数 delay 10ms 后再次读取一次 gpio 引脚输入电平来实现按键的软件消抖。

参考正点原子视频教程和文档,bsp_key.c 源码如下:


#include "bsp_delay.h"
#include "bsp_key.h"
#include "bsp_gpio.h"/** @description 	: 按键初始化。* @param – base 	: 无* @return 			: 无*/
void key_init(void)
{gpio_pin_config_t config;/* 1. 初始化IO复用,复用为GPIO1_IO18 */IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0);/** * bit[0]		0		SRE,低偏摆率* bit[2:1]		00		Reserved(未使用)* bit[5:3]		000		DSE(当gpio位output时,驱动能力),本节gpio为input模式所以选择DSE=0关闭output* bit[7:6]		10		SPEED,速率,选择100MHz* bit[10:8]	000		Reserved(未使用)* bit[11]		0		ODE,开路输出,本节gpio为input,开路输出关闭* bit[12]		1		PKE, Pull/Keeper (上拉/保持器 开关),这里使能* bit[13]		1		PUE, 选择是Keeper还是PULL,本节这里选择 1 (PULL)* bit[15:14]	11		PUS, 上拉电阻阻值,本节选择22K欧姆上拉电阻* bit[16]		0		HYS, 磁滞,本节不使用,选择0* bit[31-17]	0		Reserved(未使用)** 最终选择的电气特性寄存器值:* 1111 0000 1000 0000 = 0xf080*//* 2. 设置 UART1_CTS_B IO 的电气特性 */IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xf080);/* 3. 初始化 GPIO1_IO18 设置为输入 */config.directioin = kGPIO_DigitalInput;gpio_init(GPIO1, 18, &config);	
}int key_read(void)
{return gpio_pinread(GPIO1, 18);
}/** @description 	: 获取按键值。* @param – base 	: 无* @return 			: 0 没有按键按下,其它值:对应的按键值。*/
int key_getvalue(void)
{int ret = 0;static int release = 1;if((release == 1) && (gpio_pinread(GPIO1, 18) == 0)){ /* KEY0 按下 */release = 0;			/* 标记按键按下 */delay(10);				/* 时延消抖 */if(key_read() == 0){	/* 按键按下 */ret =  KEY0_VALUE;}}else if((gpio_pinread(GPIO1, 18) == 1)){			/* KEY0 释放 */release = 1;			/* 标记按键释放 */ret = 0;}return ret;
}

在复用 UART1_CTS_B IO 为 GPIO1_IO18,并且设置 UART1_CTS_B IO接口的电气特性时,这里设置的值为 ‘0xF080

    /* 2. 设置 UART1_CTS_B IO 的电气特性 */IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xf080);

0xF080’ 这个值是怎么确定的呢?和之前分析“LED灯驱动程序GPIO引脚 output 电气特性”寄存器值的方式一样,需要参考《I.MX6ULL参考手册》第32章中 UART1_CTS_B 寄存器中每一个bit的定义,根据 UART1_CTS_B 工作在 input 模式,选择低速率,上拉电阻阻值的选择等,确定每一个bit的值,最终确定此处应该选择的io接口电气特性寄存器值为‘0xF080’。

3.3 bsp/bsp_gpio.c 接口函数

在这些LED灯驱动程序,Beep蜂鸣器启动程序,和按键驱动程序中,对GPIOx->DR, GPIOx->GDIR 寄存器组的操作是相似的,本节实验中将会把对 gpio 操作的api接口函数抽象出来放到 bsp/bsp_gpio.c 中,实现代码的复用和封装,也方便后续的开发使用。这也是我们自己写的 BSP 接口函数。

bsp_gpio.h

#ifndef __BSP_GPIO_H__
#define __BSP_GPIO_H__#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
#include "cc.h"typedef enum _gpio_pin_direction
{kGPIO_DigitalOutput = 0U,	/*输出*/kGPIO_DigitalInput  = 1U,	/*输入*/} gpio_pin_direction_t;typedef struct _gpio_pin_config 
{gpio_pin_direction_t directioin;	/* GPIO 方向:输入还是输出 */int outputLogic; 					/* 如果是输出的话,默认输出电平 */
} gpio_pin_config_t;/* 初始化函数 */
void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config);
void gpio_pinwrite(GPIO_Type *base, int pin, int value);
int  gpio_pinread(GPIO_Type *base, int pin);#endif

bsp_gpio.c 

#include "bsp_gpio.h"/** @description 	: GPIO初始化。* @param - base 	: 要初始化的寄存器组。* @param - pin		: 要初始化的寄存器脚号。* @param - config	: GPIO 配置结构体。* @return 			: 无*/
void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config)
{if(config){if(config->directioin == kGPIO_DigitalOutput){base->GDIR |= (1<<pin);								/* 输出 */gpio_pinwrite(base, pin, config->outputLogic);		/* 默认输出电平 */}else if(config->directioin == kGPIO_DigitalInput){base->GDIR &= ~(1<<pin);							/* 输入 */}}
}/** @description 	: 指定 GPIO 输出高或者低电平。* @param – base 	: 要输出的 GPIO 组。* @param – pin 	: 要输出的 GPIO 脚号。* @param - value	: 要输出的电平, 1 输出高电平, 0 输出低低电平* @return 			: 无*/
void gpio_pinwrite(GPIO_Type *base, int pin, int value)
{if(value == 0)base->DR &= ~(1<<pin);elsebase->DR |= (1<<pin);
}/** @description 	: 读取指定 GPIO 的电平值。* @param – base 	: 要读取的 GPIO 组。* @param – pin 	: 要读取的 GPIO 脚号。* @return 			: 1 读取高电平, 0 读取低低电平。*/
int  gpio_pinread(GPIO_Type *base, int pin)
{return ((base->DR >> pin) & 0x1);
}

3. 编译按键驱动实验程序

正点原子第13.1,13.2,13.3 视频教程里,正点哥在做实验时遇到了一个有趣的错误,在第13讲的视频教程里,正点哥发现当在 imx6u.lds 链接脚本里带上 '.bss' 分区的时,编译出来的 .bin 镜像烧录到SD卡上LED灯和蜂鸣器不能正常工作,去掉链接脚本里的 '.bss' 分区时编译出来的 .bin 镜像烧录SD卡,LED灯和蜂鸣器工作正常。在视频教程里,正点原子哥发现是链接脚本里的 .bss 段没有按照4字节对齐的原因,在视频教程里,正点原子哥本地变异的 .elf 文件的反汇编里 __bss_start 和 __bss_end 的确没有按照4字节对齐。因为 I.MX6ULL 是 ARM Contre-A7 32位的处理器,32位处理器读写内存时地址需要按照4字节对齐,如果内存起始地址不是4字节对齐的可能就会造成内存中内容读写错误的问题。

这个问题不一定会发生。这个其实和编译出来的 .elf 文件中的 .data 数据段的长度有关系,因为在链接脚本中 .bss 段时紧挨着 .data 数据段的,.data 数据段的起始地址是4字节对齐的,如果 .data数据段的长度本身是一个奇数(不能被4整除),那么 .bss_start = .data_start + .data_len 计算得到的 .bss 的起始地址就是一个非4字节对齐的地址,这样就会遇到正点原子哥视频里的问题。

在正点哥的的视频例程里,正点哥修改了 imx6u.lds 链接脚本,在定义 __bss_start 之前让 “. 当前定位符”按照4字节对齐,这样就解决了问题。

4. 烧录SD卡验证按键驱动功能

烧录SD卡验证按键驱动功能,使用正点原子提供的 'imxdownload' 烧录SD卡,然后把SD卡查到正点原子 I.MX6U APLHA/Mini 开发板上,上电验证LED灯是否闪烁,按下开发板上的按键蜂鸣器是否鸣叫,再次按下按键蜂鸣器是否停止鸣叫。

我在本地实验时,遇到了好几个问题,不过参考正点原子的按键示例源码反复修正了4次代码最终实现了按键开关蜂鸣器和LED灯闪烁的功能。

5. 总结

按键实验中遇到的问题记录和分析:

问题1: 按下按键之后不松开,蜂鸣器快速的10ms进行发出一次鸣叫。
原因:   在我最开始写的 key_getvalue() 函数中错误的将 'static uint8_t released' 的标志置零,正确的做法应该是在检测到按键gpio input 引脚的电平为高电平时才将 'static uint8_t released' 的标志置零。

问题2: 参考正点原子哥的Makefile,开启从编译 .o 文件的' -O2 ' 优化后,短时延函数失效。

原因: 短时延函数里的 'delay_short()' 空循环函数被编译器优化掉,造成通过空循环忙等的短时延函数失效。

解决方法: 在正点原子的示例源码中,delay_short() 函数的参数被声明为了 'volatile int ' 类型,把参数变量为 volatile 就可以避免掉编译器的优化,让编译器生成的汇编指令老老实实的按照我们的源程序执行空循环。

把变量声明为 "Volatile" 指示编译器不要进行优化。

按照示例源码的将short_dealy()的形参声明为 volatile ,然后重新编译烧录SD卡验证下是否解决问题。从反汇编源码来看,使用 "volatile" 关键字之后已经让编译器不再优化掉 short_delay() 的源码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/320666.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自动化运维工具---Ansible

一 Puppet Puppet是历史悠久的运维工具之一。它是一种基础架构即代码(laC)工具&#xff0c;使用户可以定义其基础 架构所需的状态&#xff0c;并使系统自动化以实现相同状态。 Puppet可监视用户的所有系统&#xff0c;并防止任何偏离已定义状态的情况。从简单的工作流程自动…

Mysql数据在磁盘上的存储结构

一. 前言 一行数据的存储格式大致如下所示: 变长字段的长度列表&#xff0c;null值列表&#xff0c;数据头&#xff0c;column01的值&#xff0c;column02的值&#xff0c;column0n的值… 二. 变长字段 在MySQL里有一些字段的长度是变长的&#xff0c;是不固定的&#xff0c;…

设计模式Java实现-工厂模式

✨这里是第七人格的博客✨小七&#xff0c;欢迎您的到来~✨ &#x1f345;系列专栏&#xff1a;设计模式&#x1f345; ✈️本篇内容: 工厂模式✈️ &#x1f371;本篇收录完整代码地址&#xff1a;https://gitee.com/diqirenge/design-pattern &#x1f371; 楔子 记得刚…

Python量化炒股的统计数据图

Python量化炒股的统计数据图 单只股票的收益统计图 查看单只股票的收盘价信息 单击聚宽JoinQuant量化炒股平台中的“策略研究/研究环境”命令&#xff0c;进入Jupyter Notebook的研究平台。然后单击“新建”按钮&#xff0c;创建Python3文件&#xff0c;输入如下代码如下&am…

ComfyUI搭建和注意事项for WIN[笔记]

下载ComfyUI(GitHub - comfyanonymous/ComfyUI: The most powerful and modular stable diffusion GUI, api and backend with a graph/nodes interface.) 从源码上搭建比较麻烦&#xff0c;一般不推荐&#xff0c;所以跑到release里面找一个下载。我的显卡是GeFore GTX 1050 …

STM32编译前置条件配置

本文基于stm32f104系列芯片&#xff0c;记录编程代码前需要的操作&#xff1a; 添加库文件 在ST官网下载标准库STM32F10x_StdPeriph_Lib_V3.5.0&#xff0c;解压后&#xff0c;得到以下界面 启动文件 进入Libraries&#xff0c;然后进入CMSIS&#xff0c;再进入CM3&#xff…

深度学习中的不确定性量化:技术、应用和挑战综述(一)

不确定性量化(UQ)在减少优化和决策过程中的不确定性方面起着关键作用&#xff0c;应用于解决各种现实世界的科学和工程应用。贝叶斯近似和集成学习技术是文献中使用最广泛的两种UQ方法。在这方面&#xff0c;研究人员提出了不同的UQ方法&#xff0c;并测试了它们在各种应用中的…

018、Python+fastapi,第一个Python项目走向第18步:ubuntu24.04 安装cuda和pytorch环境

一、说明 我们安装了pytorch环境之后&#xff0c;会用yolo v9 来测试一下&#xff0c;看8g 显存能不能跑下来&#xff0c;上次用无影云电脑&#xff0c;4cpu8g内存直接爆了&#xff0c;云电脑也死机了&#xff0c;提示一直占用内存不释放&#xff0c;我自己的云电脑不能占用内…

Java中的maven的安装和配置

maven的作用 依赖管理 方便快捷的管理项目依赖的资源&#xff0c;避免版本冲突问题 统一项目管理 提供标准&#xff0c;统一的项目结构 项目构建 标准跨平台&#xff08;Linux、windows、MacOS&#xff09;的自动化项目构建方式 maven的安装和配置 在maven官网下载maven Ma…

【革命启示录】Spring框架:Java开发的“核聚变”能量源!

Hello&#xff0c;我是阿佑&#xff0c;今天给大家整的活是 《Java开发的“核聚变”能量源》 文章目录 Spring框架原理详解一、引言简介目的特点例子 二、背景介绍问题解决方案例子 三、核心概念3.1 控制反转&#xff08;Inversion of Control, IoC&#xff09;定义实现例子与代…

多商户Docker Supervisor进程管理器部署

Dockerfile 根目录下没有Dockerfile的可以复制下面的命令 # 使用基础镜像 FROM leekay0218/crmeb-mer## 复制代码 ## 在本地调试注释掉&#xff0c;使用映射把文件映射进去 #ADD ./ /var/www# 设置工作目录 WORKDIR /var/www# 设置时区为上海 ENV TZAsia/Shanghai RUN ln -sn…

python安装问题及解决办法(pip不是内部或外部命令也不是可运行)

pip是python的包管理工具&#xff0c;使python可在cmd&#xff08;命令行窗口&#xff0c;WinR后输入cmd&#xff09;中执行 针对 “pip不是内部或外部命令也不是可运行” 问题&#xff0c;需要在安装的时候将python添加到环境变量中 上图第三个选项必须勾选才能在cmd中使用pi…

python从0开始学习(四)

目录 前言 1、算数运算符 1.1 //:整除运算符 1.2 %:取模操作 1.3 **&#xff1a;幂运算 2、赋值运算符 3、比较运算符 4、逻辑运算符 5、位运算符 5.1 &&#xff1a;按位与 5.2 |&#xff1a;按位或 5.3 ^&#xff1a;按位异或 5.4 ~&#xff1a;按位取反 5.5…

如何保证Redis双写一致性?

目录 数据不一致问题 数据库和缓存不一致解决方案 1. 先更新缓存&#xff0c;再更新数据 该方案数据不一致的原因 2. 先更新数据库&#xff0c;再更新缓存 3. 先删除缓存&#xff0c;再更新数据库 延时双删 4. 先更新数据库&#xff0c;再删除缓存 该方案数据不一致的…

我独自升级崛起游戏账号登录注册教程 (5.8最新版)

新韩漫公司所发布的这项动作游戏已向玩家们敞开大门&#xff0c;为大家带来了前所未有的游戏体验和乐趣。这个游戏内包含了大量令人着迷的故事、令人印象深刻的战斗场景以及丰富多样的娱乐元素。在这其中最为引人注目的一点就是游戏内容中融入了“虚拟角色”的元素&#xff0c;…

【webrtc】RemoteAudioSource的创建线程

m98 代码&#xff1a;I:\webrtc m98_yjf\src\pc\rtp_transmission_manager.cc RtpTransmissionManager::CreateReceiver 在信令线程创建receiver receiver 是&#xff1a; rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>receiver;其实际…

专家解读 | NIST网络安全框架(1):框架概览

随 着信息技术的快速发展&#xff0c;组织面临着越来越严峻的网络安全挑战。NIST网络安全框架&#xff08;NIST Cybersecurity Framework&#xff0c;CSF&#xff09;是一个灵活的综合性指南&#xff0c;旨在协助各类组织建立、改进和管理网络安全策略&#xff0c;以加强网络安…

【docker 】 push 镜像提示:denied: requested access to the resource is denied

往 Docker Registry &#xff08;私服&#xff09;push 镜像提示&#xff1a;denied: requested access to the resource is denied 镜像push 语法&#xff1a;docker push <registry-host>:<registry-port>/<repository>:<tag> docker push 192.16…

【软件工程】需求分析

目录 前言需求分析需求获取UML概述用例图用例图的组成用例图中的符号和含义包含的两种使用场景 用例图补充&#xff1a;“系统”用例模型建模确定系统参与者确定系统用例 用例文档用例文档组成部分 活动图组成元素初始节点和终点活动节点转换决策与分支、合并分岔与汇合 类图类…

将文本中的unicode字符替换成汉字

背景介绍 msql workbench导出数据库表的数据 导出的json数据 [{"english_id":1, "english_word":"ambition", "homophonic":"am-\u4ffa\uff0cbi-\u5fc5,tion-\u80dc\uff1a\u4ffa\u5fc5\u80dc", "chinese":&quo…