美文网首页
迅为IMX6ULL开发板Linux蜂鸣器实验

迅为IMX6ULL开发板Linux蜂鸣器实验

作者: TL_6cdd | 来源:发表于2020-08-14 11:55 被阅读0次

    在上一章讲解了添加 LED 灯驱动的整个流程和测试结果,这一章在来看一下蜂鸣器的驱动,蜂鸣器和

    LED 灯的驱动其实是一样的,都是控制 GPIO 引脚输出高低电平,在本章继续学习一下蜂鸣器的驱动,也算

    是在巩固一遍驱动的添加流程。

    37.1 蜂鸣器设备注册流程 蜂鸣器设备注册流程

    和 LED 灯驱动注册一样,蜂鸣器注册流程也分为下面几步:

    5 硬件原理图分析,确定控制 LED 的 GPIO 信息。

    6 根据 GPIO 信息在设备树文件中添加 pinctrl 信息

    7 在设备树中创建 LED 的设备节点,并加入 GPIO 信息

    8 编写 LED 设备驱动程序

    接下来根据上面这四步来添加一下蜂鸣器的设备驱动。

    2 37.2 蜂鸣器硬件原理图分析

    蜂鸣器一端接 VSYS 电压(3.3V),另一端接控制引脚,只不过多了一个三极管。控制引脚为:SNVS_TAMPER1。

    3 37.3 修改设备树文件 修改设备树文件

    37.3.1  添加 pinctrl  信息

    在 i.MX6UL 终结者开发板中使用 SNVS_TAMPER1 这个引脚来控制蜂鸣器设备。打开 topeet_emmc_4_3.dts

    文件在 iomux 节点的 imx6ul-evk 子节点下创建一个名为“pinctrl_beep”的子节点,具体内容如下:

    pinctrl_beep: gpio-beep {

    fsl,pins = <

    MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x10B0 /* beep */

    >;

    };

    在 pinctrl_beep 节点中,将 SNVS_TAMPER1 引脚复用为 GPIO5_IO01,宏定义

    MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 在 arch/arm/boot/dts/imx6ull-pinfunc-snvs.h 文件中。

    37.3.2  添加 beep  设备节点

    在 topeet_emmc_4_3.dts 文件下,在根节点“/”下创建 LED 节点,节点名为“beep”,具体内容如下:

    1 beep {

    2 #address-cells = <1>;

    3 #size-cells = <1>;

    4 compatible = "beep";

    5 pinctrl-names = "default";

    6 pinctrl-0 = <&pinctrl_beep>;

    7 beep-gpio = <&gpio5 1 GPIO_ACTIVE_HIGH>;

    8 status = "okay";

    9 };

    第 6 行,pinctrl-0 属性设置蜂鸣器所使用的 GPIO 对应的 pinctrl 节点。

    第 7 行,beep-gpio 属性指定了蜂鸣器所使用的 GPIO。

    接下来就是检查蜂鸣器使用的GPIO引脚SNVS_TAMPER1有没有被其他pinctrl节点使用,在检查GPIO5_IO01

    这个 GPIO 有没有被其他外设使用,如果有的话都屏蔽掉。

    设备树编写完成以后使用“make dtbs”命令重新编译设备树,然后使用新编译出来的 topeet_emmc_4_3.dtb

    文件启动 Linux 系统。启动成功以后进入“/proc/device-tree”目录中查看“beep”节点是否存在,如果存

    在的话就说明设备树基本修改成功(具体作用还要驱动验证),结果如下图所示:

    37.4 编写蜂鸣器驱动程序 编写蜂鸣器驱动程序

    本实验例程路径:i.MX6UL 终结者光盘资料/06_Linux 驱动例程/03_beep

    蜂鸣器的驱动程序和 LED 的驱动程序基本一致,创建 beep.c 文件。具体内容如下:

    1 #include

    2 #include

    3 #include

    4 #include

    5 #include

    6 #include

    7 #include

    8 #include

    9 #include

    10 #include

    11 #include

    12 #include

    13 #include

    14 #include

    15 #include

    16 #include

    17

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

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

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

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

    22

    23

    24 /* beep 设备结构体 */

    25 struct beep_dev{

    26 dev_t devid; /* 设备号 */

    27 struct cdev cdev; /* cdev */

    28 struct class *class; /* 类 */

    29 struct device *device; /* 设备 */

    30 int major; /* 主设备号 */

    31 int minor; /* 次设备号 */

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

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

    34 };

    35

    36 struct beep_dev beep; /* beep 设备 */

    37

    38 /*

    39 * @description : 打开设备

    40 * @param – inode : 传递给驱动的 inode

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

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

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

    44 */

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

    46 {

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

    48 return 0;

    49 }

    50

    51 /*

    52 * @description : 向设备写数据

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

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

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

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

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

    58 */

    59 static ssize_t beep_write(struct file *filp, const char __user *buf,

    60 size_t cnt, loff_t *offt)

    61 {

    62 int retvalue;

    63 unsigned char databuf[1];

    64 unsigned char beepstat;

    65 struct beep_dev *dev = filp->private_data;

    66

    67 retvalue = copy_from_user(databuf, buf, cnt);

    68 if(retvalue < 0) {

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

    70 return -EFAULT;

    71 }

    72

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

    74

    75 if(beepstat == BEEPON) {

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

    77 } else if(beepstat == BEEPOFF) {

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

    79 }

    80 return 0;

    81 }

    82

    83 /*

    84 * @description : 关闭/释放设备

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

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

    87 */

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

    89 {

    90 return 0;

    91 }

    92

    93 /* 设备操作函数 */

    94 static struct file_operations beep_fops = {

    95 .owner = THIS_MODULE,

    96 .open = beep_open,

    97 .write = beep_write,

    98 .release = beep_release,

    99 };

    100

    101 /*

    102 * @description : 驱动入口函数

    103 * @param : 无

    104 * @return : 无

    105 */

    106 static int __init beep_init(void)

    107 {

    108 int ret = 0;

    109

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

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

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

    113 if(beep.nd == NULL) {

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

    115 return -EINVAL;

    116 } else {

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

    118 }

    119

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

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

    122 if(beep.beep_gpio < 0) {

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

    124 return -EINVAL;

    125 }

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

    127

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

    129 ret = gpio_direction_output(beep.beep_gpio, 0);

    130 if(ret < 0) {

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

    132 }

    133

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

    135 /* 1、创建设备号 */

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

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

    138 register_chrdev_region(beep.devid, BEEP_CNT, BEEP_NAME);

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

    140 alloc_chrdev_region(&beep.devid, 0, BEEP_CNT, BEEP_NAME);

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

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

    143 }

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

    145

    146 /* 2、初始化 cdev */

    147 beep.cdev.owner = THIS_MODULE;

    148 cdev_init(&beep.cdev, &beep_fops);

    149

    150 /* 3、添加一个 cdev */

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

    152

    153 /* 4、创建类 */

    154 beep.class = class_create(THIS_MODULE, BEEP_NAME);

    155 if (IS_ERR(beep.class)) {

    156 return PTR_ERR(beep.class);

    157 }

    158

    159 /* 5、创建设备 */

    160 beep.device = device_create(beep.class, NULL, beep.devid, NULL,

    161 BEEP_NAME);

    162 if (IS_ERR(beep.device)) {

    163 return PTR_ERR(beep.device);

    164 }

    165

    166 return 0;

    167 }

    168

    169 /*

    170 * @description : 驱动出口函数

    171 * @param : 无

    172 * @return : 无

    173 */

    174 static void __exit beep_exit(void)

    175 {

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

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

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

    179

    180 device_destroy(beep.class, beep.devid);

    181 class_destroy(beep.class);

    182 }

    183

    184 module_init(beep_init);

    185 module_exit(beep_exit);

    186 MODULE_LICENSE("GPL");

    187 MODULE_AUTHOR("topeet");

    5 37.5 编写应用测试程序 编写应用测试程序

    创建应用测试程序 beep_test.c,内容如下:

    1 #include "stdio.h"

    2 #include "unistd.h"

    3 #include "sys/types.h"

    4 #include "sys/stat.h"

    5 #include "fcntl.h"

    6 #include "stdlib.h"

    7 #include "string.h"

    8

    9 #define BEEPOFF 0

    10 #define BEEPON 1

    11

    12 /*

    13 * @description : main 主程序

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

    15 * @param - argv : 具体参数

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

    17 */

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

    19 {

    20 int fd, retvalue;

    21 char *filename;

    22 unsigned char databuf[1];

    23

    24 if(argc != 3){

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

    26 return -1;

    27 }

    28

    29 filename = argv[1];

    30

    31 /* 打开 beep 驱动 */

    32 fd = open(filename, O_RDWR);

    33 if(fd < 0){

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

    35 return -1;

    36 }

    37

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

    39

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

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

    42 if(retvalue < 0){

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

    44 close(fd);

    45 return -1;

    46 }

    47

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

    49 if(retvalue < 0){

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

    51 return -1;

    52 }

    53 return 0;

    54 }

    beep_test.c 的文件内容和 gpioled_test.c 的内容差不多,都是对文件的打开、关闭、写操作。

    6 37.6 编译运行测试 编译运行测试

    37.6.1  编译蜂鸣器驱动文件

    同 LED 驱动文件一样,创建 Makefile 文件,将 obj-m 的值改为 beep.o,Makefile 文件内容如下:

    KERNELDIR := /home/topeet/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga

    CURRENT_PATH := $(shell pwd)

    obj-m := beep.o

    build: kernel_modules

    kernel_modules:

    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

    clean:

    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

    首先我们在终端输入两个命令(设置两个环境变量):

    export ARCH=arm

    export CROSS_COMPILE=arm-linux-gnueabihf-

    然后执行“make”命令编译模块,编译完成生成 beep.ko,如下图所示:

    37.6.2  编译应用测试程序

    输入如下命令编译应用测试程序:

    arm-linux-gnueabihf-gcc -o beep_test beep_test.c

    编译完成后,会生成 beep_test 可执行文件。如下图所示:

    37.6.3  运行测试

    启动开发板,将编译好的 beep.ko 驱动模块和 beep_test 应用测试文件拷贝到/lib/modules/4.1.15 目录下(检

    查开发板根文件系统中有没有“/lib/modules/4.1.15”这个目录,如果没有的话需要自行创建一下。开发板

    中使用的是光盘资料里面提供的 busybox 文件系统,光盘资料的“i.MX6UL 终结者光盘资料\08_开发板系统

    镜像\03_文件系统镜像\01_Busybox 文件系统”目录下)。输入下面命令加载模块:

    depmod

    modprobe beep

    驱动加载成功后,显示下面的信息:

    然后使用应用测试程序来验证一下驱动是否正确,输入下面的命令打开蜂鸣器:

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

    查看开发板上的蜂鸣器是否有响声,如果有鸣叫声说明驱动正常工作。

    然后输入下面命令关闭蜂鸣器:

    ./beep_test /dev/beep 0 //关闭蜂鸣器

    正常情况下,蜂鸣器停止鸣叫。

    使用下面的命令卸载模块:

    rmmod beep //卸载模块

    相关文章

      网友评论

          本文标题:迅为IMX6ULL开发板Linux蜂鸣器实验

          本文链接:https://www.haomeiwen.com/subject/qprgdktx.html