ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ArduPilot 的 RC AllInOnePRU 固件深度解析:基于 BeagleBone Black PRU 的 12 路 PWM 输出与 RC 输入实现

ArduPilot 的 RC AllInOnePRU 固件深度解析:基于 BeagleBone Black PRU 的 12 路 PWM 输出与 RC 输入实现 ArduPilot 的 RC AllInOnePRU 固件深度解析基于 BeagleBone Black PRU 的 12 路 PWM 输出与 RC 输入实现【免费下载链接】ardupilotArduPlane, ArduCopter, ArduRover, ArduSub source项目地址: https://gitcode.com/GitHub_Trending/ar/ardupilot导读本文以 ArduPilot 仓库中 Tools/Linux_HAL_Essentials/pru/aiopru/README.md 为线索完整拆解 RC AllInOnePRURC AllInOnePRU固件的功能定位、底层时序原理、构建安装流程与板卡适配差异。读完本文你将掌握该 PRU 固件如何在 BeagleBone Black 上同时实现 5ns 精度的单通道 RC 输入与 1us 精度的 12 路 PWM 输出理解其 IEP 定时器、ECAP 捕获、共享 RAM 通信等核心机制并能自行完成pasm工具链安装、固件重编译与测试验证。一、项目定位Linux 平台下的 PRU 实时外设固件ArduPilot 在 BeagleBone 系列BBB上运行时将飞行控制中的高实时性外设任务——RC 信号捕获与多路 PWM 电机/舵机输出——下沉到 AM335x SoC 内置的PRUProgrammable Real-time Unit可编程实时单元上执行避免 Linux 内核调度抖动对毫秒级航模信号的影响。这一整套 PRU 固件均位于 Tools/Linux_HAL_Essentials/pru 目录包含aiopru/本文主角——RC AllInOnePRU同时承担 RC 输入与 PWM 输出pwmpru/独立的 PWM 输出固件pwmpru1.crcinpru/独立的 RC 输入捕获固件rcinpru0.crangefinderpru/测距仪辅助固件。RC AllInOnePRU 将两者合一旨在以单个 PRU 内核服务整机的遥控与动力通道需求。其源码主体为 RcAioPRU.p454 行 PRU 汇编官方能力描述为1 通道 RCInput精度 5ns12 通道 RCOutput精度 1us使用 BeagleBone Black 时可直接使用RC AllInOnePRU can be used with BeagleBone Black固件使用 PRU 汇编语言编写。二、时序基础5ns 与 1us 精度从何而来固件的全部精度指标都建立在定时器频率选择上。以 BeagleBone BlackAM335xPRU-ICSS版本的 RcAioPRU.p 为例文件头部直接定义了时间基准// Timer #define TICK_PER_US 200 #define TICK_PER_MS 200000 // PWM // 0 us #define PWM_PULSE_DEFAULT (0 * TICK_PER_US) // 50 Hz #define PWM_FREQ_DEFAULT (20 * TICK_PER_MS)TICK_PER_US 200意味着每个定时器 tick 对应5ns1/200 us这正是 RC 输入 5ns 精度的来源PWM 输出最小时间步进为 1usTICK_PER_US与12 通道 RCOutput 1us 精度的描述吻合。默认 PWM 频率为 50Hz周期 20ms默认脉冲宽度为 0us由宿主固件在运行时覆盖。主循环中PRU 通过IEPIndustrial Ethernet Peripheral定时器获得全局时间基准// PRU Constants Table #define ECAP C3 #define RAM C24 #define IEP C26三个常量表槽位分别映射到 ECAP输入捕获、共享 RAM与 Linux 宿主通信、IEP定时器。初始化宏INITRcAioPRU.p依次完成复位全部 PWM 引脚 → 清零寄存器 → 初始化环形缓冲区 → 对 12 个通道做load balancing 相位错开各通道 next_time 从 1000 tick 起以 1000 tick 递增→ 默认关闭所有通道 → 写入 0us 脉宽与 50Hz 周期 → 复位并启动 IEP 计数器。三、12 路 PWM 输出的实现RCOUT_PWM 宏PWM 生成逻辑集中在RCOUT_PWM宏RcAioPRU.p主循环对每个通道依次调用一次核心状态机如下pwm: // Handle arithmetic and counter overflow, check if there is something to do sub register.temp, CH_X_NEXT_TIME, register.time mov register.temp1, 0xF0000000 qbgt pwmend, register.temp, register.temp1 ... // Load pulse duration lbco register.temp, RAM, CH_X_PULSE_TIME_RAM_OFFSET, 4 // Calculate time to next event add CH_X_NEXT_TIME, CH_X_NEXT_TIME, register.temp // Do not set pin if pulse time is 0 qbeq pwmend, register.temp, 0 // Check if channel is enabled qbbc pwmend, CH_X_ENABLE // Set pin set RC_CH_X_PIN其设计要点溢出安全判断用0xF0000000掩码比较next_time - time既处理了 32 位定时器回绕也判断是否到达该通道的下一次动作时刻高电平阶段读取该通道的脉宽CH_X_PULSE_TIME_RAM_OFFSET若脉宽为 0 或通道未使能则跳过否则置位对应引脚低电平阶段读取周期T_TIME与脉宽用T - pulse计算剩余时间到点后清除引脚从而实现标准 PWM 波形。每个通道的引脚映射与周期参数通过 RAM 偏移CH_x_PULSE_TIME_RAM_OFFSET、CH_x_T_TIME_RAM_OFFSET见 RcAioPRU.p从共享内存读取宿主端写入即生效。四、单通道 RC 输入的实现ECAP 捕获 环形缓冲区RC 输入侧利用 PRU-ICSS 的ECAPEnhanced Capture模块以硬件方式捕获 PPM/SBUS 脉冲沿的时间戳。初始化宏RCIN_ECAP_INITRcAioPRU.p配置 ECAP 工作于差分模式difference modeECAP_CTRRST1 1捕获事件 1下降沿后复位计数器ECAP_CAP1POL 1事件 1 由下降沿触发ECAP_CAP2POL 0事件 2 由上升沿触发默认ECAP_CTRRST2 1捕获事件 2上升沿后再次复位计数器ECAP_CAPLDEN 1使能 CAP1~CAP4 寄存器在捕获时装载。这样CAP1/CAP2 中连续两个沿之间的差值即代表脉冲的 S0/S1 持续时间天然抗计数器回绕。捕获宏RCIN_ECAPRcAioPRU.p将 CAP1、CAP2 共 8 字节的时间戳对写入共享 RAM 中的环形缓冲区RCIN_RINGBUFFERSIZE 300头尾指针分别位于RCIN_RING_HEAD_OFFSET 0x1000与RCIN_RING_TAIL_OFFSET 0x1002随后写尾指针、更新索引到达缓冲区末尾时回绕。Linux 宿主侧只需轮询头尾指针即可增量消费捕获数据这正是 RCInput_AioPRU.cpp 中_timer_tick()以 1kHz 频率读取环形缓冲区、将脉冲换算为s1_t / TICK_PER_US微秒值的调用链基础。五、板卡变体与适配从 12 通道到 Pocket 与 DShotaiopru目录内同时维护了多个针对不同硬件修订的 PRU 源文件通过条件编译#ifdef BBBMINI / BBBLUE / POCKET区分引脚映射变体源文件PWM 通道数说明BeagleBone Black MiniRcAioPRU.p12默认目标引脚直接映射到 PRU 的r30寄存器位BeagleBone BlueRcAioPRU.p8-DBBBLUE编译PocketBeagleRcAioPRU.p6-DPOCKET编译PocketBeagle Rev2RcAioPRUPB2.p6新增FAILSAFE_PERIOD失联保护机制与COUNT_REG0/1、CMP_CFG_REG等寄存器定义PocketBeagle Rev2 DShotRcAioPRU_PB2_DShot.p6DShot基于 ~250MHz IEP4ns/tick实现 DShot 协议位时序与 SBUS 捕获引脚映射示例如下RcAioPRU.p#ifdef BBBMINI #define RC_CH_1_PIN r30.t10 #define RC_CH_2_PIN r30.t8 ... #define RC_CH_12_PIN r30.t0 #endif #ifdef POCKET #define RC_CH_1_PIN r30.t7 ... #define RC_CH_6_PIN r30.t6 #endif从源码结构看通道数量的差异既取决于目标板卡的物理引出引脚也决定了主循环中实际展开的RCOUT_PWM调用次数RcAioPRU.p 以#ifdef BBBMINI / BBBLUE控制。六、构建与安装pasm 工具链及固件重编译原 README 给出了完整的工具链安装与重编译步骤本文原样继承并补充仓库内 Makefile 细节。6.1 安装 PRU 汇编器 pasmRC AllInOnePRU 固件由 TI 官方 PRU 汇编器pasm编译。安装步骤如下源自 READMEgit clone https://github.com/beagleboard/am335x_pru_package.git cd am335x_pru_package make sudo make install该仓库为 beagleboard 维护的 AM335x PRU 开发包安装后提供pasm命令。6.2 重编译 RCAioPRU.pcd ardupilot/Tools/Linux_HAL_Essentials/pru/aiopru makemake实际执行的命令见 Makefile它会以pasm -V3V3 指令集逐个生成各板卡的二进制头文件RcAioPRU: RcAioPRU.p pasm -V3 -c RcAioPRU.p RcAioPRU_BBBMINI -DBBBMINI pasm -V3 -c RcAioPRU.p RcAioPRU_BBBLUE -DBBBLUE pasm -V3 -c RcAioPRU.p RcAioPRU_POCKET -DPOCKET pasm -V3 -c RcAioPRUPB2.p RcAioPRU_POCKET2 pasm -V3 -c RcAioPRU_PB2_DShot.p RcAioPRU_POCKET2_DShot -CPRUcode_DShot编译产物即仓库中已存在的RcAioPRU_BBBMINI_bin.h、RcAioPRU_BBBLUE_bin.h、RcAioPRU_POCKET_bin.h、RcAioPRU_POCKET2_bin.h、RcAioPRU_POCKET2_DShot_bin.h——这些.h是 PRU 机器码数组被 Linux 宿主驱动以#include方式直接嵌入固件镜像。6.3 其他构建目标make debug为所有目标追加-DDEBUG编译此时 PRU 会额外将 IEP 计数器值写入TIME_OFFSET并记录最大主循环周期到MAX_CYCLE_TIME_OFFSET见 RcAioPRU.p 与MAX_CYCLE_TIME宏用于排查定时器与实时性问题make test用宿主机 gcc 编译 RcAioPRUTest.c 生成测试程序RcAioPRUTestmake clean删除上述生成的_bin.h与测试程序。七、测试与验证RcAioPRUTest 与 start_test仓库为固件提供了独立的宿主测试程序 RcAioPRUTest.c它通过/dev/mem与mmap直接映射 PRU 的控制寄存器RCOUT_PRUSS_CTRL_BASE、指令内存RCOUT_PRUSS_IRAM_BASE以及输入/输出共享 RAMRCIN_PRUSS_RAM_BASE/RCOUT_PRUSS_RAM_BASE完整模拟了 ArduPilot 宿主驱动的加载与通信过程加载与自检复位 PRU1*ctrl 0→ 将PRUcode拷入 IRAM → 启动 PRU*ctrl | 2→ 写入各通道 50Hz 周期并全部使能看门狗式重载循环检查 IEP 计数器pwm-time是否真正启动若仍为0xffffffff则自动复位、重载、重启 PRU配合make debug编译才能观测该值数据观测每 1 秒扫描环形缓冲区 300 个条目统计 S0/S1 的最小值、最大值与抖动jitter单位为 ns并打印头尾指针、最大周期、当前时间戳动态激励给 12 个通道写入 900~1900us 随机脉宽ch2 固定 1500us验证 PWM 输出随写即变。配套脚本 start_test 用于准备测试环境导出 GPIO80 并拉高PocketBeagle 上的 RC 输入使能将P8_15引脚 mux 切换为pruecapin_puPRU ECAP 输入 上拉随后以sudo运行RcAioPRUTest。其打印信息head/tail/s0/s1/jitter可直接用于评估 RC 输入捕获的实时性与稳定性。八、宿主侧集成与 ArduPilot Linux HAL 的协同PRU 固件本身不感知飞行逻辑真正消费/驱动它的是 libraries/AP_HAL_Linux 下的 HAL 驱动RCOutput 驱动RCOutput_AioPRU.cpp 根据板卡子类型HAL_BOARD_SUBTYPE_LINUX_BLUE/LINUX_POCKET/ 默认 BBBMINI#include对应_bin.h在init()中完成同样的复位 → 装载 IRAM → 启动 PRU流程并默认将全部通道设为 50Hzset_freq(0xFFFFFFFF, 50)后续顶层载具代码可按需覆盖频率RCInput 驱动RCInput_AioPRU.cpp 在init()中映射 RC 输入共享 RAM并在_timer_tick()1kHz 调度中消费环形缓冲区将脉冲转换为微秒供上层_process_rc_pulse解析。由此形成完整的实时闭环Linux 用户态 → mmap 共享 RAM → PRU 汇编固件IEP/ECAP 硬件计时→ GPIO/捕获引脚。这也解释了为何该固件只适用于 BeagleBone 系列PRU-ICSS 是 AM335x/AM62x 等 TI 处理器的特性而不适用于树莓派等无 PRU 的平台。九、注意事项与适用限制平台绑定本固件依赖 PRU-ICSS 硬件IEP、ECAP、常量表仅能在 BeagleBone Black、BeagleBone Blue、PocketBeagle 等 TI 平台运行精度前提5ns/1us 的精度指标以 IEP 计数器运行正常200MHz / 250MHz 时钟为前提调试时可借助-DDEBUG编译的TIME_OFFSET与MAX_CYCLE_TIME_OFFSET观测定时器与主循环最坏周期运行权限宿主测试程序与驱动均需以 root 权限打开/dev/mem进行物理内存映射固件与驱动配套修改RcAioPRU.p后必须重新执行make生成_bin.h并重新编译 HAL 驱动否则共享 RAM 布局不一致会导致通道错乱或 RC 输入失效。结语RC AllInOnePRU 是 ArduPilot Linux 移植中软硬协同的典型范例用 PRU 汇编把最严苛的实时信号处理从 Linux 内核调度中剥离出来以 5ns 的输入捕获精度和 1us 的输出步进精度服务航模遥控链路。通过 README、RcAioPRU.p、Makefile 与 RcAioPRUTest.c 四份文件的配合阅读你既可以完成从工具链安装、固件重编译到实测验证的全流程也能借此理解 PRU 编程中常量表、定时器配置与共享内存协议的核心方法论。【免费下载链接】ardupilotArduPlane, ArduCopter, ArduRover, ArduSub source项目地址: https://gitcode.com/GitHub_Trending/ar/ardupilot创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表