历史上的今天
返回首页

历史上的今天

今天是:2024年10月27日(星期日)

正在发生

2021年10月27日 | Linux之ARM(IMX6U)裸机按键输入实验(GPIO的输出与输入)

2021-10-27 来源:eefocus

前面几篇文章试验都是讲解如何使用 I.MX6U 的 GPIO 输出控制功能, I.MX6U 的 IO 不仅能作为输出,而且也可以作为输入。 I.MX6U-ALPHA 开发板上有一个按键,按键连接了一个 IO,将这个 IO 配置为输入功能,读取这个 IO 的值即可获取按键的状态(按下或松开)。本篇文章通过这个按键来控制蜂鸣器的开关


1、按键输入简介

按键就两个状态:按下或弹起,将按键连接到一个 IO 上,通过读取这个 IO 的值就知道按键是按下的还是弹起的。至于按键按下的时候是高电平还是低电平要根据实际电路来判断。


I.MX6U-ALPHA 开发板上有一个按键 KEY0,本篇文章我们将会编写代码通过这个 KEY0 按键来控制开发板上的蜂鸣器,按一下 KEY0 蜂鸣器打开,再按一下蜂鸣器就关闭


2、硬件原理图分析

本试验我们用到的硬件有:

1) LED 灯 LED0。

2)蜂鸣器。

3) 1 个按键 KEY0。

按键 KEY0 的原理图如图 15.2.1 所示:

从图可以看出,按键 KEY0 是连接到 I.MX6U 的 UART1_CTS 这个 IO 上的, KEY0接了一个 10K 的上拉电阻,因此 KEY0 没有按下的时候 UART1_CTS 应该是高电平,当 KEY0按下以后 UART1_CTS 就是低电平。


配置寄存器位GPIO:

配置寄存器的电器属性:

image.png?imageView2/2/w/550

3、实验程序的编写

本次实在在上一次实验的基础上完成(蜂鸣器实验),我们把上一篇的代码复制一份,在上面做修改,

重新创建 VSCode 工程,工作区名字为“key”,在工程目录的 bsp 文件夹中创建名为“key”和“gpio”两个文件夹。按键相关的驱动文件都放到“key”文件夹中,本次试验我们对 GPIO 的操作编写一个函数集合,也就是编写一个 GPIO驱动文件, GPIO 的驱动文件放到“gpio”文件夹里面。


新建 bsp_gpio.c 和 bsp_gpio.h 这两个文件,将这两个文件都保存到刚刚创建的 bsp/gpio 文件夹里面


3.1、bsp_gpio.h

#ifndef __BSP_KEY_H

#define __BSP_KEY_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 // !__BSP_KEY_H


bsp_gpio.h 中定义了一个枚举类型 gpio_pin_direction_t 和结构体 gpio_pin_config_t,枚举类型 gpio_pin_direction_t 表示 GPIO 方向,输入或输出。结构体 gpio_pin_config_t 是 GPIO 的配置结构体,里面有 GPIO 的方向和默认输出电平两个成员变量


3.2、bsp_gpio.c

#include "bsp_gpio.h"


/*

 * @description : GPIO 初始化。

 * @param - base : 要初始化的 GPIO 组。

 * @param - pin : 要初始化 GPIO 在组内的编号。

 * @param - config : GPIO 配置结构体。

 * @return : 无

 */


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); /*默认输出电平*/

    }

    

}


/*

 * @description : 读取指定 GPIO 的电平值 。

 * @param – base : 要读取的 GPIO 组。

 * @param - pin : 要读取的 GPIO 脚号。

 * @return : 无

 */


int gpio_pinread(GPIO_Type *base,int pin)

{

    return (((base->DR) >> pin) & 0x1);

}


/*

 * @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 == 0U)

    {

        base->DR &= ~(1U<

    }

    else

    {

        base->DR |= (1U << pin);  /*输出高电平*/

    }

    

}


