linux蜂鸣器实验

65 篇文章 12 订阅


从软件的角度考虑,蜂鸣器驱动和 LED 灯驱动其实是一摸一样的,都是控制IO输出高低电平。

一、蜂鸣器驱动原理

I.MX6U-ALPHA 开发板上的蜂鸣器通过SNVS_TAMPER1 引脚来控制,

①、在设备树中添加 SNVS_TAMPER1 引脚的 pinctrl 信息。
②、在设备树中创建蜂鸣器节点,在蜂鸣器节点中加入 GPIO 信息。
③、编写驱动程序和测试 APP,和LED 驱动程序和测试 APP 基本一样。

接下来我们就根据上面这三步来编写蜂鸣器 Linux 驱动

二、硬件原理图分析

省略

三、实验程序编写

1.修改设备树文件

1.添加 pinctrl 节点

I.MX6U-ALPHA开发板上的BEEP使用了SNVS_TAMPER1这个PIN,打开imx6ull-luatao-emmc.dts,在 iomuxc 节点的 imx6ul-evk 子节点下创建一个名为“pinctrl_beep”的子节点,节点内容如下所示:

/* 添加对应BEEP的节点 */
		pinctrl_beep: beepgrp{
			/* 具体的PIN信息*/	
			fsl,pins = <
			/* 设备所使用的PIN配置信息*/
				MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01  0x10B0  /* beep*/
			>;
		};

将 SNVS_TAMPER1 这 个 PIN 复 用 为 GPIO5_IO01 , 宏
MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 定义在 arch/arm/boot/dts/imx6ull-pinfunc-snvs.h文件中。

2.添加 BEEP 设备节点

在根节点“/”下创建 BEEP 节点,节点名为“beep”,节点内容如下:

/* BEEP */
	beep{
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "atkalpha-beep";

		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_beep>;
		/* 其他节点内容 */
		beep-gpio = <&gpio5 1 GPIO_ACTIVE_HIGH>;
		status = "okay";
	};

3.检查 PIN是否被其他外设使用

在本章实验中蜂鸣器使用的 PIN 为SNVS_TAMPER1,因此先检查 PIN 为 SNVS_TAMPER1这个 PIN 有没有被其他的 pinctrl 节点使用,如果有使用的话就要屏蔽掉,然后再检查GPIO5_IO01 这个 GPIO 有没有被其他外设使用,如果有的话也要屏蔽掉。

设备树编写完成以后使用“make dtbs”命令重新编译设备树,然后使用新编译出来的imx6ull-luatao-emmc.dtb 文件启动 Linux 系统。启动成功以后进入“/proc/device-tree”目录中查看“beep”节点是否存在,如果存在的话就说明设备树基本修改成功(具体还要驱动验证),结果如图所示:
在这里插入图片描述

2.蜂鸣器驱动程序编写

备树准备好以后就可以编写驱动程序了,本章实验在LED实验驱动文件 gpioled.c的基础上修改而来。新建名为“6_beep”的文件夹,然后在 6_beep 文件夹里面创建 vscode 工程,工作区命名为“beep”。工程创建好以后新建 beep.c 文件,在 beep.c 里面输入如下内容

#include <linux/types.h>

#include <linux/kernel.h>

#include <linux/delay.h>

#include <linux/ide.h>

#include <linux/init.h>

#include <linux/module.h>

#include <linux/errno.h>

#include <linux/gpio.h>

#include <linux/cdev.h>

#include <linux/device.h>

#include <linux/of.h>

#include <linux/of_address.h>

#include <linux/of_gpio.h>

#include <asm/mach/map.h>

#include <asm/uaccess.h>

#include <asm/io.h>

/***************************************************************

Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.

文件名		: beep.c

作者	  	: 左忠凯

版本	   	: V1.0

描述	   	: 蜂鸣器驱动程序。

其他	   	: 无

论坛 	   	: www.openedv.com

日志	   	: 初版V1.0 2019/7/15 左忠凯创建

***************************************************************/

#define BEEP_CNT			1		/* 设备号个数 */

