正点原子[第二期]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.mzph.cn/news/836199.shtml

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

相关文章

xhci 寄存器学习

xhci 寄存器介绍 查看linux 代码&#xff1a; 1733 /* There is one xhci_hcd structure per controller */ 1734 struct xhci_hcd { 1735 struct usb_hcd *main_hcd; 1736 struct usb_hcd *shared_hcd; 1737 /* glue to PCI and HCD framework */ 1738 stru…

实操专区-第11周-课堂练习专区-图的标记线和标记点

下载安装ECharts&#xff0c;完成如下样式图形。 代码和截图上传 完成 3.1.3.5 图的标记线和标记点 中的任务点 在一些折线图或柱状图当中&#xff0c;可以经常看到图中对最高值和最低值进行了标记。 在ECharts中&#xff0c;标记点&#xff08;markPoint&#xff09;常用于表示…

自定义实现 Java17+SpringBoot3+OpenAPI+Knife4j Starter

文章目录 前言正文1 创建starter项目1.1 依赖文件1.2 配置信息 2 自定义starer代码开发2.1 配置字段的定义2.2 自动配置类 3 验证starter3.1 测试项目的配置3.2 功能配置 application.yml3.3 测试代码3.3.1 实体类3.3.2 控制器13.3.2 控制器2 4 效果展示4.1 主页4.2 实体类列表…

Windows快速部署DCNv4(成功版)

文章目录 一、介绍二、编译DCNv42.1 下载源码2.2 编译DCNv4 三、报错提示3.1 Cuda is not available3.2 需要Microsoft Visual C 14.0 一、介绍 论文链接&#xff1a;[https://arxiv.org/pdf/2401.06197.pdf] (https://arxiv.org/pdf/2401.06197.pdf)   在这篇文章中介绍了一…

【Android】Kotlin学习之Kotlin方法的声明和传参

方法声明 普通类的方法 静态类的方法 不需要构建实例对象, 可以通过类名直接访问静态方法 : NumUtil.double(1) companion object 伴生类的方法 使用companion object 在普通类里定义静态方法 参数 括号内传入方法 : 当参数是方法时, 并且是最后一个参数 , 可以使用括号外…

Linux -- > vim

vi和vim是什么 vi和vim是两款流行的文本编辑器&#xff0c;广泛用于Unix和类Unix系统中。它们以其强大的功能和灵活的编辑能力而闻名&#xff0c;特别是在编程和系统管理中非常受欢迎。 vi&#xff08;Visual Interface&#xff09; vi是最初的文本编辑器之一&#xff0c;由…

外观模式详解

外观模式 1 概述 有些人可能炒过股票&#xff0c;但其实大部分人都不太懂&#xff0c;这种没有足够了解证券知识的情况下做股票是很容易亏钱的&#xff0c;刚开始炒股肯定都会想&#xff0c;如果有个懂行的帮帮手就好&#xff0c;其实基金就是个好帮手&#xff0c;支付宝里就…

【智能算法】最优捕食算法(OFA)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献5.代码获取 1.背景 2017年&#xff0c;GY Zhu受到动物行为生态学理论启发&#xff0c;提出了最优捕食算法&#xff08;Optimal Foraging Algorithm, OFA&#xff09;。 2.算法原理 2.1算法思想 OFA灵感来源…

Lab4: traps

RISC-V assembly Which registers contain arguments to functions? For example, which register holds 13 in mains call to printf? 根据RISC-V函数调用规范&#xff0c;函数的前8个参数使用a0-a7寄存器传递。 当main函数调用printf函数时&#xff0c;a2寄存器保存13 …

MVCC 详解

介绍 MVCC&#xff0c;全称 Multi-Version Concurrency Control&#xff0c;即多版本并发控制 MVCC的目的主要是为了提高数据库并发性能&#xff0c;用更好的方式去处理读-写冲突&#xff0c;做到即使有读写冲突时&#xff0c;也能做到不加锁。 这里的多版本指的是数据库中同时…