文件 bsp_gpio.c 中有三个函数: gpio_init、 gpio_pinread 和 gpio_pinwrite,函数 gpio_init 用于初始化指定的 GPIO 引脚,最终配置的是 GDIR 寄存器,此函数有三个参数,这三个参数的含义如下:

image.png?imageView2/2/w/550

函数 gpio_pinread 是读取指定的 GPIO 值,也就是读取 DR 寄存器的指定位,此函数有两个参数和一个返回值,参数含义如下:

image.png?imageView2/2/w/550

函数 gpio_pinwrite 是控制指定的 GPIO 引脚输入高电平(1)或者低电平(0),就是设置 DR 寄存器的指定位,此函数有三个参数,参数含义如下:

image.png?imageView2/2/w/550

我们以后就可以使用函数 gpio_init 设置指定 GPIO 为输入还是输出,使用函数 gpio_pinread和 gpio_pinwrite 来读写指定的 GPIO


接下来编写按键驱动文件,新建 bsp_key.c 和 bsp_key.h 这两个文件,将这两个文件都保存到刚刚创建的 bsp/key 文件夹里面


3.3、gpio_key.h

#ifndef __BSP_KEY_H

#define __BSP_KEY_H


#include "imx6ul.h"

#include "bsp_delay.h"


/*定义按键值*/

enum keyvalue {

    KEY_NONE =0,

    KEY_VALUE ,

};


/*函数声明*/

void key_init();

int key_get_value();


#endif // !__BSP_KEY_H


bsp_key.h 文件中定义了一个枚举类型: keyvalue, 此枚举类型表示按键值, 因为 I.MX6UALPHA 开发板上只有一个按键,因此枚举类型里面只到 KEY0_VALUE


3.4、gpio_key.c

#include "bsp_key.h"


/*

 * @description : 初始化按键

 * @param : 无

 * @return : 无

 */


void key_init(void)

{

    gpio_pin_config_t key_config;


    /* 1、初始化 IO 复用, 复用为 GPIO1_IO18 */

    IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0);


    /* 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);


   /* 3、初始化 GPIO GPIO1_IO18 设置为输入*/

   key_config.direction=kGPIO_DigitalInput;

   gpio_init(GPIO1,18,&key_config);


}


/*

* @description : 获取按键值

* @param : 无

* @return : 0 没有按键按下,其他值:对应的按键值

*/

int key_get_value(void)

{

    int ret =0;

    static unsigned char release =1; /*按键松开*/


    if((release == 1) &&  (gpio_pin_read(GPIO1,18) == 0))

    {

        delay(10);  /**/

        release = 0;

        if(gpio_pin_read(GPIO1,18) == 0)

        {

            ret = KEY_VALUE;

        }


    }

    else if(gpio_pin_read(GPIO1,18) == 1)

    {

        ret =0;

        release = 1;

    }

    return ret;

}


bsp_key.c 中一共有两个函数: key_init 和 key_getvalue, key_init 是按键初始化函数,用来初始化按键所使用的 UART1_CTS 这个 IO。函数 key_init 先设置 UART1_CTS 复用为GPIO1_IO18,然后配置 UART1_CTS 这个 IO 为速度为 100MHz,默认 22K 上拉。最后调用函数 gpio_init 来设置 GPIO1_IO18 为输入功能。


函数 key_getvalue 用于获取按键值,此函数没有参数,只有一个返回值,返回值表示按键值,返回值为 0 的话就表示没有按键按下,如果返回其他值的话就表示对应的按键按下了。获取按键值其实就是不断的读取 GPIO1_IO18 的值,如果按键按下的话相应的 IO 被拉低,那么GPIO1_IO18 值就为 0,如果按键未按下的话 GPIO1_IO18 的值就为 1。此函数中静态局部变量release 表示按键是否释放。


3.5、main.c


#include "main.h"


int main() 

