一、项目背景
常规 RS485 通信中,单片机一般通过 RS485 收发器接另外一个与单片机相连接RS485模块,再通过连接硬件UART 接收数据。相当于RS485只负责远距离传输,本质还是通过uart解析,发送和接收数据。
常规的通信链路通常如下:
单片机 UART → RS485 收发器 → A/B 总线 → RS485 收发器 → UART
本次实验没有使用收发器上述方式,而是从 USB 转 RS485 模块的 A、B 接口获取 RS485 差分总线信号。
我将 A、B 总线信号送入单片机 ADC 进行采样,并在软件中对采样结果进行电平判决、起始位识别和 UART 时序解析,最终还原出串口协议数据,再通过单片机 UART 输出到串口调试助手显示。
因此,本实验采集的是 RS485 总线侧的 A/B 信号,而不是 TTL/UART 侧的 RX 信号。
难点:波特率速率,协议解析和组合,uart串口的特性,电平判决,起始位和结束位的判断
而这次的流程如下:
串口调试助手
↓
USB 转 RS485
↓
RS485 A/B 总线
↓
单片机 ADC 采样
↓
软件解析 UART 数据帧
↓
单片机 UART
↓
USB 转 TTL
↓
串口调试助手显示
二、目标
本实验的目标包括:
-
使用 ADC 采集 RS485 A、B 总线信号;
-
根据 ADC 采样结果进行逻辑电平判决;
-
检测 UART 起始位;
-
按 UART 时序依次采集数据位;
-
判断停止位,完成单字节接收;
-
将接收到的字节组合成协议帧;
-
通过 UART 输出协议内容,并在串口调试助手中以十六进制格式显示。
三、实现原理
RS485 使用 A、B 两根线进行差分传输。总线上的差分电压会随 UART 数据位变化而变化。
本实验通过 ADC 对 A、B 信号进行采样,并根据采样值判断当前总线状态。软件检测到起始位后,按照 UART 的位时间,在每一位的中心位置进行采样,从而依次恢复数据位。
以常见的 UART 8N1 格式为例,一帧数据包括:
1 位起始位 + 8 位数据位 + 1 位停止位
其中:
-
空闲状态通常为逻辑 1;
-
起始位为逻辑 0;
-
数据位低位先发送;
-
停止位为逻辑 1。
软件解码过程如下:
检测到起始位,循环判断否则读取和延迟差一点就去的数据就会差很大
↓
延时 0.5 个 bit 时间
↓
判断是否真的为起始位
↓
采集第 0 位数据
↓
每隔 1 个 bit 时间采集下一位
↓
连续采集 8 位数据
↓
判断停止位,循环判断否则读取和延迟差一点就去的数据就会差很大
↓
得到一个完整字节
四、关键难点
1. 波特率与 ADC 采样速率
本实验中最关键的问题是采样速率。
UART 的每一位都有固定持续时间。以 9600 bps 为例:
目前最高速率为14400,可以准确的完成目标,波特率过大,对实时性要求很高
1 bit 时间 = 1 / 9600 ≈ 104 us
若 ADC 采样速度过慢,可能无法捕获起始位,也无法在数据位中心位置完成有效采样,最终会出现乱码、丢字节或帧错误。
为了保证解析效果,ADC 的有效采样率应明显高于 UART 波特率。实际测试中,可根据波特率、单片机性能和 ADC 转换时间调整采样频率。
波特率越高,单个 bit 的时间越短,对 ADC 速度、定时精度和程序响应速度的要求也越高。
2. 电平判决
ADC 采集到的是电压值,而 UART 解码需要的是逻辑 0 和逻辑 1,因此需要将模拟采样值转换为数字逻辑状态。
若分别采集 A、B 信号,可根据两者的电压关系或差分结果进行判决。为了避免噪声造成误判,建议设置高、低两个阈值,形成滞回判断。
if (adc_value > THRESHOLD_HIGH)
{
logic_level = 1;
}
else if (adc_value < THRESHOLD_LOW)
{
logic_level = 0;
}
else
{
logic_level = last_logic_level;
}
这种方式可以减少信号处于阈值附近时反复跳变的问题。
3. 起始位判断
UART 数据帧以起始位开始。软件需要先检测总线从空闲状态切换到起始位的过程。
如果起始位检测不准确,后续数据位采样时刻都会发生偏移,导致整帧数据解析错误。因此,检测到起始位后,不能立即作为第 0 位数据进行采样,而是等待电平从空闲位到起始位,再等待约 0.5 个 bit 时间检验是否真的为起始位后,再采样第一个数据位
4. 数据位采样与时序控制
UART 数据按照低位在前的顺序发送,即 bit0、bit1、bit2......bit7。
软件在检测到起始位后,以一个 bit 时间为间隔依次采集 8 次,并组合成一个字节。
uint8_t SoftUart_ReadByte(void)
{
uint8_t data = 0;
uint8_t i;
// 已检测到起始位,等待至第 0 位数据中心
Delay_1_5Bit();
for (i = 0; i < 8; i++)
{
if (Read_AdcLogicLevel() == 1)
{
data |= (1 << i);
}
Delay_1Bit();
}
return data;
}
实际工程中,建议使用定时器中断、ADC 定时触发或 DMA 等方式提高时序稳定性,避免单纯使用软件延时导致误差累积。
5. 停止位判断
读取完 8 位数据后,还需要判断停止位。
停止位正常应保持为逻辑 0。若停止位判断失败,则说明当前帧可能存在干扰、采样时序偏差或通信参数不一致等问题,可将该字节标记为帧错误并丢弃。
while(1)
{
int ret = ADC_Read();
if(ret == 0)
{
break;
}
}
6. 协议数据的组合
完成单字节接收后,还需要根据具体协议进行数据组合。
例如,若通信协议采用固定帧头、固定长度或 CRC 校验,可将接收字节依次存入缓存区,再根据协议规则判断一帧数据是否完整。
rx_buffer[rx_index++] = rx_byte;
if (rx_index >= expected_length)
{
// 判断帧头、长度、校验等
Protocol_Parse(rx_buffer, rx_index);
rx_index = 0;
}
在串口调试助手中,建议以十六进制方式显示数据,便于与发送端协议逐字节对比。
六、方案特点
本方案的优点:
-
不依赖 RS485 收发器的
RO引脚进行数据接收; -
直接观察和采集 RS485 A/B 总线侧信号;
-
可以学习 ADC 高速采样、UART 时序和协议解析;
-
适用于低波特率实验、通信信号分析及特殊场景验证;
-
可在解析数据的同时扩展总线状态监测功能。
本方案的局限性:
-
对 ADC 采样速率和定时精度要求较高;
-
波特率提高后,软件解析难度会明显增加;
-
总线噪声、阈值选择和采样偏差都可能造成误码;
-
相比硬件 UART 接收,CPU 占用更高、稳定性更依赖软件;
-
A/B 总线接入 ADC 前应做好限流、分压或钳位保护,避免异常电压损坏 ADC 引脚。
七、总结
本实验通过直接采集 USB 转 RS485 模块 A、B 接口的总线信号,利用单片机 ADC 完成电平采样,并在软件中实现 UART 起始位检测、数据位读取、停止位判断和协议组合。
最终,单片机能够将解析出的协议数据通过 UART 输出到串口调试助手,并以与发送端一致的格式显示。
该实验说明:在采样率和时序精度满足要求的前提下,ADC 不仅可以采集模拟量,也能够用于低速 RS485 通信信号的采集和软件解析。虽然实际产品通常仍优先采用 RS485 收发器配合硬件 UART 的方案,但 ADC 软解码方式对于学习通信原理、分析总线波形和验证协议解析流程具有一定参考价值。
补充:
方法一:利用AB进行ADC转换后的数据进行差值,根据差值判断逻辑0和1
局限,分压电阻不够时,电压大于3300mv,差值为0,导致数据不准确
方法二:利用单口判断逻辑1或逻辑0,当为逻辑1时差值比较的,A口的电压值就比较高,相反A口的逻辑值就比较小,这样的方法比较准确和快速
缺点:需要中间值来判断电平状态
必须通过协议解析提取数据,因为uarttx发送完数据后回回到默认电平状态,一般是高电平,所以在空闲时间回出现大量的逻辑1