Linux PWM子系统框架详解
目录
一、引言
在嵌入式Linux系统中,PWM(Pulse Width Modulation,脉冲宽度调制)是一种常用的外设控制方式,广泛应用于LED调光、电机控制、蜂鸣器驱动等场景。Linux内核为PWM提供了统一的子系统接口,使得驱动开发者和应用层可以方便地进行配置和控制。本文将深入分析Linux PWM子系统的整体架构、核心组件、驱动接口和使用方式,帮助开发者全面理解和掌握PWM子系统的开发与调试。
二、PWM基本概念回顾
2.1 PWM定义
PWM是一种通过调节脉冲信号的占空比(duty cycle)来实现模拟信号近似输出的技术,其核心思想是将固定频率的脉冲信号的高电平时间进行调整。
2.2 关键参数
- 周期(Period):完整一个高低电平循环所需时间。
- 占空比(Duty Cycle):一个周期中高电平持续的时间,占总周期的比例。
- 极性(Polarity):表示高电平是否在前。
2.3 应用场景
- LED亮度控制
- 有刷/无刷电机调速
- 蜂鸣器控制
- 信号模拟输出
三、Linux PWM子系统架构
3.1 架构图
用户空间
↓
/sys/class/pwm/ // Sysfs接口
/dev/pwmchipX // Character Device接口(v5.8后)
↓
kernel层
→ pwm-core.c // 核心框架
→ pwm-sysfs.c // sysfs支持
→ pwm-cdev.c // char dev支持
→ 驱动: pwm-xxx.c // 各SoC或平台PWM控制器驱动
→ 硬件PWM控制器 // 实际外设
3.2 主要组成
- PWM core:位于
drivers/pwm/pwm-core.c
,负责统一管理PWM控制器和PWM设备,提供注册、分配、释放、使能、禁用等API。 - PWM chip:表示一个PWM控制器,由驱动程序注册。
- PWM device:具体的PWM通道(channel),是用户可控制的对象。
- Sysfs接口:通过文件系统路径控制PWM设备,如
/sys/class/pwm/pwmchip0/
。 - Char Device接口:新增字符设备节点,支持epoll等操作。
四、PWM核心数据结构详解
4.1 struct pwm_chip
struct pwm_chip {
struct device *dev;
const struct pwm_ops *ops;
int base;
unsigned int npwm;
struct pwm_device *pwms;
struct list_head list;
};
dev
:绑定的device设备ops
:操作函数集合base
:通道基编号(-1表示自动分配)npwm
:控制器支持的PWM通道数
4.2 struct pwm_device
struct pwm_device {
struct pwm_chip *chip;
unsigned int hwpwm;
unsigned int period;
unsigned int duty_cycle;
bool enabled;
unsigned int polarity;
struct pwm_state state;
};
hwpwm
:硬件通道编号state
:当前PWM状态,包含周期、占空比、极性等
4.3 struct pwm_ops
struct pwm_ops {
int (*request)(...);
void (*free)(...);
int (*enable)(...);
void (*disable)(...);
int (*apply)(...); // 推荐使用
struct module *owner;
};
- 各函数用于PWM申请、释放、启动、停止、配置等操作
五、PWM控制器驱动开发
5.1 驱动注册
pwmchip_add(&chip);
- 驱动中定义并初始化
pwm_chip
结构体,然后通过pwmchip_add
向PWM core注册
5.2 实现pwm_ops
static const struct pwm_ops my_pwm_ops = {
.enable = my_pwm_enable,
.disable = my_pwm_disable,
.apply = my_pwm_apply,
.owner = THIS_MODULE,
};
5.3 设备树支持
pwm0: pwm@0 {
compatible = "vendor,pwm";
reg = <...>;
#pwm-cells = <3>; // 通常为period、duty_cycle、polarity
};
驱动中实现.of_xlate()
和.of_parse()
以解析设备树配置。
六、用户空间控制方式
6.1 Sysfs方式
echo 0 > /sys/class/pwm/pwmchip0/export
echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/period
echo 500000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable
6.2 Char Device方式(Linux 5.8+)
int fd = open("/dev/pwmchip0", O_RDWR);
struct pwm_args args = {
.period = 1000000,
.duty_cycle = 500000,
};
ioctl(fd, PWM_IOCTL_SET_CONFIG, &args);
ioctl(fd, PWM_IOCTL_ENABLE);
6.3 Libgpiod/libpwm封装
一些上层库提供更简洁的接口封装,例如libpwm,适用于快速应用开发。
七、内核中PWM子系统API分析
7.1 通道控制API
struct pwm_device *pwm_get(...);
int pwm_apply_state(...);
void pwm_enable(...);
void pwm_disable(...);
void pwm_put(...);
7.2 内核驱动中使用PWM
设备驱动中可通过设备树或平台数据获取PWM资源,然后使用pwm_apply_state
进行控制。
八、实例分析:LED调光驱动
8.1 设备树配置
led-pwm {
compatible = "pwm-led";
pwms = <&pwm0 0 1000000 0>;
};
8.2 驱动核心代码
struct pwm_device *pwm = devm_pwm_get(...);
struct pwm_state state;
pwm_get_state(pwm, &state);
state.period = 1000000;
state.duty_cycle = 300000;
pwm_apply_state(pwm, &state);
pwm_enable(pwm);
九、调试技巧与常见问题
9.1 无法导出pwm
- 检查是否正确注册驱动
- 查看
dmesg
中是否报错
9.2 PWM无波形输出
- 检查频率和占空比是否合理
- 是否成功调用
pwm_enable
- 是否存在引脚复用冲突
9.3 多设备抢占冲突
- 保证独占PWM通道,避免多个设备重复申请
十、总结
Linux PWM子系统提供了完整的抽象和封装,极大地方便了平台相关驱动开发和用户层控制。通过统一的数据结构、标准的API接口、清晰的设备树绑定方式,PWM设备的接入和使用变得更加规范和稳定。随着Linux内核版本的迭代,字符设备接口等新特性也在不断增强其灵活性和功能性。掌握PWM子系统,不仅是深入嵌入式Linux开发的基础,也是实现复杂外设控制的重要一环。