{

    int i=0;

    int keyvalue=0;   

    unsigned char led_status= OFF;

    unsigned char beep_status= OFF;


    clk_enable();  //使能外设时钟

    led_init(); //初始化LED

    init_beep();//初始化蜂鸣器

    key_init(); //初始化key

    

    while(1)

    {

        keyvalue = key_getvalue();

        if(keyvalue)

        {

            switch (keyvalue)

            {

            case KEY_VALUE:

                beep_status=!beep_status;

                beep_switch(beep_status);

                break;

            

            default:

                break;

            }

        }

        

        i++;

        if(i==50)

        {

            i=0;

            led_status=!led_status;

            led_switch(LED0,led_status);

        }


    }


    return 0;

}


main.c 函数先初始化 led 灯、蜂鸣器和按键,然后在 while(1)循环中不断的调用函数key_getvalue 来读取按键值,如果 KEY0 按下的话就打开/关闭蜂鸣器。 LED0 作为系统提示指示灯闪烁,闪烁周期大约为 500ms。


4、编译下载验证

4.1、连接脚本的编写

SECTIONS

{

    . = 0x87800000;

    .text :

    {

        obj/start.o

        *(.text)

    }

    .rodata ALIGN(4) : {*(.rodata*)}

    .data ALIGN(4) : {*(.data)}

    . = ALIGN(4) ;

    __bss_start = .;

    .bss ALIGN(4) : { *(.bss) *(COMMON)}

    __bss_end = .;

}


这里注意bss段需要四字节对其,否则会清除其他字段的内容,造成程序的崩溃


4.2、Makefile的编写

CROSS_COMPILE ?= arm-linux-gnueabihf-

TARGET   ?= key


CC := $(CROSS_COMPILE)gcc

LD := $(CROSS_COMPILE)ld

OBJCOPY := $(CROSS_COMPILE)objcopy

OBJDUMP := $(CROSS_COMPILE)objdump


INCDIRS := imx6ul

   bsp/clk

   bsp/led

   bsp/delay

   bsp/beep

   bsp/gpio

   bsp/key

       

SRCDIRS := project

   bsp/clk

   bsp/led

   bsp/delay

   bsp/beep

   bsp/key

   bsp/gpio 

   

   

INCLUDE := $(patsubst %, -I %, $(INCDIRS))


SFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.s))

CFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))


SFILENDIR := $(notdir  $(SFILES))

CFILENDIR := $(notdir  $(CFILES))


SOBJS := $(patsubst %, obj/%, $(SFILENDIR:.s=.o))

COBJS := $(patsubst %, obj/%, $(CFILENDIR:.c=.o))

OBJS := $(SOBJS) $(COBJS)


VPATH := $(SRCDIRS)


.PHONY: clean

$(TARGET).bin : $(OBJS)

$(LD) -Timx6ul.lds -o $(TARGET).elf $^

$(OBJCOPY) -O binary -S $(TARGET).elf $@

