搜索
bottom↓
回复: 0

【正点原子Linux连载】第四十六章Linux蜂鸣器实验--摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南

[复制链接]

出0入234汤圆

发表于 2020-6-30 15:21:24 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2020-10-26 11:26 编辑

1)实验平台:正点原子阿尔法Linux开发板
2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html  
4)对正点原子Linux感兴趣的同学可以加群讨论:
876919289 QQ群头像.png
5)关注正点原子公众号,获取最新资料


100846rel79a9p4uelap24.jpg

100846f1ce1fg14zbg0va4.png


第四十六章Linux蜂鸣器实验

        
        上一章实验中我们借助pinctrl和gpio子系统编写了LED灯驱动,I.MX6U-ALPHA开发板上还有一个蜂鸣器,从软件的角度考虑,蜂鸣器驱动和LED灯驱动其实是一摸一样的,都是控制IO输出高低电平。本章我们就来学习编写蜂鸣器的Linux驱动,也算是对上一章讲解的pinctrl和gpio子系统的巩固。


46.1蜂鸣器驱动原理
        蜂鸣器驱动原理已经在第十四章有了详细的讲解,I.MX6U-ALPHA开发板上的蜂鸣器通过SNVS_TAMPER1引脚来控制,本节我们来看一下如果在Linux下编写蜂鸣器驱动需要做哪些工作:
        ①、在设备树中添加SNVS_TAMPER1引脚的pinctrl信息。
        ②、在设备树中创建蜂鸣器节点,在蜂鸣器节点中加入GPIO信息。
        ③、编写驱动程序和测试APP,和第四十五章的LED驱动程序和测试APP基本一样。
        接下来我们就根据上面这三步来编写蜂鸣器Linux驱动。
46.2硬件原理图分析
本章实验硬件原理图参考14.3小节即可。
46.3实验程序编写
        本实验对应的例程路径为:开发板光盘->2、Linux驱动例程->6_beep。
        本章实验在四十二章实验的基础上完成,重点是将驱动改为基于设备树的.
46.3.1 修改设备树文件
        1、添加pinctrl节点
        I.MX6U-ALPHA开发板上的BEEP使用了SNVS_TAMPER1这个PIN,打开imx6ull-alientek-emmc.dts,在iomuxc节点的imx6ul-evk子节点下创建一个名为“pinctrl_beep”的子节点,节点内容如下所示:
示例代码46.3.1.1 SNVS_TAMPER1 pinctrl节点
1 pinctrl_beep: beepgrp {
2     fsl,pins =<
3         MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01     0x10B0/* beep */
4>;
5};
        第3行,将SNVS_TAMPER1这个PIN复用为GPIO5_IO01,宏MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01定义在arch/arm/boot/dts/imx6ull-pinfunc-snvs.h文件中。
        2、添加BEEP设备节点
        在根节点“/”下创建BEEP节点,节点名为“beep”,节点内容如下:
示例代码46.3.1.2 创建BEEP蜂鸣器节点
  1. 1 beep {
  2. 2     #address-cells =<1>;
  3. 3     #size-cells =<1>;
  4. 4     compatible ="atkalpha-beep";
  5. 5     pinctrl-names ="default";
  6. 6     pinctrl-0=<&pinctrl_beep>;
  7. 7     beep-gpio =<&gpio5 1 GPIO_ACTIVE_HIGH>;
  8. 8     status ="okay";
  9. 9};
复制代码

        第6行,pinctrl-0属性设置蜂鸣器所使用的PIN对应的pinctrl节点。
        第7行,beep-gpio属性指定了蜂鸣器所使用的GPIO。
        3、检查PIN是否被其他外设使用
        在本章实验中蜂鸣器使用的PIN为SNVS_TAMPER1,因此先检查PIN为SNVS_TAMPER1这个PIN有没有被其他的pinctrl节点使用,如果有使用的话就要屏蔽掉,然后再检查GPIO5_IO01这个GPIO有没有被其他外设使用,如果有的话也要屏蔽掉。
        设备树编写完成以后使用“makedtbs”命令重新编译设备树,然后使用新编译出来的imx6ull-alientek-emmc.dtb文件启动Linux系统。启动成功以后进入“/proc/device-tree”目录中查看“beep”节点是否存在,如果存在的话就说明设备树基本修改成功(具体还要驱动验证),结果如图46.3.1.1所示:
