当前位置: 首页 > news >正文

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  stm32编写Modbus步骤 - 纯洁de小学生 - 博客园 (cnblogs.com)

相关文章:

stm32编写Modbus步骤

1. modbus协议简介&#xff1a; modbus协议基于rs485总线&#xff0c;采取一主多从的形式&#xff0c;主设备轮询各从设备信息&#xff0c;从设备不主动上报。 日常使用都是RTU模式&#xff0c;协议帧格式如下所示&#xff1a; 地址 功能码 寄存器地址 读取寄存器…...

基于 Transformer 的大语言模型

语言建模作为语言模型&#xff08;LMs&#xff09;的基本功能&#xff0c;涉及对单词序列的建模以及预测后续单词的分布。 近年来&#xff0c;研究人员发现&#xff0c;扩大语言模型的规模不仅增强了它们的语言建模能力&#xff0c;而且还产生了处理传统NLP任务之外更复杂任务…...

证照之星是一款很受欢迎的证件照制作软件

证照之星是一款很受欢迎的证件照制作软件&#xff0c;证照之星可以为用户提供“照片旋转、裁切、调色、背景处理”等功能&#xff0c;满足用户对证件照制作的基本需求。本站证照之星下载专题为大家提供了证照之星电脑版、安卓版、个人免费版等多个版本客户端资源&#xff0c;此…...

不定时更新 解决无法访问GitHub github.com 打不开 dns访问加速

1 修改hosts Windows 10为例,‪文件C:\Windows\System32\drivers\etc\hosts 管理员打开记事本来修改 文件-打开-“C:\Windows\System32\drivers\etc\hosts” 20.205.243.168 api.github.com 185.199.108.154 github.githubassets.com 185.199.108.133 raw.githubusercontent.…...

单向环形链表的创建与判断链表是否有环

单向环形链表的创建与单向链表的不同在于&#xff0c;最后一个节点的next需要指向头结点&#xff1b; 判断链表是否带环&#xff0c;只需要使用两个指针&#xff0c;一个步长为1&#xff0c;一个步长为2&#xff0c;环状链表这两个指针总会相遇。 如下示例代码&#xff1a; l…...

JVM堆栈的区别、分配内存与并发安全问题、对象定位

一、堆和栈的区别 堆&#xff08;Heap&#xff09;和栈&#xff08;Stack&#xff09;是两种基本的数据结构&#xff0c;它们在内存管理、程序执行流程控制等方面扮演着重要角色。在编程语言尤其是Java这样的高级语言环境中&#xff0c;堆和栈的概念被用来描述程序运行时的内存…...

Python教程:机器学习 - 百分位数(4)

什么是百分位数&#xff1f; 统计学中使用百分位数&#xff08;Percentiles&#xff09;为您提供一个数字&#xff0c;该数字描述了给定百分比值小于的值。 例如&#xff1a;假设我们有一个数组&#xff0c;包含住在一条街上的人的年龄。 ages [5,31,43,48,50,41,7,11,15,3…...

数据结构习题(快期末了)

一个数据结构是由一个逻辑结构和这个逻辑结构上的一个基本运算集构成的整体。 从逻辑关系上讲&#xff0c;数据结构主要分为线性结构和非线性结构两类。 数据的存储结构是数据的逻辑结构的存储映像。 数据的物理结构是指数据在计算机内实际的存储形式。 算法是对解题方法和…...

Http协议:Http缓存

文章目录 Cookie和Session缓存有效性检查整体流程总结Cookie和Session Cookie 客户端的缓存 Session 服务端的缓存,存储服务器与客户端一次会话的过程中的数据/资源 两者区别 是服务端与客户端的不同需求造成的 有效期 Cookie的有效期很长,Session的较短 原因:服务…...

idea插件开发之hello idea plugin

写在前面 最近一直想研究下自定义idea插件的内容&#xff0c;这样如果是想要什么插件&#xff0c;但又一时找不到合适的&#xff0c;就可以自己来搞啦&#xff01;这不终于有时间来研究下&#xff0c;但过程可谓是一波三折&#xff0c;再一次切身体验了下万事开头难。那么&…...

