3、CSMA/CD原理在RS485上的应用:载波侦听、冲突检测、随机退避算法
各位同学,咱们今天聊点硬核的。CSMA/CD,这名字听着挺唬人,其实就是「先听再说,撞了就退」。
我在做多主站项目时,最头疼的就是总线冲突。两台设备同时发数据,总线上一片混乱。后来我琢磨着,能不能把以太网那套CSMA/CD搬过来用?嗯,还真行。
3.1 载波侦听:先听听总线有没有人说话
说白了,就是发数据前先看看总线忙不忙。RS485是半双工的,同一时间只能一个人说话。
怎么侦听?我习惯用GPIO检测收发器的RE/DE引脚状态。但更靠谱的办法是:检测总线上的电平变化。
核心思路:如果总线上有数据,差分电压A-B就不会是0V。用ADC或者比较器都能判断。
我在项目中遇到过一个问题:有些廉价收发器在空闲时,A-B电压会飘。后来我加了个滞回比较器,才稳定下来。
// 伪代码:载波侦听
bool carrier_sense() {
// 读取差分电压
int diff = read_ab_voltage();
// 阈值判断,我一般设200mV
if (abs(diff) > 200) {
return true; // 总线忙
}
return false; // 总线空闲
}
小技巧:侦听时间不能太短。我建议至少持续侦听3个位时间,防止误判。
3.2 冲突检测:怎么知道撞车了?
你想想看,如果两个站同时侦听到总线空闲,然后同时发数据,那就撞了。
RS485上怎么检测冲突?我总结了三种方法:
| 方法 | 原理 | 我推荐度 |
|---|---|---|
| 电平对比法 | 发数据时,同时读总线。如果读到的不等于发出的,说明有冲突 | ⭐⭐⭐⭐⭐ |
| CRC校验法 | 接收方校验CRC,如果错误,说明可能冲突了 | ⭐⭐⭐ |
| 超时检测法 | 发完数据后,等应答。超时没收到,就当冲突处理 | ⭐⭐ |
我个人最常用电平对比法。为什么?因为它实时性最好。发一个bit,立刻读回来对比。不一致?好,撞了。
注意:电平对比法对硬件有要求。收发器的驱动能力和接收延迟要匹配。我曾经用某款国产芯片,驱动上升沿太慢,导致误判。后来换了TI的SN65HVD3082才解决。
// 冲突检测示例
bool detect_collision(uint8_t tx_byte) {
uint8_t rx_byte = read_byte_from_bus();
if (rx_byte != tx_byte) {
// 冲突!我遇到过这种情况,多半是另一个站也在发
return true;
}
return false;
}
3.3 随机退避算法:撞了之后怎么办?
检测到冲突,不能马上重发。否则两个站又会同时发,继续撞。这就是死锁。
解决办法?随机退避。每个站等一个随机时间,再尝试重发。
我常用的退避算法是这样的:
- 初始退避窗口:比如0~10ms
- 随机等待:在这个窗口内随机选一个时间
- 指数增长:如果又冲突了,窗口翻倍。比如第二次0~20ms,第三次0~40ms
- 最大限制:窗口不能无限大,我一般设上限500ms
关键点:退避时间必须是随机的。如果所有站都用固定算法,那还是撞。
// 随机退避算法
void backoff(int collision_count) {
// 计算退避窗口,指数增长
int window = BASE_BACKOFF * (1 << collision_count);
// 限制最大窗口
if (window > MAX_BACKOFF) {
window = MAX_BACKOFF;
}
// 随机等待
int wait_time = rand() % window;
delay_ms(wait_time);
}
我的经验:BASE_BACKOFF设多少合适?我建议根据波特率来。9600bps时,我设5ms。115200bps时,设1ms。你想想看,波特率越高,位时间越短,退避时间也可以相应缩短。
3.4 完整流程:从侦听到重发
把上面三步串起来,就是一个完整的CSMA/CD流程:
- 载波侦听:检查总线是否空闲。忙就等。
- 发送数据:空闲了,开始发。
- 冲突检测:边发边读,看有没有冲突。
- 无冲突:发完收工,等应答。
- 有冲突:立即停止发送,执行随机退避。
- 重试:退避结束后,回到步骤1。
我曾经在一个项目里,用这套流程实现了32个从站同时上报数据。刚开始总丢包,后来发现是退避窗口设得太小。调整后,成功率从70%提到了99.5%。
避坑指南:我曾经犯过一个错误——退避时没有关发送器。结果退避期间,发送器还挂在总线上,导致其他站发不了。记住:退避前一定要把RE/DE引脚拉低,让收发器进入接收模式。
3.5 实际应用中的注意事项
CSMA/CD在RS485上不是万能的。我总结了几点:
- 总线长度:超过1000米时,信号延迟会变大。冲突检测的实时性会下降。
- 节点数量:超过32个节点,建议加中继器。否则驱动能力不够。
- 波特率:我建议不超过115200bps。再高的话,位时间太短,冲突检测来不及。
- 接地:RS485一定要共地。我见过太多因为地电位差导致通信失败的案例。
嗯,今天就讲到这里。下一章咱们聊聊「令牌传递法」,那是另一种解决多主站冲突的思路。