1. modbus協議簡介:
modbus協議基於rs485總線,采取一主多從的形式,主設備輪詢各從設備信息,從設備不主動上報。
日常使用都是RTU模式,協議幀格式如下所示:
地址 功能碼 寄存器地址 讀取寄存器個數 寄存器數據1 ..... CrcL CrcH
/* 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
