1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-318813-1-1.html 4)正点原子官方B站:https://space.bilibili.com/394620890 5)正点原子STM32MP157技术交流群:691905614
上一章实验中我们借助gpio子系统编写了LED灯驱动,STM32MP1开发板上还有一个蜂鸣器,从软件的角度考虑,蜂鸣器驱动和LED灯驱动其实是一摸一样的,都是控制IO输出高低电平。本章我们就来学习编写蜂鸣器的Linux驱动,也算是对上一章讲解的gpio子系统的巩固。
26.1 蜂鸣器驱动原理 蜂鸣器常用于计算机、打印机、报警器、电子玩具等电子产品中,常用的蜂鸣器有两种:有源蜂鸣器和无源蜂鸣器,这里的有“源”不是电源,而是震荡源,有源蜂鸣器内部带有震荡源,所以有源蜂鸣器只要通电就会叫。无源蜂鸣器内部不带震荡源,直接用直流电是驱动不起来的,需要2K-5K的方波去驱动。正点原子STM32MP1开发板使用的是有源蜂鸣器,因此只要给其供电就会工作,开发板所使用的有源蜂鸣器如下图所示:
图26.1.1 有源蜂鸣器 有源蜂鸣器只要通电就会叫,所以我们可以做一个供电电路,这个供电电路通过一个IO来控制其通断,一般使用三极管来搭建这个电路。为什么我们不能像控制LED灯一样,直接将GPIO接到蜂鸣器的负极,通过IO输出高低来控制蜂鸣器的通断。这事因为蜂鸣器工作的电流比LED灯要大,直接将蜂鸣器接到开发板的GPIO上有可能会烧毁IO,所以我们需要通过一个三极管来间接的控制蜂鸣器的通断,相当于加了一层隔离。本章我们就驱动开发板上的有源蜂鸣器,然后编写简单的测试APP,通过APP来控制蜂鸣器鸣叫或关闭。 本节我们来看一下如果在Linux下编写蜂鸣器驱动需要做哪些工作: ①、在设备树中创建蜂鸣器节点,在蜂鸣器节点中加入GPIO信息。 ②、编写驱动程序和测试APP,和第二十五章的LED驱动程序和测试APP基本一样。 接下来我们就根据上面这两步来编写蜂鸣器Linux驱动。 26.2 硬件原理图分析 蜂鸣器的硬件原理图如图 26.2.1 所示:
图26.2.1 蜂鸣器原理图 图26.2.1中通过一个 PNP 型的三极管 8550 来驱动蜂鸣器,通过PC7这个 IO来控制三极管 Q1 的导通,当 BEEP 输出低电平的时候 Q1 导通,相当于蜂鸣器的正极连接到 3.3V电源,蜂鸣器形成一个通路,因此蜂鸣器会鸣叫。同理,当 BEEP输出高电平的时候 Q1不导通,那么蜂鸣器就没有形成一个通路,因此蜂鸣器也就不会鸣叫。 26.3 实验程序编写 本实验对应的例程路径为:开发板光盘1、程序源码2、Linux驱动例程6_beep。 本章实验在二十五章实验的基础上完成,重点是将驱动改为基于设备树的. 26.3.1 修改设备树文件 在根节点“/”下创建BEEP节点,节点名为“beep”,节点内容如下:
示例代码26.3.1.1 创建BEEP蜂鸣器节点
1 beep {
2 compatible = "alientek,beep";
3 status = "okay";
4 beep-gpio = ;
5 };
第4行,beep-gpio属性指定了蜂鸣器所使用的GPIO。 设备树编写完成以后使用“make dtbs”命令重新编译设备树,然后使用新编译出来的stm32mp157d-atk.dtb文件启动Linux系统。启动成功以后进入“/proc/device-tree”目录中查看“beep”节点是否存在,如果存在的话就说明设备树基本修改成功(具体还要驱动验证),结果如图26.3.1.1所示:
图26.3.1.1 beep节点 26.3.2 蜂鸣器驱动程序编写 设备树准备好以后就可以编写驱动程序了,本章实验在第四十五章实验驱动文件gpioled.c的基础上修改而来。新建名为“6_beep”的文件夹,然后在6_beep文件夹里面创建vscode工程,工作区命名为“beep”。工程创建好以后新建beep.c文件,在beep.c里面输入如下内容:
示例代码 26.3.2.1 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 Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
19 文件名 : beep.c
20 作者 : 正点原子Linux团队
21 版本 : V1.0
22 描述 : 蜂鸣器驱动程序。
23 其他 : 无
24 论坛 : www.openedv.com
25 日志 : 初版V1.0 2020/12/31 正点原子Linux团队创建
26 ***************************************************************/
27 #define BEEP_CNT 1 /* 设备号个数 */
28 #define BEEP_NAME "beep" /* 名字 */
29 #define BEEPOFF 0 /* 关蜂鸣器 */
30 #define BEEPON 1 /* 开蜂鸣器 */
31
32 /* beep设备结构体 */
33 struct beep_dev{
34 dev_t devid; /* 设备号 */
35 struct cdev cdev; /* cdev */
36 struct class *class; /* 类 */
37 struct device *device; /* 设备 */
38 int major; /* 主设备号 */
39 int minor; /* 次设备号 */
40 struct device_node *nd; /* 设备节点 */
41 int beep_gpio; /* beep所使用的GPIO编号 */
42 };
43
44 struct beep_dev beep; /* beep设备 */
45
46 /*
47 * @description : 打开设备
48 * @param – inode : 传递给驱动的inode
49 * @param – filp : 设备文件,file结构体有个叫做private_data的成员变量
50 * 一般在open的时候将private_data指向设备结构体。
51 * @return : 0 成功;其他 失败
52 */
53 static int led_open(struct inode *inode, struct file *filp)
54 {
55 filp->private_data = &beep; /* 设置私有数据 */
56 return 0;
57 }
58
59 /*
60 * @description : 从设备读取数据
61 * @param - filp : 要打开的设备文件(文件描述符)
62 * @param - buf : 返回给用户空间的数据缓冲区
63 * @param - cnt : 要读取的数据长度
64 * @param - offt : 相对于文件首地址的偏移
65 * @return : 读取的字节数,如果为负值,表示读取失败
66 */
67 static ssize_t led_read(struct file *filp, char __user *buf,
size_t cnt, loff_t *offt)
68 {
69 return 0;
70 }
71
72 /*
73 * @description : 向设备写数据
74 * @param - filp : 设备文件,表示打开的文件描述符
75 * @param - buf : 要写给设备写入的数据
76 * @param - cnt : 要写入的数据长度
77 * @param - offt : 相对于文件首地址的偏移
78 * @return : 写入的字节数,如果为负值,表示写入失败
79 */
80 static ssize_t led_write(struct file *filp, const char __user *buf,
size_t cnt, loff_t *offt)
81 {
82 int retvalue;
83 unsigned char databuf[1];
84 unsigned char ledstat;
85 struct beep_dev *dev = filp->private_data;
86
87 retvalue = copy_from_user(databuf, buf, cnt);
88 if(retvalue beep_gpio, 0); /* 打开蜂鸣器 */
97 } else if(ledstat == BEEPOFF) {
98 gpio_set_value(dev->beep_gpio, 1); /* 关闭蜂鸣器 */
99 }
100 return 0;
101 }
102
103 /*
104 * @description : 关闭/释放设备
105 * @param - filp : 要关闭的设备文件(文件描述符)
106 * @return : 0 成功;其他 失败
107 */
108 static int led_release(struct inode *inode, struct file *filp)
109 {
110 return 0;
111 }
112
113 /* 设备操作函数 */
114 static struct file_operations beep_fops = {
115 .owner = THIS_MODULE,
116 .open = led_open,
117 .read = led_read,
118 .write = led_write,
119 .release = led_release,
120 };
121
122 /*
123 * @description : 驱动出口函数
124 * @param : 无
125 * @return : 无
126 */
127 static int __init led_init(void)
128 {
129 int ret = 0;
130 const char *str;
131
132 /* 设置LED所使用的GPIO */
133 /* 1、获取设备节点:beep */
134 beep.nd = of_find_node_by_path("/beep");
135 if(beep.nd == NULL) {
136 printk("beep node not find!\r\n");
137 return -EINVAL;
138 }
139
140 /* 2.读取status属性 */
141 ret = of_property_read_string(beep.nd, "status", &str);
142 if(ret
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?