触觉智能RK3576开发板实战(下)PWM驱动开发与温控风扇Demo实战演示
本系列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开源鸿蒙等多个操作系统。
- 触觉智能RK3576开发板实战(下)PWM驱动开发与温控风扇Demo实战演示
- 智能防雷在线接地电阻监测:精度方法对比与行业部署选型
- RK3576开发板UART驱动实战教程,基于触觉智能开发板演示
- SZ100 云台式激光甲烷遥测仪:基于 TDLAS 技术的燃气场站全域泄漏监测方案
- 你身边藏着多少陶瓷天线? 它们默默连接着你一天的无线生活
- 香蕉派开源社区 Banana Pi BPI-M7S采用瑞芯微RK3588s芯片设计
- 明晚19:30首播!DA14535/DA14531 BLE开发实战!还有大量开发板周边等你来拿!
- 稳先微电子超级PMOS系列智能功率开关产品介绍
- 全链路安全方案,助力CRA合规与IEC 62443认证-MYC-LR3576核心板
- 代码从失控到可控 | 阿里云Qoder开源Better Harness
- 智能硬件方案开发公司哪家好?做过消费电子产品的推荐这几家
- 从“数字世界”到“物理世界”——稳石机器人AI赋能智能物流的产业实践
- 一文讲透做智能家居产品,语音提示芯片怎么选
- 工业视觉传感器多维对比:一机多用场景下如何选择可靠方案?
- 阿里云灵骏真武M890超节点实例现已开放邀测
- 简化工业组网|GW 串口服务器对接智嵌云,串口数据云端统一管理