image002.gif

图46.3.1.1 beep子节点

46.3.2 蜂鸣器驱动程序编写
        设备树准备好以后就可以编写驱动程序了,本章实验在第四十五章实验驱动文件gpioled.c的基础上修改而来。新建名为“6_beep”的文件夹,然后在6_beep文件夹里面创建vscode工程,工作区命名为“beep”。工程创建好以后新建beep.c文件,在beep.c里面输入如下内容:
示例代码46.3.2.1 beep.c文件代码段
  1. 1   #include <linux/types.h>
  2. 2   #include <linux/kernel.h>
  3. 3   #include <linux/delay.h>
  4. 4   #include <linux/ide.h>
  5. 5   #include <linux/init.h>
  6. 6   #include <linux/module.h>
  7. 7   #include <linux/errno.h>
  8. 8   #include <linux/gpio.h>
  9. 9   #include <linux/cdev.h>
  10. 10  #include <linux/device.h>
  11. 11  #include <linux/of.h>
  12. 12  #include <linux/of_address.h>
  13. 13  #include <linux/of_gpio.h>
  14. 14  #include <asm/mach/map.h>
  15. 15  #include <asm/uaccess.h>
  16. 16  #include <asm/io.h>
  17. 17/***************************************************************
  18. 18  Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
  19. 19文件名                : beep.c
  20. 20作者        : 左忠凯
  21. 21版本        : V1.0
  22. 22描述        : 蜂鸣器驱动程序。
  23. 23其他        : 无
  24. 24论坛        : <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  25. 25 日志        : 初版V1.0 2019/7/15 左忠凯创建
  26. 26  ***************************************************************/
  27. 27  #define BEEP_CNT                1                /* 设备号个数        */
  28. 28  #define BEEP_NAME               "beep"        /* 名字                */
  29. 29  #define BEEPOFF                  0                /* 关蜂鸣器                */
  30. 30  #define BEEPON                   1                /* 开蜂鸣器                */
  31. 31
  32. 32
  33. 33/* beep设备结构体 */
  34. 34struct beep_dev{
  35. 35      dev_t devid;        /* 设备号                                        */
  36. 36struct cdev cdev;        /* cdev                                             */
  37. 37struct class *class;        /* 类                                                */
  38. 38struct device *device;        /* 设备                                                */
  39. 39int major;        /* 主设备号                                        */
  40. 40int minor;        /* 次设备号                                        */
  41. 41struct device_node  *nd;        /* 设备节点                                                */
  42. 42int beep_gpio;        /* beep所使用的GPIO编号        */
  43. 43};
  44. 44
  45. 45struct beep_dev beep;        /* beep设备 */
  46. 46
  47. 47/*
  48. 48   * @description          : 打开设备
  49. 49   * @param – inode        : 传递给驱动的inode
  50. 50   * @param - filp         : 设备文件,file结构体有个叫做private_data的成员变量
  51. 51   *                    一般在open的时候将private_data指向设备结构体。
  52. 52   * @return               : 0 成功;其他失败
  53. 53   */
  54. 54staticint beep_open(struct inode *inode,struct file *filp)
  55. 55{
  56. 56      filp->private_data =&beep;        /* 设置私有数据 */
  57. 57return0;
  58. 58}
  59. 59
  60. 60/*
  61. 61   * @description        : 向设备写数据
  62. 62   * @param - filp         : 设备文件,表示打开的文件描述符
  63. 63   * @param - buf          : 要写给设备写入的数据
  64. 64   * @param - cnt          : 要写入的数据长度
  65. 65   * @param - offt         : 相对于文件首地址的偏移
  66. 66   * @return                : 写入的字节数,如果为负值,表示写入失败
  67. 67   */
  68. 68static ssize_t beep_write(struct file *filp,constchar __user *buf,
  69. size_t cnt, loff_t *offt)
  70. 69{
  71. 70int retvalue;
  72. 71unsignedchar databuf[1];
  73. 72unsignedchar beepstat;
  74. 73struct beep_dev *dev = filp->private_data;
  75. 74
  76. 75      retvalue = copy_from_user(databuf, buf, cnt);
  77. 76if(retvalue <0){
  78. 77          printk("kernel write failed!\r\n");
  79. 78return-EFAULT;
  80. 79}
  81. 80
  82. 81      beepstat = databuf[0];                                /* 获取状态值 */
  83. 82
  84. 83if(beepstat == BEEPON){
  85. 84          gpio_set_value(dev->beep_gpio,0);/* 打开蜂鸣器 */
  86. 85}elseif(beepstat == BEEPOFF){
  87. 86          gpio_set_value(dev->beep_gpio,1);/* 关闭蜂鸣器 */
  88. 87}
  89. 88return0;
  90. 89}
  91. 90
  92. 91/*
  93. 92   * @description          : 关闭/释放设备
  94. 93   * @param - filp         : 要关闭的设备文件(文件描述符)
  95. 94   * @return                : 0 成功;其他失败
  96. 95   */
  97. 96staticint beep_release(struct inode *inode,struct file *filp)
  98. 97{
  99. 98return0;
  100. 99}
  101. 100
  102. 101/* 设备操作函数 */
  103. 102staticstruct file_operations beep_fops ={
  104. 103.owner = THIS_MODULE,
  105. 104.open = beep_open,
  106. 105.write = beep_write,
  107. 106.release =  beep_release,
  108. 107};
  109. 108
  110. 109/*
  111. 110  * @description         : 驱动入口函数
  112. 111  * @param               : 无
  113. 112  * @return              : 无
  114. 113  */
  115. 114staticint __init beep_init(void)
  116. 115{
  117. 116int ret =0;
  118. 117
  119. 118/* 设置BEEP所使用的GPIO */
  120. 119/* 1、获取设备节点:beep */
  121. 120     beep.nd = of_find_node_by_path("/beep");
  122. 121if(beep.nd ==NULL){
  123. 122         printk("beep node not find!\r\n");
  124. 123return-EINVAL;
  125. 124}else{
  126. 125         printk("beep node find!\r\n");
  127. 126}
  128. 127
  129. 128/* 2、获取设备树中的gpio属性,得到BEEP所使用的GPIO编号 */
  130. 129     beep.beep_gpio = of_get_named_gpio(beep.nd,"beep-gpio",0);
  131. 130if(beep.beep_gpio <0){
  132. 131         printk("can't get beep-gpio");
  133. 132return-EINVAL;
  134. 133}
  135. 134     printk("led-gpio num = %d\r\n", beep.beep_gpio);
  136. 135
  137. 136/* 3、设置GPIO5_IO01为输出,并且输出高电平,默认关闭BEEP */
  138. 137     ret = gpio_direction_output(beep.beep_gpio,1);
  139. 138if(ret <0){
  140. 139         printk("can't set gpio!\r\n");
  141. 140}
  142. 141
  143. 142/* 注册字符设备驱动 */
  144. 143/* 1、创建设备号 */
  145. 144if(beep.major){                                /*  定义了设备号 */
  146. 145         beep.devid = MKDEV(beep.major,0);
  147. 146         register_chrdev_region(beep.devid, BEEP_CNT, BEEP_NAME);
  148. 147}else{                /* 没有定义设备号 */
  149. 148         alloc_chrdev_region(&beep.devid,0, BEEP_CNT, BEEP_NAME);
  150. 149         beep.major = MAJOR(beep.devid);        /* 获取分配号的主设备号 */
  151. 150         beep.minor = MINOR(beep.devid);        /* 获取分配号的次设备号 */
  152. 151}
  153. 152     printk("beep major=%d,minor=%d\r\n",beep.major, beep.minor);
  154. 153
  155. 154/* 2、初始化cdev */
  156. 155     beep.cdev.owner = THIS_MODULE;
  157. 156     cdev_init(&beep.cdev,&beep_fops);
  158. 157
  159. 158/* 3、添加一个cdev */
  160. 159     cdev_add(&beep.cdev, beep.devid, BEEP_CNT);
  161. 160
  162. 161/* 4、创建类 */
  163. 162     beep.class = class_create(THIS_MODULE, BEEP_NAME);
  164. 163if(IS_ERR(beep.class)){
  165. 164return PTR_ERR(beep.class);
  166. 165}
  167. 166
  168. 167/* 5、创建设备 */
  169. 168     beep.device = device_create(beep.class,NULL, beep.devid,NULL,
  170. BEEP_NAME);
  171. 169if(IS_ERR(beep.device)){
  172. 170return PTR_ERR(beep.device);
  173. 171}
  174. 172
  175. 173return0;
  176. 174}
  177. 175
  178. 176/*
  179. 177  * @description         : 驱动出口函数
  180. 178  * @param               : 无
  181. 179  * @return              : 无
  182. 180  */
  183. 181staticvoid __exit beep_exit(void)
  184. 182{
  185. 183/* 注销字符设备驱动 */
  186. 184     cdev_del(&beep.cdev);                                                                /*  删除cdev */
  187. 185     unregister_chrdev_region(beep.devid, BEEP_CNT);        /* 注销设备号 */
  188. 186
  189. 187     device_destroy(beep.class, beep.devid);
  190. 188     class_destroy(beep.class);
  191. 189}
  192. 190
  193. 191 module_init(beep_init);
  194. 192 module_exit(beep_exit);
  195. 193 MODULE_LICENSE("GPL");
  196. 194 MODULE_AUTHOR("zuozhongkai");
