Linux-ARM裸机(五)-蜂鸣器与按键输入

蜂鸣器实验

蜂鸣器介绍

常用的蜂鸣器有两种:有源蜂鸣器和无源蜂鸣器,这里的有“源”不是电源,而是震荡源,有源蜂鸣器内部带有震荡源,所以有源蜂鸣器只要通电就会叫。无源蜂鸣器内部不带震荡源,直接用直流电是驱动不起来的,需 2K-5K 的方波驱动。

为什么不像控制 LED 灯一样,直接将GPIO 接到蜂鸣器的负极,通过 IO 输出高低来控制蜂鸣器的通断。因为蜂鸣器工作的电流比LED 灯大,直接将蜂鸣器接到 I.MX6U 的 GPIO 上有可能会烧毁 IO,所以通过一个三极管来间接控制蜂鸣器的通断,相当于加一层隔离。
 

硬件电路部分

BEEP为低电平0的时候,上图中PNP三极管导通,相当于蜂鸣器的正极连接到 DCDC_3V3,蜂鸣器两端存在压差,蜂鸣器会响。

BEEP的控制IO为SNVS_TAMPER1

实验程序编写

1、初始化SNVS_TAMPER1这个IO复用为GPIO5_IO01

2、设置SNVS_TAMPER1这个IO的电气属性

3、初始化GPIO

4、控制GPIO输出高低电平

详细编写流程:

新建 VSCode 工程,工程创建完成后在 bsp 文件夹下新建名为“beep”的文件夹,蜂鸣器驱动文件都放到“beep”文件夹里。

新建 beep.h和beep.c文件,保存到 bsp/beep 文件夹里面。beep.h 很简单,就是函数声明。beep.c 文件一共有两个函数:beep_init 和 beep_switch,beep_init 用来初始化 BEEP 所用的 GPIO,也就是 SNVS_TAMPER1,将其复用为 GPIO5_IO01,和 LED 灯初始化函数一样。 beep_switch 函数作为控制 BEEP 的开关,也就是设置GPIO5_IO01 的高低电平。

main函数先使能所有外设时钟,然后初始化LED和BEEP。最终在 while(1)循环中周期性开关 LED 灯和蜂鸣器,周期大约为 500ms。

Makefile:使用之前编写的通用 Makefile,修改变量 TARGET 为 beep,在变量 INCDIRS 和 SRCDIRS 中追加“bsp/beep”。就是添加蜂鸣器驱动头文件路径和源文件路径。

链接脚本就使用之前的链接脚本文件 imx6ul.lds 即可,不用做修改。

按键输入实验

按键介绍

按键两个状态:按下或弹起,将按键连接到一个 IO 上,通过读取这个 IO 的值就知道按键是按下还是弹起的。至于按键按下时是高还是低电平要根据实际电路来判断。前面都是使用I.MX6U 的 GPIO 作为输出使用,当 GPIO 连接按键的时就要做为输入使用。本实验主要工作就是配置按键所连接的 IO 为输入功能,然后读取这个 IO 的值来判断按键是否按下。

最终实现:I.MX6U-ALPHA 开发板上有一个按键 KEY0,本实验将编写代码通过此 KEY0 按键来控制开发板上的蜂鸣器,按一下 KEY0 蜂鸣器打开,再按一下蜂鸣器关闭。

硬件电路部分

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

实验程序编写

1、设置UART1_CTS复用为GPIO1_IO18

2、设置UART1_CTS的电气属性

3、配置GPIO1_IO08为输入模式

4、读取按键值,也就是GPIO1_IO08的高低电平。

分析:按键按下为低电平,就是低电平有效,因此,IO口应该设置为默认上拉,当按键KET0按下,IO口电平接地拉低。

电气属性配置(各个位应设置的值)

IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0);
IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18,0xf080);

0XF080 = 1111 0000 1000 0000

bit0:低压摆率,bit5~3:IO口用作输入,因此设置为输出驱动关闭,bit7~6:设置速度100MHz,bit11:开路输出关闭,bit12:使能或者禁止上下拉/状态保持器功能与bit13配合使用,bit13:当 IO 作为输入的时候,这个位用来设置 IO 使用上下拉还是状态保持器,bit15~14:设置上拉电阻阻值,bit16:设置为0,关闭迟滞比较器。如下图:

设置GPIO为输入:

GPIO1->GDIR &= ~(1<<18);

读取按键值

int read_key(void)
{
    int ret=0;
    ret = ((GPIO1->DR) >> 18) & 0x1;
    return ret;
}

按键消抖

int key_getvalue(void)//按键按下返回值返回1,按键松手时候返回值返回0
{
    int ret = 0;
    static unsigned char state = 1;//state为松手检测位
    if((read_key() == 0) && (state==1))
    {
        delay(10);
        state = 0;
        if(read_key() == 0)
        {
            ret = KEY0_VALUE;
        }
    }   
    else if((read_key() == 1))
    {
        ret = KEY_NONE;
        state = 1;      //方便下次按下
    }
    return ret;
}

加上清除bss段代码不运行原因:(设置清bss段四字节对齐)

反汇编文件中的_bss_start = 0X87800255。对于 32 位的 SOC 来说,一般访问是 4 字节访问的 0X0,0X4,0X8,0XC。芯片处理的时候以4 字节访问,因此会从0X878000254 开始清除BSS段,然而 0X878000254 不属于 BSS 段。所以我们需要对_bss_start 进行四字节对齐。按照四字节对齐的原理,_bss_start=0X87800258。所以需设置_bss_start 为四字节对齐。

链接脚本文件里面加上一句四字节对齐代码,如下:

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_start段从0x87800258开始。

通用GPIO驱动编写(以按键输入实验为例)

.h头文件中定义一个枚举类型GPIO结构体,来存储表示GPIO输入/输出的设置。

/* 枚举类型和结构体定义 */
typedef enum _gpio_pin_direction
{
    kGPIO_DigitalInput = 0U, /* 输入 */
    kGPIO_DigitalOutput = 1U, /* 输出 */
} gpio_pin_direction_t;

.h头文件中再定义一个结构体,用以配置gpio的传输方向、默认电平

/* GPIO 配置结构体 */
typedef struct _gpio_pin_config
{
    gpio_pin_direction_t direction; /* GPIO 方向:输入/输出,上面枚举定义的两个选项 */
    uint8_t outputLogic; /* 如果是输出的话,默认输出电平 */
} gpio_pin_config_t;

.h头文件中为函数声明

/* 函数声明 */
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);

.c源文件中为函数体

/*gpio初始化*/
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);/* 默认输出电平 */
    }
}
/*gpio读*/
int gpio_pinread(GPIO_Type *base, int pin)
{
    return (((base->DR) >> pin) & 0x1);
}
/*gpio写*/
void gpio_pinwrite(GPIO_Type *base, int pin, int value)
{
   if (value == 0U)
   {
       base->DR &= ~(1U << pin); /* 输出低电平 */
   }
   else
   {
       base->DR |= (1U << pin); /* 输出高电平 */
   }
}

希望本文可以帮助到各位读者,文章如有不足,欢迎大家指出,如果文章帮到你了,请一定帮忙点个赞哦。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值