Sm4【国密4加密解密】

当我们开发金融、国企、政府信息系统时&#xff0c;不仅要符合网络安全的等保二级、等保三级&#xff0c;还要求符合国密的安全要求&#xff0c;等保测评已经实行很久了&#xff0c;而国密测评近两年才刚开始。那什么是密码/国密&#xff1f;什么是密评&#xff1f;本文就关于密…...

git如果将多次提交压缩成一次

将N个提交压缩到单个提交中有两种方式&#xff1a; git reset git reset的本意是版本回退&#xff0c;回退时可以选择保留commit提交。我们基于git reset的作用&#xff0c;结合新建分支&#xff0c;可以实现多次commit提交的合并。这个不需要vim编辑&#xff0c;很少有冲突。…...

android用Retrofit进行网络请求和解析

Retrofit 的原理 Retrofit的核心原理包括动态代理与Service Method的构建、注解解析与请求配置、网络请求执行与响应处理等。它是一个类型安全的HTTP客户端&#xff0c;用于Android和Java平台&#xff0c;通过将HTTP API转化为Java接口的方式&#xff0c;简化了网络请求的编写…...

list容器的基本使用

目录 前言一&#xff0c;list的介绍二&#xff0c;list的基本使用2.1 list的构造2.2 list迭代器的使用2.3 list的头插&#xff0c;头删&#xff0c;尾插和尾删2.4 list的插入和删除2.5 list 的 resize/swap/clear 前言 list中的接口比较多&#xff0c;与string和vector类似&am…...

34万汉语词语成语反义词ACCESS\EXCEL数据库

反义词就是两个意思相反的词&#xff0c;包括&#xff1a;绝对反义词和相对反义词。分为成对的意义相反、互相对立的词。如&#xff1a;真——假&#xff0c;动——静&#xff0c;拥护——反对。这类反义词所表达的概念意义互相排斥。或成对的经常处于并举、对待位置的词。如&a…...

yum方式更新Jenkins

目的 使用yum方式更新Jenkins。 步骤 查看最新可用版本 $ yum list jenkins Last metadata expiration check: 0:03:44 ago on Fri Jun 14 06:10:01 2024. Installed Packages jenkins.noarch 2.452.1-1.1 jenkins Available Pa…...

欢乐钓鱼大师保姆级教程,云手机辅助攻略解析!

在这份攻略中&#xff0c;我们将为大家详细介绍如何在《欢乐钓鱼大师》中快速提升钓鱼技能和游戏进展&#xff0c;避免常见的新手误区和不必要的资源浪费。无论是钓鱼点的选择、装备的合理使用还是技能的优化&#xff0c;我们都会一一为您详细解析&#xff0c;帮助您成为一名优…...

数据结构:手撕代码——顺序表

目录 1.线性表 2.顺序表 2.1顺序表的概念 2.2动态顺序表实现 2.2-1 动态顺序表实现思路 2.2-2 动态顺序表的初始化 2.2-3动态顺序表的插入 检查空间 尾插 头插 中间插入 2.2-4 动态顺序表的删除 尾删 头删 中间删除 2.2. 5 动态顺序表查找与打印、销毁 查找 …...

jenkins使用注意问题

1.在编写流水线时并不知道当前处在哪个目录&#xff0c;导致名使用不当&#xff0c;以及文件位置不清楚 流水线任务默认路径是&#xff0c;test4_mvn为jenkins任务名 [Pipeline] sh (hide)pwd /var/jenkins_home/workspace/test4_mvn maven任务也是&#xff0c;看来是一样的…...

Kaggle -- Titanic - Machine Learning from Disaster

新手kaggle之旅&#xff1a;1 . 泰坦尼克号 使用一个简单的决策树进行模型构建&#xff0c;达到75.8%的准确率&#xff08;有点低&#xff0c;但是刚开始&#xff09; 完整代码如下&#xff1a; import pandas as pd import numpy as npdf pd.read_csv("train.csv&quo…...

