- modbus协议简介:
modbus协议基于rs485总线,采取一主多从的形式,主设备轮询各从设备信息,从设备不主动上报。
日常使用都是RTU模式,协议帧格式如下所示:
地址 功能码 寄存器地址 读取寄存器个数 寄存器数据1 ..... CrcL CrcH
|-------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 | /*
AA 03 00 00 00 0A DC 16
addr cmd regH regL lenH lenL crcL crcH 主机发送
AA 03 14 00 00 00 00 00 00 00 00 00 03 00 01 00 00 00 18 00 1C 00 00 81 4B 从机回复
addr cmd datelen ....
AA 10 00 0a 00 01 02 00 02 主机修改从机寄存器值
addr cmd regH regL regNum datalen data
*/
|
功能码及对应的操作字长:
目前比较简单的实现了读多个保持寄存器,以及写多个保持寄存器,由于不是使用的PLC,所以寄存器地址的划分没有严格按照上表,具体地址后面解释。
2.Modbus协议编写步骤:很多设备厂家都会有自己的modbus协议,大多数都不是很标准
(1)分析板子的具体信息,编写不同的设备结构体,比如只读的结构体,可读写的结构体,保存配置信息的结构体(当主机发送改变配置信息的消息帧时,会改变相应的变量,并写入flash)
(2) modbus寄存器映射,定义保持寄存器的指针;
(2)本此编写采用轮询处理485串口接受到的数据,每次的间隔肯定大于3.5个字符时间(标准的Modbus帧间隔),所以不用但心接受不完整的情况。串口接收完成之后
会首先进行处理在串口数据中找出符合要求,接收正确的数据帧,并记录其功能码,输出帧的真实地址,就可以得到主机想要操作的从机的寄存器地址。
(3)根据上一步获取的从机寄存器地址,对保持寄存器的指针进行偏移指向,即指向不同信息结构体的首地址,此过程判断寄存器地址是否溢出。
(4)根据功能码,进行解析操作设备,读写操作就是将寄存器地址里的值直接操作指针读取出/写入。
以上过程都会判断是否错误发生,错误码如下所示:
(1)0x01 功能码错误,或者不存在
(2)0x02 寄存器地址超出范围
(3)0x04 CRC校验错误
错误回复帧的格式为:地址码 功能码|0x80 错误码 CRCL CRCH
下面就是本次用到的代码,包括将配置信息结构体读写flash:
1 /******************************************* Modbus **********************************************/
2
3 uint16_t *Modbus_HoldReg = NULL;//保持寄存器
4 TRtuCommand g_tCurRtuCmd;
5
6 /*
7 AA 03 00 00 00 0A DC 16
8 addr cmd regH regL lenH lenL crcL crcH 读寄存器值
9
10 AA 03 14 00 00 00 00 00 00 00 00 00 03 00 01 00 00 00 18 00 1C 00 00 81 4B
11 addr cmd datelen ....
12
13 AA 10 00 0a 00 01 02 00 02 写寄存器值
14 addr cmd regH regL regNum datalen data
15 */
16
17
18
19 /*====================================================================
20 函数名:Modbus_RegMap
21 功 能:根据读取寄存器的起始地址选择映射对象,将不同的地址映射到
22 不同的结构体数据
23 输入参数说明:
24 输出参数说明:
25 返回值说明:无
26 备 注:
27 ====================================================================*/
28 void Modbus_RegMap(uint16_t wStartAddr)
29 {
30 uint16_t woffset = 0;
31 uint16_t wTemp = 0;
32
33 if((wStartAddr >= REG_BASE_INFO_OFFSET) && (wStartAddr < (REG_BASE_INFO_OFFSET + REG_BASE_INFO_NUM)))
34 {
35 Modbus_HoldReg = (uint16_t *)&g_tdeviceinfo;
36 woffset = wStartAddr - REG_BASE_INFO_OFFSET;
37 wTemp = REG_BASE_INFO_NUM;
38 }
39 else if(wStartAddr >= (REG_BASE_INFO_OFFSET + REG_BASE_INFO_NUM) && (wStartAddr < REG_CONFIG_INFO_OFFSET) )
40 {
41 g_tCurRtuCmd.m_byExceptionCode = 0x02; //异常码0x02超出寄存器范围
42 }
43 else if((wStartAddr >= REG_CONFIG_INFO_OFFSET) && (wStartAddr < (REG_CONFIG_INFO_OFFSET + REG_CONFIG_INFO_NUM )))
44 {
45 Modbus_HoldReg = (uint16_t *)&g_tConfigInfo;
46 woffset = wStartAddr - REG_CONFIG_INFO_OFFSET;
47 wTemp = REG_CONFIG_INFO_NUM;
48 }
49 else if(wStartAddr >= (REG_CONFIG_INFO_OFFSET + REG_CONFIG_INFO_NUM))
50 {
51 g_tCurRtuCmd.m_byExceptionCode = 0x02; //异常码0x02超出寄存器范围
52 }
53 g_tCurRtuCmd.m_wStartAddr = woffset;
54 g_tCurRtuCmd.m_wRegOffsetNum = wTemp;
55 }
56
57
58 /*====================================================================
59 函数名:DeviceInfoRefresh
60 功 能:更新设备运行的状态值同时更新modbus寄存器的值
61 输入参数说明:
62 输出参数说明:
63 返回值说明:无
64 备 注:
65 ====================================================================*/
66 void DeviceInfoRefresh(void)
67 {
68
69 GetHumiAndTempVlue();
70
71 g_tdeviceinfo.m_wlightStripRly = HAL_GPIO_ReadPin(DOOR_LED_RELAY_GPIO_Port,DOOR_LED_RELAY_Pin);
72 g_tdeviceinfo.m_wFanRly = HAL_GPIO_ReadPin(FAN_RELAY_GPIO_Port,FAN_RELAY_Pin);
73 g_tdeviceinfo.m_wWarningLed1 = HAL_GPIO_ReadPin(WARNING_LED_1_GPIO_Port,WARNING_LED_1_Pin);
74 g_tdeviceinfo.m_wWarningLed2 = HAL_GPIO_ReadPin(WARNING_LED_2_GPIO_Port,WARNING_LED_2_Pin);
75
76 g_tdeviceinfo.m_wGMvalue = LightLevelPersenGet(); /* 光照等级 */
77 g_tdeviceinfo.m_wDoorLimit = HAL_GPIO_ReadPin(LIMIT_SW_DOOR_GPIO_Port,LIMIT_SW_DOOR_Pin);
78 g_tdeviceinfo.m_wWaterLimit = HAL_GPIO_ReadPin(WATER_MARK_GPIO_Port,WATER_MARK_Pin);
79 g_tdeviceinfo.m_Temp = (uint16_t)s_tsht2xInfo.m_fTemp;
80 g_tdeviceinfo.m_Humi = (uint16_t)s_tsht2xInfo.m_fHumi;
81 g_tdeviceinfo.m_vibration = Mma8452StatusGet();
82 }
83
84
85 /*====================================================================
86 函数名:RtuReceiveHandle
87 功 能:处理接受到的modbus数据,并读取/设置相应寄存器的值
88 输入参数说明:
89 pbydata :串口接收到的数据
90 输出参数说明:
91 dwLength :输入数据长度
92 返回值说明:无
93 备注:由于modubusRtu函数不支持功能码0x06(写单一寄存器),所以0x06不处理
94 ====================================================================*/
95 void RtuReceiveHandle(uint8_t *pbydata,uint32_t dwLength)
96 {
97 uint8_t i;
98 uint16_t wCrc = 0;
99 uint16_t wIndex = 0, wRealLength = 0, wStartOff = 0;
100 uint8_t byAddr = (g_tConfigInfo.m_bydeviceAddr) & 0xFF;
101 g_tCurRtuCmd.m_byExceptionCode = 0;
102
103 if(pbydata == NULL || dwLength == 0)
104 {
105 TCLX_PLATFORM_DIAG(("No data received\n"));
106 return;
107 }
108
109 for(wIndex = 0; wIndex < dwLength; wIndex++)
110 {
111 if(modubusRtu(pbydata + wIndex, dwLength - wIndex, &byAddr, 1, &wRealLength, &(g_tCurRtuCmd.m_byFunCode), &wStartOff))
112 {
113 wStartOff += wIndex; /* 找到真实的Modbus数据帧 */
114
115 /* 记录命令,在主循环处理 */
116 g_tCurRtuCmd.m_wStartAddr = (pbydata[wStartOff + 2] << 8) + pbydata[wStartOff + 3];
117
118 Modbus_RegMap(g_tCurRtuCmd.m_wStartAddr);
119
120 TCLX_PLATFORM_DIAG(("Offset[%d] Len[%d] FunCode[0x%x] StartAddr[0x%x] RegNum[%d]\n", wStartOff, wRealLength,g_tCurRtuCmd.m_byFunCode, g_tCurRtuCmd.m_wStartAddr, g_tCurRtuCmd.m_wRegNum));
121
122 switch(g_tCurRtuCmd.m_byFunCode)
123 {
124 case 0x03:
125 g_tCurRtuCmd.m_wRegNum = (pbydata[wStartOff + 4] << 8) + pbydata[wStartOff + 5];
126 if((g_tCurRtuCmd.m_wRegNum + g_tCurRtuCmd.m_wStartAddr) <= g_tCurRtuCmd.m_wRegOffsetNum)
127 {
128 abySendData[0] = g_tConfigInfo.m_bydeviceAddr;
129 abySendData[1] = g_tCurRtuCmd.m_byFunCode;
130 abySendData[2] = g_tCurRtuCmd.m_wRegNum * 2;
131 for(i = 0; i < g_tCurRtuCmd.m_wRegNum; i++)
132 {
133 abySendData[3+i*2] = (Modbus_HoldReg[g_tCurRtuCmd.m_wStartAddr+i]>>8)&0xFF;// /先发送高字节--在发送低字节
134 abySendData[4+i*2] = (Modbus_HoldReg[g_tCurRtuCmd.m_wStartAddr+i])&0xFF; //
135 }
136 wCrc = crc16(abySendData, g_tCurRtuCmd.m_wRegNum*2 + 3);
137 abySendData[g_tCurRtuCmd.m_wRegNum*2 + 3] = wCrc & 0x00FF;
138 abySendData[g_tCurRtuCmd.m_wRegNum*2 + 4] = (wCrc >> 8) & 0x00FF;
139
140 usart_send(USART_485_INDEX, abySendData, g_tCurRtuCmd.m_wRegNum*2 + 5);
141 }
142 else
143 {
144 g_tCurRtuCmd.m_byExceptionCode = 0x02; //异常码,超出寄存范围
145 }
146 break;
147 case 0x06:
148
149 Modbus_HoldReg[g_tCurRtuCmd.m_wStartAddr] = pbydata[wStartOff + 4]<<8 | ((uint16_t)pbydata[wStartOff + 5]);//高字节在前
150
151 abySendData[0] = pbydata[wStartOff];
152 abySendData[1] = pbydata[wStartOff + 1];
153 abySendData[2] = pbydata[wStartOff + 2];
154 abySendData[3] = pbydata[wStartOff + 3];
155 abySendData[4] = pbydata[wStartOff + 4];
156 abySendData[5] = pbydata[wStartOff + 5];
157
158 wCrc = crc16(abySendData,6);
159
160 abySendData[6]=(wCrc>>8)&0xFF;
161 abySendData[7]=(wCrc)&0xFF;
162 usart_send(USART_485_INDEX, abySendData,8);
163 break;
164
165 case 0x10:
166 g_tCurRtuCmd.m_wRegNum = (pbydata[wStartOff + 4] << 8) + pbydata[wStartOff + 5];
167 if((g_tCurRtuCmd.m_wRegNum + g_tCurRtuCmd.m_wStartAddr) <= g_tCurRtuCmd.m_wRegOffsetNum)
168 {
169 for(i=0;i<g_tCurRtuCmd.m_wRegNum ;i++)
170 {
171 Modbus_HoldReg[g_tCurRtuCmd.m_wStartAddr+i]= pbydata[wStartOff + 7+i*2] <<8 ; //低字节在前
172 Modbus_HoldReg[g_tCurRtuCmd.m_wStartAddr+i]|=((uint16_t)pbydata[wStartOff + 8+i*2]); //高字节在后
173 }
174 abySendData[0] = pbydata[wStartOff];
175 abySendData[1] = pbydata[wStartOff + 1];
176 abySendData[2] = pbydata[wStartOff + 2];
177 abySendData[3] = pbydata[wStartOff + 3];
178 abySendData[4] = pbydata[wStartOff + 4];
179 abySendData[5] = pbydata[wStartOff + 5];
180
181 wCrc = crc16(abySendData,6);
182 abySendData[6]=(wCrc>>8)&0xFF;
183 abySendData[7]=(wCrc)&0xFF;
184
185 /* 如果配置信息发生改变就写入flash,不用做比较相等处理,写flash函数已经处理 */
186 writeConfigInfoToFlash(CONFIG_DATA_FLASH_ADDR,&g_tConfigInfo);
187
188
189 usart_send(USART_485_INDEX, abySendData,8);
190 }
191 else
192 {
193 g_tCurRtuCmd.m_byExceptionCode = 0x02; //异常码,超出寄存范围
194 }
195 break;
196 default:
197 g_tCurRtuCmd.m_byExceptionCode = 0x01; //异常码,功能码错误或者不存在
198 break;
199 }
200
201 if(g_tCurRtuCmd.m_byExceptionCode != 0)
202 {
203 TCLX_PLATFORM_DIAG(("exception code[%d]\n", g_tCurRtuCmd.m_byExceptionCode));
204 abySendData[0] = g_tConfigInfo.m_bydeviceAddr;
205 abySendData[1] = g_tCurRtuCmd.m_byFunCode + 0x80;
206 abySendData[2] = g_tCurRtuCmd.m_byExceptionCode;
207 wCrc = crc16(abySendData, 3);
208 abySendData[3] = wCrc & 0x00FF;
209 abySendData[4] = (wCrc >> 8) & 0x00FF;
210 usart_send(USART_485_INDEX, abySendData, 5);
211 }
212
213 memset(&g_tCurRtuCmd, 0, sizeof(TRtuCommand));
214
215 wIndex += (wStartOff + wRealLength - 1);
216 }/* switch(g_tCurRtuCmd.m_byFunCode) */
217
218 }/* if modbusRtu do.... */
219 usartRcvRestore(USART_485_INDEX);
220 }
221
222
223 /************************************** flash opration *****************************************/
224
225
226 /*====================================================================
227 函数名:Read_FlashData
228 功 能:从flash读取配置信息
229 输入参数说明:
230 FlashReadBaseAdd:配置信息基地址
231 输出参数说明:
232 DeviceCfg :配置参数
233 返回值说明:无
234 备 注:
235 ====================================================================*/
236 void Read_FlashData(uint32_t FlashReadBaseAdd,DeviceConfigInfo_t *DeviceCfg)
237 {
238 if(NULL == DeviceCfg)
239 {
240 return;
241 }
242 __IO DeviceConfigInfo_t *ptPos = (__IO DeviceConfigInfo_t*)FlashReadBaseAdd;
243
244 memcpy(DeviceCfg,(const char *)ptPos,sizeof(DeviceConfigInfo_t));
245 }
246
247 /*====================================================================
248 函数名:writeConfigInfoToFlash
249 功 能:向flash写配置信息
250 输入参数说明:
251 FlashReadBaseAdd:配置信息基地址
252 输出参数说明:
253 DeviceCfg :配置参数
254 返回值说明:无
255 备 注:
256 ====================================================================*/
257 void writeConfigInfoToFlash(uint32_t FlashWriteBaseAdd,DeviceConfigInfo_t *DeviceCfg)
258 {
259 uint8_t byIndex = 0;
260 uint16_t wIndex = 0;
261
262 DeviceConfigInfo_t DeviceCfgTemp = {0};
263
264 for(byIndex = 0;byIndex < 10;byIndex++)
265 {
266 Read_FlashData(FlashWriteBaseAdd,&DeviceCfgTemp);
267
268 if(0 == memcmp(&DeviceCfg,&DeviceCfgTemp,sizeof(DeviceConfigInfo_t)))
269 {
270 TCLX_PLATFORM_DIAG(("write succeed: Data equal\r\n"));
271 return;
272 }
273 else
274 {
275 HAL_Delay(500);
276 DIS_INT;
277 HAL_StatusTypeDef status = HAL_OK;
278 if(HAL_OK != (status = HAL_FLASH_Unlock()))
279 {
280 TCLX_PLATFORM_DIAG((" falsh unlock err\r\n"));
281 continue;
282 }
283 FLASH_EraseInitTypeDef f;
284 f.TypeErase = FLASH_TYPEERASE_PAGES;
285 f.PageAddress = (uint32_t)FlashWriteBaseAdd;
286 f.NbPages = 1;
287 uint32_t PageError = 0;
288
289 if(HAL_OK != (status = HAL_FLASHEx_Erase(&f, &PageError)))
290 {
291 if(0 != PageError)
292 {
293 TCLX_PLATFORM_DIAG(("HAL_FLASHEx_Erase:failed(%d-%d)\n",status,PageError));
294 HAL_FLASH_Lock();
295 continue;
296 }
297 }
298 for(wIndex = 0; wIndex < (sizeof(DeviceConfigInfo_t) / sizeof(uint32_t)); wIndex ++)
299 {
300 if(HAL_OK != (status = HAL_FLASH_Program(TYPEPROGRAM_WORD,FlashWriteBaseAdd + (wIndex * sizeof(uint32_t)) ,((uint32_t *)DeviceCfg)[wIndex])))
301 {
302 TCLX_PLATFORM_DIAG(("HAL_FLASH_Program:CONFIG_DATA_FLASH_ADDR failed(%d)\n",status));
303 HAL_FLASH_Lock();
304 continue;
305 }
306
307 }
308 if(HAL_OK != (status = HAL_FLASH_Lock()))
309 {
310 TCLX_PLATFORM_DIAG(("HAL_FLASH_Lock:HAL_FLASH_Lock(%d)\n",status));
311 }
312 EN_INT;
313 return ;
314 }
315 }
316 }
参考下面例程,此例程比较详细
1 #include "modbus.h"
2 #include "led.h"
3 #include "lcd.h"
4 #include "stm32f10x_tim.h"
5
6
7 ///
8 u32 RS485_Baudrate=9600;//通讯波特率
9 u8 RS485_Parity=0;//0无校验;1奇校验;2偶校验
10 u8 RS485_Addr=1;//从机地址
11 u16 RS485_Frame_Distance=4;//数据帧最小间隔(ms),超过此时间则认为是下一帧
12
13 u8 RS485_RX_BUFF[2048];//接收缓冲区2048字节
14 u16 RS485_RX_CNT=0;//接收计数器
15 u8 RS485_FrameFlag=0;//帧结束标记
16 u8 RS485_TX_BUFF[2048];//发送缓冲区
17 u16 RS485_TX_CNT=0;//发送计数器
18
19
20 //Modbus寄存器和单片机寄存器的映射关系
21 vu32 *Modbus_InputIO[100];//输入开关量寄存器指针(这里使用的是位带操作)
22 vu32 *Modbus_OutputIO[100];//输出开关量寄存器指针(这里使用的是位带操作)
23 u16 *Modbus_HoldReg[1000];//保持寄存器指针
24 u32 testData1=1201,testData2=1002,testData3=2303,testData4=8204;
25 void Modbus_RegMap(void)
26 {
27
28
29 //输入开关量寄存器指针指向
30 Modbus_InputIO[0]=(vu32*)&PEin(4);//KEY0 //&PEin(4):取PE4的地址,(vu32*)&PEin(4)将PE4地址强制转换为uw32类型的地址,Modbus_InputIO[0]=(vu32*)&PEin(4); 将转换好的地址送给地址指针Modbus_InputIO[0];
31 Modbus_InputIO[1]=(vu32*)&PEin(3);//KEY1 //*Modbus_InputIO[0] 取出地址中的内容。
32 Modbus_InputIO[2]=(vu32*)&PEin(2);//KEY2
33 Modbus_InputIO[3]=(vu32*)&PAin(0);//KEY3
34
35 //输出开关量寄存器指针指向
36 Modbus_OutputIO[0]=(vu32*)&PBout(5);//LED0
37 Modbus_OutputIO[1]=(vu32*)&PEout(5);//LED1
38
39 //保持寄存器指针指向
40 Modbus_HoldReg[0]=(u16*)&testData1;//测试数据1
41 Modbus_HoldReg[1]=(u16*)&testData2;//((u16*)&testData1)+1;//测试数据1
42 Modbus_HoldReg[2]=(u16*)&testData3;//(u16*)&testData2;//测试数据2
43 Modbus_HoldReg[3]=(u16*)&testData4;//((u16*)&testData2)+1;//测试数据2
44 Modbus_HoldReg[4]=(u16*)&testData1;
45 Modbus_HoldReg[5]=(u16*)&testData2;
46 Modbus_HoldReg[6]=(u16*)&testData3;
47 }
48 /
49
50 //CRC校验 自己后面添加的
51
52 const u8 auchCRCHi[] = {
53 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
54 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
55 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
56 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
57 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
58 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
59 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
60 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
61 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
62 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
63 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
64 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
65 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40} ;
66
67
68 const u8 auchCRCLo[] = {
69 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
70 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
71 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
72 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
73 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
74 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
75 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
76 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
77 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
78 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
79 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
80 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
81 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,0x43, 0x83, 0x41, 0x81, 0x80, 0x40} ;
82
83
84 u16 CRC_Compute(u8 *puchMsg, u16 usDataLen)
85 {
86 u8 uchCRCHi = 0xFF ;
87 u8 uchCRCLo = 0xFF ;
88 u32 uIndex ;
89 while (usDataLen--)
90 {
91 uIndex = uchCRCHi ^ *puchMsg++ ;
92 uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;
93 uchCRCLo = auchCRCLo[uIndex] ;
94 }
95 return ((uchCRCHi<< 8) | (uchCRCLo)) ;
96 }//uint16 crc16(uint8 *puchMsg, uint16 usDataLen)
97
98
99 //初始化USART2
100 void RS485_Init(void)
101 {
102 GPIO_InitTypeDef GPIO_InitStructure;
103 USART_InitTypeDef USART_InitStructure;
104 NVIC_InitTypeDef NVIC_InitStructure;
105 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD,ENABLE);
106 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
107
108 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2;//PA2(TX)复用推挽输出
109 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
110 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
111 GPIO_Init(GPIOA,&GPIO_InitStructure);
112 GPIO_SetBits(GPIOA,GPIO_Pin_2);//默认高电平
113
114 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;//PA3(RX)输入上拉
115 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //修改原GPIO_Mode_IPU(输入上拉)->GPIO_Mode_IN_FLOATING(浮空输入)/
116 GPIO_Init(GPIOA,&GPIO_InitStructure);
117
118 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//修改PG9(RE/DE)通用推挽输出->PD7(RE/DE)通用推挽输出//
119 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
120 GPIO_Init(GPIOG,&GPIO_InitStructure);
121 GPIO_ResetBits(GPIOG,GPIO_Pin_9);//默认接收状态
122
123 USART_DeInit(USART2);//复位串口2
124 USART_InitStructure.USART_BaudRate=RS485_Baudrate;
125 USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
126 USART_InitStructure.USART_WordLength=USART_WordLength_8b;
127 USART_InitStructure.USART_StopBits=USART_StopBits_1;
128 USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;//收发模式
129 switch(RS485_Parity)
130 {
131 case 0:USART_InitStructure.USART_Parity=USART_Parity_No;break;//无校验
132 case 1:USART_InitStructure.USART_Parity=USART_Parity_Odd;break;//奇校验
133 case 2:USART_InitStructure.USART_Parity=USART_Parity_Even;break;//偶校验
134 }
135 USART_Init(USART2,&USART_InitStructure);
136
137 USART_ClearITPendingBit(USART2,USART_IT_RXNE);
138 USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);//使能串口2接收中断
139
140 NVIC_InitStructure.NVIC_IRQChannel=USART2_IRQn;
141 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
142 NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
143 NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
144 NVIC_Init(&NVIC_InitStructure);
145
146 USART_Cmd(USART2,ENABLE);//使能串口2
147 RS485_TX_EN=0;//默认为接收模式
148
149 Timer7_Init();//定时器7初始化,用于监视空闲时间
150 Modbus_RegMap();//Modbus寄存器映射
151 }
152
153 //定时器7初始化
154 void Timer7_Init(void)
155 {
156 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
157 NVIC_InitTypeDef NVIC_InitStructure;
158
159 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE); //TIM7时钟使能
160
161 //TIM7初始化设置
162 TIM_TimeBaseStructure.TIM_Period = RS485_Frame_Distance*10; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
163 TIM_TimeBaseStructure.TIM_Prescaler =7200; //设置用来作为TIMx时钟频率除数的预分频值 设置计数频率为10kHz
164 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
165 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
166 TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
167
168 TIM_ITConfig( TIM7, TIM_IT_Update, ENABLE );//TIM7 允许更新中断
169
170 //TIM7中断分组配置
171 NVIC_InitStructure.NVIC_IRQChannel =TIM7_IRQn; //TIM7中断
172 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级2级
173 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
174 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
175 NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
176 }
177
178
179
180 //
181 //发送n个字节数据
182 //buff:发送区首地址
183 //len:发送的字节数
184 void RS485_SendData(u8 *buff,u8 len)
185 {
186 RS485_TX_EN=1;//切换为发送模式
187 while(len--)
188 {
189 while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)==RESET);//等待发送区为空
190 USART_SendData(USART2,*(buff++));
191 }
192 while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET);//等待发送完成
193 }
194
195
196 /
197 void USART2_IRQHandler(void)//串口2中断服务程序
198 {
199
200 u8 res;
201 u8 err;
202
203 if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)
204 {
205 if(USART_GetFlagStatus(USART2,USART_FLAG_NE|USART_FLAG_FE|USART_FLAG_PE)) err=1;//检测到噪音、帧错误或校验错误
206 else err=0;
207 LED0=0;
208 res=USART_ReceiveData(USART2); //读接收到的字节,同时相关标志自动清除
209
210 if((RS485_RX_CNT<2047)&&(err==0))
211 {
212 RS485_RX_BUFF[RS485_RX_CNT]=res;
213 RS485_RX_CNT++;
214
215 TIM_ClearITPendingBit(TIM7,TIM_IT_Update);//清除定时器溢出中断
216 TIM_SetCounter(TIM7,0);//当接收到一个新的字节,将定时器7复位为0,重新计时(相当于喂狗)
217 TIM_Cmd(TIM7,ENABLE);//开始计时
218 }
219 }
220 }
221
222 ///
223 //用定时器7判断接收空闲时间,当空闲时间大于指定时间,认为一帧结束
224 //定时器7中断服务程序
225 void TIM7_IRQHandler(void)
226 {
227 if(TIM_GetITStatus(TIM7,TIM_IT_Update)!=RESET)
228 {
229 TIM_ClearITPendingBit(TIM7,TIM_IT_Update);//清除中断标志
230 TIM_Cmd(TIM7,DISABLE);//停止定时器
231 RS485_TX_EN=1;//停止接收,切换为发送状态
232 RS485_FrameFlag=1;//置位帧结束标记
233 }
234 }
235
236 /
237 //RS485服务程序,用于处理接收到的数据(请在主函数中循环调用)
238 u16 startRegAddr;
239 u16 RegNum;
240 u16 calCRC;
241 void RS485_Service(void)
242 {
243 u16 recCRC;
244 if(RS485_FrameFlag==1)
245 {
246 if(RS485_RX_BUFF[0]==RS485_Addr)//地址正确
247 {
248 if((RS485_RX_BUFF[1]==01)||(RS485_RX_BUFF[1]==02)||(RS485_RX_BUFF[1]==03)||(RS485_RX_BUFF[1]==05)||(RS485_RX_BUFF[1]==06)||(RS485_RX_BUFF[1]==15)||(RS485_RX_BUFF[1]==16))//功能码正确
249 {
250 startRegAddr=(((u16)RS485_RX_BUFF[2])<<8)|RS485_RX_BUFF[3];//获取寄存器起始地址
251 if(startRegAddr<1000)//寄存器地址在范围内
252 {
253 calCRC=CRC_Compute(RS485_RX_BUFF,RS485_RX_CNT-2);//计算所接收数据的CRC
254 recCRC=RS485_RX_BUFF[RS485_RX_CNT-1]|(((u16)RS485_RX_BUFF[RS485_RX_CNT-2])<<8);//接收到的CRC(低字节在前,高字节在后)
255 if(calCRC==recCRC)//CRC校验正确
256 {
257 ///显示用
258
259 LCD_ShowxNum(10,230,RS485_RX_BUFF[0],3,16,0X80);//显示数据
260 LCD_ShowxNum(42,230,RS485_RX_BUFF[1],3,16,0X80);//显示数据
261 LCD_ShowxNum(74,230,RS485_RX_BUFF[2],3,16,0X80);//显示数据
262 LCD_ShowxNum(106,230,RS485_RX_BUFF[3],3,16,0X80);//显示数据
263 LCD_ShowxNum(138,230,RS485_RX_BUFF[4],3,16,0X80);//显示数据
264 LCD_ShowxNum(170,230,RS485_RX_BUFF[5],3,16,0X80);//显示数据
265 ///
266 /
267 switch(RS485_RX_BUFF[1])//根据不同的功能码进行处理
268 {
269 case 2://读输入开关量
270 {
271 Modbus_02_Solve();
272 break;
273 }
274
275 case 1://读输出开关量
276 {
277 Modbus_01_Solve();
278 break;
279 }
280
281 case 5://写单个输出开关量
282 {
283 Modbus_05_Solve();
284 break;
285 }
286
287 case 15://写多个输出开关量
288 {
289 Modbus_15_Solve();
290 break;
291 }
292
293 case 03: //读多个寄存器
294 {
295 Modbus_03_Solve();
296 break;
297 }
298
299 case 06: //写单个寄存器
300 {
301 Modbus_06_Solve();
302 break;
303 }
304
305 case 16: //写多个寄存器
306 {
307 Modbus_16_Solve();
308 break;
309 }
310
311
312 }
313 //
314 }
315 else//CRC校验错误
316 {
317
318 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
319 RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
320 RS485_TX_BUFF[2]=0x04; //异常码
321 RS485_SendData(RS485_TX_BUFF,3);
322 }
323 }
324 else//寄存器地址超出范围
325 {
326 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
327 RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
328 RS485_TX_BUFF[2]=0x02; //异常码
329 RS485_SendData(RS485_TX_BUFF,3);
330 }
331 }
332 else//功能码错误
333 {
334 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
335 RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
336 RS485_TX_BUFF[2]=0x01; //异常码
337 RS485_SendData(RS485_TX_BUFF,3);
338 }
339 }
340
341 RS485_FrameFlag=0;//复位帧结束标志
342 RS485_RX_CNT=0;//接收计数器清零
343 RS485_TX_EN=0;//开启接收模式
344 }
345 }
346
347 //Modbus功能码02处理程序/程序已验证OK -----必须先配置PE4 PE3 PE2 PA0 初始化按键才可以OK KEY_Init();
348 //读输入开关量
349 void Modbus_02_Solve(void)
350 {
351 u16 ByteNum;
352 u16 i;
353 RegNum= (((u16)RS485_RX_BUFF[4])<<8)|RS485_RX_BUFF[5];//获取寄存器数量
354 if((startRegAddr+RegNum)<100)//寄存器地址+数量在范围内
355 {
356 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
357 RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
358 ByteNum=RegNum/8;//字节数
359 if(RegNum%8) ByteNum+=1;//如果位数还有余数,则字节数+1
360 RS485_TX_BUFF[2]=ByteNum;//返回要读取的字节数
361 for(i=0;i<RegNum;i++)
362 {
363 if(i%8==0) RS485_TX_BUFF[3+i/8]=0x00;
364 RS485_TX_BUFF[3+i/8]>>=1;//低位先发送
365 RS485_TX_BUFF[3+i/8]|=((*Modbus_InputIO[startRegAddr+i])<<7)&0x80;
366 if(i==RegNum-1)//发送到最后一个位了
367 {
368 if(RegNum%8) RS485_TX_BUFF[3+i/8]>>=8-(RegNum%8);//如果最后一个字节还有余数,则剩余MSB填充0
369 }
370 }
371 calCRC=CRC_Compute(RS485_TX_BUFF,ByteNum+3);
372 RS485_TX_BUFF[ByteNum+3]=(calCRC>>8)&0xFF;
373 RS485_TX_BUFF[ByteNum+4]=(calCRC)&0xFF;
374 RS485_SendData(RS485_TX_BUFF,ByteNum+5);
375 }
376 else//寄存器地址+数量超出范围
377 {
378 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
379 RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
380 RS485_TX_BUFF[2]=0x02; //异常码
381 RS485_SendData(RS485_TX_BUFF,3);
382 }
383 }
384
385 //Modbus功能码01处理程序 ///程序已验证OK
386 //读输出开关量
387 void Modbus_01_Solve(void)
388 {
389 u16 ByteNum;
390 u16 i;
391 RegNum= (((u16)RS485_RX_BUFF[4])<<8)|RS485_RX_BUFF[5];//获取寄存器数量
392 if((startRegAddr+RegNum)<100)//寄存器地址+数量在范围内
393 {
394 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
395 RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
396 ByteNum=RegNum/8;//字节数
397 if(RegNum%8) ByteNum+=1;//如果位数还有余数,则字节数+1
398 RS485_TX_BUFF[2]=ByteNum;//返回要读取的字节数
399 for(i=0;i<RegNum;i++)
400 {
401 if(i%8==0) RS485_TX_BUFF[3+i/8]=0x00;
402 RS485_TX_BUFF[3+i/8]>>=1;//低位先发送
403 RS485_TX_BUFF[3+i/8]|=((*Modbus_OutputIO[startRegAddr+i])<<7)&0x80;
404 if(i==RegNum-1)//发送到最后一个位了
405 {
406 if(RegNum%8) RS485_TX_BUFF[3+i/8]>>=8-(RegNum%8);//如果最后一个字节还有余数,则剩余MSB填充0
407 }
408 }
409 calCRC=CRC_Compute(RS485_TX_BUFF,ByteNum+3);
410 RS485_TX_BUFF[ByteNum+3]=(calCRC>>8)&0xFF;
411 RS485_TX_BUFF[ByteNum+4]=(calCRC)&0xFF;
412 RS485_SendData(RS485_TX_BUFF,ByteNum+5);
413 }
414 else//寄存器地址+数量超出范围
415 {
416 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
417 RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
418 RS485_TX_BUFF[2]=0x02; //异常码
419 RS485_SendData(RS485_TX_BUFF,3);
420 }
421 }
422
423 //Modbus功能码05处理程序 ///程序已验证OK
424 //写单个输出开关量
425 void Modbus_05_Solve(void)
426 {
427 if(startRegAddr<100)//寄存器地址在范围内
428 {
429 if((RS485_RX_BUFF[4]==0xFF)||(RS485_RX_BUFF[5]==0xFF)) *Modbus_OutputIO[startRegAddr]=0x01;
430 else *Modbus_OutputIO[startRegAddr]=0x00;
431
432 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
433 RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
434 RS485_TX_BUFF[2]=RS485_RX_BUFF[2];
435 RS485_TX_BUFF[3]=RS485_RX_BUFF[3];
436 RS485_TX_BUFF[4]=RS485_RX_BUFF[4];
437 RS485_TX_BUFF[5]=RS485_RX_BUFF[5];
438
439 calCRC=CRC_Compute(RS485_TX_BUFF,6);
440 RS485_TX_BUFF[6]=(calCRC>>8)&0xFF;
441 RS485_TX_BUFF[7]=(calCRC)&0xFF;
442 RS485_SendData(RS485_TX_BUFF,8);
443 }
444 else//寄存器地址超出范围
445 {
446 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
447 RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
448 RS485_TX_BUFF[2]=0x02; //异常码
449 RS485_SendData(RS485_TX_BUFF,3);
450 }
451 }
452
453 //Modbus功能码15处理程序 //程序已验证OK
454 //写多个输出开关量
455 void Modbus_15_Solve(void)
456 {
457 u16 i;
458 RegNum=(((u16)RS485_RX_BUFF[4])<<8)|RS485_RX_BUFF[5];//获取寄存器数量
459 if((startRegAddr+RegNum)<100)//寄存器地址+数量在范围内
460 {
461 for(i=0;i<RegNum;i++)
462 {
463 if(RS485_RX_BUFF[7+i/8]&0x01) *Modbus_OutputIO[startRegAddr+i]=0x01;
464 else *Modbus_OutputIO[startRegAddr+i]=0x00;
465 RS485_RX_BUFF[7+i/8]>>=1;//从低位开始
466 }
467
468 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
469 RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
470 RS485_TX_BUFF[2]=RS485_RX_BUFF[2];
471 RS485_TX_BUFF[3]=RS485_RX_BUFF[3];
472 RS485_TX_BUFF[4]=RS485_RX_BUFF[4];
473 RS485_TX_BUFF[5]=RS485_RX_BUFF[5];
474 calCRC=CRC_Compute(RS485_TX_BUFF,6);
475 RS485_TX_BUFF[6]=(calCRC>>8)&0xFF;
476 RS485_TX_BUFF[7]=(calCRC)&0xFF;
477 RS485_SendData(RS485_TX_BUFF,8);
478 }
479 else//寄存器地址+数量超出范围
480 {
481 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
482 RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
483 RS485_TX_BUFF[2]=0x02; //异常码
484 RS485_SendData(RS485_TX_BUFF,3);
485 }
486 }
487
488 //Modbus功能码03处理程序///已验证程序OK
489 //读保持寄存器
490 void Modbus_03_Solve(void)
491 {
492 u8 i;
493 RegNum= (((u16)RS485_RX_BUFF[4])<<8)|RS485_RX_BUFF[5];//获取寄存器数量
494 if((startRegAddr+RegNum)<1000)//寄存器地址+数量在范围内
495 {
496 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
497 RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
498 RS485_TX_BUFF[2]=RegNum*2;
499 for(i=0;i<RegNum;i++)
500 {
501 RS485_TX_BUFF[3+i*2]=(*Modbus_HoldReg[startRegAddr+i]>>8)&0xFF;// /先发送高字节--在发送低字节
502 RS485_TX_BUFF[4+i*2]=(*Modbus_HoldReg[startRegAddr+i])&0xFF; //
503 }
504 calCRC=CRC_Compute(RS485_TX_BUFF,RegNum*2+3);
505 RS485_TX_BUFF[RegNum*2+3]=(calCRC>>8)&0xFF; //CRC高地位不对吗? // 先高后低
506 RS485_TX_BUFF[RegNum*2+4]=(calCRC)&0xFF;
507 RS485_SendData(RS485_TX_BUFF,RegNum*2+5);
508 }
509 else//寄存器地址+数量超出范围
510 {
511 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
512 RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
513 RS485_TX_BUFF[2]=0x02; //异常码
514 RS485_SendData(RS485_TX_BUFF,3);
515 }
516 }
517
518
519 //Modbus功能码06处理程序 //已验证程序OK
520 //写单个保持寄存器
521 void Modbus_06_Solve(void)
522 {
523 *Modbus_HoldReg[startRegAddr]=RS485_RX_BUFF[4]<<8;//高字节在前 修改为高字节在前,低字节在后
524 *Modbus_HoldReg[startRegAddr]|=((u16)RS485_RX_BUFF[5]);//低字节在后
525
526 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
527 RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
528 RS485_TX_BUFF[2]=RS485_RX_BUFF[2];
529 RS485_TX_BUFF[3]=RS485_RX_BUFF[3];
530 RS485_TX_BUFF[4]=RS485_RX_BUFF[4];
531 RS485_TX_BUFF[5]=RS485_RX_BUFF[5];
532
533 calCRC=CRC_Compute(RS485_TX_BUFF,6);
534 RS485_TX_BUFF[6]=(calCRC>>8)&0xFF;
535 RS485_TX_BUFF[7]=(calCRC)&0xFF;
536 RS485_SendData(RS485_TX_BUFF,8);
537 }
538
539 //Modbus功能码16处理程序 /已验证程序OK
540 //写多个保持寄存器
541 void Modbus_16_Solve(void)
542 {
543 u8 i;
544 RegNum= (((u16)RS485_RX_BUFF[4])<<8)|((RS485_RX_BUFF[5]));//获取寄存器数量
545 if((startRegAddr+RegNum)<1000)//寄存器地址+数量在范围内
546 {
547 for(i=0;i<RegNum;i++)
548 {
549 *Modbus_HoldReg[startRegAddr+i]=RS485_RX_BUFF[7+i*2]; //低字节在前 /// 低字节在前,高字节在后正常
550 *Modbus_HoldReg[startRegAddr+i]|=((u16)RS485_RX_BUFF[8+i*2])<<8; //高字节在后
551 }
552
553 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
554 RS485_TX_BUFF[1]=RS485_RX_BUFF[1];
555 RS485_TX_BUFF[2]=RS485_RX_BUFF[2];
556 RS485_TX_BUFF[3]=RS485_RX_BUFF[3];
557 RS485_TX_BUFF[4]=RS485_RX_BUFF[4];
558 RS485_TX_BUFF[5]=RS485_RX_BUFF[5];
559
560 calCRC=CRC_Compute(RS485_TX_BUFF,6);
561 RS485_TX_BUFF[6]=(calCRC>>8)&0xFF;
562 RS485_TX_BUFF[7]=(calCRC)&0xFF;
563 RS485_SendData(RS485_TX_BUFF,8);
564 }
565 else//寄存器地址+数量超出范围
566 {
567 RS485_TX_BUFF[0]=RS485_RX_BUFF[0];
568 RS485_TX_BUFF[1]=RS485_RX_BUFF[1]|0x80;
569 RS485_TX_BUFF[2]=0x02; //异常码
570 RS485_SendData(RS485_TX_BUFF,3);
571 }
572 }
573
574
575
576 stm32编写Modbus步骤 - 纯洁de小学生 - 博客园 (cnblogs.com)