stm32编写Modbus步骤

1. 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     data15 */16 17 18 19 /*====================================================================20   函数名:Modbus_RegMap21   功  能:根据读取寄存器的起始地址选择映射对象,将不同的地址映射到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   函数名:DeviceInfoRefresh60   功  能:更新设备运行的状态值同时更新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   函数名:RtuReceiveHandle87   功  能:处理接受到的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);//KEY233         Modbus_InputIO[3]=(vu32*)&PAin(0);//KEY334         35         //输出开关量寄存器指针指向36         Modbus_OutputIO[0]=(vu32*)&PBout(5);//LED037         Modbus_OutputIO[1]=(vu32*)&PEout(5);//LED138         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;//测试数据243         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  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/346073.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL基础---库的操作和表的操作(配着自己的实操图,简单易上手)

绪论​ 勿问成功的秘诀为何&#xff0c;且尽全力做您应该做的事吧。–美华纳&#xff1b;本章是MySQL的第二章&#xff0c;本章主要写道MySQL中库和表的增删查改以及对库和表的备份处理&#xff0c;本章是基于上一章所写若没安装mysql可以查看Linux下搭建mysql软件及登录和基本…

ubuntu18.04离线安装Mysql

查看系统位数 首先看自己Ubuntu是32还是64位的 sudo uname --m 我是64位。 下载mysql MySQL :: Download MySQL Community Server (Archived Versions) 上传到服务器上 解压 mkdir mysql8 sudo tar -xvf mysql-server_8.0.11-1ubuntu18.04_amd64.deb-bundle.tar -C ./mysql8…

1 c++多线程创建和传参

什么是进程&#xff1f; 系统资源分配的最小单位。 什么是线程&#xff1f; 操作系统调度的最小单位&#xff0c;即程序执行的最小单位。 为什么需要多线程&#xff1f; &#xff08;1&#xff09;加快程序执行速度和响应速度, 使得程序充分利用CPU资源。 &#xff08;2&…

【MySQL】(基础篇二) —— MySQL初始用

MySQL初始用 目录 MySQL初始用基本语法约定选择数据库查看数据库和表其它的SHOW 在Navicat中&#xff0c;大部分数据库管理相关的操作都可以通过图形界面完成&#xff0c;这个很简单&#xff0c;大家可以自行探索。虽然Navicat等图形化数据库管理工具为操作和管理数据库提供了非…

upload-labs-第五关

目录 第五关 1、构造.user.ini文件 2、构造一个一句话木马文件&#xff0c;后缀名为jpg 3、上传.user.ini文件后上传flag.jpg 4、上传成功后访问上传路径 第五关 原理&#xff1a; 这一关采用黑名单的方式进行过滤&#xff0c;不允许上传php、php3、.htaccess等这几类文件…

AB测试学习(附有相关代码)

目录 一、基本概念1. 定义2. 作用3. 原理 二、实验基本原则三、实验步骤四、实验步骤详解1. 确定实验目的2. 确定实验变量3. 实验指标设计3.1 实验指标类型&#xff08;按作用区分&#xff09;3.1.1 核心指标3.1.2 驱动指标&#xff08;跟踪指标&#xff09;3.1.3 护栏指标 3.2…

vue-router全部搞定(附源码)

源码下载链接&#xff08;先转存&#xff0c;后下载&#xff09;&#xff1a;https://pan.quark.cn/s/b0c6edd68c21 怎么用vue-cli搭建项目 我们固然可以用传统htmljs的方式来搭建vue项目&#xff0c;但是如果组件很多&#xff0c;就需要通过Vue.component的方式一个个去引入…

mathematica中针对三维图中的颜色和填充透明度进行指定

颜色指定使用的命令为&#xff1a;PlotStyle 填充的透明度使用的命令为&#xff1a;FillingStyle 示例代码&#xff1a; Clear["Global*"] Plot3D[{Sin[x^2 y], Sin[x^2 - y]}, {x, -2, 2}, {y, -2, 2}, PlotStyle -> {Directive[Red, Specularity[White, 100…

1.VMware软件的安装与虚拟机的创建

1. VMware软件的安装 1.1 为什么需要虚拟机 嵌入式Linux开发需要在Linux系统下运行&#xff0c;我们选择Ubuntu。   1、双系统安装     有问题&#xff0c;一次只能使用一个系统。Ubuntu基本只做编译用。双系统安装不能同时运行Windows和Linux。   2、虚拟机软件   …

Golang Context详解

文章目录 基本介绍context源码剖析Context接口emptyCtxcancelCtxtimerCtxvalueCtx context使用案例协程取消超时控制数据共享 基本介绍 基本介绍 在Go 1.7版本中引入了上下文&#xff08;context&#xff09;包&#xff0c;用于在并发编程中管理请求范围的数据、控制生命周期、…

k8s 1.28 搭建rabbitmq集群

1.环境 1.1 k8s 1.28 1.2 rabbit 3.8 1.3 工作空间default 1.4 注意&#xff0c;内存最好充足一点&#xff0c;因为我就两个节点一个master、一个node&#xff0c;起初我的node是8g&#xff0c;还剩3~4G&#xff0c;集群竟然一直起不来&#xff0c;后来将虚拟机内存扩大&#x…

数字孪生智慧水利:精准管理与智能决策的新时代

图扑数字孪生技术在智慧水利中的应用&#xff0c;通过虚拟模型与真实水利系统的无缝连接&#xff0c;实现对水资源和水利工程的全面监控和精细管理。实时数据采集与动态模拟提升了水利系统的预测和响应能力&#xff0c;从洪水预警到水质监测&#xff0c;数字孪生助力各项决策更…

【Meetup】探索Apache SeaTunnel的二次开发与实战案例

在数据科技快速演进的今天&#xff0c;业务场景的复杂化和数据量的激增&#xff0c;推动了大数据技术的迅速发展&#xff0c;在众多开源大数据处理工具中&#xff0c;Apache SeaTunnel以其强大的数据集成能力&#xff0c;成为众多企业的首选。 但随着应用深入&#xff0c;企业面…

Java_Map集合

认识Map集合 Map集合称为双列集合&#xff0c;格式&#xff1a;{key1value&#xff0c;key2value2,key3value3,…},一次需要存一对数据作为一个元素。 Map集合的每个元素“Keyvalue” 称为一个键值对/键值对对象/一个Entry对象&#xff0c;Map集合也被叫做“键值对集合” Map集…

ViT:2 理解CLIP

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;则提…

简单介绍一下vim

简单介绍一下vim 一、vim是什么&#xff1f;二、vim的优点三、vi/vim的使用命令模式输入模式底线命令模式 四、vi/vim 按键说明&#xff08;一&#xff09;命令模式可用的光标移动、复制粘贴、搜索替换等移动光标的方法:搜索替换的方法删除、复制与贴上的方法 &#xff08;二&a…

Unity 集成 FMOD 音频管理插件 2.02

Unity 集成 FMOD 音频管理插件 2.02 3. 集成教程&#xff1a;3.1 设置Unity项目3.2 设置FMOD项目3.3 设置 FMOD for Unity3.4 添加声音&#xff1a;卡丁车引擎3.5 添加声音&#xff1a;氛围3.6 添加声音&#xff1a;音乐3.7 删除现有音频3.8 下一步 10. 脚本 API 参考10.1 基础…

linux的持续性学习

安装php 第一步&#xff1a;配置yum源 第二步&#xff1a;下载php。 yum install php php-gd php-fpm php-mysql -y 第三步&#xff1a;启动php。 systemctl start php-fpm 第四步&#xff1a;检查php是否启动 lsof -i :9000 计划任务 作用&am…

设计模式- 责任链模式(行为型)

责任链模式 责任链模式是一种行为模式&#xff0c;它为请求创建一个接收者对象的链&#xff0c;解耦了请求的发送者和接收者。责任链模式将多个处理器串联起来形成一条处理请求的链。 图解 角色 抽象处理者&#xff1a; 一个处理请求的接口&#xff0c;可以通过设置返回值的方…

SpringBootWeb 篇-深入了解 Redis 五种类型命令与如何在 Java 中操作 Redis

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 Redis 概述 1.1 Redis 下载与安装 2.0 Redis 数据类型 3.0 Redis 常见五种类型的命令 3.1 字符串操作命令 3.2 哈希操作命令 3.3 列表操作命令 3.4 集合操作命令 …