用IMX6UL开发板编写按键输入实验

news2024/11/29 7:51:09

        在之前我们都是讲解如何使用IMX6UL的GPIO输出控制等功能,IMX6U的IO不仅能作为输出,而且也可以作为输入,而我们开发板上具有一个按键,按键肯定是连接了一个IO口的额,我们在这一节将会把IO配置成输入功能,读取这个IO的值,即可获取按键的状态,然后可以利用这个特性来控制蜂鸣器

        那么我们对这个实验做一个简单的分析,按键就只有两个状态,按下或者弹起,将按键连接在一个IO口上,读取这个IO口就知道按键是按下的还是弹起的。后面需要根据实际电路图来判断按键按下的时候是产生高电平还是低电平
        下面就开始硬件的分析,按键KEY0连接到UART_CTS这个引脚的IO上,KEY0上面接了一个10K的上拉电阻,因此KEY0在没有被按下的时候是一个高电平的状态,当KEY0按下以后就改变了引脚的状态,也就是低电平

        那么我们创建一个文件夹作为本次实验,可以是07_key,然后将前面蜂鸣器的代码复制过来,我们在此基础上添加代码,这一节实验除了要编写按键实验以外,我们还有个任务要求是,将我们的GPIO编写一个函数集合,也可以理解为封装一下,类似于STM32中的库,这样在之后每次编写的时候就不用再去反复操作寄存器了
        首先创建bsp_gpio.h和bsp_gpio.c,然后先编写如下代码

typedef enum _gpio_pin_direction
{
    kGPIO_DigitalInput = 0U,
    kGPIO_DigitalOutput = 1U,
}gpio_pin_direction_t;

        这里我们定义了一个名为_gpio_pin_direction的枚举类型,并通过类型别名,也就是起了另一个名字叫做gpio_pin_direction_t,也就是在之后引用这个名字的时候就变相的引用了上面的枚举类型,使用 gpio_pin_direction_t 会比直接使用 _gpio_pin_direction 更简洁
        kGPIO_DigitalInput = 0U是枚举的第一个成员,也被称为枚举常量表示GPIO引脚被配置为数字输入模式。0U表示这个成员的值是0,U后缀表示这是一个无符号整形(unsigned integer)字面量,保证涉及枚举值的计算时保持一致,避免隐式类型转换,且枚举成员主要用于表示一组固定的、命名的常量值
         然后定义一个结构体,

#ifndef _BSP_GPIO_H
#define _BSP_GPIO_H
#include "imx6ul.h"

typedef enum _gpio_pin_direction
{
    kGPIO_DigitalInput = 0U,
    kGPIO_DigitalOutput = 1U,
}gpio_pin_direction_t;

typedef struct _gpio_pin_config
{
    gpio_pin_direction_t direction; /*GPIO方向,输入还是输出*/
    uint8_t outputLogic;            /*如果是输出的话,默认输出电平*/
}gpio_pin_config_t;

        在结构体的这行代码中,gpio_pin_direction_t意思是声明了一个名为direction的枚举变量,它的类型是direction_t。也就是上面的枚举类型(gpio_pin_direction_t diretion)。这个变量可以存储该枚举类型中定义的任何一个常量(也称为枚举成员)
        这里的意思是,在结构体中的枚举变量direction可以被赋值为  kGPIO_DigitalInput = 0U或者kGPIO_DigitalOutput = 1U,中的任何一个。这些常量是枚举类型gpio_pin_direction_t的成员
        outputLogic是创建一个这个uint8_t的变量,被用作了结构体的一部分

#include "bsp_gpio.h"

