Linux PWM (脉冲宽度调制) 全面技术指南


目录

  1. 技术原理深度解析
  2. Linux PWM 子系统架构
  3. 用户空间控制 (Sysfs)
  4. 内核驱动开发实践
  5. 应用场景案例
  6. 附录

1. 技术原理深度解析

1.1 什么是 PWM?

PWM (Pulse Width Modulation),即脉冲宽度调制,是一种利用数字信号控制模拟电路的技术。它通过快速开关电源输出,在负载上产生一个平均电压,从而实现对功率的连续控制。

1.2 核心概念与公式

在这里插入图片描述

图1:PWM波形参数示意图

  • 周期 (Period, TTT): 脉冲信号重复一次所需的时间。单位通常为纳秒(ns)。
  • 频率 (Frequency, fff): 每秒钟脉冲重复的次数。f=1/Tf = 1/Tf=1/T
  • 占空比 (Duty Cycle, DDD): 高电平持续时间 (TonT_{on}Ton) 与整个周期 (TTT) 的比值。
    D=TonT×100% D = \frac{T_{on}}{T} \times 100\% D=TTon×100%
  • 平均电压 (VavgV_{avg}Vavg): 输出到负载上的等效电压。
    Vavg=Vcc×D V_{avg} = V_{cc} \times D Vavg=Vcc×D

1.3 功率控制原理

当PWM频率足够高时(超过负载的响应时间),负载(如电机或LED)不会看到电压的快速切换,而是看到一个稳定的平均电压。

  • LED: 调节占空比 = 调节电流有效值 = 调节亮度。
  • 电机: 调节占空比 = 调节电枢电压 = 调节转速/扭矩。

2. Linux PWM 子系统架构

Linux内核通过通用的PWM子系统(PWM Subsystem)来管理不同SoC的PWM控制器,为上层驱动和用户空间提供统一的接口。

在这里插入图片描述

图2:Linux PWM子系统架构图

2.1 核心组件

  • Consumer Drivers: 使用PWM功能的内核驱动(如 leds-pwm, pwm-beeper, pwm-fan)。
  • PWM Core: 核心层 (drivers/pwm/core.c),提供 pwm_get, pwm_config, pwm_enable 等API,并管理Sysfs接口。
  • Chip Drivers: SoC厂商提供的具体硬件驱动(如 pwm-rockchip.c, pwm-imx.c),实现 pwm_ops 回调函数。

2.2 设备树 (DTS) 配置

在Device Tree中,PWM控制器通常作为提供者(Provider),具体设备(如背光)作为消费者(Consumer)。

Provider (SoC端):

pwm0: pwm@ff1b0020 {
    compatible = "rockchip,rk3399-pwm";
    reg = <0x0 0xff1b0020 0x0 0x10>;
    #pwm-cells = <3>; /* cell 0: channel, cell 1: period, cell 2: polarity */
    status = "okay";
};

Consumer (板级端 - LED):

backlight {
    compatible = "pwm-backlight";
    pwms = <&pwm0 0 25000 0>; /* Channel 0, Period 25000ns (40kHz), Normal Polarity */
    brightness-levels = <0 4 8 16 32 64 128 255>;
    default-brightness-level = <6>;
};

3. 用户空间控制 (Sysfs)

对于没有特定内核驱动接管的PWM通道,用户可以通过 /sys/class/pwm/ 接口直接控制。这在调试或简单应用中非常有用。

3.1 接口详解

路径: /sys/class/pwm/pwmchipN/ (N为控制器编号)

  1. export: 写入通道号(如0)以导出对应的 pwm0 目录。
  2. pwmX/period: 设置周期(纳秒)。
  3. pwmX/duty_cycle: 设置高电平时间(纳秒)。必须小于周期。
  4. pwmX/polarity: 设置极性 (normalinversed)。
  5. pwmX/enable: 写入1使能,写入0禁止。

3.2 操作示例 (Shell脚本)

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

# 2. 设置周期为 1ms (1,000,000 ns) -> 1kHz
echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/period

