title : “串口通信全解(三)”
date : 2021-05-25
tags :
- 设备通信
串口通信全解(三)
介绍完了串口通信的基础和通信时序,已经可以实现数据的传输。本文要介绍的通信协议是通信双方对传输数据格式的一种规则和约定,相当于机器和机器之间交流的语言。有了通信协议,不仅可以增加通信的可靠性,还可以实现更多的功能。接下来将使用MODBUS-RTU工控协议作例子进行讲解。
MODBUS-RTU简介
Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气 Schneider Electric)于1979年为使用可编程逻辑控制器(PLC)通信而发表。**Modbus已经成为工业领域通信协议的业界标准(Defacto),并且现在是工业电子设备之间常用的连接方式。**如果要开发工控设备,学习和能够使用MODBUS协议十分重要。
Modbus协议目前存在用于串口、以太网以及其他支持互联网协议的网络的版本。串口的Modbus协议包括Modbus RTU和Modbus ASCII。以太网的Modbus协议主要是Modbus-TCP。
本人目前使用的是MODBUS-RTU协议。
MODBUS-RTU结构
MODBUS-RTU的协议结构由**“设备地址 + 功能码 + 数据码 + 校验码”**组成。
-
设备地址: 长度为一个字节。范围是0-255(其中有效范围是1-247,其他有特殊用途,比如255是广播地址),意义在于选择应答此次指令的从设备,相当于先点名来指派任务。
-
功能码: 长度为一个字节。不同功能码对应不同功能,发送了功能码,就相当于告诉了从设备将要执行的任务类型。常用功能码有03和06,03功能为读取设备寄存器数据,06功能为将数据写入寄存器。
-
**数据码:**一般占用四个字节。根据功能码不同,有不同结构,将会在后续的实例中进行说明。
-
**校验码:**占用两个字节。为了保证指令没有发生错误,需要把前面的数据进行计算看数据是否一致,如果一致,就说明这帧数据是正确的,再进行回复。如果不一样,说明这帧数据在传输的时候出了问题,不需要进行操作。MODBUS-RTU的校验码为CRC校验。
下面使用两种常用的功能码进行举例说明MODBUS-RTU的数据包结构。
(1)03功能(读寄存器操作)
数据包格式:
设备地址 | 功能码 | 起始地址(高位) | 起始地址(低位) | 寄存器个数(高位) | 寄存器个数(低位) | CRC校验码(高位) | CRC校验码(低位) |
---|---|---|---|---|---|---|---|
0x01 | 0x03 | 0x00 | 0x02 | 0x00 | 0x03 | 0xA4 | 0x0B |
此数据包功能表示为从0x01编号从设备的第0x0002个寄存器地址开始,往后读取0x0003个寄存器的值,CRC 码用于校验。
返回数据形式为设备地址+功能码(03)+读取的字节长度(为寄存器个数的2倍)+读取到的寄存器值+CRC校验码
本例返回值为:
0x01 0x03 0x06 0xXX 0xXX 0xXX 0xXX 0xXX 0xXX CRC(H) CRC(L)
(2)06功能(写寄存器操作)
数据包格式:
设备地址 | 功能码 | 目标地址(高位) | 目标地址(低位) | 写入数据(高位) | 写入数据(低位) | CRC校验码(高位) | CRC校验码(低位) |
---|---|---|---|---|---|---|---|
0x01 | 0x06 | 0x00 | 0x3C | 0x01 | 0x90 | 0x48 | 0x3A |
此数据包功能表示为在0x01编号的从设备地址为0x003C寄存器中写入数据0x0190,CRC码用于校验。
从设备响应的返回数据和接收到的数据包相同。
本例返回值为:
0x01 0x06 0x00 0x3C 0x01 0x90 0x48 CRC(H) CRC(L)
CRC码
CRC码全称为循环冗余校验码,因为数据包的每一位数据都将参与CRC码计算,对结果产生很大影响,所以CRC码可以检测出多位错误。
现给出一种Verilog书写的CRC码计算方法。
初始化CRC校验码为‘0xFFFF’,每有一个字节接收过来后,与当前CRC校验码的低位进行异或运算,进行八次右移。如果移除的位是‘1’,再使用‘0xA001’进行异或;如果移除的位是‘0’,不进行操作。
//对缓存器中所有的数据进行计算得到CRC码
module CRC_generator(en,num,data_in,CLK,CRC_out,cnt,over,N,start);
input CLK,en;
input [7:0] data_in;
input [7:0] num;
output reg [15:0] CRC_out;
output reg [3:0]cnt;
output reg over,start;
output reg [7:0] N;
reg enbuf,enrise,enfall,error;
initial
begin
enbuf = 0;
enrise = 0;
over = 0;
N = 8'b00000000;
cnt = 4'b0000;
CRC_out = 16'b1111111111111111;
error = 0;
start = 0;
end
//en(数据传输完毕指示信号)的下降沿检测, 下降沿出现表示开始了下一次通信
always @(posedge CLK)
begin
enbuf <= en;
enfall <= (~en) & enbuf;
end
always @(posedge CLK)
begin
//新字节和低八位异或
if((en ==1)&&(N < num-2)&&(start == 0)&&(over == 0)&&(num != 0))//num-2表示最后两位不参与运算
begin
CRC_out[7:0] = CRC_out[7:0] ^ data_in;
start = 1;//移位操作准备开始
end
///八次移位操作开始
else if(start == 1)
begin
if(cnt < 4'b1000)
begin
if(CRC_out[0] == 1)//移除的位是1
begin
CRC_out = CRC_out >> 1;
CRC_out = CRC_out ^ 16'b1010000000000001;//和0xA001异或
cnt = cnt + 4'b0001;
end
else begin
CRC_out = CRC_out >> 1;
cnt = cnt + 4'b0001;
end
end
else begin//一个字节完成,准备计算一个字节
cnt = 4'b0000;
N = N+1;
start = 0;
end
end
//八次移位操作结束,准备下一个数据
//发送的数据全部计算完毕,发出指示信号,等待下一次通讯发送数据
else if((en == 1)&&(over == 0))
begin
CRC_out[15:8] <= CRC_out[7:0];
CRC_out[7:0] <= CRC_out[15:8];//高低字节交换
over = 1; //CRC码计算完毕指令
cnt = 4'b0000;
end
//下一次通信开始,将所有值复位准备重新进行计算
else if(enfall == 1)
begin
over = 0;
N = 0;
cnt = 4'b0000;
CRC_out = 16'b1111111111111111;
end
else error <= 1;
end
endmodule
总结
数据的通信协议是人为定义的,是为了实现某一特定任务设置的。相应的,想要实现通信协议,就要编写相应的协议发送、协议接收和协议校验代码,对发送的每一个字节数据赋予意义。MODBUS-RTU协议只是众多通信协议的一种,但是它们的本质是相同的。希望这篇文章能帮助你进一步理解通信协议。