【Linux 驱动开发】Linux PWM (脉冲宽度调制) 全面技术指南

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端):

dts 复制代码
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):

dts 复制代码
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脚本)

bash 复制代码
# 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 驱动代码示例

c 复制代码
#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
相关推荐
Doro再努力1 小时前
Vim 快速上手实操手册:从入门到生产环境实战
linux·编辑器·vim
wypywyp1 小时前
8. ubuntu 虚拟机 linux 服务器 TCP/IP 概念辨析
linux·服务器·ubuntu
Doro再努力1 小时前
【Linux操作系统10】Makefile深度解析:从依赖推导到有效编译
android·linux·运维·服务器·编辑器·vim
senijusene1 小时前
Linux软件编程:IO编程,标准IO(1)
linux·运维·服务器
忧郁的橙子.1 小时前
02-本地部署Ollama、Python
linux·运维·服务器
醇氧1 小时前
【linux】查看发行版信息
linux·运维·服务器
No8g攻城狮2 小时前
【Linux】Windows11 安装 WSL2 并运行 Ubuntu 22.04 详细操作步骤
linux·运维·ubuntu
XiaoFan0122 小时前
免密批量抓取日志并集中输出
java·linux·服务器
souyuanzhanvip3 小时前
ServerBox v1.0.1316 跨平台 Linux 服务器管理工具
linux·运维·服务器
HalvmånEver4 小时前
Linux:线程互斥
java·linux·运维