
🎬 渡水无言 :个人主页渡水无言
❄专栏传送门 : 《linux专栏》《嵌入式linux驱动开发》《linux系统移植专栏》
❄专栏传送门 : 《freertos专栏》 《STM32 HAL库专栏》
⭐️流水不争先,争的是滔滔不绝
📚博主简介:第二十届中国研究生电子设计竞赛全国二等奖 |国家奖学金 | 省级三好学生
| 省级优秀毕业生获得者 | csdn新星杯TOP18 | 半导纵横专栏博主 | 211在读研究生
在这里主要分享自己学习的linux嵌入式领域知识;有分享错误或者不足的地方欢迎大佬指导,也欢迎各位大佬互相三连
目录
[3.1.1、添加 pinctrl 节点(配置 PIN 复用)](#3.1.1、添加 pinctrl 节点(配置 PIN 复用))
[3.1.3、检查 PIN 是否被其他外设占用](#3.1.3、检查 PIN 是否被其他外设占用)
[设备结构体设计(对应代码第 33--42 行)](#设备结构体设计(对应代码第 33–42 行))
[私有数据传递(对应代码第 54 行)](#私有数据传递(对应代码第 54 行))
[write 函数(第 66--87 行,核心控制逻辑)](#write 函数(第 66–87 行,核心控制逻辑))
[驱动入口函数 beep_init(第 112--170 行,驱动初始化)](#驱动入口函数 beep_init(第 112–170 行,驱动初始化))
[驱动出口函数 beep_exit(第 177--186 行,资源释放)](#驱动出口函数 beep_exit(第 177–186 行,资源释放))
[4.1、编译驱动程序和测试 APP](#4.1、编译驱动程序和测试 APP)
前言
上一章我们通过pinctrl和GPIO子系统完成了 LED 灯驱动开发,而蜂鸣器驱动的核心逻辑和 LED 完全一致 ------ 都是控制 GPIO 输出高低电平。本章将以蜂鸣器为例,巩固 pinctrl+GPIO 子系统的使用方法,同时熟悉不同 GPIO 引脚(SNVS_TAMPER1)的设备树配置和驱动适配。
一、硬件原理分析

- 该电路使用 PNP 型三极管 8550 驱动蜂鸣器,由
SNVS_TAMPER1引脚控制。 - 当
SNVS_TAMPER1输出低电平时,三极管导通,蜂鸣器通电鸣叫。 - 当
SNVS_TAMPER1输出高电平时,三极管截止,蜂鸣器断电停止鸣叫
实验硬件:I.MX6ULL-ALPHA 开发板;
内核版本:4.1.15;
硬件原理如上图。
二、蜂鸣器驱动核心原理
蜂鸣器驱动的开发流程和 LED 完全一致,核心三步:
1、设备树中添加 SNVS_TAMPER1 引脚的 pinctrl 复用配置;
2、设备树中创建蜂鸣器节点,绑定 GPIO 信息;
3、基于字符设备驱动框架编写驱动 + 测试 APP(复用 LED 驱动逻辑)。
三、实验程序编写
3.1、设备树修改总流程

3.1、修改设备树文件
LED 灯使用SNVS_TAMPER1引脚(),需在设备树中完成 3 项配置:
添加 pinctrl 节点。
添加beep 设备节点。
检查引脚冲突。
3.1.1、添加 pinctrl 节点(配置 PIN 复用)
打开开发板设备树imx6ull-alientek-emmc.dts,在&iomuxc节点的imx6ul-evk子节点下,新增pinctrl_beep子节点,配置 SNVS_TAMPER1 的复用和电气属性
pinctrl_beep: beepgrp {
fsl,pins = <
MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x10B0 /* beep:复用为GPIO5_IO01 */
>;
};
MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01:SNVS_TAMPER1 引脚复用为 GPIO5_IO01(该宏定义在imx6ull-pinfunc-snvs.h中);0x10B0:电气属性值(上下拉、驱动能力等,和 LED 配置一致)。
3.1.2、添加蜂鸣器设备节点
在设备树根节点/下创建beep节点,绑定 pinctrl 和 GPIO 信息:
beep {
#address-cells = <1>;
#size-cells = <1>;
compatible = "atkalpha-beep"; /* 驱动匹配标识,自定义 */
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_beep>; /* 绑定pinctrl节点 */
beep-gpio = <&gpio5 1 GPIO_ACTIVE_HIGH>; /* GPIO5_IO01,默认高电平(不叫) */
status = "okay";
};
pinctrl-0 = <&pinctrl_beep>:关联上一步创建的 pinctrl 节点,内核自动初始化 PIN;
beep-gpio = <&gpio5 1 GPIO_ACTIVE_HIGH>:指定蜂鸣器使用 GPIO5 第 1 号引脚,高电平触发(需和硬件逻辑匹配)。
3.1.3、检查 PIN 是否被其他外设占用
和 LED 驱动一样,需检查 SNVS_TAMPER1 引脚是否被其他外设占用:
搜索SNVS_TAMPER1,确认无其他 pinctrl 节点使用该引脚;
搜索gpio5 1,确认 GPIO5_IO01 未被其他设备节点占用;
若有冲突,屏蔽对应配置行(加/* */)。
3.2、编译设备树
设备树编写完成以后使用如下命令重新编译设备树:
make dtbs
再把新生成的imx6ull-alientek-emmc.dtb 文件复制到tftp里,使用如下命令:
sudo cp arch /arm/boot/dts/imx6ull-alientek-emmc.dtb/home/duan/linux/tftp/ -f
然后使用新编译出来的 imx6ull-alientek-emmc.dtb 文件启动 Linux 系统。
启动成功以后进入"/proc/device-tree"目录中 查看"gpioled"节点是否存在,使用如下命令:
cd /proc/device-tree
如下图所示:

可以看到beep节点是存在的。
3.3、蜂鸣器 驱动程序编写
设备树准备好以后就可以编写驱动程序了,本期实验是在之前博客的led实验基础上修改而来。新建名为"5_gpioled"文件夹,然后在 5_gpioled 工程创建好以后新建 gpioled.c 文件,驱动程序基于字符设备驱动框架,核心是通过 GPIO 子系统 API 操作 GPIO,无需直接操作寄存器。
3.3.1、驱动程序执行流程图

3.3.2、驱动代码
1 #include <linux/types.h> // 基本类型定义
2 #include <linux/kernel.h> // 内核核心函数(printk)
3 #include <linux/delay.h> // 延时函数
4 #include <linux/ide.h> // IDE相关宏
5 #include <linux/init.h> // 模块初始化/退出宏
6 #include <linux/module.h> // 模块核心头文件
7 #include <linux/errno.h> // 错误码定义
8 #include <linux/gpio.h> // GPIO子系统核心头文件
9 #include <linux/cdev.h> // 字符设备驱动核心头文件
10 #include <linux/device.h> // 设备类/设备节点创建
11 #include <linux/of.h> // 设备树核心头文件
12 #include <linux/of_address.h> // 设备树地址解析
13 #include <linux/of_gpio.h> // 设备树GPIO解析函数
14 #include <asm/mach/map.h> // 内存映射
15 #include <asm/uaccess.h> // 用户/内核空间数据拷贝
16 #include <asm/io.h> // IO操作函数
17
18 /***************************************************************
19 Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
22 版本 : V1.0
23 描述 : 蜂鸣器驱动程序。
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 : 设备文件,保存私有数据
50 * @return : 0 成功;其他 失败
51 */
52 static int beep_open(struct inode *inode, struct file *filp)
53 {
54 filp->private_data = &beep; /* 绑定设备结构体到文件私有数据 */
55 return 0;
56 }
57
58 /*
59 * @description : 向设备写数据(核心:控制蜂鸣器开关)
60 * @param - filp : 设备文件
61 * @param - buf : 用户空间写入的数据
62 * @param - cnt : 数据长度
63 * @param - offt : 文件偏移
64 * @return : 0 成功;负值 失败
65 */
66 static ssize_t beep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
67 {
68 int retvalue;
69 unsigned char databuf[1];
70 unsigned char beepstat;
71 struct beep_dev *dev = filp->private_data; /* 获取设备结构体 */
72
73 /* 从用户空间拷贝数据到内核空间 */
74 retvalue = copy_from_user(databuf, buf, cnt);
75 if (retvalue < 0) {
76 printk("kernel write failed!\r\n");
77 return -EFAULT;
78 }
79
80 beepstat = databuf[0]; /* 获取蜂鸣器状态(1=开,0=关) */
81 if (beepstat == BEEPON) {
82 gpio_set_value(dev->beep_gpio, 0); /* 打开蜂鸣器(根据硬件逻辑调整电平) */
83 } else if (beepstat == BEEPOFF) {
84 gpio_set_value(dev->beep_gpio, 1); /* 关闭蜂鸣器 */
85 }
86 return 0;
87 }
88
89 /*
90 * @description : 关闭/释放设备
91 * @param - filp : 要关闭的设备文件
92 * @return : 0 成功;其他 失败
93 */
94 static int beep_release(struct inode *inode, struct file *filp)
95 {
96 return 0;
97 }
98
99 /* 设备操作函数集 */
100 static struct file_operations beep_fops = {
101 .owner = THIS_MODULE,
102 .open = beep_open,
103 .write = beep_write,
104 .release = beep_release,
105 };
106
107 /*
108 * @description : 驱动入口函数(加载驱动时执行)
109 * @param : 无
110 * @return : 0 成功;负值 失败
111 */
112 static int __init beep_init(void)
113 {
114 int ret = 0;
115
116 /* 1、获取设备树节点:beep */
117 beep.nd = of_find_node_by_path("/beep");
118 if (beep.nd == NULL) {
119 printk("beep node not find!\r\n");
120 return -EINVAL;
121 } else {
122 printk("beep node find!\r\n");
123 }
124
125 /* 2、解析设备树的beep-gpio属性,获取GPIO编号 */
126 beep.beep_gpio = of_get_named_gpio(beep.nd, "beep-gpio", 0);
127 if (beep.beep_gpio < 0) {
128 printk("can't get beep-gpio\r\n");
129 return -EINVAL;
130 }
131 printk("beep-gpio num = %d\r\n", beep.beep_gpio);
132
133 /* 3、配置GPIO为输出模式,默认高电平(关闭蜂鸣器) */
134 ret = gpio_direction_output(beep.beep_gpio, 1);
135 if (ret < 0) {
136 printk("can't set gpio!\r\n");
137 }
138
139 /* 4、注册字符设备驱动(标准流程) */
140 /* 4.1 创建设备号(手动/自动分配) */
141 if (beep.major) {
142 beep.devid = MKDEV(beep.major, 0);
143 register_chrdev_region(beep.devid, BEEP_CNT, BEEP_NAME);
144 } else {
145 alloc_chrdev_region(&beep.devid, 0, BEEP_CNT, BEEP_NAME);
146 beep.major = MAJOR(beep.devid);
147 beep.minor = MINOR(beep.devid);
148 }
149 printk("beep major=%d,minor=%d\r\n", beep.major, beep.minor);
150
151 /* 4.2 初始化cdev */
152 beep.cdev.owner = THIS_MODULE;
153 cdev_init(&beep.cdev, &beep_fops);
154
155 /* 4.3 添加cdev到内核 */
156 cdev_add(&beep.cdev, beep.devid, BEEP_CNT);
157
158 /* 4.4 创建类 */
159 beep.class = class_create(THIS_MODULE, BEEP_NAME);
160 if (IS_ERR(beep.class)) {
161 return PTR_ERR(beep.class);
162 }
163
164 /* 4.5 创建设备(生成/dev/beep节点) */
165 beep.device = device_create(beep.class, NULL, beep.devid, NULL, BEEP_NAME);
166 if (IS_ERR(beep.device)) {
167 return PTR_ERR(beep.device);
168 }
169 return 0;
170 }
171
172 /*
173 * @description : 驱动出口函数(卸载驱动时执行)
174 * @param : 无
175 * @return : 无
176 */
177 static void __exit beep_exit(void)
178 {
179 /* 注销字符设备驱动 */
180 cdev_del(&beep.cdev);
181 unregister_chrdev_region(beep.devid, BEEP_CNT);
182
183 /* 销毁设备和类 */
184 device_destroy(beep.class, beep.devid);
185 class_destroy(beep.class);
186 }
187
188 module_init(beep_init);
189 module_exit(beep_exit);
190 MODULE_LICENSE("GPL");
191 MODULE_AUTHOR("zuozhongkai");
| 行号范围 | 核心功能 | 关键说明 |
|---|---|---|
| 33--42 | 设备结构体定义 | 封装驱动所有资源,Linux 驱动核心设计 |
| 54 | 绑定私有数据 | 传递设备上下文,替代全局变量 |
| 74 | 内核 / 用户空间数据拷贝 | 必须用 copy_from_user,禁止直接访问用户指针 |
| 82/84 | GPIO 电平控制 | GPIO 子系统 API,无需操作寄存器 |
| 117 | 获取设备树节点 | of_find_node_by_path 匹配设备树节点 |
| 126 | 解析 GPIO 属性 | of_get_named_gpio 转换 GPIO 编号 |
| 134 | 配置 GPIO 输出 | gpio_direction_output 设置输出模式 |
| 141--168 | 字符设备注册流程 | 创建设备号→初始化 cdev→创建类 / 设备 |
| 177--186 | 资源释放 | 与 init 反向操作,避免内存泄漏 |
| 188--191 | 模块注册 + 协议声明 | 内核识别驱动的关键,必须声明 GPL 协议 |
3.3.3、分析驱动代码
设备结构体设计(对应代码第 33--42 行)
在设备结构体beep_dev中加入 beep_gpio 这个成员变量,此成员变量保存蜂鸣器 所使用的 GPIO 编号。
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 };
私有数据传递(对应代码第 54 行)
第 54 行,将设备结构体变量beep 设置为 filp 的私有数据 private_data。
filp->private_data = &beep;
这种将设备结构体设置为 filp 私有数据的方法在 Linux 内核驱动里面非常常见,方便 write 里取用,这样在后续操作函数中方便地获取设备上下文,避免使用全局变量。
write 函数(第 66--87 行,核心控制逻辑)
66 static ssize_t beep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
67 {
68 int retvalue;
69 unsigned char databuf[1];
70 unsigned char beepstat;
71 struct beep_dev *dev = filp->private_data; /* 获取设备结构体 */
72
73 /* 从用户空间拷贝数据到内核空间 */
74 retvalue = copy_from_user(databuf, buf, cnt);
75 if (retvalue < 0) {
76 printk("kernel write failed!\r\n");
77 return -EFAULT;
78 }
79
80 beepstat = databuf[0]; /* 获取蜂鸣器状态(1=开,0=关) */
81 if (beepstat == BEEPON) {
82 gpio_set_value(dev->beep_gpio, 0); /* 打开蜂鸣器 */
83 } else if (beepstat == BEEPOFF) {
84 gpio_set_value(dev->beep_gpio, 1); /* 关闭蜂鸣器 */
85 }
86 return 0;
87 }
第 71 行:从filp->private_data恢复设备结构体指针,是open函数的反向操作,实现上下文传递。
第 74 行:使用copy_from_user拷贝用户空间数据到内核空间 ------ 这是 Linux 内核的强制规范,禁止直接访问用户空间指针(避免内存越界、权限问题)。
第 82/84 行:通过gpio_set_value控制 GPIO 电平,完全基于 GPIO 子系统 API,无需操作底层寄存器。
注意:电平值(0/1)需根据硬件原理图调整,本实验中蜂鸣器为(低电平触发鸣叫,高电平关闭)。
驱动入口函数 beep_init(第 112--170 行,驱动初始化)
112 static int __init beep_init(void)
113 {
114 int ret = 0;
115
116 /* 1、获取设备树节点:beep */
117 beep.nd = of_find_node_by_path("/beep");
118 if (beep.nd == NULL) {
119 printk("beep node not find!\r\n");
120 return -EINVAL;
121 } else {
122 printk("beep node find!\r\n");
123 }
124
125 /* 2、解析设备树的beep-gpio属性,获取GPIO编号 */
126 beep.beep_gpio = of_get_named_gpio(beep.nd, "beep-gpio", 0);
127 if (beep.beep_gpio < 0) {
128 printk("can't get beep-gpio\r\n");
129 return -EINVAL;
130 }
131 printk("beep-gpio num = %d\r\n", beep.beep_gpio);
132
133 /* 3、配置GPIO为输出模式,默认高电平(关闭蜂鸣器) */
134 ret = gpio_direction_output(beep.beep_gpio, 1);
135 if (ret < 0) {
136 printk("can't set gpio!\r\n");
137 }
138
139 /* 4、注册字符设备驱动(标准流程) */
140 /* 4.1 创建设备号(手动/自动分配) */
141 if (beep.major) {
142 beep.devid = MKDEV(beep.major, 0);
143 register_chrdev_region(beep.devid, BEEP_CNT, BEEP_NAME);
144 } else {
145 alloc_chrdev_region(&beep.devid, 0, BEEP_CNT, BEEP_NAME);
146 beep.major = MAJOR(beep.devid);
147 beep.minor = MINOR(beep.devid);
148 }
149 printk("beep major=%d,minor=%d\r\n", beep.major, beep.minor);
150
151 /* 4.2 初始化cdev */
152 beep.cdev.owner = THIS_MODULE;
153 cdev_init(&beep.cdev, &beep_fops);
154
155 /* 4.3 添加cdev到内核 */
156 cdev_add(&beep.cdev, beep.devid, BEEP_CNT);
157
158 /* 4.4 创建类 */
159 beep.class = class_create(THIS_MODULE, BEEP_NAME);
160 if (IS_ERR(beep.class)) {
161 return PTR_ERR(beep.class);
162 }
163
164 /* 4.5 创建设备(生成/dev/beep节点) */
165 beep.device = device_create(beep.class, NULL, beep.devid, NULL, BEEP_NAME);
166 if (IS_ERR(beep.device)) {
167 return PTR_ERR(beep.device);
168 }
169 return 0;
170 }
设备树解析(第 117/126 行):
第 117 行of_find_node_by_path("/beep"):通过绝对路径找到设备树中定义的beep节点,是驱动与设备树绑定的第一步;
第 126 行of_get_named_gpio(beep.nd, "beep-gpio", 0):解析设备树中beep-gpio属性,将<&gpio5 1 GPIO_ACTIVE_HIGH>转换为内核可识别的 GPIO 编号(本实验中为 129)。
GPIO 初始化(第 134 行):gpio_direction_output将 GPIO 配置为输出模式,并设置默认电平为高(关闭蜂鸣器),替代了裸机开发中直接配置GPIOx_GDIR寄存器的操作。
字符设备注册(第 141--168 行):遵循 Linux 字符设备驱动的标准流程 ------ 创建设备号→初始化 cdev→添加 cdev→创建类→创建设备,最终自动生成/dev/beep设备节点,用户层可直接操作。
驱动出口函数 beep_exit(第 177--186 行,资源释放)
177 static void __exit beep_exit(void)
178 {
179 /* 注销字符设备驱动 */
180 cdev_del(&beep.cdev);
181 unregister_chrdev_region(beep.devid, BEEP_CNT);
182
183 /* 销毁设备和类 */
184 device_destroy(beep.class, beep.devid);
185 class_destroy(beep.class);
186 }
核心原则:与beep_init的操作完全反向,确保驱动卸载时释放所有占用的内核资源(设备号、cdev、类、设备节点),避免内存泄漏。
第 180 行:删除 cdev 结构体,注销字符设备;
第 181 行:释放申请的设备号;
第 184 行:销毁/dev/beep设备节点;
第 185 行:销毁设备类。
无 GPIO 释放:GPIO 子系统会自动管理 GPIO 资源,无需手动释放,体现了内核子系统的「自动化管理」优势。
3.4、测试APP
3.4.1测试APP执行流程图

3.4.2、程序编写
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名 : beepApp.c
描述 : beep测试APP。
其他 : 无
使用方法 :./beepApp /dev/beep 0 关闭蜂鸣器
./beepApp /dev/beep 1 打开蜂鸣器
***************************************************************/
#define BEEPOFF 0
#define BEEPON 1
/*
* @description : main主程序
* @param - argc : argv数组元素个数
* @param - argv : 具体参数
* @return : 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{
int fd, retvalue;
char *filename;
unsigned char databuf[1];
if(argc != 3){
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
/* 打开beep驱动 */
fd = open(filename, O_RDWR);
if(fd < 0){
printf("file %s open failed!\r\n", argv[1]);
return -1;
}
databuf[0] = atoi(argv[2]); /* 要执行的操作:打开或关闭 */
/* 向/dev/beep文件写入数据 */
retvalue = write(fd, databuf, sizeof(databuf));
if(retvalue < 0){
printf("BEEP Control Failed!\r\n");
close(fd);
return -1;
}
retvalue = close(fd); /* 关闭文件 */
if(retvalue < 0){
printf("file %s close failed!\r\n", argv[1]);
return -1;
}
return 0;
}
测试 APP 代码和 LED 几乎一样,仅修改命名和提示信息,核心逻辑为:
校验命令行参数(必须传入设备路径和控制指令);
打开/dev/beep设备文件;
向驱动写入控制指令(1 = 开,0 = 关);
关闭设备文件。
四、运行测试
4.1、编译驱动程序和测试 APP
编写 Makefile 文件,本次实验的 Makefile 文件和之前的led实验基本一样,只是将 obj-m 变量的值改为beep.o,Makefile 内容如下所示:
KERNELDIR := /home/duan/linux/linux-imx-rel_imx_4.1.15_2.1.1_ga_alientek_v2.2
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
输入如下命令编译出驱动模块文件:
make -j32
编译成功以后就会生成一个名为"beep.ko"的驱动模块文件。
编译测试 APP
输入如下命令编译测试beepApp.c 这个测试程序:
arm-linux-gnueabihf-gcc beepApp.c -o beepApp
4.2、运行测试
将上一小节编译出来的beep.ko和beepApp 这两个文件拷贝到 rootfs/lib/modules/4.1.15 目录中。
sudo cp beep.ko /home/duan/linux/nfs/rootfs/lib/modules/4.1.15/ -f
sudo cp beepApp /home/duan/linux/nfs/rootfs/lib/modules/4.1.15/ -f
进入到目录 lib/modules/4.1.15 中,输入如下命令加载beep.ko 驱动模块:
depmod //第一次加载驱动的时候需要运行此命令
modprobe beep.ko //加载驱动
驱动加载成功以后会在终端中输出一些信息,如下图所示:

从上图可以看出,beep 这个节点找到了,并且 GPIO5_IO01 这个 GPIO 的编号为 129。
使用 beepApp 软件来测试驱动是否工作正常,输入如下命令打开蜂鸣器:
./beepApp /dev/beep 1 //打开蜂鸣器
输入上述命令,查看 I.MX6U-ALPHA 开发板上的蜂鸣器是否有鸣叫,如果鸣叫的话说明驱动工作正常。在输入如下命令关闭蜂鸣器:
./beepApp /dev/beep 0 //关闭蜂鸣器
输入上述命令以后观察 I.MX6U-ALPHA 开发板上的蜂鸣器是否停止鸣叫。如果要卸载驱动的话输入如下命令即可:
rmmod beep.ko
总结
本期博客完成了基于 pinctrl+GPIO 子系统的蜂鸣器的实验。