1. PWM 子系统概述
1.1 PWM(脉冲宽度调制)的基本原理
脉冲宽度调制(PWM)是一种通过调节固定频率方波的占空比来模拟模拟信号电平的数字调制技术。其核心参数包括:
| 参数 | 符号 | 定义 | 典型范围 |
|---|---|---|---|
| 周期 | T | 一个完整脉冲序列的时间长度 | 1 µs ~ 1 s |
| 频率 | f = 1/T | 每秒脉冲周期数 | 1 Hz ~ 1 MHz |
| 占空比 | D = t_on / T | 高电平持续时间与周期的比值 | 0% ~ 100% |
| 分辨率 | N | 占空比可调节的步进级数 | 8 bit ~ 16 bit |
关键公式: 输出电压平均值 Vavg = Vhigh × D,其中 Vhigh 为高电平电压,D 为占空比。
下图展示了 PWM 信号在不同占空比下的波形对比:
1.2 PWM 在嵌入式系统中的应用场景
PWM 在嵌入式系统中应用极为广泛,覆盖从电机控制到人机交互的多个领域:
- 电机调速: 直流电机、步进电机、无刷直流电机(BLDC)的速度与扭矩控制。
- LED 调光: 背光亮度调节、RGB 色彩混合、指示灯呼吸效果。
- 音频输出: 通过低通滤波器将 PWM 转换为模拟音频信号。
- 加热控制: 加热丝、热敏打印头的功率调节。
- 伺服控制: 舵机角度控制(标准周期 20 ms,占空比 1~2 ms)。
- 电源管理: DC-DC 转换器开关控制、电池充电管理。
性能关注点: 在嵌入式 Linux 启动阶段,PWM 控制器的注册延迟会直接影响外设初始化时序,进而影响系统整体启动时间。对于需要快速进入工作状态的设备(如摄像头云台、无人机电调),PWM 子系统的启动效率尤为关键。
1.3 内核 PWM 子系统的设计目标与架构概览
Linux 内核 PWM 子系统(drivers/pwm/)的设计目标包括:
| 设计目标 | 说明 |
|---|---|
| 硬件抽象 | 屏蔽不同 SoC 的 PWM 控制器差异,提供统一的 API 接口 |
| 设备树驱动 | 通过设备树(Device Tree)描述 PWM 控制器和消费者关系 |
| 引用计数 | 支持多个消费者共享同一 PWM 通道时的资源管理 |
| 原子操作 | 提供可在中断上下文使用的快速配置接口 |
| 延迟优化 | 减少控制器注册、通道申请、参数配置过程中的时间开销 |
PWM 子系统的软件架构分为三层:
PWM 核心层提供的关键数据结构包括:
// pwm.h - 核心数据结构
struct pwm_chip {
struct device *dev;
const struct pwm_ops *ops;
struct pwm_device *pwms;
unsigned int npwm; // 通道数量
struct pwm_device * (*of_xlate)(struct pwm_chip *chip,
const struct of_phandle_args *args);
};
struct pwm_device {
struct pwm_chip *chip;
unsigned int hwpwm; // 硬件通道索引
struct pwm_state state; // 当前状态
struct list_head node; // 链表节点
};
struct pwm_ops {
int (*request)(struct pwm_chip *chip, struct pwm_device *pwm);
int (*free)(struct pwm_chip *chip, struct pwm_device *pwm);
int (*config)(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns);
int (*set_polarity)(struct pwm_chip *chip, struct pwm_device *pwm,
enum pwm_polarity polarity);
int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm);
void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm);
};
启动阶段关键路径: 在系统启动时,每个 PWM 控制器驱动通过
pwmchip_add() 向核心层注册。该函数内部会分配 pwm_device 数组、初始化链表、创建设备文件节点。注册耗时与 npwm(通道数)和底层硬件初始化复杂度直接相关,是启动优化的重点对象。
PWM 子系统的设备树绑定示例:
// 设备树节点示例 (imx6qdl.dtsi)
pwm1: pwm@02080000 {
compatible = "fsl,imx6q-pwm", "fsl,imx27-pwm";
reg = <0x02080000 0x4000>;
interrupts = <0 83 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6QDL_CLK_PWM1>,
<&clks IMX6QDL_CLK_PWM1>;
clock-names = "ipg", "per";
#pwm-cells = <2>;
status = "disabled";
};