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>

  • 相关阅读:
    element ui 表单清空
    element ui 覆盖样式 方法
    element ui 修改表单值 提交无效
    element ui 抽屉里的表单输入框无法修改值
    element ui 抽屉首次显示 闪烁
    css 左侧高度 跟随右侧内容高度 自适应
    PICNUF框架
    elementui 抽屉组件标题 出现黑色边框
    vue 子组件跨多层调用父组件中方法
    vue 编辑table 数据 未点击提交,table里的数据就发生了改变(深拷贝处理)
  • 原文地址:https://www.cnblogs.com/liyue3/p/13501372.html
Copyright © 2011-2022 走看看