$(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis


$(SOBJS) : obj/%.o : %.s

$(CC) -Wall -nostdlib -c -O2  $(INCLUDE) -o $@ $<


$(COBJS) : obj/%.o : %.c

$(CC) -Wall -nostdlib -c -O2  $(INCLUDE) -o $@ $<

clean:


4.3、编译下载

使用 Make 命令编译代码,编译成功以后使用软件 imxdownload 将编译完成的 key.bin 文件下载到 SD 卡中,命令如下:


chmod 777 imxdownload              //给予 imxdownload 可执行权限,一次即可

./imxdownload key.bin  /dev/sdd    //烧写到 SD 卡中


烧写成功以后将 SD 卡插到开发板的 SD 卡槽中,然后复位开发板。如果代码运行正常的话 LED0 会以大约 500ms 周期闪烁, 按下开发板上的 KEY0 按键,蜂鸣器打开,再按下 KEY0按键,蜂鸣器关闭。

推荐阅读

史海拾趣

Hitachi (Renesas )公司的发展小趣事

在电子行业中,Hitachi(日立)及其子公司如Renesas(瑞萨电子,原日立半导体部门独立后成立)的发展历史充满了技术创新、市场扩张与全球合作的精彩故事。以下是五个相关故事,每个故事均基于事实描述:

1. 从电机制造到半导体巨头的转型

日立公司成立于1910年,最初以生产电动机和矿山设备起家,旨在支持日本的工业化进程。随着技术的不断进步和市场需求的多样化,日立逐渐将业务扩展到电子领域。1960年代,日立开始涉足半导体技术,并在随后的几十年里,通过持续的研发投入和技术创新,逐步建立了在半导体行业的领先地位。1999年,日立半导体部门独立成为Renesas Technology Corporation(瑞萨科技),进一步专注于微控制器、模拟IC、电源管理IC等产品的研发与生产,为汽车电子、工业控制、消费电子等多个领域提供关键解决方案。

2. 全球化战略与市场拓展

进入21世纪,日立及Renesas积极实施全球化战略,通过在全球各地设立研发中心、生产基地和销售网络,加速其产品和技术的国际化进程。例如,Renesas在美国、欧洲和亚洲等地建立了多个研发中心,与当地企业和研究机构紧密合作,共同推动半导体技术的创新与发展。同时,通过并购和合作,Renesas不断扩展其产品线,提升在全球市场的竞争力。

3. 技术创新引领行业发展

日立及Renesas在半导体领域的技术创新是其持续发展的重要驱动力。多年来,公司投入大量资源进行研发,不断推出具有行业影响力的新产品和技术。例如,在汽车电子领域,Renesas的MCU(微控制器)和功率半导体产品广泛应用于发动机控制、车身控制、安全系统等关键系统,为汽车行业的智能化和电动化转型提供了重要支持。此外,Renesas还在物联网、工业4.0等新兴领域积极布局,推动相关技术的研发与应用。

4. 应对挑战与危机管理

在发展过程中,日立及Renesas也面临过诸多挑战和危机。例如,2011年日本东北太平洋地区地震和海啸对日立及Renesas的生产基地造成了严重影响。面对这一突如其来的灾难,公司迅速启动应急响应机制,调整生产计划,确保关键产品的供应不受影响。同时,公司还加强了供应链的多元化和风险管理,以应对未来可能发生的类似危机。

5. 可持续发展与社会责任

作为一家全球性的电子企业,日立及Renesas始终将可持续发展和社会责任视为企业发展的重要组成部分。公司致力于开发环保型产品和解决方案,推动绿色能源和节能减排技术的应用。同时,公司还积极参与社会公益活动,支持教育、环保和社区发展项目,展现了作为全球企业公民的责任感。例如,日立通过引入“光伏+储能”智慧能源体系,构建绿色生产基地,为实现“双碳”目标贡献力量。

以上五个故事展示了Hitachi(日立)及其子公司Renesas在电子行业中的发展历程和成就,体现了公司在技术创新、市场拓展、危机管理、可持续发展和社会责任等方面的努力和贡献。

长园维安(CYGWAYON)公司的发展小趣事

长园维安始终关注环保和可持续发展。公司积极推行绿色制造和循环经济模式,降低生产过程中的能耗和排放。同时,长园维安还注重产品环保性能的研发和改进,为客户提供更加环保、节能的产品。这些努力使长园维安在电子行业中树立了绿色发展的典范。

天二科技(EVER OHMS)公司的发展小趣事

面对不断变化的市场需求和技术挑战,天二科技始终坚持持续创新和客户至上的经营理念。公司与客户保持紧密合作,深入了解客户的需求和反馈,不断推出符合市场需求的新产品和解决方案。同时,天二科技还注重培养员工的创新意识和能力,鼓励员工积极参与技术创新和研发工作。这种持续创新和客户至上的经营理念使得天二科技在电子行业中始终保持领先地位。

Concord Semiconductor Corp公司的发展小趣事

随着业务规模的不断扩大,Concord Semiconductor Corp开始将市场拓展作为重要的发展战略。公司通过参加国际电子展会、举办技术研讨会等方式,积极宣传自己的产品和技术,提升品牌知名度。同时,公司还针对不同地区的市场需求,推出定制化产品和服务,进一步巩固了市场地位。

Bivar公司的发展小趣事

在电子行业的初期,Bivar公司以其创新的技术突破赢得了市场的认可。公司研发出一款新型电阻器,具有高稳定性和长寿命的特点,迅速在行业内引起了关注。凭借这一技术优势,Bivar逐渐在电阻器市场上占据了一席之地,并逐渐扩大生产规模,实现了公司的初步成长。

Goldentech Discrete Semiconductor Inc公司的发展小趣事

在20世纪90年代末,Goldentech Discrete Semiconductor Inc.(以下简称“Goldentech”)由一群来自顶尖学术机构的半导体专家创立。公司从成立之初就专注于研发高性能的离散半导体器件,特别是针对当时迅速发展的移动通信市场。Goldentech的创新团队成功开发出了一种新型的低功耗、高频率的晶体管,这一技术突破迅速吸引了多家手机制造商的注意。凭借这一技术优势,Goldentech在短时间内成为移动通信领域离散半导体器件的主要供应商之一,推动了公司业务的快速增长。

问答坊 | AI 解惑

如何实现MSP430远程升级?

如何实现MSP430远程升级? 请参考下面的附件!希望对大家有所帮助!…

查看全部问答>

请教个很白痴的关于quartus的问题

要是你们觉得很小白也不要不告诉我。。。。本人正在做毕设  边学边做的 我要做语音信号处理,用quartus。。。含燥的信号进行滤波处理。。。。。可是我不知道怎么把信号输入到quartus里。。。 有人能具体告诉我下么。。就是我自己录得语 ...…

查看全部问答>

分享一个自己的程序.串口DMA可超时收发

很简单的程序.在网上却不太好找.最后还是用智林的例程改的.最近要做的摄像头部分.基本用这个就能搞定了.接下来要看看AD DA TIMER I2C了~大家一起加油…

查看全部问答>

如何有效利用职场空白期?

透视职场,职场人很少能够从一而终,上一份职业刚刚结束,下一份工作还未正式确定,其实,身处职场难免会由于一些原因而遭遇空白期。专家提醒,职场人求职时切忌刻意遮掩曾经的空白期,诚信就业,从容应对才是正确的做法。     人力资源 ...…

查看全部问答>

51单片机学习笔记之点亮LED

   以前,本来有学习过一段时间的51单片机,但是换工作后一直加班都没时间继续学下去。现在终于有空闲时间了,决定继续学下去,每天至少写一个程序,无论多少。先重温一下以前学过的东西:    单片机最简单和基础的操作就是对 ...…

查看全部问答>

PADS_器件封装制作过程

自从2008年注册eeworld已经5个年头了。期间,在论坛上下载了很多东西。但是由于个人原因,从中汲取的知识很少。后来有幸,做了PCB部分的版主,但我自己感觉给论坛出力很少。近来由于公司工作较为繁多,所以很少登陆eeworld。但是一有时间就会登陆ee ...…

查看全部问答>

msp430 脉冲信号检测

如何利用msp430f149对外部脉冲信号计数…

查看全部问答>

msp430f5438驱动16位并行DA

前辈们,请教了,用msp430F5438驱动16位并行输入的DA。DA采用的是ANALOG DEVICE的5546芯片,基准电压用MAX6350提高5V参考电压。 请问这个程序怎么编?小弟新上手,求高手指教!…

查看全部问答>

AD采样最高位出错

           用的TLC2543   12位的AD芯片   采样范围为 0~5V              现在当采样范围为 0~2.5V时 12位AD的采样值得最高位及 ...…

查看全部问答>

cc3200传送数据到云端资料库

最近想要利用cc3200传送数据到云端资料库 要传送的资料有温度二氧化碳湿度 由于是第一次碰这一块 不清楚该从哪几个范例着手 想请问一下要让数据可以传到云端资料库 云端资料库连线是需要帐号密码的 要找哪几个范例做研究? wlan_ap? wlan_s ...…

查看全部问答>