Springboot+Vue项目-基于Java+MySQL的毕业就业信息管理系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…

java异常,日志,线程堆栈与Jvm调优

一.知识目录&#xff1a; 二.什么是java异常&#xff1a; 2.1 Throwable类中的重要方法: (1)四个构造方法&#xff08;用来构造throwable对象&#xff0c;不同构造方法可以传递不同的参数值&#xff09;&#xff1a; /** 构造一个将 null 作为其详细消息的新 throwable */ Thr…

在STM32中用寄存器方式点亮流水灯

文章目录 实验资料一、对寄存器的理解1.通俗认识寄存器2.深入了解寄存器&#xff08;1&#xff09;端口配置低寄存器&#xff08;配置0到7引脚的寄存器&#xff09;&#xff08;2&#xff09;端口配置高寄存器&#xff08;配置8到15引脚&#xff09; 3.GPIO口的功能描述 二、配…

鸿蒙内核源码分析(Shell解析篇) | 应用窥视内核的窗口

系列篇从内核视角用一句话概括shell的底层实现为&#xff1a;两个任务&#xff0c;三个阶段。其本质是独立进程&#xff0c;因而划到进程管理模块。每次创建shell进程都会再创建两个任务。 客户端任务(ShellEntry)&#xff1a; 负责接受来自终端(控制台)敲入的一个个字符&…

【云原生】 Kubernetes核心概念

目录 引言 一、部署方式回溯 &#xff08;一&#xff09;传统部署时代 &#xff08;二&#xff09;虚拟化部署时代 &#xff08;三&#xff09;容器部署时代 二、Kubernetes基本介绍 &#xff08;一&#xff09;为什么使用k8s &#xff08;二&#xff09;主要功能 &am…

乡村振兴与数字乡村建设:加强农村信息化建设,推动数字乡村发展,提升乡村治理和服务水平,构建智慧化的美丽乡村

目录 一、引言 二、数字乡村建设的必要性 1、推动农村经济转型升级 2、提升乡村治理水平 3、改善乡村民生福祉 三、数字乡村建设的现状与挑战 1、现状 2、挑战 四、数字乡村建设的未来发展路径 1、加强农村信息化基础设施建设 2、提升农民信息素养和技能水平 3、制…

py黑帽子学习笔记_环境准备

1 下载os装os 下载一个kali虚机镜像然后用虚机管理软件创虚机&#xff0c;装完如下图&#xff0c;我用的版本是2024.1的版本kali-linux-2024.1-installer-amd64&#xff0c;可以从镜像站下载&#xff0c;官网下的慢还断网Index of /kali-images/kali-2024.1/ | 清华大学开源软…

C++高精度算法-加法

引子 在C++的运算中,难免会出现很大很大的数,下面是各个关键字的表示范围 但是如果要表示的数超过了long long可以表示的最大值( 2 64 2^{64} 264-1) 怎么办呢? 如果强制表示,就会溢出,这里的溢出大家可以自行百度,反正就是会出一些-5665434之类的数 现在,就要切入正…

网络基础-Telnet协议

Telnet&#xff08;Telecommunication Network&#xff09;是一种基于文本的远程终端协议&#xff0c;允许用户通过网络连接到远程计算机&#xff0c;并在远程计算机上执行命令&#xff1b;它使用TCP作为传输层协议&#xff0c;并依赖于网络连接在客户端和服务器之间进行通信&a…

MySQL 身份认证漏洞 CVE-2012-2122

漏洞影响版本 MariaDB versions from 5.1.62, 5.2.12, 5.3.6, 5.5.23 are not.MySQL versions from 5.1.63, 5.5.24, 5.6.6 are not.演示 开启靶场 进入漏洞目录 cd /root/vulhub/mysql/CVE-2012-2122开启漏洞靶场 docker-compose up -d攻击 直接 运行 这个命令 for i i…