本篇作为基础上篇,先系统梳理PWM核心概念,重点拆解 RK3576平台PWM标准设备树配置方法,同时结合板载外设实操PWM脉冲捕获功能,延伸实现GPIO红外接收、PWM 调制红外发射两大常用案例,打好硬件配置与脉冲信号采集的理论实操基础,下篇再深入内核驱动开发、温控风扇项目落地与全套调试排错方案。
PWM的基本概率与工作原理
- PWM是什么
PWM(Pulse Width Modulation,脉冲宽度调制)是一种数字信号技术,通过改变脉冲的占空比来控制平均电压。
- PWM关键参数
参数 |
说明 |
典型应用 |
周期(T) |
一个完整脉冲的时间 |
控制精度 |
占空比 |
高电平时间占比 |
LED亮度、电机速度 |
频率 |
每秒脉冲数 |
避免人耳可闻噪声 |
- 触觉智能Purple Pi OH2 PWM的应用
•Fan(风扇):PWM调速
•Backlight(背光):PWM调光
•Speaker:PWM音频(部分实现)
•IR Remote(红外遥控):PWM红外发射/ GPIO红外接收
- RK3576多路PWM资源说明
•pwm0~pwm7:常规PWM(支持输出、Capture、IR发射)
•pwm1_6ch_*:6通道PWM(用于背光等)
•高级功能:波形发生器、频率计、Counter输入、Capture模式、IR发射
- PWM工作原理-占空比控制
不同占空比直观展示:
•25%占空比:██░░░░░░
•50%占空比:████░░░░
•75%占空比:██████░░
•100%占空比:████████
- PWM在Linux中的实现
用户空间通过ioctl/write操作PWM子系统,PWM子系统配置控制器寄存器,控制器输出波形至外部设备;外部设备在Capture模式下可将信号参数回传给控制器,GPIO IR接收头则通过rc-core子系统处理信号。附整体架构图
PWM设备树配置
- 使能PWM控制器
需编辑触觉智能Purple Pi OH的SDK的ido-evb7609-v1a-mipi.dtsi,配置代码如下:
&pwm1_6ch_5 { status = "okay"; };
- 添加背光设备
配置代码如下:
/ { /* 背光设备 */ backlight: backlight { compatible = "pwm-backlight"; pwms = <&pwm1_6ch_5 0 25000 0>; /* PWM, channel, period(ns), polarity */ /* 亮度等级表 */ brightness-levels = < 0 20 20 21 21 22 22 23 23 24 24 25 25 26 26 27 /* ... 更多等级 ... */ 248 249 250 251 252 253 254 255 >; default-brightness-level = <200>; /* 电源控制 */ enable-gpios = <&gpio4 RK_PA1 GPIO_ACTIVE_HIGH>; }; /* 风扇 PWM 控制 */ fan: pwm-fan { compatible = "pwm-fan"; pwms = <&pwm2 0 40000 0>; /* 25KHz PWM 频率 */ /* 温度与转速曲线 */ cooling-levels = <0 100 150 200 255>; #cooling -cells = <2>; }; };
- Pinctrl配置
配置代码如下:
&pinctrl { pwm1 { pwm1m0_pins: pwm1m0-pins { rockchip,pins = <1 RK_PB6 2 &pcfg_pull_none>; }; }; pwm2 { pwm2m0_pins: pwm2m0-pins { rockchip,pins = <1 RK_PB7 2 &pcfg_pull_none>; }; }; };
PWM脉冲捕获
- 什么是PWM Capture?
PWM Capture模式将PWM引脚从输出切换为输入,用于捕获外部信号的周期和占空比,是PWM控制器的双向能力。
- RK3576 Capture工作原理
RK3576的PWM控制器(v4版本)实现Capture流程:
1.将PWM引脚配置为输入模式(PWM_IN_SEL)
2.使能边沿检测中断(CAP_LPR_INT低电平周期中断、CAP_HPR_INT高电平周期中断)
3.硬件自动将高/低电平时间分别写入HPC(High Period Counter)和LPC(Low Period Counter)寄存器
4.驱动读取寄存器并计算为纳秒值
1.将PWM引脚配置为输入模式(PWM_IN_SEL)
2.使能边沿检测中断(CAP_LPR_INT低电平周期中断、CAP_HPR_INT高电平周期中断)
3.硬件自动将高/低电平时间分别写入HPC(High Period Counter)和LPC(Low Period Counter)寄存器
4.驱动读取寄存器并计算为纳秒值
- 内核空间Capture API
配置代码如下:
#include struct pwm_capture { unsigned int period; /* 捕获到的周期 (ns) */ unsigned int duty_cycle; /* 捕获到的占空比 (ns) */ }; /* 捕获外部 PWM 信号 */ int pwm_capture(struct pwm_device *pwm, struct pwm_capture *result, unsigned long timeout);
- Capture驱动示例
配置代码如下:
#include #include #include struct my_pwm_capture_dev { struct pwm_device *pwm; struct device *dev; }; static int my_capture_probe(struct platform_device *pdev) { struct my_pwm_capture_dev *priv; int ret; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->dev = &pdev->dev; /* 获取 PWM 设备(与 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; } platform_set_drvdata(pdev, priv); dev_info(&pdev->dev, "PWM capture device initialized\n"); return 0; } /* 读取捕获结果 */ static int my_capture_read(struct my_pwm_capture_dev *priv, unsigned int *period_ns, unsigned int *duty_ns) { struct pwm_capture result; int ret; /* 捕获外部信号,超时 100ms */ ret = pwm_capture(priv->pwm, &result, 100); if (ret) { dev_err(priv->dev, "Failed to capture PWM: %d\n", ret); return ret; } *period_ns = result.period; *duty_ns = result.duty_cycle; dev_info(priv->dev, "Captured: period=%uns, duty=%uns\n", result.period, result.duty_cycle); return 0; } /* 计算频率和占空比百分比 */ static void my_capture_calc_freq_duty(unsigned int period_ns, unsigned int duty_ns, unsigned int *freq_hz, unsigned int *duty_pct) { if (period_ns == 0) return; *freq_hz = 1000000000 / period_ns; *duty_pct = (duty_ns * 100) / period_ns; } static int my_capture_remove(struct platform_device *pdev) { return 0; } static const struct of_device_id my_capture_of_match[] = { { .compatible = "mycompany,my-pwm-capture", }, { } }; static struct platform_driver my_capture_driver = { .probe = my_capture_probe, .remove = my_capture_remove, .driver = { .name = "my-pwm-capture", .of_match_table = my_capture_of_match, }, }; module_platform_driver(my_capture_driver); MODULE_LICENSE("GPL");
- Capture注意事项
要点 |
说明 |
PWM不能同时输出和捕获 |
Capture模式下PWM必须处于禁用状态 |
超时设置 |
建议100ms,过短可能导致无中断触发而超时 |
时钟精度 |
捕获精度取决于PWM控制器的输入时钟频率 |
pinctrl复用 |
Capture复用同一PWM引脚,无需额外pinctrl配置 |
RK3576支持 |
PWM v3/v4均支持Capture模式 |
GPIO IR 红外接收与发射
- 红外遥控基础
红外遥控使用 NEC 协议传输数据,载波频率通常为 38KHz。NEC 协议帧结构:引导码(9ms高+4.5ms低) + 地址码(8bit) + 地址反码(8bit) + 命令码(8bit) + 命令反码(8bit)
逻辑 |
编码方式 |
逻辑"0" |
560μs高+ 560μs低(总1.12ms) |
逻辑"1" |
560μs高+ 1.69ms低(总2.25ms) |
重复码 |
9ms高+ 2.25ms低+ 560μs高 |
NEC协议时序图示意图:
- Linux rc-core 子系统
Linux 通过 rc-core 子系统统一管理红外遥控,支持 NEC、NEC32、RC-5、RC-6、Sony SIRC、JVC、Sharp、XMP 等协议。
- 红外接收的设备树与内核配置
设备树配置代码如下:
/ { /* GPIO 红外接收头 */ ir_receiver: ir-receiver { compatible = "gpio-ir-receiver"; gpios = <&gpio0 RK_PA5 GPIO_ACTIVE_LOW>; linux,rc-map-name = "rc-rc6-mce"; /* 使用预定义的按键映射 */ linux,autosuspend-period = <125>; /* 自动挂起延迟 (ms) */ }; };
属性 |
说明 |
必填 |
compatible |
必须为"gpio-ir-receiver" |
是 |
gpios |
红外接收头连接的GPIO |
是 |
linux,rc-map-name |
按键映射表名称 |
否(默认使用rc-empty-map) |
linux,autosuspend-period |
自动挂起延迟(ms) |
否 |
确保以下内核配置已启用:
CONFIG_RC_CORE=y CONFIG_GPIO_IR_RECEIVER=m # 或 y CONFIG_RC_DECODERS=y # 包含 NEC、RC5、RC6 等解码器 CONFIG_RC_MAP=y
- 自定义按键映射表
创建/etc/rc_keymaps/my_remote.map,配置代码如下:
# 按键映射表格式:scancode keycode # scancode: 遥控器发送的原始 NEC 码 # keycode: 映射到的 Linux 按键 0x12ff01 KEY_POWER 0x12ff02 KEY_VOLUMEUP 0x12ff03 KEY_VOLUMEDOWN 0x12ff04 KEY_UP 0x12ff05 KEY_DOWN 0x12ff06 KEY_LEFT 0x12ff07 KEY_RIGHT 0x12ff08 KEY_ENTER
加载映射表:
iir-keytable -c # 清除现有映射 ir-keytable -p NEC -p RC-6 # 启用解码协议 ir-keytable -w /etc/rc_keymaps/my_remote.map # 加载映射
- 用户空间调试
配置代码如下:
# 查看红外设备 ir-keytable # 输出示例: # Found /sys/class/rc/rc0/ with: # Name: gpio_ir_recv # Driver: gpio-ir-recv # Default keymap: rc-rc6-mce # Input device: /dev/input/event2 # Enabled protocols: nec # 实时监控按键事件 ir-keytable -t # 输出示例: # 1234.567890: event type EV_MSC, evscancode = 0x12ff01 # 1234.567891: event type EV_KEY, KEY_POWER(116) pressed # 1234.567891: event type EV_KEY, KEY_POWER(116) released # 查看原始事件 cat /dev/input/event2 | hexdump # 使用 evtest 测试 evtest /dev/input/event2
- RK3576 硬件 IR发射
RK3576的PWM控制器内置硬件IR发射引擎,支持NEC协议的自动调制,无需CPU干预时序。IR 硬件引擎工作流程如图所示:
1.用户写入NEC码数据(7字节格式)
2.PWM控制器通过IR_TRANS_CTRL0寄存器启动发射
3.硬件自动产生38KHz载波并按NEC协议调制
4.发射完成后触发IR_TRANS_END_INT中断
1.用户写入NEC码数据(7字节格式)
2.PWM控制器通过IR_TRANS_CTRL0寄存器启动发射
3.硬件自动产生38KHz载波并按NEC协议调制
4.发射完成后触发IR_TRANS_END_INT中断
IR 相关寄存器:
寄存器 |
偏移 |
功能 |
IR_TRANS_ARBITER |
0x180 |
通道仲裁 |
IR_TRANS_CTRL0 |
0x184 |
发射控制 |
IR_TRANS_PRE |
0x188 |
引导码预装值 |
IR_TRANS_SPRE |
0x18C |
重复码预装值 |
IR_TRANS_LD |
0x190 |
数据低电平周期 |
IR_TRANS_HD |
0x194 |
数据高电平周期 |
IR_TRANS_DATA_VALUE |
0x1A0 |
NEC码数据值 |
INT_EN |
- |
中断使能 |
- 红外发射的设备树与内核配置
设备树配置代码如下:
&pwm2 { status = "okay"; pinctrl-0 = <&pwm2m0_pins>; rockchip,pwm-ir-transmit; /* 使能 IR 发射功能 */ };
属性 |
说明 |
必填 |
rockchip,pwm-ir-transmit |
使能PWM硬件IR发射 |
是 |
内核配置代码如下:
CONFIG_RC_CORE=y # 必须启用(IR 发射依赖 rc-core) CONFIG_PWM_ROCKCHIP=y # 或 m
- NEC码数据格式
IR 发射使用 7 字节缓冲区,格式如下:
txbuf[0]: NEC 引导码低电平时间 (μs),通常9000 txbuf[1]: NEC 引导码高电平时间 (μs),通常 4500 txbuf[2]: 重复码高电平时间 (μs),通常 2250 txbuf[3]: 逻辑 "0" 和 "1" 低电平时间 (μs),通常 560 txbuf[4]: 逻辑 "0" 高电平时间 (μs),通常 560 txbuf[5]: 逻辑 "1" 高电平时间 (μs),通常 1690 txbuf[6]: 32位 NEC 数据
32位数据格式(txbuf[6]):
bit[31:24] bit[23:16] bit[15:8] bit[7:0] 命令反码 命令码 地址反码 地址码
例如:地址 0x01,命令 0x10
•地址码 = 0x01
•地址反码 = 0xFE(~0x01)
•命令码 = 0x10
•命令反码 = 0xEF(~0x10)
•txbuf[6] = 0xEF10FE01
- IR发射驱动示例
配置代码如下:
#include #include #include struct my_pwm_ir_dev { struct rc_dev *rcdev; struct platform_device *pdev; }; /* NEC 协议红外发射 */ static int my_pwm_ir_tx(struct rc_dev *dev, unsigned int *txbuf, unsigned int count) { struct my_pwm_ir_dev *priv = dev->priv; struct pwm_device *pwm; int ret; if (count < 2) return -EINVAL; pwm = dev->priv; if (!pwm) return -ENODEV; /* 通过 PWM IR 硬件发射 */ /* 注意:实际发射由 pwm-rockchip 驱动的 * rockchip_pwm_ir_transmit 处理 */ ret = pwm_ir_transmit(pwm, txbuf, count); if (ret < 0) dev_err(&dev->dev, "IR transmit failed: %d\n", ret); return ret; } static int my_pwm_ir_probe(struct platform_device *pdev) { struct my_pwm_ir_dev *priv; struct rc_dev *rcdev; int ret; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; /* 注册 rc_dev */ rcdev = devm_rc_allocate_device(&pdev->dev, RC_DRIVER_IR_RAW_TX); if (!rcdev) return -ENOMEM; rcdev->driver_name = "my-pwm-ir"; rcdev->device_name = "PWM IR Transmitter"; rcdev->tx_ir = my_pwm_ir_tx; rcdev->priv = priv; rcdev->allowed_protocols = RC_BIT_NEC | RC_BIT_NECX | RC_BIT_NEC32; ret = devm_rc_register_device(&pdev->dev, rcdev); if (ret) { dev_err(&pdev->dev, "Failed to register rc device: %d\n", ret); return ret; } priv->rcdev = rcdev; priv->pdev = pdev; dev_info(&pdev->dev, "PWM IR transmitter registered\n"); return 0; } static const struct of_device_id my_pwm_ir_of_match[] = { { .compatible = "mycompany,my-pwm-ir", }, { } }; static struct platform_driver my_pwm_ir_driver = { .probe = my_pwm_ir_probe, .driver = { .name = "my-pwm-ir", .of_match_table = my_pwm_ir_of_match, }, }; module_platform_driver(my_pwm_ir_driver); MODULE_LICENSE("GPL");
- 用户空间IR 发射
配置代码如下:
# 使用 ir-ctl 发送 NEC 码 # 格式:ir-ctl --transmit= --send="protocol:scancode" # 发送 NEC POWER 键(地址 0x01,命令 0x10) ir-ctl -d /dev/lirc0 --send=nec:0x12ff01 # 查看 IR 设备 ir-ctl -i # 输出示例: # lirc0: PWM IR Transmitter # driver: my-pwm-ir # Features: send # Allowed protocols: NEC NECX NEC32 # 发送重复码(长按效果) ir-ctl -d /dev/lirc0 --send=nec:0x12ff01 --send=nec:0x12ff01 --send=nec:0x12ff01 # 使用 evtest 验证(发射 + 接收回路测试) ir-ctl -d /dev/lirc0 --send=nec:0x12ff01 ir-keytable -t # 在另一终端中观察
- IR发射注意事项
要点 |
说明 |
CONFIG_RC_CORE必须启用 |
IR发射依赖rc-core子系统 |
PWM输出和IR互斥 |
IR发射时PWM不能处于输出模式 |
pinctrl配置 |
确保PWM引脚连接到IR LED |
载波频率 |
硬件自动使用PWM配置的载波频率(通常38KHz) |
超时处理 |
驱动自动等待发射完成,超时3倍帧时间 |
产品简介
触觉智能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开源鸿蒙等多个操作系统。
- 随机文章
- 热门文章
- 水泥行业无线数采物联网解决方案
- 什么是电动机定子和转子?定子和转子有什么作用?
- 铝业车间:Profinet转CanOpen协议转换网关
- “文物会说话”有声海报|笔锋勾勒春江水,水墨晕染万重山
- 从"听见"到"秒懂",Leion Hey2如何重新定义跨语言沟通新标准?
- 从敦煌到大足 两大世界文化遗产首次在重庆联展
- 轴距2945mm 全新奥迪Q5L申报 配第五代EA888发动机
- 美国大举扩充成熟制程
- 夏日来点冷知识|AIGC科普动漫小剧场:在古代如何过夏天?关于消暑纳凉 古人的脑洞大着呢
- 小米YU7 vs 宝马M2:电动与燃油的对决
- 1感染甲流后该如何科学调养?饮食起居这样做,感染甲流后该如何科学调养?饮食起居这样做
- 2马克龙去的这所大学,太宝藏了吧!,马克龙去的这所大学,太宝藏了吧!
- 3北方多地迎来降雪降温天气 各部门联动“战”寒潮筑牢安全防线,北方多地迎来降雪降温天气 各部门联动“战”寒潮筑牢安全防线
- 4福州发布公告:吴石故居将封闭施工,展开系统性修缮
- 5“无保护”攀岩真的无保护吗?“无保护”攀岩真的无保护吗?
- 6城中话债|激活民间投资:让有效率的资本站上C位
- 7晚间重磅!又一万亿级券商将诞生 券业并购潮涌
- 8科学家的照片排在董事长之上,科学家的照片排在董事长之上
- 911月份“菜篮子”产品价格呈现季节性上涨 多因素推高生产成本,11月份“菜篮子”产品价格呈现季节性上涨 多因素推高生产成本
- 10何立峰:有力有序有效做好2026年金融重点工作