触觉智能RK3576开发板实战(下)PWM驱动开发与温控风扇Demo实战演示

科技时尚 2026-08-11 chy123 4865

本系列PWM驱动教程,基于触觉智能RK3576开发板Purple Pi OH2进行讲解与真机调试。

图片

上篇我们系统梳理了PWM的核心概念,完成了RK3576平台设备树的配置,并通过红外收发案例打通了脉冲信号采集的基础链路,对上篇感兴趣可关注触觉智能查看往期内容。

本篇将开展PWM驱动开发教学,手把手带大家完成一个“温控风扇”的项目Demo落地,并汇总PWM调试与排错手册,助你轻松掌握RK3576的PWM开发全流程。

PWM驱动开发

  • 内核空间PWM操作

代码如下:

#include 
#include 
struct my_pwm_dev {
    struct pwm_device *pwm;
    struct device *dev;
};
/* 申请 PWM */
static int my_pwm_probe(struct platform_device *pdev)
{
    struct my_pwm_dev *priv;
    struct pwm_state state;
    int ret;
    priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;
    priv->dev = &pdev->dev;
    /* 从设备树获取 PWM */
    priv->pwm = devm_pwm_get(&pdev->dev, NULL);
    if (IS_ERR(priv->pwm)) {
        ret = PTR_ERR(priv->pwm);
        if (ret != -EPROBE_DEFER)
            dev_err(&pdev->dev, "Failed to get PWM: %d\n", ret);
        return ret;
    }
    /* 配置 PWM 参数 */
    pwm_init_state(priv->pwm, &state);
    /* 设置周期:25000ns = 40KHz */
    state.period = 25000;
    /* 设置占空比:50% */
    state.duty_cycle = 12500;
    /* 使能 PWM */
    state.enabled = true;
    ret = pwm_apply_state(priv->pwm, &state);
    if (ret) {
        dev_err(&pdev->dev, "Failed to apply PWM state: %d\n", ret);
        return ret;
    }
    dev_info(&pdev->dev, "PWM initialized: period=%u, duty=%u\n",
             state.period, state.duty_cycle);
    platform_set_drvdata(pdev, priv);
    return 0;
}
/* 设置占空比 */
static int my_pwm_set_duty(struct my_pwm_dev *priv, unsigned int duty_percent)
{
    struct pwm_state state;
    u64 duty_cycle;
    int ret;
    if (duty_percent > 100)
        return -EINVAL;
    pwm_get_state(priv->pwm, &state);
    /* 计算新的占空比 */
    duty_cycle = (u64)state.period * duty_percent;
    do_p(duty_cycle, 100);
    state.duty_cycle = duty_cycle;
    state.enabled = (duty_percent > 0);
    ret = pwm_apply_state(priv->pwm, &state);
    if (ret)
        dev_err(priv->dev, "Failed to set PWM duty: %d\n", ret);
    return ret;
}
/* LED 调光函数 */
static int my_pwm_set_brightness(struct my_pwm_dev *priv, int brightness)
{
    /* brightness: 0-255 */
    int duty = (brightness * 100) / 255;
    return my_pwm_set_duty(priv, duty);
}
static int my_pwm_remove(struct platform_device *pdev)
{
    struct my_pwm_dev *priv = platform_get_drvdata(pdev);
    /* 禁用 PWM */
    pwm_disable(priv->pwm);
    return 0;
}
static const struct of_device_id my_pwm_of_match[] = {
    { .compatible = "mycompany,my-pwm-device", },
    { }
};
static struct platform_driver my_pwm_driver = {
    .probe = my_pwm_probe,
    .remove = my_pwm_remove,
    .driver = {
        .name = "my-pwm",
        .of_match_table = my_pwm_of_match,
    },
};
module_platform_driver(my_pwm_driver);
MODULE_LICENSE("GPL");

  • 使用PWM子系统API

配置代码如下:

# 导出 PWM(如果内核支持 sysfs 接口)
echo 0 > /sys/class/pwm/pwmchip0/export
# 设置周期(ns)
echo 25000 > /sys/class/pwm/pwmchip0/pwm0/period
# 设置占空比(ns)
echo 12500 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
# 使能 PWM
echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable
# 修改占空比
echo 5000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle   # 20%
echo 20000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle  # 80%
# 禁用 PWM
echo 0 > /sys/class/pwm/pwmchip0/pwm0/enable

  • 用户空间PWM操作

配置代码如下:


#include 
#include 
#include 
#include 
#include 
#define PWM_PATH "/sys/class/pwm/pwmchip0/pwm0"
int pwm_write(const char *file, const char *val)
{
    char path[256];
    int fd;
    snprintf(path, sizeof(path), "%s/%s", PWM_PATH, file);
    fd = open(path, O_WRONLY);
    if (fd < 0) {
        perror("open");
        return -1;
    }
    if (write(fd, val, strlen(val)) < 0) {
        perror("write");
        close(fd);
        return -1;
    }
    close(fd);
    return 0;
}
int main(int argc, char *argv[])
{
    int duty;
    char buf[32];
    if (argc < 2) {
        printf("Usage: %s \n", argv[0]);
        return -1;
    }
    duty = atoi(argv[1]);
    if (duty < 0 || duty > 100) {
        printf("Duty must be 0-100\n");
        return -1;
    }
    /* 设置周期 25us (40KHz) */
    pwm_write("period", "25000");
    /* 设置占空比 */
    snprintf(buf, sizeof(buf), "%d", duty * 250);
    pwm_write("duty_cycle", buf);
    /* 使能 PWM */
    pwm_write("enable", "1");
    printf("PWM set to %d%% duty cycle\n", duty);
    return 0;
}

  • C程序控制PWM

配置代码如下:

/ {
    /* 温度传感器 */
    thermal-zones {
        cpu_thermal: cpu-thermal {
            polling-delay-passive = <250>;  /* 被动轮询间隔 */
            polling-delay = <1000>;        /* 主动轮询间隔 */
            /* 热传感器 */
            thermal-sensors = <&tsadc 0>;
            trips {
                cpu_cool: cpu-cool {
                    temperature = <55000>;   /* 55°C */
                    hysteresis = <5000>;
                    type = "active";
                };
                cpu_hot: cpu-hot {
                    temperature = <70000>;   /* 70°C */
                    hysteresis = <5000>;
                    type = "active";
                };
                cpu_crit: cpu-crit {
                    temperature = <95000>;   /* 95°C */
                    hysteresis = <2000>;
                    type = "critical";
                };
            };
            cooling-maps {
                map0 {
                    trip = <&cpu_cool>;
                    cooling-device = <&fan THERMAL_NO_LIMIT 1>;
                };
                map1 {
                    trip = <&cpu_hot>;
                    cooling-device = <&fan 2 THERMAL_NO_LIMIT>;
                };
            };
        };
    };
};

温度控制风扇实现

  • Thermal温控风扇工作逻辑

设备树温控配置如下,实现温度与风扇转速的联动调控:

/ {
    /* 温度传感器 */
    thermal-zones {
        cpu_thermal: cpu-thermal {
            polling-delay-passive = <250>;  /* 被动轮询间隔 */
            polling-delay = <1000>;        /* 主动轮询间隔 */
            /* 热传感器 */
            thermal-sensors = <&tsadc 0>;
            trips {
                cpu_cool: cpu-cool {
                    temperature = <55000>;   /* 55°C */
                    hysteresis = <5000>;
                    type = "active";
                };
                cpu_hot: cpu-hot {
                    temperature = <70000>;   /* 70°C */
                    hysteresis = <5000>;
                    type = "active";
                };
                cpu_crit: cpu-crit {
                    temperature = <95000>;   /* 95°C */
                    hysteresis = <2000>;
                    type = "critical";
                };
            };
            cooling-maps {
                map0 {
                    trip = <&cpu_cool>;
                    cooling-device = <&fan THERMAL_NO_LIMIT 1>;
                };
                map1 {
                    trip = <&cpu_hot>;
                    cooling-device = <&fan 2 THERMAL_NO_LIMIT>;
                };
            };
        };
    };
};