void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config)
{
    if(config->direction == kGPIO_DigitalInput)
    {
        base->GDIR &= ~(1 << pin);
    }
    else
    {
        base->GDIR |= 1 << pin;
        gpio_pinwrite(base,pin, config->outputLogic);
    }
}

        然后我们在bsp_gpio.c当中这么写,首先这段代码的作用是用于初始化GPIO引脚的函数,它接受三个返回参数,一个指向GPIO基地址的指针base,一个整数pin表示要操作的引脚编号,以及一个指向gpio_pin_config_t结构体的指针config,这个函数当中有一个if判断语句,如果方向是输入,那么就执行if语句当中的对应引脚给初始化
        那么反之如何不是输入,而是输出,那么就通过下面一个自定义的函数,老控制要输出的电平,所以接下来还要继续编写gpio_pinwrite


        

        那么现在就开始编写引脚输出电平的函数,代码如下,这个代码不需要怎么解释,也就是0对应输出低电平,然后操作GPIOx_DR寄存器,如果不是0那么就输出高电平,同样也是控制GPIOx_DR寄存器,来输出高电平

void gpio_pinwrite(GPIO_Type *base ,int pin, int value)
{
    if(value == 0u)
    {
        base->DR &= ~(1U << pin); //输出低电平
    }
    else
    {
        base->DR |= 1U << pin;  //输出高电平
    }
}

        那么有了输出电平的控制函数,那么我们设计到按键,所以我们也需要一个读取GPIO电平的状态读取函数,所以下一个我们编写这个函数,然后代码如下,主要会解释这个return函数的含义
        一个括号一个括号来解释,首先,通过指针base访问GPIO的寄存器(DR)的当前值
        ((base->DR) >> pin ),然后将这个值右移pin位,目的就是将我们需要读取的引脚移动到最低位进行读取,因为前面提到过,如果需要读取寄存器当中某个位的值,必须要将其移动到最右边。
        然后下一步就是将移动到最右边的最低位进行保留,然后将其他的位全部清零
        结合return就是将,最后引脚的结果返回,返回的是一个整数,要么是0也就是低电平,要么就是高电平也就是数据位1
        简单来描述就是,gpio_pinread 函数通过读取GPIO模块的数据寄存器(DR),并将指定引脚的状态移动到最低位,然后通过与 0x1 进行按位与操作来提取这个状态,最后返回这个状态。

int gpio_pinread(GPIO_Type *base , int pin)
{
    return (((base->DR) >> pin ) &0x1);
}

        最后需要完成GPIO函数的封装就是将上面我们描写的函数然后在.h文件当中去申明一下,下面将展示一个完整gpio.c和gpio.h文件

//bsp_gpio.c

#include "bsp_gpio.h"

void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config)
{
    if(config->direction == kGPIO_DigitalInput)
    {
        base->GDIR &= ~(1 << pin);
    }
    else
    {
        base->GDIR |= 1 << pin;
        gpio_pinwrite(base,pin, config->outputLogic);
    }
}

int gpio_pinread(GPIO_Type *base , int pin)
{
    return (((base->DR) >> pin ) &0x1);
}

void gpio_pinwrite(GPIO_Type *base ,int pin, int value)
{
    if(value == 0u)
    {
        base->DR &= ~(1U << pin); //输出低电平
    }
    else
    {
        base->DR |= 1U << pin;
    }
}
//_bsp_gpio.h

#ifndef _BSP_GPIO_H
#define _BSP_GPIO_H
#include "imx6ul.h"

typedef enum _gpio_pin_direction
{
    kGPIO_DigitalInput = 0U,
    kGPIO_DigitalOutput = 1U,
}gpio_pin_direction_t;

typedef struct _gpio_pin_config
{
    gpio_pin_direction_t direction; /*GPIO方向,输入还是输出*/
    uint8_t outputLogic;            /*如果是输出的话,默认输出电平*/
}gpio_pin_config_t;

void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config);
int gpio_pinread(GPIO_Type *base, int pin);
void gpio_pinwrite(GPIO_Type *base, int pin, int value);

#endif           

        在完成了GPIO的封装后,下一步就要开始编写按键的初始化了,创建一个文件夹命名为key,下面创建两个文件分别是bsp_key.c和bap_key.h,然后我们先看原理图,key引脚接在UART1_CTS这个引脚上面

        首先编写.h文件,创建一个枚举类型keyvalue,因为我们的开发板上只有一个按键,但是考虑到代码的移植性,所以这里枚举了三个成员变量分别为,KEY0,1,2,

#ifndef _BSP_KEY_H
#define _BSP_KEY_H
#include "imx6ul.h"


