3. Linux PWM子系统架构:PWM核心层、PWM控制器驱动、PWM用户空间接口

在嵌入式Linux系统中,PWM(脉冲宽度调制)背光调节并非由单个驱动文件独立完成,而是依托一套层次分明的子系统架构。理解这套架构是开发稳定、可移植背光驱动的基石。本章将深入剖析Linux PWM子系统的三层模型:PWM核心层、PWM控制器驱动以及PWM用户空间接口,并结合电子书阅读器的背光场景进行实战解读。

架构概览: Linux PWM子系统采用“核心层 + 控制器驱动 + 消费者/用户接口”的分层设计。核心层提供统一的API和数据结构,控制器驱动负责操作硬件寄存器,用户空间接口则允许应用程序或背光驱动直接控制PWM输出。

3.1 PWM子系统三层架构总览

下图展示了PWM子系统的完整软件栈。从底层的硬件寄存器到顶层的用户空间控制,每一层都承担着明确的职责。

Linux PWM 子系统三层架构图 用户空间 / 消费者层 背光驱动 (pwm-backlight) / sysfs 接口 / 用户态程序 通过 pwm_apply_state() 或 sysfs 文件操作 PWM PWM 核心层 (drivers/pwm/core.c) pwm_chip / pwm_device 结构体管理 pwm_apply_state() / pwm_get() / pwm_put() API 设备树解析与 pwmchip 注册/注销 PWM 控制器驱动层 (drivers/pwm/pwm-*.c) pwm_ops 结构体实现:.apply() .get_state() .request() .free() 操作硬件寄存器:设置周期、占空比、极性、使能 例如:pwm-sun4i.c, pwm-imx.c, pwm-rockchip.c 硬件 PWM 定时器 / 寄存器
关键理解: 背光驱动(如 pwm-backlight)属于“消费者层”,它调用核心层提供的通用 API,而核心层再回调具体控制器驱动的 pwm_ops 函数。这种设计使得背光驱动无需关心底层是哪个 SoC 的 PWM 模块,实现了代码复用和硬件解耦。

3.2 PWM核心层:驱动骨架与API

PWM核心层位于 drivers/pwm/core.c,是整个子系统的中枢。它定义了两个核心数据结构:pwm_chip 和 pwm_device。

3.2.1 核心数据结构

数据结构 作用 关键成员
pwm_chip 代表一个PWM控制器硬件实例,管理一组PWM通道 dev (设备指针), ops (操作函数集), npwm (通道数), pwms (pwm_device数组)
pwm_device 代表一个独立的PWM输出通道 chip (所属控制器), pwm (通道索引), state (当前状态), label (使用者标签)
pwm_state 描述PWM输出的完整状态 period (周期ns), duty_cycle (占空比ns), polarity (极性), enabled (使能标志)

3.2.2 核心API函数

核心层向消费者驱动(如背光驱动)提供以下关键API:

  • pwm_get() / pwm_put():获取/释放一个PWM设备的引用。通常基于设备树中的 pwms 属性进行查找。
  • pwm_apply_state():核心函数,用于将 pwm_state 配置应用到硬件。内部会调用控制器驱动的 .apply() 回调。
  • pwm_config():便捷函数,用于设置周期和占空比。
  • pwm_enable() / pwm_disable():使能或禁用PWM输出。
  • pwm_set_polarity():设置PWM极性(正常或反转)。
注意: 在较新的内核版本(如5.x+)中,pwm_config()、pwm_enable() 等旧API已被标记为弃用,推荐统一使用 pwm_apply_state() 进行原子化配置。背光驱动开发时应优先采用新API。

3.3 PWM控制器驱动:硬件操作实现

控制器驱动是真正与硬件寄存器打交道的部分。每个SoC厂商(如全志、NXP、Rockchip)都需要为其PWM模块实现一个驱动,并填充 struct pwm_ops 结构体。

3.3.1 pwm_ops 结构体

struct pwm_ops {
    int (*request)(struct pwm_chip *chip, struct pwm_device *pwm);
    void (*free)(struct pwm_chip *chip, struct pwm_device *pwm);
    int (*apply)(struct pwm_chip *chip, struct pwm_device *pwm,
                 const struct pwm_state *state);
    int (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm,
                     struct pwm_state *state);
    // ... 其他可选回调
};

3.3.2 关键回调函数职责

