【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
相关推荐
代码游侠2 小时前
Linux系统编程 - 文件操作
linux·运维·服务器·学习
Web极客码2 小时前
CentOS与RHEL安装EPEL源解析错误修复
linux·centos·php
杰哥技术分享3 小时前
宿主机(CentOS)没有安装 PHP,但想使用php
linux·centos·php
Xの哲學3 小时前
Linux I3C驱动深度剖析: 从原理到实战的全面解析
linux·服务器·算法·架构·边缘计算
赖small强3 小时前
【Linux 进程管理】Linux 可执行程序运行机制深度解析
linux·可执行程序
casdfxx3 小时前
配置v3s支持8188eu、8192cu网卡(三)-openssh不能登录linux开发板。
linux·服务器·网络
遇见火星3 小时前
Linux 服务器被入侵后,如何通过登录日志排查入侵源?【实战指南】
linux·运维·服务器·入侵·日志排查
凤凰战士芭比Q3 小时前
(一)zabbix7.0(安装、自定义监控、告警)
linux
gis分享者3 小时前
如何在 Shell 脚本中使用管道(pipeline)实现数据传递?(容易)
linux·pipeline·shell·脚本·管道·数据传递