enum keyvalue{

    KEY_NONE =0,
    Key0_Value,
    Key1_Value,
    key2_value,
};

#endif

        然后开始编写.c文件,代码如下

/*初始化按键*/
void key_init()
{
    gpio_pin_config_t key_config;

    IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0); //IO引脚复用,将引脚复用为GPIO1_IO18

    /* 2、、配置UART1_CTS_B的IO属性	
	 *bit 16:0 HYS关闭
	 *bit [15:14]: 11 默认22K上拉
	 *bit [13]: 1 pull功能
	 *bit [12]: 1 pull/keeper使能
	 *bit [11]: 0 关闭开路输出
	 *bit [7:6]: 10 速度100Mhz
	 *bit [5:3]: 000 关闭输出
	 *bit [0]: 0 低转换率
	 */
    IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xF080); /*1111 0000 1000 0000*/

    /*初始化GPIO*/
    key_config.direction = kGPIO_DigitalInput;
    gpio_init(GPIO1,18,&key_config);    
}

        首先IOMUXC_SetPinMux这个函数依然是为了设置IO引脚的复用,然后IOMUXC_SetPinConfig是为了设置对应引脚的电气属性,为什么是0xF080我已经注释出来了,直接参考就可以了
        首先我们需要了解的就是下面的gpio_init这个函数的参数,主要是最后一个参数gpio_pin_config_t* config,意味着这个函数期望接受一个指向该结构体实例的指针作为参数,这个指针通常指向一个已经初始化好的gpio_pin_config_t实例
        所以我们就需要在代码中创建一个结构体实例,这个实例也是一个具体的变量,包含了结构体定义的所有成员变量。所以代码为gpio_pin_config_t key_config;
        然后接下里就要设置,结构体实例的成员变量,为结构体中的各个成员变量赋予适当的值,创建了实例过后,通过代码 key_config.direction = kGPIO_DigitalInput;访问key_config结构体中名为direction的成员方式,然后将kGPIO_DigitalInput 的值赋给 key_config.direction,那么按键的初始化就完成了

        那么接下就要开始编写按键电平读取的函数了,这里定义一个ret变量将其初始化为0,用于存储函数的返回值,然后再定义一个静态的unsigned char类型的变量release,将其初始化为1.为什么使用静态,因为可以保证在函数调用期间保持其值不变,在之后我们会使用这个变量来防止按键抖动,因为暂时没有涉及到寄存器,所以这里统一使用延时消抖的方法来避免

int getvalue()
{
    int ret = 0;
    static unsigned char release = 1;

    if((release == 1)&&(gpio_pinread(GPIO1,18)==0))
    {
        delay(10);
        release = 0;
        if(gpio_pinread(GPIO1,18) == 0)
        {
            ret =Key0_Value;
        }
    }
    else if(gpio_pinread(GPIO1,18) == 1)
    {
        ret = 0;
        release = 1;
    }

    return ret;
}

         首先,先检查release以及GPIO1的IO18是否为低电平,如果为低电平就说明按键被按下了,如果条件满足条件则,先执行去除抖动,然后将release设置为0,表示现在处于去抖动的状态,然后再去检查对应的GPIO1的IO18引脚是否为低电平,如果为低电平则则将ret设置为Key0_Value这个值,最终将ret的值返回
        但是如果在刚开始的时候,if的条件判断错误,release 不为1,或者 GPIO1 的第18引脚为高电平(1),则将ret设置为0,表示按键未被按下,并且将release设置重置为1,为下一次检测做准备。最后的操作同样是将ret的值返回
        然后最后就是在bsp_key.h申明一下我们在这里编写的函数,驱动以及初始化函数全部都已经编写完了,接下来就可以开始准备编写主函数

#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_led.h"
#include "bsp_beep.h"
#include "bsp_key.h"