回调 触发时机 驱动实现要点
.request() 消费者调用 pwm_get() 时 初始化通道时钟、引脚复用,确保硬件可用
.free() 消费者调用 pwm_put() 时 关闭时钟、释放引脚,将通道复位到安全状态
.apply() 消费者调用 pwm_apply_state() 时 根据 pwm_state 设置硬件寄存器:周期、占空比、极性、使能位。这是最核心的回调
.get_state() 系统启动或状态同步时 从硬件寄存器读取当前配置,填充 pwm_state 返回给核心层

3.3.3 控制器驱动注册流程

一个典型的PWM控制器驱动在 probe 函数中完成以下步骤:

  1. 获取硬件资源: 映射寄存器基地址、获取时钟、配置引脚复用。
  2. 初始化 pwm_chip: 设置 npwm(通道数)、ops、dev 等字段。
  3. 调用 pwmchip_add(): 将 pwm_chip 注册到核心层,核心层会为其分配 pwm_device 并加入全局列表。
  4. 设备树匹配: 驱动通过 of_match_table 与设备节点匹配,解析如时钟频率、通道配置等平台数据。
实战提示: 在电子书阅读器项目中,如果使用全志A64或瑞芯微RK3566,对应的驱动文件分别是 pwm-sun4i.c 和 pwm-rockchip.c。阅读这些源码时,重点关注 .apply() 函数中如何计算分频系数和占空比寄存器值。

3.4 PWM用户空间接口:sysfs与背光控制

除了通过内核驱动(如 pwm-backlight)控制PWM,Linux还提供了用户空间接口,方便调试或直接控制。最常用的接口是 sysfs。

3.4.1 sysfs 接口路径

当PWM控制器驱动注册成功后,会在 /sys/class/pwm/ 下创建目录。例如:

/sys/class/pwm/pwmchip0/   # 第一个PWM控制器
├── npwm                    # 该控制器支持的PWM通道数(只读)
├── export                  # 向用户空间导出PWM通道(写通道号)
├── unexport                # 取消导出PWM通道(写通道号)
└── pwm0/                   # 导出后的PWM通道0目录
    ├── period              # 周期(单位:纳秒)
    ├── duty_cycle          # 占空比(单位:纳秒)
    ├── polarity            # 极性:normal 或 inversed
    ├── enable              # 使能:1 开启,0 关闭
    └── power/              # 电源管理相关

3.4.2 用户空间调光示例

以下命令演示了如何通过sysfs直接控制PWM输出,实现背光调节:

# 1. 导出PWM通道0
echo 0 > /sys/class/pwm/pwmchip0/export

# 2. 设置周期为 20ms(50Hz,适合背光)
echo 20000000 > /sys/class/pwm/pwmchip0/pwm0/period

# 3. 设置占空比为 10ms(50% 亮度)
echo 10000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle

# 4. 设置极性(通常为 normal)
echo normal > /sys/class/pwm/pwmchip0/pwm0/polarity

# 5. 使能PWM输出
echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable

# 6. 调节亮度:将占空比改为 2ms(10% 亮度)
echo 2000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle

3.4.3 用户空间接口与背光驱动的协作

在电子书阅读器产品中,通常不会直接通过sysfs控制背光,而是由 pwm-backlight 驱动管理。该驱动在内部调用 pwm_apply_state(),同时也会在 /sys/class/backlight/ 下创建标准的背光接口(如 brightness、max_brightness)。用户空间的调光服务(如 Android 的 Light HAL 或 Weston 的背光插件)通过操作这些标准接口,间接控制PWM。

架构优势总结:
  • 解耦: 背光驱动无需知道PWM寄存器细节,控制器驱动无需关心背光曲线。
  • 标准化: 用户空间通过统一的 /sys/class/backlight/ 接口控制,应用层代码可跨平台。
  • 可调试: sysfs 接口为开发初期提供了直接操作PWM的能力,方便验证硬件时序。

3.5 本章小结

Linux PWM子系统的三层架构——核心层、控制器驱动层、用户空间接口层——为背光调节提供了清晰、可扩展的软件基础。核心层通过 pwm_chip 和 pwm_device 管理硬件资源,控制器驱动通过 pwm_ops 实现硬件操作,而用户空间接口(sysfs)和标准背光类驱动则提供了灵活的控制途径。掌握这一架构,是后续编写和调试背光驱动的关键前提。