复制代码

        beep.c中的内容和上一章的gpioled.c中的内容基本一样,只是换为了初始化SNVS_TAMPER1这个PIN,这里就不详细的讲解了。
46.3.3编写测试APP
        测试APP在上一章实验的ledApp.c文件的基础上完成,新建名为beepApp.c的文件,然后输入如下所示内容:
示例代码46.3.3.1 beepApp.c文件
  1. 1  #include "stdio.h"
  2. 2  #include "unistd.h"
  3. 3  #include "sys/types.h"
  4. 4  #include "sys/stat.h"
  5. 5  #include "fcntl.h"
  6. 6  #include "stdlib.h"
  7. 7  #include "string.h"
  8. 8/***************************************************************
  9. 9  Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
  10. 10文件名                : beepApp.c
  11. 11作者        : 左忠凯
  12. 12版本        : V1.0
  13. 13描述        : beep测试APP。
  14. 14其他        : 无
  15. 15使用方法                :./beepApp /dev/beep  0 关闭蜂鸣器
  16. 16          ./beepApp /dev/beep  1 打开蜂鸣器
  17. 17论坛        : <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  18. 18 日志        : 初版V1.0 2019/7/15左忠凯创建
  19. 19 ***************************************************************/
  20. 20
  21. 21 #define BEEPOFF          0
  22. 22 #define BEEPON           1
  23. 23
  24. 24/*
  25. 25  * @description          : main主程序
  26. 26  * @param - argc         : argv数组元素个数
  27. 27  * @param - argv         : 具体参数
  28. 28  * @return                : 0 成功;其他失败
  29. 29  */
  30. 30int main(int argc,char*argv[])
  31. 31{
  32. 32        int fd, retvalue;
  33. 33        char*filename;
  34. 34        unsignedchar databuf[1];
  35. 35
  36. 36        if(argc !=3){
  37. 37                printf("Error Usage!\r\n");
  38. 38        return-1;
  39. 39        }
  40. 40
  41. 41        filename = argv[1];
  42. 42
  43. 43        /* 打开beep驱动 */
  44. 44        fd = open(filename, O_RDWR);
  45. 45        if(fd <0){
  46. 46        printf("file %s open failed!\r\n", argv[1]);
  47. 47        return-1;
  48. 48        }
  49. 49
  50. 50        databuf[0]= atoi(argv[2]);/* 要执行的操作:打开或关闭 */
  51. 51
  52. 52        /* 向/dev/beep文件写入数据 */
  53. 53        retvalue = write(fd, databuf,sizeof(databuf));
  54. 54        if(retvalue <0){
  55. 55                printf("BEEP Control Failed!\r\n");
  56. 56        close(fd);
  57. 57        return-1;
  58. 58        }
  59. 59
  60. 60        retvalue = close(fd);/* 关闭文件 */
  61. 61        if(retvalue <0){
  62. 62        printf("file %s close failed!\r\n", argv[1]);
  63. 63        return-1;
  64. 64        }
  65. 65        return0;
  66. 66}