int main()
{
    int keyvalue = 0;
    unsigned char led_state = 0;
    unsigned char beep_state = 0;

    clk_enable();
    led_init();
    beep_init();
    key_init();

    while(1)
    {
        keyvalue = key_getvalue();
        if(key_getvalue)
        {
            switch ((keyvalue))
            {
            case Key0_Value:
                beep_state = !beep_state;
                beep_switch(beep_switch);
                break;
            }
        
        }
            led_state =!led_state;
            led_switch(LED0,led_state);
        
            delay(10);
        }
    }
    return 0;
}

        上面的代码是主函数的代码,主要目的就是同过按键控制BSP蜂鸣器,通过key_getvalue,来判断按键是否按下,如果按下就直接通过switch语句,就可以使得蜂鸣器使能了
        然后keyvalue循环执行完后,就开始执行下面的LED常亮的代码,通过反转前面的变量,再通过switch语句来控制LED0的亮状态,然后返回值,主函数的代码就执行完成了,最后将我们的代码烧写到板子当中,然后我们的这项实验就完成了

        感谢各位的阅读,如果有任何问题的欢迎在评论区指出,或者与作者讨论
        

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

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

相关文章

JAVA基础面试题汇总(持续更新)

1、精确运算场景使用浮点型运算问题 精确运算场景&#xff08;如金融领域计算应计利息&#xff09;计算数字&#xff0c;使用浮点型&#xff0c;由于精度丢失问题&#xff0c;会导致计算后的结果和预期不一致&#xff0c;使用Bigdecimal类型解决此问题&#xff0c;示例代码如下…

Java类的生命周期浅析

知识前提 在了解类的生命周期之前&#xff0c;有必要先了解一下jvm的内存结构。如下所示&#xff1a; 在了解完jvm的内存结构之后&#xff0c;就知道了例如堆区&#xff0c;栈区&#xff0c;常量池和方法区等概念。 也了解到了&#xff0c;编写的代码&#xff0c;是先需要通过…

使用pytdx获取股票信息总结

使用pytdx获取股票相关信息汇总 行情接口标准行情对接总结界面展示性能问题数据可靠性 附录代码 行情接口 pytdx中提供了hq&#xff08;标准行情&#xff09;及exhq&#xff08;扩展市场行情&#xff09;两种接口&#xff0c;扩展市场目前已经失效无法使用。 标准行情 这里只…

4. Getter和Setter注解与lombok

文章目录 1. 什么是Getter和Setter注解2. 什么是lombokjava自带的jar包 3. 从maven仓库里找lombok相关jar包4. 把jar包导入项目另一个jar包导入途径 5. 正式使用注解① 问题② 解决方案提示 6. 如果还想对某个成员变量添加限制怎么办7. 内容出处 1. 什么是Getter和Setter注解 官…

【包教包会】速通LLM《从头开始构建大型语言模型》免费pdf分享

介绍 在当今人工智能技术飞速发展的时代&#xff0c;大型语言模型&#xff08;LLM&#xff09;作为聊天机器人、文本生成和理解等应用的核心&#xff0c;已经成为研究和商业领域的关注焦点。尽管这些模型的应用无处不在&#xff0c;但对于大多数开发者来说&#xff0c;它们的工…

AI正悄然地影响着企业数字化转型

2022年底发布的ChatGPT将人工智能技术上升到了一个新的高度。如今&#xff0c;人工智能正彻底改变数字转型的进程&#xff0c;为企业提供优化运营和提升客户体验的机会。利用算法、数据分析、机器学习等人工智能技术结合企业自身情况&#xff0c;可以推动企业持续创新&#xff…

每日OJ题_牛客_mari和shiny_线性dp_C++_Java

目录 牛客_mari和shiny_线性dp 题目解析 C代码 Java代码 牛客_mari和shiny_线性dp mari和shiny (nowcoder.com) 描述&#xff1a; mari每天都非常shiny。她的目标是把正能量传达到世界的每个角落&#xff01; 有一天&#xff0c;她得到了一个仅由小写字母组成的字…

ElasticSearch 备考 -- Snapshot Restore

一、题目 备份集群下的索引 task&#xff0c;存储快照名称为 snapshot_1 二、思考 这个涉及的是集群的备份&#xff0c;主要是通过创建快照&#xff0c;涉及到以下2步骤 Setp1&#xff1a;注册一个备份 snapshot repository Setp2&#xff1a;创建 snapshot 可以通过两种方…

