
清空记录
历史记录
取消
清空记录
历史记录

本系列PWM驱动教程,基于触觉智能RK3576开发板Purple Pi OH2进行讲解与真机调试。
PWM驱动开发
代码如下:
#include <linux/pwm.h>
#include <linux/module.h>
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_div(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(如果内核支持 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配置代码如下:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#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 <duty_percent>\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;
}配置代码如下:
/ {
/* 温度传感器 */
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-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>;
};
};
};
};
};红外遥控使用 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
pwm-tool -d /sys/class/pwm/pwmchip0/pwm0 \
-p 25000 -c 12500 -e
# 调整占空比
pwm-tool -d /sys/class/pwm/pwmchip0/pwm0 -c 20000配置代码如下:
# 查看红外设备状态
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)
•检查设备树status = "okay"
•确认 pinctrl 配置正确
•验证 PWM 时钟是否使能
•检查 PWM 时钟源精度
•周期值是否过小而超出分辨率
•PWM 频率是否合适(通常 25KHz)
•检查风扇 PWM 输入类型(正逻辑/负逻辑)
•确认最小启动占空比
•确认 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 内核
产品简介
