Featured image of post RS485

RS485

RS485通信

串口通信

image-20260322143629009

串口通信是全双工通信

需要定义好帧格式和波特率

一帧串口数据:

image-20260322142904961

低电平作为起始位,高电平作为停止位,这是常见的帧格式

波特率常见的为:9600、19200、115200

9600:代表一秒内串口输出9600个高低电平

采用TTL逻辑(Transistor-transistor logic 晶体管-晶体管 逻辑)

输出高电平最小2.4V,输出低电平最大0.4V

一般认为串口通信的高电平为5V,低电平为0V

image-20260322144134518

抗干扰能力弱

常用于:开发板的两个芯片之间,芯片和电脑的通讯,距离不超过1m

RS232

Recommended Standard

还是全双工通信

image-20260322144439965

一般常用的就只有三个接口

image-20260322144529528

单片机进行RS232通信:通过添加一个电平转换芯片MAX232

image-20260322145218498

将TTL电平转换为232电平,5V转换为12V,0V转换为-12V

image-20260322145407529 image-20260322145622397

转换后的

image-20260322145818932

增强了抗干扰能力,通信距离可达到15m,速率为20K(19200波特率)

RS485

就是在串口通信的基础上添加一个485转换芯片

485转换芯片:将收到的串口信号转换为差分信号

image-20260322150508263

采用的半双工通信,同一时间只能接收数据或者发送数据

image-20260322150601629

差分信号只需要两跟线,不需要地线

image-20260322150645469

当信号A小于信号B时,代表逻辑0

当信号A大于信号B时,代表逻辑1

最大特点:抗干扰能力强

RS485通信使用的时两根信号线的插值来表示逻辑0和逻辑1

两根信号线采用双绞线的方式连接在一起,这样即使受到干扰,也是两根线同时受到干扰

image-20260322151112789

传输距离最远可达1200米,频率可达50M

对比

image-20260322151235601

Modbus协议

主机请求报文

地址码 功能码 寄存器起始地址 寄存器个数 CRC校验

8bit 8bit 16bit 16bit 16bit

例子:

主机发送Tx:01 03 00 00 00 02 C4 0B

从机应答报文

地址码 功能码 字节数 数据1 数据2 CRC校验

8bit 8bit 寄存器*2 16bit 16bit 16bit

例子:

从机应答RX:01 03 04 00 14 00 28 BA 29

功能码

功能码 核心功能 常用场景
01 读线圈状态(开关量) 读继电器、开关状态
02 读离散输入状态 读按钮、传感器输入
03 读保持寄存器(数值) 读电压、温度等数值(你示例用的)
04 读输入寄存器 读外部设备采集的数值
05 写单个线圈 控制单个继电器通断
06 写单个保持寄存器 设定单个参数(如阈值)
15 写多个线圈 批量控制继电器
16 写多个保持寄存器 批量设定参数

CRC

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
uint16_t modbus_crc16(const uint8_t *data, uint16_t length) {
    uint16_t crc = 0xFFFF;
    for (uint16_t i = 0; i < length; i++) {
        crc ^= (uint16_t)data[i]; // 异或当前字节
        for (uint8_t j = 0; j < 8; j++) { // 处理每个位
            if (crc & 1) {
                crc >>= 1;
                crc ^= 0xA001; // Modbus CRC多项式
            } else {
                crc >>= 1;
            }
        }
    }
    return crc;
}

把数据当成二进制数,用约定的多项式做 “模 2 除法”,得到的余数就是校验码

crc = 0xFFFF; 设置的初始都为1

crc ^= (uint16_t)data[i]; 异或数据,相同为0,不同为1

为什么用异或呢? 因为能清楚的知道哪个数据发生错误 只要数据错 1 位,我必须能发现!

if (crc & 1) { 最后一位是1?够不够除? crc »= 1; 先移位(挪一位) crc ^= 0xA001; 够除!减去“除数”(异或多项式) } else { crc »= 1; 不够除!只移位,不减 }

    1. 为什么不用加法(+)

​ 不同数据可能算出相同结果,错误会被掩盖,还会进位溢出。

例子:

​ 原始:1+2+3 = 6

​ 出错:1+4+1 = 6

​ 结果一样,但数据已经错了,校验失效

    1. 为什么不用减法(-)

​ 会出现负数,不符合数据校验逻辑,也不稳定。

例子:

​ 2-3-5 = -6

​ 负数不适合作为校验值。

    1. 为什么不用乘法(×)

​ 数值会急剧变大,很容易溢出,程序直接出错。

例子:

​ 2×3×100×200 = 120000

​ 远超单片机 16 位范围,结果失真。

    1. 为什么不用除法(/)

​ 运算慢,会产生余数,无法固定校验格式。

例子:

​ 7÷2=3 余 1

​ 余数不好处理,不适合通信校验。

    1. 为什么用异或(^)
    • 一位出错,结果完全不同,检错强

    • 不进位、不溢出,运算快

    • 是 CRC 校验的标准运算


我们做除法 123 ÷ 5

  1. 先看第一位 1

    1 比 5 小 → 不够除

  2. 再看前两位 12

    12 比 5 大 → 够除

对应到 CRC 代码里:

​ 最后一位 = 0 → 太小了不够除

​ 最后一位 = 1→ 够大了够除


最后更新于 2026-03-28 13:56