ARTICLE DETAIL

资讯详情

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

Arduino-ESP32 RMT 外设完全指南:红外遥控、WS2812 RGB LED 与自定义数字信号的精确时序收发

Arduino-ESP32 RMT 外设完全指南:红外遥控、WS2812 RGB LED 与自定义数字信号的精确时序收发 Arduino-ESP32 RMT 外设完全指南红外遥控、WS2812 RGB LED 与自定义数字信号的精确时序收发【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32本篇技术指南以 Arduino-ESP32 内核的 Remote Control TransceiverRMT外设为核心系统讲解其内存块架构、完整 API 函数族、数据结构与辅助宏并结合仓库中真实的 WS2812 灯带驱动、硬件 LED 闪烁与 FrSKY XJT D16 遥控协议接收三个示例帮助读者掌握利用 RMT 实现红外收发、NeoPixel 驱动与自定义时序协议解析的完整实战能力。RMT 外设是什么Remote Control TransceiverRMT外设最初是为红外收发设计的但它可以用于生成或接收许多其他类型的、对时序精度有严格要求的数字信号。在 Arduino-ESP32 内核中RMT 的硬件抽象层HAL实现位于 cores/esp32/esp32-hal-rmt.c其公开 API 声明在 cores/esp32/esp32-hal-rmt.h。RMT 擅长精确控制脉冲宽度与时序适合以下典型应用红外IR遥控协议NEC、RC5、Sony 等WS2812/NeoPixel RGB LED 控制对时序要求苛刻自定义通信协议有特定时序要求各类脉冲生成应用RMT 的工作方式是把数字信号编码为一系列具有特定时长的高/低脉冲序列。每个 RMT 符号symbol表示两个连续的脉冲level0/duration0第一段脉冲的电平与时长和level1/duration1第二段脉冲的电平与时长。RMT 内存块Memory Blocks架构RMT 通道使用内存块存储信号数据。不同 SoC 的可用内存块数量不同且部分型号 TX发送与 RX接收内存是分离的ESP32 SoC内存块数量备注ESP32共 8 块TX/RX 通道共享ESP32-S2共 4 块TX/RX 通道共享ESP32-S34 块 TX 4 块 RXTX 与 RX 内存分离ESP32-C32 块 TX 2 块 RXTX 与 RX 内存分离ESP32-C52 块 TX 2 块 RXTX 与 RX 内存分离ESP32-C62 块 TX 2 块 RXTX 与 RX 内存分离ESP32-H22 块 TX 2 块 RXTX 与 RX 内存分离ESP32-P44 块 TX 4 块 RXTX 与 RX 内存分离每个内存块可以存储RMT_SYMBOLS_PER_CHANNEL_BLOCK个符号ESP32/ESP32-S2 为 64 个ESP32-S3/ESP32-C3/ESP32-C5/ESP32-C6/ESP32-H2/ESP32-P4 为 48 个。该常量在源码中定义为SOC_RMT_MEM_WORDS_PER_CHANNEL见 cores/esp32/esp32-hal-rmt.h随编译目标 SoC 自动取值。注意每个 RMT 符号占 4 字节32 位包含两段脉冲的电平与时长信息。也就是说每个内存块为RMT_SYMBOLS_PER_CHANNEL_BLOCK * 4字节。Arduino-ESP32 RMT API 详解本节覆盖 Arduino Core 3.x 中rmtInit()以来的完整 RMT 函数族。所有函数均以 GPIO 引脚号作为通道标识返回值统一为booltrue表示执行成功false表示失败。rmtInit —— 初始化通道初始化指定 GPIO 引脚的 RMT 通道并配置方向、内存大小与频率bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t memsize, uint32_t frequency_Hz);参数说明pin— 用于 RMT 的 GPIO 引脚号。channel_direction— 通道方向RMT_RX_MODE— 接收模式用于读取信号RMT_TX_MODE— 发送模式用于输出信号memsize— 为该通道预留的内存块数量RMT_MEM_NUM_BLOCKS_1— 1 块RMT_MEM_NUM_BLOCKS_2— 2 块RMT_MEM_NUM_BLOCKS_3— 3 块仅 ESP32RMT_MEM_NUM_BLOCKS_4— 4 块仅 ESP32RMT_MEM_NUM_BLOCKS_5至RMT_MEM_NUM_BLOCKS_8— 5~8 块仅 ESP32从源码看rmt_reserve_memsize_t枚举的 3~8 块成员受SOC_RMT_TX_CANDIDATES_PER_GROUP宏条件编译保护见 cores/esp32/esp32-hal-rmt.h这解释了为什么只有 ESP32 才能预留超过 2 个内存块。frequency_Hz— RMT 通道频率tick 频率单位 Hz必须在312.5 kHz 至 80 MHz之间。源码在 cores/esp32/esp32-hal-rmt.c 中对该范围做了显式校验超出范围会打印Bad RMT frequency resolution错误并返回false。频率决定了脉冲时长的分辨率频率tick 周期适用场景10 MHz100 ns高精度适合 WS2812 等灯带1 MHz1 µs适合大多数红外协议400 kHz2.5 µs适合较慢的协议注意RMT 的 tick 由频率参数直接决定。例如 100 ns tick 对应 10 MHz即frequency_Hz 10,000,000。这正是 Arduino Core 3 引入的新行为tick 在rmtInit()中通过频率设定源码中的bus-frequency_Hz字段即保存该频率见 cores/esp32/esp32-hal-rmt.c。rmtDeinit —— 释放通道反初始化 RMT 通道并释放该引脚占用的所有资源bool rmtDeinit(int pin);参数pin— 已用 RMT 初始化的 GPIO 引脚。底层实现会释放驱动并禁用该引脚的 RMT 功能。rmtSetEOT —— 设置传输结束电平设置 RMT 引脚在传输结束时的电平End of TransmissionEOT。该设置会影响rmtWrite()、rmtWriteAsync()或rmtWriteLooping()写完数据后引脚的状态bool rmtSetEOT(int pin, uint8_t EOT_Level);参数说明pin— 配置为 RMT TX 模式的 GPIO 引脚。EOT_Level— 传输结束电平0LOW— 传输结束后引脚置低默认值非零HIGH— 传输结束后引脚置高注意该设置只影响传输过程本身不影响传输开始前的空闲电平。传输前的空闲电平可用digitalWrite(pin, level)手动设置。源码注释明确指出 EOT 电平可按引脚分别设置且同一引脚在不同次传输之间可以随时修改见 cores/esp32/esp32-hal-rmt.h。仓库中 RMT_EndOfTransmissionState 示例专门演示了该特性的使用。rmtWrite —— 阻塞式发送以阻塞模式发送 RMT 数据函数会一直等待所有数据发送完成或超时bool rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t timeout_ms);参数说明pin— 配置为 RMT TX 模式的 GPIO 引脚。data— 指向待发送的rmt_data_t符号数组。num_rmt_symbols— 要发送的 RMT 符号数量。timeout_ms— 超时时间毫秒。使用RMT_WAIT_FOR_EVER可无限等待。阻塞模式函数仅在全部数据发送完毕或超时后才返回。若发送超时函数返回false此后rmtTransmitCompleted()会持续返回false直到传输真正完成。示例rmt_data_t symbols[] { {8, 1, 4, 0}, // 高电平 8 ticks低电平 4 ticks表示 bit 1 {4, 1, 8, 0} // 高电平 4 ticks低电平 8 ticks表示 bit 0 }; rmtWrite(pin, symbols, 2, RMT_WAIT_FOR_EVER);rmtWriteAsync —— 异步发送以非阻塞异步模式发送 RMT 数据函数启动传输后立即返回bool rmtWriteAsync(int pin, rmt_data_t *data, size_t num_rmt_symbols);参数与rmtWrite一致仅无超时参数。非阻塞模式执行后立即返回可用rmtTransmitCompleted()查询传输是否结束。注意如果上一次传输尚未完成就再次调用rmtWriteAsync()函数会立即返回false表示失败而不会等待上一次传输结束见 cores/esp32/esp32-hal-rmt.h。该通道必须先完成前一次传输才能开始新传输。rmtWriteLooping —— 无限循环发送以无限循环模式发送 RMT 数据数据将持续发送直到被停止bool rmtWriteLooping(int pin, rmt_data_t *data, size_t num_rmt_symbols);循环模式数据在硬件层面连续循环发送。停止循环的方式用新数据调用rmtWrite()或rmtWriteAsync()以NULL数据或零长度调用rmtWriteLooping()。注意循环模式需要以全零符号{0, 0, 0, 0}作为数据结束标记EOF。在 RMT_LED_Blink 示例中每个循环数据数组末尾都带有{0, 0, 0, 0}结束符正是这一约束的体现。注意循环模式激活期间rmtTransmitCompleted()恒返回true因为此时该查询无实际意义。rmtWriteRepeated —— 固定次数重复发送将 RMT 数据重复发送指定次数bool rmtWriteRepeated(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t loop_count);参数loop_count— 重复发送次数至少为 1。规则如下loop_count 0— 非法不发送任何数据loop_count 1— 仅发送一次等同单次发送loop_count 1— 循环重复发送注意循环计数特性仅在部分 SoC 上受支持在不支持的 SoC 上该函数返回false。从 RMT_LED_Blink 示例可以看到它的典型用法——按 2/4/8 次重复点亮 LED闪烁完成后自动停止。rmtTransmitCompleted —— 查询发送完成状态检查 RMT 传输是否完成、通道是否可发送新数据bool rmtTransmitCompleted(int pin);返回true表示所有数据已发送完毕且通道可进行新传输否则返回false。注意若rmtWrite()超时或调用了rmtWriteAsync()本函数会持续返回false直到所有数据发送完成。当rmtWriteLooping()处于激活状态时本函数恒返回true因为此时查询无效。rmtRead —— 阻塞式接收发起阻塞式接收操作读取 RMT 数据并存入用户提供的缓冲区bool rmtRead(int pin, rmt_data_t *data, size_t *num_rmt_symbols, uint32_t timeout_ms);参数说明pin— 配置为 RMT RX 模式的 GPIO 引脚。data— 指向接收缓冲区rmt_data_t数组。num_rmt_symbols— 指向变量的指针调用时该变量保存最大可读符号数返回时被更新为实际读取的符号数。timeout_ms— 超时时间毫秒可用RMT_WAIT_FOR_EVER无限等待。阻塞模式函数等待数据到达或超时。若读操作超时num_rmt_symbols不会改变之后可用rmtReceiveCompleted()检查是否有数据到达。若超时后数据异步到达data与*num_rmt_symbols仍会被异步更新见 cores/esp32/esp32-hal-rmt.h 的注释说明。rmtReadAsync —— 异步接收发起非阻塞异步接收操作启动接收后立即返回bool rmtReadAsync(int pin, rmt_data_t *data, size_t *num_rmt_symbols);非阻塞模式执行后立即返回可用rmtReceiveCompleted()检查数据是否已就绪。数据接收完成后缓冲区data与num_rmt_symbols指向的变量会被异步更新。rmtReceiveCompleted —— 查询接收完成状态检查 RMT 数据接收是否完成、缓冲区是否有新数据可处理bool rmtReceiveCompleted(int pin);返回true表示已收到数据且缓冲区可用否则返回false。注意当新的rmtRead()或rmtReadAsync()被调用时接收状态信息会被重置。rmtSetCarrier —— 载波调制/解调为 RMT TX 或 RX 通道设置载波频率调制/解调。红外遥控如 NEC 协议的 38 kHz 载波就是典型应用bool rmtSetCarrier(int pin, bool carrier_en, bool carrier_level, uint32_t frequency_Hz, float duty_percent);参数说明pin— 已配置的 RMT GPIO 引脚。carrier_en— 启用/禁用载波调制TX或解调RX。carrier_level— 载波极性true— 正极性高有效false— 负极性低有效frequency_Hz— 载波频率Hz例如 38 kHz 红外载波填38000。duty_percent— 占空比浮点数 0.0 至 1.0如0.33表示 33% 占空比0.5表示 50% 方波。注意Arduino Core 3 改变了参数形式原先以 ticks 表示的 low/high 现在改为载波频率Hz加占空比百分比浮点 0.0~1.0。源码注释给出的示例是38.5KHz 33% High 38500, 0.33见 cores/esp32/esp32-hal-rmt.h。示例38.5 kHz 载波、33% 占空比rmtSetCarrier(pin, true, true, 38500, 0.33);rmtSetRxMinThreshold —— 接收噪声滤波阈值设置 RX 通道的最小脉冲宽度滤波阈值小于该阈值的脉冲将被视为噪声忽略bool rmtSetRxMinThreshold(int pin, uint8_t filter_pulse_ticks);参数说明pin— 配置为 RMT RX 模式的 GPIO 引脚。filter_pulse_ticks— 最小脉冲宽度RMT ticks。宽度高或低小于该值的脉冲会被滤除设为0可禁用滤波。注意阈值以 RMT tick 为单位具体时间取决于rmtInit()中设置的 RMT 频率。源码实现中会先把 ticks 换算为纳秒再写入硬件滤波寄存器见 cores/esp32/esp32-hal-rmt.c并校验不能超过RMT_LL_MAX_FILTER_VALUE。rmtSetRxMaxThreshold —— 接收空闲阈值设置 RX 通道的最大空闲阈值。当超过该阈值仍未检测到边沿时接收过程视为结束bool rmtSetRxMaxThreshold(int pin, uint16_t idle_thres_ticks);参数说明pin— 配置为 RMT RX 模式的 GPIO 引脚。idle_thres_ticks— 最大空闲时间RMT ticks。超过该时间没有边沿变化接收结束。该阈值同时决定接收数据末尾会读入多少低/高电平位。注意空闲阈值以 RMT tick 为单位取决于rmtInit()设置的频率。源码实现同样会换算为纳秒并校验RMT_LL_MAX_IDLE_VALUE上限见 cores/esp32/esp32-hal-rmt.c。RMT 数据结构rmt_data_trmt_data_t表示一个 RMT 符号两个连续脉冲其定义与 cores/esp32/esp32-hal-rmt.h 中的声明完全一致typedef union { struct { uint32_t duration0 : 15; // 第一段脉冲时长RMT ticks uint32_t level0 : 1; // 第一段脉冲电平0 LOW, 1 HIGH uint32_t duration1 : 15; // 第二段脉冲时长RMT ticks uint32_t level1 : 1; // 第二段脉冲电平0 LOW, 1 HIGH }; uint32_t val; // 以 32 位整数值访问 } rmt_data_t;每个 RMT 符号包含两段脉冲第一段脉冲level0电平持续duration0个 tick第二段脉冲level1电平持续duration1个 tick由于时长字段各为 15 位单个符号内的脉冲时长上限为 32767 个 tick。注意duration0/duration1为 tick 数而非绝对时间实际时长 tick 数 × 每个 tick 的时间由rmtInit()频率决定。构造示例// 创建一个符号高电平 8 ticks随后低电平 4 ticks rmt_data_t symbol { .duration0 8, .level0 1, .duration1 4, .level1 0 }; // 或者使用结构体顺序初始化 rmt_data_t symbol2 {8, 1, 4, 0};辅助宏RMT_SYMBOLS_OF —— 计算符号数量用于计算数组中 RMT 符号个数的辅助宏#define RMT_SYMBOLS_OF(x) (sizeof(x) / sizeof(rmt_data_t))示例rmt_data_t data[] { {8, 1, 4, 0}, {4, 1, 8, 0} }; size_t num_symbols RMT_SYMBOLS_OF(data); // 返回 2该宏定义于 cores/esp32/esp32-hal-rmt.h在RMT_LED_Blink示例中配合循环/重复发送大量使用。RMT_WAIT_FOR_EVER —— 无限等待阻塞操作中表示无限期等待的常量#define RMT_WAIT_FOR_EVER ((uint32_t)portMAX_DELAY)将该常量作为rmtWrite()或rmtRead()的timeout_ms参数即可无限等待rmtWrite(pin, data, num_symbols, RMT_WAIT_FOR_EVER);RMT_SYMBOLS_PER_CHANNEL_BLOCK —— 单块符号数定义每个内存块可容纳的 RMT 符号数ESP32/ESP32-S2每块 64 个符号ESP32-S3/ESP32-C3/ESP32-C5/ESP32-C6/ESP32-H2/ESP32-P4每块 48 个符号示例为 1 个内存块分配缓冲区rmt_data_t buffer[RMT_SYMBOLS_PER_CHANNEL_BLOCK];示例应用从 RGB 灯带到遥控协议接收仓库在 libraries/ESP32/examples/RMT 目录下提供了 8 个完整示例覆盖发送、接收、回环测试、载波/回调等场景。以下是文档重点引用的三个示例的要点分析。RMTWrite_RGB_LED —— WS2812 RGB LED 驱动RMTWrite_RGB_LED.ino 演示了用 RMT 驱动 WS2812NeoPixelRGB LED。其核心思路是把每个 RGB LED 的 24 位颜色数据RGB 各 8 位编码为 RMT 符号位1高电平 0.8 µs 低电平 0.4 µs位0高电平 0.4 µs 低电平 0.85 µs初始化时使用 10 MHz100 ns tick以获得高精度if (!rmtInit(BUILTIN_RGBLED_PIN, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 10000000)) { Serial.println(init sender failed\n); }示例中颜色按位展开为符号数组led_data[NR_OF_ALL_BITS]24 * LED 数量个符号再用rmtWrite(..., RMT_WAIT_FOR_EVER)阻塞发送实现 8×4 灯阵中一个彩色亮点沿灯带移动的效果。RMT_LED_Blink —— 纯硬件 LED 闪烁RMT_LED_Blink.ino 演示了用 RMT 的多种写 API 让普通 GPIO 上的 LED 由硬件产生闪烁波形无需传统 Arduino Blink 代码中的延时循环。该示例以 400 kHz2.5 µs tick初始化数据数组以 25000 tick 1 秒为单位组合出 1s/500ms/250ms 的亮灭节奏并依次演示rmtWriteLooping()— 硬件持续循环闪烁rmtWrite()— 阻塞式单次发送注意传入RMT_SYMBOLS_OF(data) - 1去掉 EOF 符号rmtWriteAsync()while (!rmtTransmitCompleted(BLINK_GPIO));— 异步发送配合完成查询rmtWriteRepeated()— 按 2/4/8 次重复闪烁后自动停止用全零符号{0, 0, 0, 0}停止循环闪烁。RMTReadXJT —— FrSKY XJT D16 遥控协议接收RMTReadXJT.ino 演示了用 RMT RX 接收 FrSKY 遥控器的 XJT D16 协议帧。该协议位时序为0位 6 µs 低 / 10 µs 高1位 14 µs 低 / 10 µs 高见示例头部的注释说明。示例定义xjt_packet_t联合体解析 20 字节帧结构同步头 0x7E、接收机 ID、标志位、8 个通道数据、CRC-16 与帧尾并通过XJT_VALID(i)宏校验符号时序#define XJT_VALID(i) (i-level0 !i-level1 i-duration0 8 i-duration0 11)接收流程为rmtRead()/rmtReadAsync()读入符号 → 按位恢复数据 → 5 个连续 1 的位填充bit stuffing处理 → 通道值解析完整展示了把 RMT 原始脉冲符号解码为高层协议数据的通用套路。使用建议与注意事项先选频率再算 tick所有时长都以 tick 为单位因此rmtInit()的频率是后续所有时序计算的基础。建议按协议需求先确定分辨率如 WS2812 用 10 MHz、红外用 1 MHz 或 400 kHz再以tick 数 × tick 时长 物理时间换算数据。内存块按需预留单块容量有限48 或 64 个符号。一次发送需要更多符号时如多颗 WS2812应使用RMT_MEM_NUM_BLOCKS_2及以上或分多次发送注意 3 块以上仅 ESP32 支持。异步 API 的并发约束rmtWriteAsync()在上一传输未完成时直接返回false循环模式下rmtTransmitCompleted()恒为true这些边界行为在实际工程中需要显式处理。循环发送必须有 EOF使用rmtWriteLooping()或rmtWriteRepeated()loop_count 1时数据数组末尾必须包含{0, 0, 0, 0}结束符号。载波与滤波配合使用红外接收场景建议同时配置rmtSetCarrier()38 kHz 解调与rmtSetRxMinThreshold()噪声滤波并用rmtSetRxMaxThreshold()界定一帧数据的结束三者协同可获得稳定的接收效果。更多示例如 RMTLoopback 回环自测、RMTCallback 回调模式、Legacy_RMT_Driver_Compatible 旧驱动兼容层等均可直接在本仓库的 libraries/ESP32/examples/RMT 目录中查看与运行。【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表