zoukankan      html  css  js  c++  java
  • 迅为IMX6ULL开发板Linux蜂鸣器实验

    在上一章讲解了添加 LED 灯驱动的整个流程和测试结果,这一章在来看一下蜂鸣器的驱动,蜂鸣器和
    LED 灯的驱动其实是一样的,都是控制 GPIO 引脚输出高低电平,在本章继续学习一下蜂鸣器的驱动,也算
    是在巩固一遍驱动的添加流程。
    37.1 蜂鸣器设备注册流程 蜂鸣器设备注册流程
    和 LED 灯驱动注册一样,蜂鸣器注册流程也分为下面几步:
    5 硬件原理图分析,确定控制 LED 的 GPIO 信息。
    6 根据 GPIO 信息在设备树文件中添加 pinctrl 信息
    7 在设备树中创建 LED 的设备节点,并加入 GPIO 信息
    8 编写 LED 设备驱动程序
    接下来根据上面这四步来添加一下蜂鸣器的设备驱动。
    2 37.2 蜂鸣器硬件原理图分析
    <ignore_js_op>
    蜂鸣器一端接 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”节点是否存在,如果存
    在的话就说明设备树基本修改成功(具体作用还要驱动验证),结果如下图所示:
    <ignore_js_op>
    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! ");
    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! ");
    115 return -EINVAL;
    116 } else {
    117 printk("beep node find! ");
    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 ", 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! ");
    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 ",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! ");
    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! ", 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! ");
    44 close(fd);
    45 return -1;
    46 }
    47
    48 retvalue = close(fd); /* 关闭文件 */
    49 if(retvalue < 0){
    50 printf("file %s close failed! ", 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,如下图所示:
    <ignore_js_op>
    37.6.2  编译应用测试程序
    输入如下命令编译应用测试程序:
    arm-linux-gnueabihf-gcc -o beep_test beep_test.c
    编译完成后,会生成 beep_test 可执行文件。如下图所示:
    <ignore_js_op>
    37.6.3  运行测试
    启动开发板,将编译好的 beep.ko 驱动模块和 beep_test 应用测试文件拷贝到/lib/modules/4.1.15 目录下(检
    查开发板根文件系统中有没有“/lib/modules/4.1.15”这个目录,如果没有的话需要自行创建一下。开发板
    中使用的是光盘资料里面提供的 busybox 文件系统,光盘资料的“i.MX6UL 终结者光盘资料8_开发板系统
    镜像3_文件系统镜像1_Busybox 文件系统”目录下)。输入下面命令加载模块:
    depmod
    modprobe beep
    驱动加载成功后,显示下面的信息:
    <ignore_js_op>
    然后使用应用测试程序来验证一下驱动是否正确,输入下面的命令打开蜂鸣器:
    ./beep_test /dev/beep 1 //打开蜂鸣器
    查看开发板上的蜂鸣器是否有响声,如果有鸣叫声说明驱动正常工作。
    然后输入下面命令关闭蜂鸣器:
    ./beep_test /dev/beep 0 //关闭蜂鸣器
    正常情况下,蜂鸣器停止鸣叫。
    使用下面的命令卸载模块:
    rmmod beep //卸载模块
    <ignore_js_op>

  • 相关阅读:
    汉语-词语:什么
    汉语-词语:甚么
    汉语-汉字:心
    汉语-词汇:头脑
    汉语-词语:冰冷
    汉语-词汇:冷静
    两个int类型的数据相加,有可能会出现超出int的表示范围。
    两个int类型的数据相加,有可能会出现超出int的表示范围。
    成员变量与局部变量的区别_
    函数额基本概述
  • 原文地址:https://www.cnblogs.com/liyue3/p/13501372.html
Copyright © 2011-2022 走看看