调试方法&常见问题

  • 查看PWM状态

红外遥控使用 NEC 协议传输数据,载波频率通常为 38KHz。NEC 协议帧结构:

# 查看 PWM 设备
ls /sys/class/pwm/
# 查看 PWM 状态
cat /sys/kernel/debug/pwm
# 查看背光状态
cat /sys/class/backlight/backlight/brightness
cat /sys/class/backlight/backlight/max_brightness

  • 示波器测量

数据如下:

# PWM 参数计算
# 频率 = 1 / 周期(s)
# 占空比 = 高电平时间 / 周期 × 100%
# 25KHz 风扇 PWM
周期 = 40us = 40000ns
# 50% 占空比
高电平时间 = 20us

  • 使用pwm-tool工具

配置代码如下:

# 配置 PWM
pwm-tool -d /sys/class/pwm/pwmchip0/pwm0 \
         -p 25000 -c 12500 -e
# 调整占空比
pwm-tool -d /sys/class/pwm/pwmchip0/pwm0 -c 20000

  • IR调试工具

配置代码如下:

# 查看红外设备状态
ir-keytable
# 实时监控按键事件
ir-keytable -t
# 发送 NEC 红外码
ir-ctl -d /dev/lirc0 --send=nec:0x12ff01
# 查看 rc-core 调试信息
cat /sys/kernel/debug/ir_raw_event
# 验证红外接收/发射回路
# 终端 1: ir-keytable -t
# 终端 2: ir-ctl -d /dev/lirc0 --send=nec:0x12ff01
# 观察终端 1 是否收到事件

  • 通用故障排查流程图

如图所示:

图片

  • 红外接收无按键事件

•检查GPIO极性是否正确(GPIO_ACTIVE_LOW)

•确认rc-core驱动已加载(lsmod | grep gpio_ir)

•使用ir-keytable -t查看原始事件

•确认红外接收头供电正常(通常3.3V)

  • PWM无输出

•检查设备树status = "okay"

•确认pinctrl配置正确

•验证PWM时钟是否使能

  • 占空比不准确

•检查PWM时钟源精度

•周期值是否过小而超出分辨率

  • 风扇不转或异常

•PWM频率是否合适(通常25KHz)

•检查风扇PWM输入类型(正逻辑/负逻辑)

•确认最小启动占空比

  • PWM Capture无响应

•确认PWM处于禁用状态(不能同时输出和捕获)

•检查外部信号是否有足够的边沿变化

•超时值是否设置过小(建议>= 100ms)

•确认PWM引脚已正确配置为输入模式

  • 红外发射无效果

•确认CONFIG_RC_CORE=y已启用

•检查设备树中rockchip,pwm-ir-transmit属性

•PWM引脚是否连接到IR LED

•使用示波器验证PWM引脚是否有38KHz载波输出

  • 参考文档

•RKDocs/common/PWM/

•Documentation/devicetree/bindings/pwm/pwm.txt

•Documentation/devicetree/bindings/media/gpio-ir-receiver.yaml

•Documentation/devicetree/bindings/rc/rc.yaml

•drivers/pwm/pwm-rockchip.c— Rockchip PWM驱动(Capture + IR发射)

•drivers/media/rc/gpio-ir-recv.c— GPIO IR接收驱动

•include/linux/pwm.h— PWM子系统API头文件

•include/media/rc-core.h— rc-core子系统API

本教程适用于Purple Pi OH2 (RK3576)开发板,基于Android 14 / Linux 6.1内核

图片

产品简介

触觉智能Purple Pi OH2 RK3576开发板,4核A72+4核A53+M0多核异构处理器,主频最高2.2GHz,6Tops算力NPU!配套RK3576核心板40.5×40.5mm极致小尺寸,全新Linux6.1内核,广泛支持Linux、Android、OpenHarmony开源鸿蒙等多个操作系统

配套RK3576核心板40.5×40.5mm极 致小尺寸,全新Linux6.1内核,广泛支持Linux、Android、OpenHarmony开源鸿蒙等多个操作系统。