
1. 项目概述在RK3506上搞定PWM输入捕获最近在做一个工业网关项目需要精确测量外部传感器发来的PWM信号频率和占空比核心板选型正好落在了触觉智能的RK3506开发板上。这块板子接口丰富性能也够用但上手调试PWM输入捕获功能时发现官方资料和默认驱动并不完整走了一些弯路。PWM输入捕获简单说就是让开发板能“听懂”外部设备发来的PWM信号解析出它的周期和脉宽这对于电机转速检测、舵机信号解码、遥控器信号读取等场景至关重要。瑞芯微的RK3506芯片本身硬件是支持这个功能的但软件层面需要我们自己动手配置和调试。如果你也在RK3506或其他类似平台上折腾PWM输入希望这篇从零开始的踩坑记录能帮你省下大把时间。2. 核心需求与方案选型解析2.1 为什么需要PWM输入捕获在很多嵌入式应用里PWM信号就像一种通用的“模拟语言”。比如一个超声波传感器可能用特定占空比的PWM波表示距离一个编码器可能输出频率与转速成正比的PWM信号。我们的开发板作为主控需要解读这种语言。输出PWM信号相对简单内核驱动通常直接提供但输入捕获则需要硬件定时器/计数器在输入信号的边沿上升沿或下降沿触发精确记录时间戳再计算周期和占空比。RK3506的PWM控制器在硬件上支持这种输入捕获模式但默认的Linux内核驱动可能只开启了输出功能这就是我们需要动手配置的原因。2.2 RK3506开发板与PWM资源摸底我用的这块触觉智能RK3506开发板核心是一颗RK3506J芯片3核Cortex-A7加一个Cortex-M0的多核架构外设资源很给力。关于PWM首先要搞清楚硬件资源分布。根据芯片手册和开发板原理图RK3506通常提供多路PWM输出它们可能被映射到不同的引脚并且可能被其他功能复用比如背光控制、蜂鸣器。第一步也是最重要的一步就是确认你计划使用的PWM输入引脚没有被系统其他功能占用。以我的板子为例PWM0默认用于LCD背光控制如果你把它配置为输入屏幕可能就不亮了。所以我选择了PWM1这一路作为输入捕获的试验通道。你需要查阅你的开发板资料找到正确的PWM控制器编号和通道号。2.3 软件方案内核驱动配置与DTS修改Linux系统下PWM子系统提供了统一的用户接口就是/sys/class/pwm/下面的那些文件。实现输入捕获需要底层驱动支持。瑞芯微提供的内核驱动源码中PWM驱动框架是完整的但输入捕获相关的函数可能需要根据具体的芯片型号进行实现和使能。我们的工作就是通过配置内核编译选项和设备树DTS把这些功能激活并正确映射到硬件引脚上。这里不涉及编写全新的驱动更多的是进行配置和移植。方案很明确修改设备树指定引脚功能配置内核打开对应驱动重新编译并测试。3. 开发环境搭建与内核配置详解3.1 获取与准备内核源码首先你需要拿到针对RK3506开发板的内核源码。通常可以从开发板供应商如触觉智能的Git仓库或资料包中获得。确保你获取的是与你的硬件和BSP版本匹配的源码。将源码解压到你的Linux编译主机上我用的是一台Ubuntu 20.04的PC并安装必要的编译工具链。# 安装交叉编译工具链具体名称可能因供应商而异 sudo apt-get update sudo apt-get install gcc-arm-linux-gnueabihf # 进入内核源码目录 cd /path/to/your/linux-kernel # 加载默认配置文件通常由供应商提供如rockchip_linux_defconfig make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- rockchip_linux_defconfig注意编译主机环境要保持干净避免多个版本工具链冲突。如果供应商提供了完整的SDK包最好使用包里自带的工具链兼容性最有保障。3.2 关键内核配置开启PWM输入捕获支持内核的配置决定了哪些功能会被编译进内核。我们需要确保CONFIG_PWM_ROCKCHIP这个驱动被编译为内置y而不是模块m或关闭is not set。使用make menuconfig进行图形化配置是最直接的方式。make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- menuconfig进入配置界面后按以下路径查找按/键进入搜索输入PWM_ROCKCHIP找到配置项的位置。通常位于Device Drivers - Pulse-Width Modulation (PWM) Support - Rockchip PWM support。确保它前面是[*]星号表示编译进内核而不是[ ]或[M]。除了这个总开关还要留意其依赖的子选项特别是与输入捕获Capture相关的。有些平台可能需要额外打开CONFIG_PWM_ROCKCHIP_CAPTURE之类的选项但在RK3506的当前驱动中输入捕获功能是集成在主驱动里的确保主驱动打开即可。配置完成后保存并退出。3.3 设备树DTS修改引脚功能映射设备树是告诉内核硬件如何连接的关键。我们需要修改开发板对应的DTS文件例如arch/arm/boot/dts/rockchip/rk3506-xxx.dtsxxx是你的板子型号在其中添加或修改PWM控制器的节点。目标是配置PWM1控制器并将其通道0映射到具体的物理引脚上。你需要参考RK3506的芯片手册和开发板原理图找到PWM1_CH0对应的引脚号例如GPIO1_C2以及该引脚正确的复用功能pinctrl配置。/* 在DTS文件中找到或添加 pwm1_8ch_0 节点 */ pwm1_8ch_0 { /* 指定引脚控制状态名 */ pinctrl-names active; /* 引用预先定义好的引脚复用配置这个配置通常在其他地方定义好了 */ pinctrl-0 rm_io24_pwm1_ch0; // 这里的 rm_io24_pwm1_ch0 是一个pinctrl节点需要确认其定义存在且正确 /* 启用该PWM控制器 */ status okay; };这里有个大坑pinctrl-0引用的那个节点如rm_io24_pwm1_ch0必须在你使用的内核DTS中有明确定义。如果供应商的DTS里没有你需要根据芯片手册的IOMUX表格自己添加或者咨询供应商获取正确的pinctrl节点名称。这一步配错了内核就无法正确控制硬件引脚后续所有操作都会失败。3.4 内核编译与固件烧录配置完成后就可以开始编译内核了。# 编译内核镜像zImage和设备树二进制文件dtb make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- -j$(nproc) # 编译内核模块如果需要 make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- modules -j$(nproc)编译成功后在arch/arm/boot/目录下会生成zImage在arch/arm/boot/dts/目录下会生成对应的.dtb文件。你需要将这两个文件或者打包成统一固件烧录到开发板。烧录方法取决于你的开发板启动方式如通过USB OTG使用RKDevTool或从TF卡启动。烧录完成后启动开发板通过串口登录系统。4. 系统启动验证与PWM子系统初探4.1 确认PWM控制器已成功加载开发板启动后首先检查PWM驱动是否正常加载以及我们配置的PWM设备是否出现在系统中。# 登录开发板系统后查看内核启动日志 dmesg | grep -i pwm # 你应该能看到类似这样的信息表明pwm驱动已初始化并且找到了我们配置的控制器 [ 1.234567] rockchip-pwm ff170000.pwm: Rockchip PWM probed接下来检查/sys/class/pwm/目录这里应该会出现以pwmchipX命名的目录每个目录代表一个PWM控制器。ls -la /sys/class/pwm/如果看到了pwmchip0或其他编号说明PWM子系统已经就绪。进入该目录可以查看这个控制器支持多少个PWM通道。cd /sys/class/pwm/pwmchip0 cat npwm通常输出8表示这个控制器支持8个通道对应RK3506的8通道PWM。4.2 理解sysfs下的PWM接口Linux的PWM子系统通过sysfs提供了简洁的用户空间控制接口这对测试来说非常方便。主要操作文件如下export向这个文件写入一个通道号如0即可“导出”该通道随后会在当前目录下生成一个pwmX如pwm0的子目录。unexport写入通道号用于取消导出。在pwmX目录内关键的控制文件有period设置或读取PWM周期单位纳秒 ns。duty_cycle设置或读取PWM高电平时间单位纳秒 ns。enable写入1启动PWM输出写入0停止。polarity设置极性normal表示高电平有效inversed表示低电平有效。capture这是我们实现输入捕获的关键文件。读取它可以获取捕获到的周期和占空比。实操心得在操作sysfs接口时务必注意权限。通常需要root用户。另外export一个通道后必须进入生成的pwmX目录进行操作。一个通道在同一时刻只能用于一种模式输出或输入不能同时进行。5. 双板测试实战搭建PWM输入捕获环境最可靠的测试方法是使用两个相同的RK3506开发板一个作为信号发生器输出PWM另一个作为信号分析仪输入捕获。这能排除信号源不稳定的干扰直接验证驱动配置是否正确。5.1 硬件连接准备准备两块RK3506开发板我们称为板A用于捕获和板B用于输出。确认引脚根据原理图找到板A上我们配置为PWM输入的那个引脚例如PWM1_CH0对应的物理插针。同样找到板B上一个可以输出PWM的引脚为了简单也用PWM1_CH0但需在板B上配置为输出模式。物理连接使用杜邦线将板B的PWM输出引脚连接到板A的PWM输入引脚。共地极其重要必须用另一根杜邦线将两块开发板的GND地引脚连接在一起。没有共地参考电平不一致信号无法被正确识别是导致捕获失败的常见原因。供电确保两块开发板都已正常上电启动。5.2 信号源板板B配置输出标准PWM波在板B上我们通过sysfs接口配置PWM输出一个已知参数的信号方便验证捕获结果。# 登录板B的终端 # 1. 导出PWM通道0假设我们要使用pwmchip0的通道0 cd /sys/class/pwm/pwmchip0 echo 0 export # 2. 进入该通道的控制目录 cd pwm0 # 3. 设置PWM周期为20微秒即频率为50kHz echo 20000 period # 20000 纳秒 20 微秒 # 4. 设置高电平时间为10微秒占空比50% echo 10000 duty_cycle # 5. 设置极性为正常高电平有效 echo normal polarity # 6. 使能PWM输出 echo 1 enable执行完这些命令板B的指定引脚就应该输出一个周期20us、占空比50%的方波。你可以用示波器探头连接到这个引脚和地之间验证波形是否正确。如果没有示波器就完全依赖板A的捕获结果来判断。5.3 捕获板板A配置启动输入捕获模式在板A上我们将同一个PWM通道配置为输入捕获模式。# 登录板A的终端 # 1. 导出PWM通道0 cd /sys/class/pwm/pwmchip0 echo 0 export # 2. 进入该通道的控制目录 cd pwm0注意在输入捕获模式下我们不需要设置period,duty_cycle,enable等文件。PWM控制器硬件会监听输入引脚的变化。5.4 执行捕获与结果分析在板A的pwm0目录下直接读取capture文件。cat capture如果一切配置正确硬件连接无误你会立刻看到类似下面的输出20000 10000这表示捕获成功输出的两个数字单位都是纳秒ns。第一个值20000是捕获到的信号周期第二个值10000是捕获到的高电平脉宽占空比时间。这正好对应了我们板B设置的20us周期和10us高电平时间完美匹配结果解读驱动底层通过硬件捕获两次边沿例如上升沿到上升沿的时间差计算出周期再捕获一个周期内高电平的持续时间计算出占空比。然后通过capture文件将这个struct pwm_capture数据结构包含period和duty_cycle两个成员的值返回给用户空间。6. 深度排查当捕获失败时怎么办实际操作中一次成功的概率没那么高。下面是我在调试过程中遇到的各种问题及解决方法堪称“血泪史”。6.1 问题一读取capture文件超时Connection timed out这是最典型的错误现象就是执行cat capture后卡住最后报错read error: Connection timed out。同时dmesg内核日志会打印类似Failed to wait for LPR/HPR interrupt的错误。排查思路与解决步骤检查硬件连接最优先共地了吗这是99%新手会忽略的问题。确保A板和B板的GND用导线可靠连接。信号线连接是否正确接触是否良好换一根杜邦线试试。用万用表测量一下板B的输出引脚在使能PWM后电压是否有变化比如在0V和3.3V之间跳变确保信号源本身是工作的。检查引脚复用与DTS配置确认板A的PWM输入引脚没有被其他功能占用。例如有些引脚可能默认是GPIO、UART或I2C。检查DTS中该引脚的pinctrl配置确保它被设置为PWM功能而不是其他。使用cat /sys/kernel/debug/pinctrl/pinctrl-handles或cat /sys/kernel/debug/gpio如果内核开启了DEBUG_FS来查看引脚当前的实际复用状态。更直接的方法是查看供应商提供的引脚复用表。检查内核驱动配置再次确认内核编译时CONFIG_PWM_ROCKCHIPy已设置。可以检查/proc/config.gz或运行zcat /proc/config.gz | grep PWM_ROCKCHIP来验证运行中的内核配置。确认使用的内核镜像和设备树dtb文件确实是刚才编译并烧录的新版本。有时烧录工具选错文件会导致配置未生效。检查信号参数是否在硬件支持范围内PWM控制器的输入捕获功能对信号频率有范围限制。信号太快周期太短或太慢周期太长都可能无法捕获。RK3506的PWM控制器时钟源和分频器配置决定了其可捕获的范围。如果板B输出的信号频率异常比如因为period设置错误单位弄混成了微秒而不是纳秒也会导致捕获失败。用示波器检查信号频率是否符合预期。6.2 问题二捕获到的数值不稳定或误差大如果cat capture能出结果但数值跳动很大或者与信号源设置值有固定偏差。排查思路信号质量问题PWM信号线是否过长是否靠近电源等干扰源尝试缩短连接线并加上一个上拉或下拉电阻根据实际情况改善信号边沿质量。时钟源误差开发板的主时钟或PWM专用时钟可能存在微小偏差。这是硬件固有误差只要误差在可接受范围内比如1%以内通常可以忽略。如果需要高精度可能需要校准时钟源。软件读取时机cat capture命令会触发一次捕获。如果读取瞬间正好发生在信号边沿附近可能会读到不稳定的值。可以在应用程序中连续读取多次然后取平均值或中位数以提高稳定性。6.3 问题三系统中找不到pwmchipX目录如果/sys/class/pwm/目录是空的或者没有pwmchip0。排查思路驱动未加载或加载失败用dmesg | grep pwm查看启动日志看是否有probe失败的错误信息。可能原因是DTS中PWM控制器的寄存器地址compatible rockchip,rk3506-pwm后的寄存器地址错误或者时钟、中断等资源申请失败。内核配置完全错误可能CONFIG_PWM_ROCKCHIP根本没有被编译进内核。需要重新配置并编译内核。6.4 调试技巧与常用命令速查表问题现象可能原因排查命令/方法cat capture超时1. 硬件未共地2. 引脚复用错误3. 信号源未工作4. 驱动配置错误1. 检查GND连接2.dmesg | grep pwm看错误3. 测量信号源引脚电压4. 检查/sys/kernel/debug/pinctrl/捕获值跳动大1. 信号干扰2. 读取时机问题1. 缩短连线远离干扰源2. 软件端多次读取取平均无pwmchip0目录1. 驱动未加载2. DTS节点status未okay1.dmesg | grep -i pwm2. 检查DTS中status “okay”捕获值恒为01. 信号极性反了2. 硬件引脚损坏1. 尝试在信号源板切换polarity2. 更换测试引脚7. 从测试到应用编写用户空间程序通过命令行测试成功后下一步就是将这些操作集成到你的应用程序中。我们不能一直靠shell脚本需要编写C或Python程序来周期性地读取PWM捕获值。7.1 C语言示例程序下面是一个简单的C程序它打开PWM捕获的sysfs接口循环读取并打印捕获到的周期和占空比。#include stdio.h #include stdlib.h #include fcntl.h #include unistd.h #include string.h #include errno.h int main() { const char *capture_path /sys/class/pwm/pwmchip0/pwm0/capture; char buffer[64]; unsigned int period_ns, duty_ns; int fd; int ret; // 1. 打开capture文件只读 fd open(capture_path, O_RDONLY); if (fd 0) { perror(Failed to open capture file); return -1; } // 2. 循环读取捕获数据 while (1) { lseek(fd, 0, SEEK_SET); // 每次读取前回到文件开头 ret read(fd, buffer, sizeof(buffer) - 1); if (ret 0) { perror(Failed to read from capture); close(fd); return -1; } buffer[ret] \0; // 确保字符串结束 // 3. 解析读取到的字符串 period duty_cycle if (sscanf(buffer, %u %u, period_ns, duty_ns) 2) { float period_us period_ns / 1000.0; float duty_us duty_ns / 1000.0; float freq_khz 1000000.0 / period_us; // 频率 1 / 周期 float duty_ratio (duty_us / period_us) * 100.0; // 占空比百分比 printf(Captured: Period%.2f us, Duty%.2f us, Freq%.2f kHz, Duty Ratio%.1f%%\n, period_us, duty_us, freq_khz, duty_ratio); } else { printf(Failed to parse capture data: %s\n, buffer); } usleep(500000); // 每隔500ms读取一次避免过于频繁 } close(fd); return 0; }编译与运行# 在开发板上使用交叉编译工具链编译 arm-linux-gnueabihf-gcc pwm_capture.c -o pwm_capture # 运行程序需要root权限 sudo ./pwm_capture注意事项capture文件是“一次性”的。每次读取都会触发一次新的硬件捕获操作并阻塞直到捕获完成或超时。因此读取频率不能高于输入信号的频率。程序中的usleep用于控制读取间隔。在实际应用中你可能需要根据信号频率和实时性要求来调整这个间隔或者使用多线程/异步IO来处理。7.2 进阶思考精度、实时性与多路捕获精度问题sysfs接口的精度受内核和硬件限制。对于更高精度的测量如纳秒级可能需要直接操作硬件寄存器或使用内核模块。但对于大多数调频、测速应用纳秒级精度已经足够。实时性上述方式是在用户空间轮询存在一定的延迟和不确定性。如果对实时性要求极高如无人机飞控可能需要考虑编写内核驱动通过中断方式将捕获数据直接送入应用程序或者使用Linux的IIO工业IO子系统框架它可能提供更专业的脉冲计数和捕获接口。多路捕获RK3506的PWM控制器支持多通道。你可以在DTS中配置多个通道然后在用户空间为每个通道导出独立的pwmX目录分别进行读取。注意硬件资源如中断线是否足够。8. 项目总结与延伸应用这次在触觉智能RK3506开发板上调试PWM输入捕获功能从查阅资料、配置内核、修改DTS到双板联调、排查故障最终成功实现了稳定的信号测量。整个过程的核心在于理解Linux内核的设备树机制和驱动框架以及硬件引脚复用的概念。最大的收获是解决了那个“捕获超时”的坑根本原因就是两块板子之间忘了接共地线——一个低级错误却浪费了最多的时间。这个功能一旦调通应用场景就打开了。除了文章开头说的电机测速、舵机信号解码还可以用来测量脉冲频率比如测量风扇转速传感器的输出。解码遥控器信号很多航模遥控器接收机输出的是PPM或PWM信号。工业传感器接口部分距离、流量传感器以PWM形式输出模拟量。作为简单的频率计使用。RK3506本身接口丰富性能也足够加上现在稳定工作的PWM输入捕获作为工业网关或控制器的大脑就更称职了。最后给同样在嵌入式Linux路上摸索的朋友一个建议遇到硬件相关的问题一定要示波器先行。它能直观地告诉你信号有没有、对不对比任何软件调试都管用。