1. modbus协议简介:
modbus协议基于rs485总线,采取一主多从的形式,主设备轮询各从设备信息,从设备不主动上报。
日常使用都是RTU模式,协议帧格式如下所示:
地址 功能码 寄存器地址 读取寄存器个数 寄存器数据1 ..... CrcL CrcH
1 2 3 4 5 6 7 8 9 |
|
功能码及对应的操作字长:
目前比较简单的实现了读多个保持寄存器,以及写多个保持寄存器,由于不是使用的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