蜂鸣器实验
蜂鸣器介绍
常用的蜂鸣器有两种:有源蜂鸣器和无源蜂鸣器,这里的有“源”不是电源,而是震荡源,有源蜂鸣器内部带有震荡源,所以有源蜂鸣器只要通电就会叫。无源蜂鸣器内部不带震荡源,直接用直流电是驱动不起来的,需 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); /* 输出高电平 */
}
}
希望本文可以帮助到各位读者,文章如有不足,欢迎大家指出,如果文章帮到你了,请一定帮忙点个赞哦。