硬件:iMX6ULL ALPHA 开发板,蜂鸣器连接 GPIO5_IO01,低电平触发
内核:Linux 4.14
目标:以 platform driver + miscdevice 框架实现蜂鸣器字符设备驱动
一、硬件说明
| 参数 | 值 |
|---|---|
| 引脚 | GPIO5_IO01 |
| 触发电平 | 低电平有效(GPIO 低 → 蜂鸣器响) |
| 驱动方式 | NPN 三极管,GPIO 控制基极 |
二、整体架构与初始化流程
驱动初始化流程图
flowchart TD
A["DTS 节点<br/>compatible = alientek,beep"] --> B[内核创建 platform_device]
B --> C{"of_device_id 匹配<br/>beep_of_match"}
C -->|匹配成功| D[调用 beep_probe]
C -->|无匹配| Z[跳过]
D --> E["devm_kzalloc<br/>分配 beep_dev 结构体"]
E --> F["devm_gpiod_get<br/>申请 GPIO5_IO01<br/>初始逻辑低(蜂鸣器关)"]
F --> G["配置 miscdevice<br/>minor=MISC_DYNAMIC_MINOR<br/>name=beep, fops=&beep_fops"]
G --> H["misc_register<br/>主设备号 10,动态次设备号"]
H --> I["/dev/beep 节点自动创建"]
I --> J["platform_set_drvdata<br/>保存 beep_dev 指针"]
style A fill:#dae8fc
style I fill:#d5e8d4
style Z fill:#f8cecc三、miscdevice 框架介绍
Linux 内核提供了 miscdevice(杂项设备)机制,是对字符设备注册流程的高度封装:
| 对比项 | 手动注册(LED 驱动方式) | miscdevice |
|---|---|---|
| 主设备号 | 自行申请 | 固定为 10 |
| 注册流程 | alloc_chrdev_region + cdev_add + class_create + device_create | misc_register 一行 |
/dev 节点 | 自动创建 | 自动创建 |
| 代码量 | 多 | 少 |
| 适用场景 | 需要自定义主设备号、多设备实例 | 简单单一设备 |
蜂鸣器这类功能简单、只有一个实例的设备,用 miscdevice 更合适。
四、数据结构
struct beep_dev {
struct gpio_desc *gpiod; /* GPIO 描述符,devm_ 自动释放 */
struct miscdevice miscdev; /* misc 设备,自动创建 /dev/beep */
};
相比 LED 驱动,结构体极简:
- 不需要
cdev、class、device、dev_id(miscdevice 内部管理) - 不需要手动映射寄存器(改用
gpiodAPI 操作 GPIO)
数据结构类图
classDiagram
class beep_dev {
+gpio_desc *gpiod
+miscdevice miscdev
}
class miscdevice {
+int minor
+const char *name
+file_operations *fops
+device *this_device
}
class file_operations {
+open()
+write()
+release()
}
class gpio_desc {
<<kernel内部>>
+GPIO5_IO01
+极性: ACTIVE_LOW
}
class file {
+void *private_data
}
beep_dev *-- miscdevice : 内嵌
beep_dev *-- gpio_desc : 持有引用(devm_)
miscdevice --> file_operations : fops 指向
file ..> miscdevice : ①misc框架设 private_data=miscdev
file ..> beep_dev : ②beep_open改 private_data=beep_dev五、gpiod API 与 gpio API 对比
本驱动使用新版 gpiod(GPIO descriptor)API,相比旧版 gpio API 有明显优势:
| 对比项 | 旧版 gpio API | 新版 gpiod API |
|---|---|---|
| 获取 GPIO | of_get_named_gpio | devm_gpiod_get |
| 设置方向 | gpio_direction_output | 在 devm_gpiod_get 时指定 |
| 设置电平 | gpio_set_value(物理电平) | gpiod_set_value(逻辑电平) |
| 极性处理 | 手动判断高低 | 自动根据 DTS GPIO_ACTIVE_LOW/HIGH 转换 |
| 资源释放 | 手动 gpio_free | devm_ 自动释放 |
逻辑电平的意义:DTS 中声明 GPIO_ACTIVE_LOW,则:
gpiod_set_value(gpiod, 1)→ 逻辑高(有效)→ 物理低电平 → 蜂鸣器响gpiod_set_value(gpiod, 0)→ 逻辑低(无效)→ 物理高电平 → 蜂鸣器停
代码无需关心硬件极性,可读性更好。
六、DTS 配置
/* imxplore.dts */
/ {
beep {
compatible = "alientek,beep";
pinctrl-names = "default";
pinctrl-0 = <&beep_pins>;
beep-gpio = <&gpio5 1 GPIO_ACTIVE_LOW>;
status = "okay";
};
};
&iomuxc {
beep_pins: beep_pins {
fsl,pins = <
MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x10b0
>;
};
};
与 LED 驱动不同,beep 可以保留
pinctrl配置,因为 GPIO5_IO01 没有被其他驱动占用。
七、关键代码说明
open/write 调用时序图
sequenceDiagram
participant App as 用户态 App
participant VFS as VFS 层
participant Misc as misc 框架(misc_open)
participant Drv as beep 驱动(beep_open/write)
participant GPIO as gpiod 子系统
Note over App,GPIO: 打开设备
App ->> VFS : open("/dev/beep", O_RDWR)
VFS ->> Misc : misc_open(inode, filp)
Misc ->> Misc : filp->private_data = &miscdev
Misc ->> Drv : beep_fops.open(inode, filp)
Drv ->> Drv : miscdev = filp->private_data
Drv ->> Drv : dev = container_of(miscdev, beep_dev, miscdev)
Drv ->> Drv : filp->private_data = dev
Drv -->> App : fd 返回
Note over App,GPIO: 控制蜂鸣器(写 "1" 开响)
App ->> VFS : write(fd, "1", 1)
VFS ->> Drv : beep_fops.write(filp, buf, count, ppos)
Drv ->> Drv : dev = filp->private_data
Drv ->> Drv : copy_from_user → val = 1
Drv ->> GPIO : gpiod_set_value(dev->gpiod, 1)
GPIO ->> GPIO : 逻辑 1 + ACTIVE_LOW → 物理低电平
GPIO -->> App : 蜂鸣器响
Note over App,GPIO: 关闭蜂鸣器(写 "0")
App ->> VFS : write(fd, "0", 1)
VFS ->> Drv : beep_fops.write(...)
Drv ->> GPIO : gpiod_set_value(dev->gpiod, 0)
GPIO ->> GPIO : 逻辑 0 + ACTIVE_LOW → 物理高电平
GPIO -->> App : 蜂鸣器停7.1 open:通过 miscdevice 获取 beep_dev
static int beep_open(struct inode *inode, struct file *filp)
{
/*
* misc 框架在 open 时将 filp->private_data 设置为 miscdevice 指针,
* 用 container_of 反推出外层 beep_dev 结构体。
* 与 LED 驱动的 container_of(inode->i_cdev, ...) 思路相同,入口不同。
*/
struct miscdevice *miscdev = filp->private_data;
struct beep_dev *dev = container_of(miscdev, struct beep_dev, miscdev);
filp->private_data = dev;
return 0;
}
7.2 probe:devm_gpiod_get 一步完成申请+方向设置
static int beep_probe(struct platform_device *pdev)
{
struct beep_dev *dev;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
/*
* "beep" 对应 DTS 属性 "beep-gpio"
* GPIOD_OUT_LOW = 初始逻辑低 = 物理高 = 蜂鸣器关闭
*/
dev->gpiod = devm_gpiod_get(&pdev->dev, "beep", GPIOD_OUT_LOW);
dev->miscdev.minor = MISC_DYNAMIC_MINOR;
dev->miscdev.name = "beep";
dev->miscdev.fops = &beep_fops;
misc_register(&dev->miscdev);
platform_set_drvdata(pdev, dev);
return 0;
}
八、用户态测试程序
测试程序位于 app/beep_test/beep_test.c:
beep_test on # 持续响
beep_test off # 关闭
beep_test beep # 响一声(默认 200ms)
beep_test beep 500 # 响一声 500ms
beep_test beep 100 3 # 响三声,每声 100ms,间隔 100ms
九、关于 PWM 音量控制
iMX6ULL ALPHA 板使用有源蜂鸣器,内置振荡器,通电即响,音调固定。
- PWM 占空比可影响平均电压,理论上能微调音量,但效果不明显
- GPIO5_IO01 不是硬件 PWM 引脚,如需 PWM 控制需改接线
- 作为报警器使用时,on/off 控制完全够用
关联笔记
- LED_Platform驱动开发 — 同为 platform driver,对比 miscdevice 与手动 cdev 注册
- 平台设备驱动模型 — platform driver 完整框架讲解
- iMX6ULL环境数据采集站 — 蜂鸣器是采集站项目第一阶段组件
延伸阅读
蜂鸣器驱动同时涉及 Platform 框架和 PWM 能力,可按这两条线继续深入。
- 返回嵌入式 Linux 学习路线,确认本文在完整知识结构中的位置。
- Linux Platform 设备驱动模型
- PWM 子系统与 Host 驱动开发




