【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
相关推荐
lengjingzju14 小时前
一网打尽Linux IPC(三):System V IPC
linux·服务器·c语言
大聪明-PLUS14 小时前
如何编写你的第一个 Linux 内核模块
linux·嵌入式·arm·smarc
知识分享小能手15 小时前
Ubuntu入门学习教程,从入门到精通,Ubuntu 22.04文件压缩与解压缩知识点详解(12)
linux·学习·ubuntu
用户61354114601615 小时前
Krb5-libs-1.18.2-5.ky10.x86_64.rpm 安装失败怎么办?附详细步骤
linux
zhougl99616 小时前
Vuex 模块命名冲突:问题解析与完整解决方案
linux·服务器·apache
一世琉璃白_Y16 小时前
Ubuntu(VMware)虚拟机网络异常排查与解决方案
linux·网络·ubuntu
AI+程序员在路上17 小时前
网桥及IP转发在嵌入式linux eth0与wlan0连接使用方法
linux·tcp/ip·php
I · T · LUCKYBOOM18 小时前
1.Apache网站优化
linux·运维·服务器·网络·apache
JANGHIGH18 小时前
vmware安装ubuntu虚拟机后与主机win10共享文件夹
linux·运维·ubuntu
GHL28427109018 小时前
vmware中无法看到共享文件夹
linux·运维·服务器