找生网站方案———未来之窗行业应用跨平台架构

1&#xff09;网站设计方面的考虑 主色调采用于公司深蓝色颜色&#xff0c;网页整体色彩明快、大气、简洁&#xff0c;每个细节均经过精心处 理&#xff0c;网页浏览快速&#xff0c;导航明确清晰。 网页设计要充分考虑网页的整体感觉&#xff0c;每个页面的图片与网站色调的过…

YOLO11改进 | 卷积模块 | 用Ghost卷积轻量化网络【详细步骤】

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; Ghost 模块可以作为即插即用组件来升级…

C(十四)while、for、do-while循环综合(一)

uu们&#xff0c;小弟我本科在读&#xff0c;文章我会一直坚持更新下去&#xff0c;包括但不限于C初阶、C进阶、数据结构、C、Linux、MySQL、项目、QT开发、各种算法&#xff08;之后会持续更新&#xff09;&#xff0c;并且站在小白的视角尽可能通俗易懂地把这些写出来&#x…

xss之dom类型

目录 xss关于dom类型 1、闭合方式 2、闭合&#xff0c;直接输入1&#xff0c;成功闭合 上我们的pikachu xss关于dom类型 1、闭合方式 输入123&#xff0c;然后打开f12&#xff0c;审查元素&#xff0c;之前一直没有搞懂为什么要在前面加上个单引号 输入两个双引号&#x…

【STM32开发之寄存器版】(三)-详解NVIC中断

一、前言 STM32F103ZET6具备强大的中断控制能力&#xff0c;其嵌套向量中断控制器(NVIC)和处理器核的接口紧密相连&#xff0c;可以实现低延迟的中断处理和高效地处理晚到的中断。NVIC主要具备以下特性&#xff1a; 68个可屏蔽中断通道(不包含16个Cortex™-M3的中断线)&#xf…

【黑马点评】 使用RabbitMQ实现消息队列——1.Docker与RabbitMQ环境安装

黑马点评中&#xff0c;使用基于Redis的Stream实现消息队列&#xff0c;但是Strema已经不太常用。在此修改为使用RabbitMQ实现消息队列。主要包括RabbitMQ的环境准备&#xff08;Docker的下载与安装&#xff09;以及如何修改黑马点评中的代码。 【黑马点评】使用RabbitMQ实现消…

【Java数据结构】栈 (Stack)

【本节目标】 1. 栈的概念及使用 2. 相关 OJ 题 一、概念 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last…

C语言—单链表

目录 一、链表的概念及结构 二、单链表实现 &#xff08;2.1&#xff09;基本结构定义 &#xff08;2.2&#xff09;申请节点 &#xff08;2.3&#xff09;打印函数 &#xff08;2.4&#xff09;头部插入删除\尾部插入删除 &#xff08;2.4.1&#xff09;尾部插入 &…

Anaconda的安装与环境设置

文章目录 一、Anaconda介绍二、Anaconda环境搭建1. 下载Anaconda(1)官网下载(2)清华大学镜像 2. 安装Anaconda3.配置环境变量4.检验conda是否安装成功5.更改镜像源6.若菜单栏没有conda prompt 三、虚拟环境1.创建、查看、删除虚拟环境2.激活、退出虚拟环境 四、CUDA、Pytorch、…

基于SpringBoot+Vue的酒店客房管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

qemu-system-aarch64开启user用户模式网络连接

一、问题 在使用qemu构建arm64的虚拟机时&#xff0c;虚拟机没有网络&#xff0c;桥接方式相对麻烦&#xff0c;我只是需要联网更新即可。与宿主机的通信我使用共享文件夹即可满足要求。 使用指令启动虚拟机时&#xff0c;网络部分的参数为 -net user,hostfwdtcp::10022-:22 …

白板2-数学基础

高斯分布1-极大似然估计 高斯分布2-极大似然估计-无偏&有偏 高斯分布3-从概率密度角度高斯分布4-局限性高斯分布5-边缘概率及条件概率高斯分布6-求联合概率分布