蓝牙音频解码芯片TD5163介绍,支持红外遥控—拓达半导体

蓝牙芯片TD5163A是一颗支持红外遥控、FM功能和IIS音频输出的蓝牙音频解码芯片&#xff0c;此颗芯片的亮点在于同时支持真立体声&单声道、TWS功能、PWM、音乐频谱和串口AT指令控制等功能&#xff0c;芯片在支持蓝牙无损音乐播放的同时&#xff0c;还支持简单明了的串口发送A…...

windows 下 docker 入门

这里只是具体过程&#xff0c;有不清楚的欢迎随时讨论 1、安装docker &#xff0c;除了下一步&#xff0c;好像也没有其他操作了 2、安装好docker后&#xff0c;默认是运行在linux 下的&#xff0c;这时我们需要切换到windows 环境下&#xff0c; 操作&#xff1a;在右下角d…...

《别让“想太多”挡了你的骑行路,对比一下更丝滑》

在探索骑行的世界时&#xff0c;我们往往会被一些先入为主的想法所束缚。本文将带你对比骑行与其他运动和生活方式&#xff0c;揭示那些阻碍你爱上骑行的认知误区。 一、年龄不是界限&#xff1a;骑行与跑步的比较与跑步相比&#xff0c;骑行同样适合所有年龄段&#xff0c;但它…...

hadoop和hbase对应版本关系

https://hbase.apache.org/book.html#configuration...

现代X86汇编-C和ASM混合编程举例

端午假期安装好了vs c2022,并写了个简单的汇编代码&#xff0c;证明MASM真的可以运行。今天需要搞一个实实在在的C和ASM混合编程的例子&#xff0c;因为用纯汇编的求伯君写WPS的时代一去不复返了。个别关键函数用汇编&#xff0c;充分发挥CPU的特色功能&#xff0c;偶尔还是需要…...

485. 最大连续 1 的个数

给定一个二进制数组 nums &#xff0c; 计算其中最大连续 1 的个数。 示例 1&#xff1a; 输入&#xff1a;nums [1,1,0,1,1,1] 输出&#xff1a;3 解释&#xff1a;开头的两位和最后的三位都是连续 1 &#xff0c;所以最大连续 1 的个数是 3.示例 2: 输入&#xff1a;nums […...

席卷的B站《植物大战僵尸杂交版》V2.0.88整合包,PC和手机可用,含通关存档和视频教程!

今天给大家安利一款席卷B站&#xff0c;火爆全网的游戏——《植物大战僵尸杂交版》2.0.88整合包。 这个是网络上现存植物大战僵尸杂交版的最全整合&#xff0c;包含了修改工具&#xff0c;超强通关存档和高清工具。工具包有安装视频教程&#xff0c;支持手机版和pc多端使用&am…...

液晶拼接屏企业应该采取哪些措施来提升整体竞争力和市场地位呢?

步入智能科技时代以来&#xff0c;商显行业面对着各式各样的挑战&#xff0c;人工智能、AI大模型等整合中&#xff0c;液晶拼接屏企业应该采取哪些措施以提升整体竞争力和市场地位。下面小编个人观点简单说一下&#xff1b;下是一些关键的措施&#xff1a; 首先&#xff0c;加…...

PHP在线生成查询产品防伪证书系统源码

源码介绍 PHP在线生成查询产品防伪证书系统源码&#xff0c;源码自带90套授权证书模板&#xff0c;带PSD公章模板&#xff0c;证书PSD源文件。 环境要求&#xff1a;PHPMYSQL&#xff0c;PHP 版本请使用PHP5.1 ~5.3。 图片截图 源码安装说明 1.上传所有文件至你的空间服务器…...

遥控玩具车电机驱动应用中的双H桥驱动芯片

遥控玩具车的基本工作原理是通过无线电遥控器发送信号&#xff0c;这些信号被玩具车内的接收器接收并解码&#xff0c;从而控制玩具车的运行。根据车身外型的不同&#xff0c;可以分为&#xff1a;普通的私家房车、越野车、货柜车、翻斗车等等。遥控器的操作&#xff0c;如前进…...