#define BEEP_NAME			"beep"	/* 名字 */

#define BEEPOFF 			0		/* 关蜂鸣器 */

#define BEEPON 				1		/* 开蜂鸣器 */





/* beep设备结构体 */

struct beep_dev{

	dev_t devid;			/* 设备号 	 */

	struct cdev cdev;		/* cdev 	*/

	struct class *class;	/* 类 		*/

	struct device *device;	/* 设备 	 */

	int major;				/* 主设备号	  */

	int minor;				/* 次设备号   */

	struct device_node	*nd; /* 设备节点 */

	int beep_gpio;			/* beep所使用的GPIO编号		*/

};



struct beep_dev beep;		/* beep设备 */



/*

 * @description		: 打开设备

 * @param - inode 	: 传递给驱动的inode

 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量

 * 					  一般在open的时候将private_data指向设备结构体。

 * @return 			: 0 成功;其他 失败

 */

static int beep_open(struct inode *inode, struct file *filp)

{

	filp->private_data = &beep; /* 设置私有数据 */

	return 0;

}



/*

 * @description		: 向设备写数据 

 * @param - filp 	: 设备文件,表示打开的文件描述符

 * @param - buf 	: 要写给设备写入的数据

 * @param - cnt 	: 要写入的数据长度

 * @param - offt 	: 相对于文件首地址的偏移

 * @return 			: 写入的字节数,如果为负值,表示写入失败

 */

static ssize_t beep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)

{

	int retvalue;

	unsigned char databuf[1];

	unsigned char beepstat;

	struct beep_dev *dev = filp->private_data;



	retvalue = copy_from_user(databuf, buf, cnt);

	if(retvalue < 0) {

		printk("kernel write failed!\r\n");

		return -EFAULT;

	}



	beepstat = databuf[0];		/* 获取状态值 */



	if(beepstat == BEEPON) {	

		gpio_set_value(dev->beep_gpio, 0);	/* 打开蜂鸣器 */

	} else if(beepstat == BEEPOFF) {

		gpio_set_value(dev->beep_gpio, 1);	/* 关闭蜂鸣器 */

	}

	return 0;

}



/*

 * @description		: 关闭/释放设备

 * @param - filp 	: 要关闭的设备文件(文件描述符)

 * @return 			: 0 成功;其他 失败

 */

static int beep_release(struct inode *inode, struct file *filp)

{

	return 0;

}



/* 设备操作函数 */

static struct file_operations beep_fops = {

	.owner = THIS_MODULE,

	.open = beep_open,

	.write = beep_write,

	.release = 	beep_release,

};



/*

 * @description	: 驱动出口函数

 * @param 		: 无

 * @return 		: 无

 */

static int __init beep_init(void)

{

	int ret = 0;



	/* 设置BEEP所使用的GPIO */

	/* 1、获取设备节点:beep */

	beep.nd = of_find_node_by_path("/beep");

	if(beep.nd == NULL) {

		printk("beep node not find!\r\n");

		return -EINVAL;

	} else {

		printk("beep node find!\r\n");

	}



	/* 2、 获取设备树中的gpio属性,得到BEEP所使用的BEEP编号 */

	beep.beep_gpio = of_get_named_gpio(beep.nd, "beep-gpio", 0);

	if(beep.beep_gpio < 0) {

		printk("can't get beep-gpio");

		return -EINVAL;

	}

	printk("led-gpio num = %d\r\n", beep.beep_gpio);



	/* 3、设置GPIO5_IO01为输出,并且输出高电平,默认关闭BEEP */

	ret = gpio_direction_output(beep.beep_gpio, 1);

	if(ret < 0) {

		printk("can't set gpio!\r\n");

	}



	/* 注册字符设备驱动 */

	/* 1、创建设备号 */

	if (beep.major) {		/*  定义了设备号 */

		beep.devid = MKDEV(beep.major, 0);

		register_chrdev_region(beep.devid, BEEP_CNT, BEEP_NAME);

	} else {						/* 没有定义设备号 */

		alloc_chrdev_region(&beep.devid, 0, BEEP_CNT, BEEP_NAME);	/* 申请设备号 */

		beep.major = MAJOR(beep.devid);	/* 获取分配号的主设备号 */

		beep.minor = MINOR(beep.devid);	/* 获取分配号的次设备号 */

	}

	printk("beep major=%d,minor=%d\r\n",beep.major, beep.minor);	

	

	/* 2、初始化cdev */

	beep.cdev.owner = THIS_MODULE;

	cdev_init(&beep.cdev, &beep_fops);

	

	/* 3、添加一个cdev */

	cdev_add(&beep.cdev, beep.devid, BEEP_CNT);



	/* 4、创建类 */

	beep.class = class_create(THIS_MODULE, BEEP_NAME);

	if (IS_ERR(beep.class)) {

		return PTR_ERR(beep.class);

	}



	/* 5、创建设备 */

	beep.device = device_create(beep.class, NULL, beep.devid, NULL, BEEP_NAME);

	if (IS_ERR(beep.device)) {

		return PTR_ERR(beep.device);

	}

	

	return 0;

}