# 3. 设置占空比为 50% (500,000 ns)
echo 500000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle

# 4. 使能 PWM
echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable

在这里插入图片描述

图3:示波器实测波形 (50% Duty Cycle)


4. 内核驱动开发实践

编写一个使用PWM控制风扇的简单平台驱动。

4.1 驱动代码示例

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/slab.h>

struct my_pwm_fan {
    struct pwm_device *pwm;
    u32 period_ns;
};

static int my_pwm_fan_probe(struct platform_device *pdev)
{
    struct my_pwm_fan *fan;
    int ret;

    fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL);
    if (!fan) return -ENOMEM;

    /* 1. 获取 PWM 设备 (对应DTS中的 pwms 属性) */
    fan->pwm = devm_pwm_get(&pdev->dev, NULL);
    if (IS_ERR(fan->pwm)) {
        dev_err(&pdev->dev, "Failed to get pwm\n");
        return PTR_ERR(fan->pwm);
    }

    /* 2. 设置初始参数: 周期 20ms (50Hz), 占空比 0 */
    fan->period_ns = 20000000;
    pwm_set_period(fan->pwm, fan->period_ns);
    
    /* 3. 配置并使能 (初始转速 50%) */
    ret = pwm_config(fan->pwm, fan->period_ns / 2, fan->period_ns);
    if (ret < 0) return ret;

    ret = pwm_enable(fan->pwm);
    if (ret < 0) return ret;

    platform_set_drvdata(pdev, fan);
    dev_info(&pdev->dev, "PWM Fan initialized\n");
    return 0;
}

static int my_pwm_fan_remove(struct platform_device *pdev)
{
    struct my_pwm_fan *fan = platform_get_drvdata(pdev);
    
    if (fan->pwm) {
        pwm_disable(fan->pwm);
    }
    return 0;
}

static const struct of_device_id my_pwm_fan_match[] = {
    { .compatible = "vendor,my-pwm-fan" },
    { },
};
MODULE_DEVICE_TABLE(of, my_pwm_fan_match);

static struct platform_driver my_pwm_fan_driver = {
    .probe = my_pwm_fan_probe,
    .remove = my_pwm_fan_remove,
    .driver = {
        .name = "my-pwm-fan",
        .of_match_table = my_pwm_fan_match,
    },
};

module_platform_driver(my_pwm_fan_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AI Assistant");
MODULE_DESCRIPTION("Simple PWM Fan Driver");

4.2 关键API说明

  • devm_pwm_get(): 从设备树获取PWM句柄。
  • pwm_config(pwm, duty_ns, period_ns): 核心配置函数。注意这里的周期参数可能会覆盖 pwm_set_period 的设置。
  • pwm_enable() / pwm_disable(): 开启/关闭PWM信号输出。

5. 应用场景案例

5.1 电机控制 (H桥驱动)

对于直流电机,通常使用PWM控制其转速。结合H桥电路,可以实现正反转。

  • 方案: 两个PWM通道连接H桥的两个输入端。
  • 正转: PWM1输出波形,PWM2低电平。
  • 反转: PWM1低电平,PWM2输出波形。
  • 调速: 改变占空比。

5.2 呼吸灯 (Breathing LED)

利用人眼视觉暂留效应,动态连续改变占空比。

  • 算法: 使用正弦函数或指数函数生成占空比序列(Gamma校正),使亮度变化符合人眼感知线性度。

5.3 动态电压调节 (DVS)

在PMIC(电源管理芯片)中,通过PWM反馈控制Buck电路的输出电压。

  • 原理: 占空比越高 -> 反馈电压越高 -> PMIC输出电压越低(或越高,取决于电路拓扑)。这常用于CPU的DVFS(动态电压频率调整)。

6. 附录

参考文献

  1. Kernel Documentation: Documentation/pwm.txt
  2. Device Tree Bindings: Documentation/devicetree/bindings/pwm/
  3. Source Code: include/linux/pwm.h
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