复制代码

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

46.4运行测试

46.4.1 编译驱动程序和测试APP
        1、编译驱动程序
        编写Makefile文件,本章实验的Makefile文件和第四十章实验基本一样,只是将obj-m变量的值改为beep.o,Makefile内容如下所示:
示例代码46.4.1.1 Makefile文件
  1. 1  KERNELDIR:= /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
  2. ......
  3. 4  obj-m := beep.o
  4. ......
  5. 11 clean:
  6. 12$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
复制代码

        第4行,设置obj-m变量的值为beep.o。
        输入如下命令编译出驱动模块文件:
make-j32
        编译成功以后就会生成一个名为“beep.ko”的驱动模块文件。
        2、编译测试APP
        输入如下命令编译测试beepApp.c这个测试程序:
  1. arm-linux-gnueabihf-gcc beepApp.c -o beepApp
复制代码

        编译成功以后就会生成beepApp这个应用程序。
46.4.2 运行测试
        将上一小节编译出来的beep.ko和beepApp这两个文件拷贝到rootfs/lib/modules/4.1.15目录中,重启开发板,进入到目录lib/modules/4.1.15中,输入如下命令加载beep.ko驱动模块:
  1. depmod                                //第一次加载驱动的时候需要运行此命令
  2. modprobe beep.ko                //加载驱动
复制代码

        驱动加载成功以后会在终端中输出一些信息,如图46.4.2.1所示:

图46.4.2.1 驱动加载成功以后输出的信息

        从图46.4.2.1可以看出,beep这个节点找到了,并且GPIO5_IO01这个GPIO的编号为129。使用beepApp软件来测试驱动是否工作正常,输入如下命令打开蜂鸣器:
  1. ./beepApp  /dev/beep 1                //打开蜂鸣器
复制代码

        输入上述命令,查看I.MX6U-ALPHA开发板上的蜂鸣器是否有鸣叫,如果鸣叫的话说明驱动工作正常。在输入如下命令关闭蜂鸣器:
  1. ./beepApp  /dev/beep 0                //关闭蜂鸣器
复制代码

        输入上述命令以后观察I.MX6U-ALPHA开发板上的蜂鸣器是否停止鸣叫。如果要卸载驱动的话输入如下命令即可:
  1. rmmodbeep.ko
复制代码

其他的处理并发和竞争的机制,本章我们主要讲解了常用的原子操作、自旋锁、信号量和互斥体。以后我们在编写Linux驱动的时候就会频繁的使用到这几种机制,希望大家能够深入理解这几个常用的机制。

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-26 05:46

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表