/*

 * @description	: 驱动出口函数

 * @param 		: 无

 * @return 		: 无

 */

static void __exit beep_exit(void)

{

	/* 注销字符设备驱动 */

	cdev_del(&beep.cdev);/*  删除cdev */

	unregister_chrdev_region(beep.devid, BEEP_CNT); /* 注销设备号 */



	device_destroy(beep.class, beep.devid);

	class_destroy(beep.class);

}



module_init(beep_init);

module_exit(beep_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("zuozhongkai");

3.编写测试APP

beepApp.c 的文件内容和 ledApp.c 文件内容基本一样,要是对文件进行打开、写、关闭等操作。

#include "stdio.h"

#include "unistd.h"

#include "sys/types.h"

#include "sys/stat.h"

#include "fcntl.h"

#include "stdlib.h"

#include "string.h"

/***************************************************************

Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.

文件名		: beepApp.c

作者	  	: 左忠凯

版本	   	: V1.0

描述	   	: beep测试APP。

其他	   	: 无

使用方法	 :./beepApp /dev/beep  0 关闭蜂鸣器

		     ./beepApp /dev/beep  1 打开蜂鸣器

论坛 	   	: www.openedv.com

日志	   	: 初版V1.0 2019/1/30 左忠凯创建

***************************************************************/



#define BEEPOFF 	0

#define BEEPON 	1



/*

 * @description		: main主程序

 * @param - argc 	: argv数组元素个数

 * @param - argv 	: 具体参数

 * @return 			: 0 成功;其他 失败

 */

int main(int argc, char *argv[])

{

	int fd, retvalue;

	char *filename;

	unsigned char databuf[1];

	

	if(argc != 3){

		printf("Error Usage!\r\n");

		return -1;

	}



	filename = argv[1];



	/* 打开beep驱动 */

	fd = open(filename, O_RDWR);

	if(fd < 0){

		printf("file %s open failed!\r\n", argv[1]);

		return -1;

	}



	databuf[0] = atoi(argv[2]);	/* 要执行的操作:打开或关闭 */



	/* 向/dev/beep文件写入数据 */

	retvalue = write(fd, databuf, sizeof(databuf));

	if(retvalue < 0){

		printf("BEEP Control Failed!\r\n");

		close(fd);

		return -1;

	}



	retvalue = close(fd); /* 关闭文件 */

	if(retvalue < 0){

		printf("file %s close failed!\r\n", argv[1]);

		return -1;

	}

	return 0;

}

四、运行测试

depmod //第一次加载驱动的时候需要运行此命令
modprobe beep.ko //加载驱动

在这里插入图片描述
beep 这个节点找到了,并且GPIO5_IO01这个GPIO 的编号为 129。

使用 beepApp 软件来测试驱动是否工作正常,输入如下命令打开蜂鸣器:

./beepApp /dev/beep 1 //打开蜂鸣器
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值