RS485通信
串口通信
串口通信是全双工通信
需要定义好帧格式和波特率
一帧串口数据:
低电平作为起始位,高电平作为停止位,这是常见的帧格式
波特率常见的为:9600、19200、115200
9600:代表一秒内串口输出9600个高低电平
采用TTL逻辑(Transistor-transistor logic 晶体管-晶体管 逻辑)
输出高电平最小2.4V,输出低电平最大0.4V
一般认为串口通信的高电平为5V,低电平为0V
抗干扰能力弱
常用于:开发板的两个芯片之间,芯片和电脑的通讯,距离不超过1m
RS232
Recommended Standard
还是全双工通信
一般常用的就只有三个接口
单片机进行RS232通信:通过添加一个电平转换芯片MAX232
将TTL电平转换为232电平,5V转换为12V,0V转换为-12V
转换后的
增强了抗干扰能力,通信距离可达到15m,速率为20K(19200波特率)
RS485
就是在串口通信的基础上添加一个485转换芯片
485转换芯片:将收到的串口信号转换为差分信号
采用的半双工通信,同一时间只能接收数据或者发送数据
差分信号只需要两跟线,不需要地线
当信号A小于信号B时,代表逻辑0
当信号A大于信号B时,代表逻辑1
最大特点:抗干扰能力强
RS485通信使用的时两根信号线的插值来表示逻辑0和逻辑1
两根信号线采用双绞线的方式连接在一起,这样即使受到干扰,也是两根线同时受到干扰
传输距离最远可达1200米,频率可达50M
对比
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
|
|
把数据当成二进制数,用约定的多项式做 “模 2 除法”,得到的余数就是校验码。
crc = 0xFFFF; 设置的初始都为1
crc ^= (uint16_t)data[i]; 异或数据,相同为0,不同为1
为什么用异或呢? 因为能清楚的知道哪个数据发生错误 只要数据错 1 位,我必须能发现!
if (crc & 1) { 最后一位是1?够不够除? crc »= 1; 先移位(挪一位) crc ^= 0xA001; 够除!减去“除数”(异或多项式) } else { crc »= 1; 不够除!只移位,不减 }
-
- 为什么不用加法(+)
不同数据可能算出相同结果,错误会被掩盖,还会进位溢出。
例子:
原始:1+2+3 = 6
出错:1+4+1 = 6
结果一样,但数据已经错了,校验失效。
-
- 为什么不用减法(-)
会出现负数,不符合数据校验逻辑,也不稳定。
例子:
2-3-5 = -6
负数不适合作为校验值。
-
- 为什么不用乘法(×)
数值会急剧变大,很容易溢出,程序直接出错。
例子:
2×3×100×200 = 120000
远超单片机 16 位范围,结果失真。
-
- 为什么不用除法(/)
运算慢,会产生余数,无法固定校验格式。
例子:
7÷2=3 余 1
余数不好处理,不适合通信校验。
-
- 为什么用异或(^)
-
一位出错,结果完全不同,检错强
-
不进位、不溢出,运算快
-
是 CRC 校验的标准运算
我们做除法 123 ÷ 5:
-
先看第一位 1
1 比 5 小 → 不够除
-
再看前两位 12
12 比 5 大 → 够除
对应到 CRC 代码里:
最后一位 = 0 → 太小了 → 不够除
最后一位 = 1→ 够大了 → 够除