Time Division Multiplexing[TDM] Arduino实现及RFM23模块全双工改造咨询
Arduino平台RFM23半双工模块TDM模拟全双工实现方案
核心实现逻辑
TDM(时分复用)的核心是为通信网络内的所有节点分配互斥的收发时间片,通过全局时间同步避免半双工模块的收发冲突。RFM23的收发状态切换延迟小于1ms,采用10ms级时间片即可满足绝大多数低速率物联网场景的双向通信需求,单节点仅需1片RFM23即可实现原2片方案的双向通信能力。
硬件接线要求
和常规半双工RFM23接线方案完全一致,无需额外外设:
- RFM23的SPI接口(SCK、MISO、MOSI)接Arduino对应SPI引脚
- RFM23的CS引脚接Arduino自定义GPIO(示例中为10号引脚)
- RFM23的nIRQ引脚接Arduino外部中断引脚(示例中为2号引脚)
- RFM23的SDN引脚可接Arduino GPIO实现硬复位,也可直接接地
核心TDM调度规则
- 网络内设1个主节点,其余为从节点,主节点周期性发送同步帧校准所有从节点的本地时钟,确保全网络时间片对齐
- 单个通信周期可根据节点数量拆分,2节点通信时可拆分为2个等长时间片,预留1ms保护间隔抵消模块切换延迟
- 所有数据帧采用固定格式:
0xAA 0x55(帧头) + 1字节节点地址 + 1字节数据长度 + N字节 payload + 1字节CRC8校验,降低接收端识别成本
示例核心代码
基于RH_RF22库开发,可直接复用基础配置:
#include <RH_RF22.h> // 硬件引脚定义 #define RFM23_CS_PIN 10 #define RFM23_IRQ_PIN 2 RH_RF22 rfm23(RFM23_CS_PIN, RFM23_IRQ_PIN); // TDM参数配置 #define SYNC_PERIOD 1000 // 主节点同步帧发送间隔,单位ms #define TIME_SLOT_MS 10 // 单时间片长度,单位ms #define GUARD_DELAY_MS 1 // 状态切换保护间隔 bool is_master_node = true; // 当前节点身份配置,从节点设为false bool current_is_tx_slot = false; uint32_t last_sync_tick = 0; uint32_t slot_start_tick = 0; void setup() { Serial.begin(115200); // 初始化RFM23模块 if (!rfm23.init()) { Serial.println("RFM23 init failed"); while(1); } rfm23.setFrequency(433.0); // 按需修改通信频段 rfm23.setTxPower(RH_RF22_TXPOW_11DBM); } void loop() { uint32_t current_tick = millis(); // 主节点同步帧发送逻辑 if (is_master_node && current_tick - last_sync_tick >= SYNC_PERIOD) { uint8_t sync_frame[] = {0xAA, 0x55, 0x00, 0x00, 0x00}; // 同步帧固定标识 rfm23.send(sync_frame, sizeof(sync_frame)); rfm23.waitPacketSent(); last_sync_tick = current_tick; current_is_tx_slot = true; slot_start_tick = current_tick; } // 时间片切换逻辑 if (current_tick - slot_start_tick >= TIME_SLOT_MS) { current_is_tx_slot = !current_is_tx_slot; slot_start_tick = current_tick; // 切换RFM23工作状态,加保护间隔避免冲突 if (current_is_tx_slot) { rfm23.setModeTx(); } else { rfm23.setModeRx(); } delay(GUARD_DELAY_MS); } // 发送逻辑:仅当前为发送时间片时执行 if (current_is_tx_slot) { uint8_t tx_buf[] = "Test data from local node"; rfm23.send(tx_buf, sizeof(tx_buf)); rfm23.waitPacketSent(); // 按需添加自定义发送逻辑 delay(2); // 避免单时间片内重复发送 } // 接收逻辑:仅当前为接收时间片时执行 if (!current_is_tx_slot) { uint8_t rx_buf[RH_RF22_MAX_MESSAGE_LEN]; uint8_t rx_len = sizeof(rx_buf); if (rfm23.recv(rx_buf, &rx_len)) { // 跳过同步帧,处理业务数据 if (!(rx_len == 5 && rx_buf[0] == 0xAA && rx_buf[1] == 0x55)) { Serial.print("Received payload: "); Serial.write(rx_buf, rx_len); Serial.println(); } else { // 从节点收到同步帧时校准本地时间片 if (!is_master_node) { slot_start_tick = current_tick; current_is_tx_slot = false; rfm23.setModeRx(); } } } } }
调优注意事项
- 时间片长度需匹配单包数据的发送时长,若单包数据量大可适当延长时间片,避免单帧未发送完成就触发时间片切换
- 若网络内节点数量超过2个,可对应增加时间片数量,每个节点分配独立的发送时间片即可
- 长时间运行出现时钟漂移时,可缩短同步帧发送间隔到300~500ms,避免时间片错位导致的丢包
- 丢包率较高时可在数据帧中增加序号字段,在发送时间片内增加最多2次重传逻辑
内容的提问来源于stack exchange,提问作者hadi floka
相关产品推荐
相关产品推荐

