1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
|
#include "rain.h"
#include "rs485.h"
// Modbus CRC16 校验
u16 CRC16_Modbus(u8 *buf, u8 len)
{
u16 crc = 0xFFFF;
for(u8 i=0; i<len; i++)
{
crc ^= buf[i];
for(u8 j=0; j<8; j++)
{
if(crc & 0x01)
{
crc >>= 1;
crc ^= 0xA001;
}
else
{
crc >>= 1;
}
}
}
return crc;
}
// 发送标准Modbus帧
void Modbus_SendFrame(u8 addr, u8 func, u16 reg, u16 data)
{
u8 buf[8];
buf[0] = addr; // 从机地址
buf[1] = func; // 功能码
buf[2] = (reg >> 8) & 0xFF; // 寄存器高8位
buf[3] = reg & 0xFF; // 寄存器低8位
buf[4] = (data >> 8) & 0xFF;//数据/寄存器长度 高字节
buf[5] = data & 0xFF; // 数据/寄存器长度 低字节
// 计算CRC
u16 crc = CRC16_Modbus(buf, 6);
buf[6] = crc & 0xFF; // CRC低字节
buf[7] = (crc >> 8) & 0xFF; // CRC高字节
// RS485 发送
RS485_SendData(buf, 8);
}
// ==============================================
// 函数功能:读取雨量值
// 返回值:雨量(单位:mm)
// ==============================================
float Rain_ReadValue(u8 addr)
{
u8 recv_buf[16] = {0};
// 发送指令:0x03读寄存器, 地址0x0000, 长度1
Modbus_SendFrame(addr, 0x03, 0x0000, 0x0001);
// 接收回复
u8 len = RS485_ReceiveData(recv_buf, 500);
// 数据校验
if (len == 7
&& recv_buf[0] == addr
&& recv_buf[1] == 0x03
&& recv_buf[2] == 0x02)
{
// 校验CRC
u16 recv_crc = (recv_buf[6] << 8) | recv_buf[5];
u16 calc_crc = CRC16_Modbus(recv_buf, 5);
if(recv_crc == calc_crc)
{
u16 rain_val = (recv_buf[3] << 8) | recv_buf[4];
return rain_val / 10.0f;
}
}
// 读取失败返回-1
return -1.0f;
}
// ==============================================
// 函数功能:清除雨量数据
// 参数:addr - 设备地址
// 返回值:0-成功 -1-失败
// ==============================================
u8 Rain_ClearData(u8 addr)
{
u8 recv_buf[16] = {0};
// 1. 发送清零指令(0x06写寄存器, 地址0x0000, 数值0x005A)
Modbus_SendFrame(addr, 0x06, 0x0000, 0x005A);
// 2. 等待并接收应答帧,应答帧固定为6字节
u8 len = RS485_ReceiveData(recv_buf, 500);
// 3. 严格校验应答
// 应答格式:地址(0) 功能码(1) 起始寄存器(2-3) 清除命令(4-5) CRC(6-7)
if (len == 8
&& recv_buf[0] == addr
&& recv_buf[1] == 0x06
&& recv_buf[2] == 0x00 && recv_buf[3] == 0x00 // 起始寄存器匹配
&& recv_buf[4] == 0x00 && recv_buf[5] == 0x5A)// 清除命令匹配
{
// 校验CRC
u16 recv_crc = (recv_buf[7] << 8) | recv_buf[6];
u16 calc_crc = CRC16_Modbus(recv_buf, 6);
if(recv_crc == calc_crc)
{
return 0; // 清零成功
}
}
return -1; // 清零失败
}
// ==============================================
// 函数功能:修改设备地址
// 参数:old_addr - 旧设备地址 new_addr - 新设备地址
// 返回值:0-成功 -1-失败
// ==============================================
u8 Rain_SetAddr(u8 old_addr, u8 new_addr)
{
u8 recv_buf[16] = {0};
Modbus_SendFrame(old_addr, 0x06, 0x07D0, new_addr);
u8 len = RS485_ReceiveData(recv_buf, 500);
if (len == 8
&& recv_buf[0] == old_addr
&& recv_buf[1] == 0x06
&& recv_buf[2] == 0x07 && recv_buf[3] == 0xD0 // 起始寄存器匹配
&& recv_buf[4] == 0x00 && recv_buf[5] == new_addr)//
{
// 校验CRC
u16 recv_crc = (recv_buf[7] << 8) | recv_buf[6];
u16 calc_crc = CRC16_Modbus(recv_buf, 6);
if(recv_crc == calc_crc)
{
return 0; // 设置成功
}
}
return -1; // 设置失败
}
// ==============================================
// 函数功能:修改波特率
// 参数:0=2400 1=4800 2=9600 3=19200 ...
// 返回值:0-成功 -1-失败
// ==============================================
u8 Rain_SetBaud(u8 addr, u8 baud_opt)
{
u8 recv_buf[16] = {0};
// 发送指令:功能06,寄存器07D1,数据=00+baud_opt(高字节0)
Modbus_SendFrame(addr, 0x06, 0x07D1, (0x00 << 8) | baud_opt);
u8 len = RS485_ReceiveData(recv_buf, 500);
// 校验应答帧(固定8字节)
if (len == 8
&& recv_buf[0] == addr
&& recv_buf[1] == 0x06
&& recv_buf[2] == 0x07 && recv_buf[3] == 0xD1 // 寄存器地址匹配
&& recv_buf[4] == 0x00 // 高字节固定0
&& recv_buf[5] == baud_opt) // 低字节=波特率选项
{
// CRC校验
u16 recv_crc = ((u16)recv_buf[7] << 8) | recv_buf[6];
u16 calc_crc = CRC16_Modbus(recv_buf, 6);
if (recv_crc == calc_crc)
{
return 0; // 设置成功
}
}
return -1; // 设置失败
}
// ==============================================
// 函数功能:查询地址
// 返回值:0-成功 -1-失败
// ==============================================
u8 Rain_FindAddr(void)
{
u8 recv_buf[16] = {0};
// 发送广播查询指令:地址0xFF,功能03,寄存器07D0,长度1
Modbus_SendFrame(0xFF, 0x03, 0x07D0, 0x0001);
u8 len = RS485_ReceiveData(recv_buf, 500);
// 校验应答帧
if (len == 7
&& recv_buf[0] == 0xFF
&& recv_buf[1] == 0x03
&& recv_buf[2] == 0x02) // 有效字节数=2
{
// CRC校验
u16 recv_crc = ((u16)recv_buf[6] << 8) | recv_buf[5];
u16 calc_crc = CRC16_Modbus(recv_buf, 5);
if (recv_crc == calc_crc)
{
// 地址值:高字节0,低字节为真实地址
return recv_buf[4];
}
}
return 0; // 未找到设备
}
|