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     data
 15 */
 16 
 17 
 18 
 19 /*====================================================================
 20   函数名:Modbus_RegMap
 21   功  能:根据读取寄存器的起始地址选择映射对象,将不同的地址映射到
 22                     不同的结构体数据
 23   输入参数说明:
 24   输出参数说明:
 25   返回值说明:无
 26     备 注: 
 27  ====================================================================*/
 28 void Modbus_RegMap(uint16_t wStartAddr)
 29 {
 30     uint16_t woffset = 0;
 31     uint16_t wTemp = 0;
 32     
 33     if((wStartAddr >= REG_BASE_INFO_OFFSET) && (wStartAddr < (REG_BASE_INFO_OFFSET + REG_BASE_INFO_NUM)))
 34     {
 35             Modbus_HoldReg  = (uint16_t *)&g_tdeviceinfo;
 36           woffset = wStartAddr - REG_BASE_INFO_OFFSET;
 37             wTemp = REG_BASE_INFO_NUM;
 38     }
 39     else if(wStartAddr >= (REG_BASE_INFO_OFFSET + REG_BASE_INFO_NUM)  && (wStartAddr < REG_CONFIG_INFO_OFFSET) )
 40     {
 41           g_tCurRtuCmd.m_byExceptionCode = 0x02;   //异常码0x02超出寄存器范围
 42     }
 43     else if((wStartAddr >= REG_CONFIG_INFO_OFFSET) && (wStartAddr < (REG_CONFIG_INFO_OFFSET + REG_CONFIG_INFO_NUM )))
 44     {
 45             Modbus_HoldReg  = (uint16_t *)&g_tConfigInfo;
 46           woffset = wStartAddr - REG_CONFIG_INFO_OFFSET;
 47           wTemp = REG_CONFIG_INFO_NUM;
 48     }
 49     else if(wStartAddr >= (REG_CONFIG_INFO_OFFSET + REG_CONFIG_INFO_NUM))
 50     {
 51             g_tCurRtuCmd.m_byExceptionCode = 0x02;   //异常码0x02超出寄存器范围
 52     }
 53     g_tCurRtuCmd.m_wStartAddr = woffset;
 54     g_tCurRtuCmd.m_wRegOffsetNum = wTemp;
 55 }
 56 
 57 
 58 /*====================================================================
 59   函数名:DeviceInfoRefresh
 60   功  能:更新设备运行的状态值同时更新modbus寄存器的值
 61   输入参数说明:
 62   输出参数说明:
 63   返回值说明:无
 64     备 注: 
 65  ====================================================================*/
 66 void DeviceInfoRefresh(void)
 67 {
 68 
 69         GetHumiAndTempVlue();
 70     
 71         g_tdeviceinfo.m_wlightStripRly = HAL_GPIO_ReadPin(DOOR_LED_RELAY_GPIO_Port,DOOR_LED_RELAY_Pin);
 72         g_tdeviceinfo.m_wFanRly        = HAL_GPIO_ReadPin(FAN_RELAY_GPIO_Port,FAN_RELAY_Pin);
 73         g_tdeviceinfo.m_wWarningLed1   = HAL_GPIO_ReadPin(WARNING_LED_1_GPIO_Port,WARNING_LED_1_Pin);
 74         g_tdeviceinfo.m_wWarningLed2   = HAL_GPIO_ReadPin(WARNING_LED_2_GPIO_Port,WARNING_LED_2_Pin);
 75     
 76         g_tdeviceinfo.m_wGMvalue       = LightLevelPersenGet();   /* 光照等级 */
 77         g_tdeviceinfo.m_wDoorLimit     = HAL_GPIO_ReadPin(LIMIT_SW_DOOR_GPIO_Port,LIMIT_SW_DOOR_Pin);
 78         g_tdeviceinfo.m_wWaterLimit    = HAL_GPIO_ReadPin(WATER_MARK_GPIO_Port,WATER_MARK_Pin);
 79         g_tdeviceinfo.m_Temp                 = (uint16_t)s_tsht2xInfo.m_fTemp;
 80         g_tdeviceinfo.m_Humi                     = (uint16_t)s_tsht2xInfo.m_fHumi;
 81         g_tdeviceinfo.m_vibration          =  Mma8452StatusGet();
 82 }
 83 
 84 
 85 /*====================================================================
 86   函数名:RtuReceiveHandle
 87   功  能:处理接受到的modbus数据,并读取/设置相应寄存器的值
 88   输入参数说明:
 89     pbydata :串口接收到的数据
 90   输出参数说明:
 91     dwLength :输入数据长度
 92   返回值说明:无
 93     备注:由于modubusRtu函数不支持功能码0x06(写单一寄存器),所以0x06不处理
 94  ====================================================================*/
 95 void RtuReceiveHandle(uint8_t *pbydata,uint32_t dwLength)
 96 {        
 97         uint8_t i;
 98         uint16_t wCrc = 0;
 99         uint16_t wIndex = 0, wRealLength = 0, wStartOff = 0;
100         uint8_t byAddr = (g_tConfigInfo.m_bydeviceAddr) & 0xFF;
101         g_tCurRtuCmd.m_byExceptionCode = 0;
102     
103         if(pbydata == NULL || dwLength == 0)
104         {
105             TCLX_PLATFORM_DIAG(("No data received\n"));
106             return;
107         }
108             
109         for(wIndex = 0; wIndex < dwLength; wIndex++)
110         {
111             if(modubusRtu(pbydata + wIndex, dwLength - wIndex, &byAddr, 1, &wRealLength, &(g_tCurRtuCmd.m_byFunCode), &wStartOff))
112             {
113                 wStartOff += wIndex;          /* 找到真实的Modbus数据帧 */
114             
115                 /* 记录命令,在主循环处理 */
116                 g_tCurRtuCmd.m_wStartAddr = (pbydata[wStartOff + 2] << 8) + pbydata[wStartOff + 3];
117                 
118                 Modbus_RegMap(g_tCurRtuCmd.m_wStartAddr); 
119                 
120                 TCLX_PLATFORM_DIAG(("Offset[%d] Len[%d] FunCode[0x%x] StartAddr[0x%x] RegNum[%d]\n", wStartOff, wRealLength,g_tCurRtuCmd.m_byFunCode, g_tCurRtuCmd.m_wStartAddr, g_tCurRtuCmd.m_wRegNum));
121                 
122                 switch(g_tCurRtuCmd.m_byFunCode)
123                 {
124                     case 0x03:
125                             g_tCurRtuCmd.m_wRegNum = (pbydata[wStartOff + 4] << 8) + pbydata[wStartOff + 5];
126                             if((g_tCurRtuCmd.m_wRegNum + g_tCurRtuCmd.m_wStartAddr) <= g_tCurRtuCmd.m_wRegOffsetNum)
127                             {
128                                 abySendData[0] = g_tConfigInfo.m_bydeviceAddr;
129                                 abySendData[1] = g_tCurRtuCmd.m_byFunCode;
130                                 abySendData[2] = g_tCurRtuCmd.m_wRegNum * 2;
131                                  for(i = 0; i < g_tCurRtuCmd.m_wRegNum; i++)
132                                  {
133                                                 abySendData[3+i*2] = (Modbus_HoldReg[g_tCurRtuCmd.m_wStartAddr+i]>>8)&0xFF;//           /先发送高字节--在发送低字节
134                                                 abySendData[4+i*2] = (Modbus_HoldReg[g_tCurRtuCmd.m_wStartAddr+i])&0xFF; //
135                                  }
136                                 wCrc = crc16(abySendData, g_tCurRtuCmd.m_wRegNum*2 + 3);
137                                 abySendData[g_tCurRtuCmd.m_wRegNum*2 + 3] = wCrc & 0x00FF;
138                                 abySendData[g_tCurRtuCmd.m_wRegNum*2 + 4] = (wCrc >> 8) & 0x00FF;
139                              
140                                 usart_send(USART_485_INDEX, abySendData, g_tCurRtuCmd.m_wRegNum*2 + 5);
141                             }
142                             else
143                             {
144                                          g_tCurRtuCmd.m_byExceptionCode = 0x02;   //异常码,超出寄存范围
145                             }
146                         break;
147                     case 0x06:
148                             
149                             Modbus_HoldReg[g_tCurRtuCmd.m_wStartAddr]  = pbydata[wStartOff + 4]<<8  | ((uint16_t)pbydata[wStartOff + 5]);//高字节在前               
150                             
151                             abySendData[0] = pbydata[wStartOff];
152                             abySendData[1] = pbydata[wStartOff + 1];
153                             abySendData[2] = pbydata[wStartOff + 2];
154                             abySendData[3] = pbydata[wStartOff + 3];
155                             abySendData[4] = pbydata[wStartOff + 4];
156                             abySendData[5] = pbydata[wStartOff + 5];
157                             
158                             wCrc = crc16(abySendData,6);
159 
160                             abySendData[6]=(wCrc>>8)&0xFF;
161                             abySendData[7]=(wCrc)&0xFF;
162                             usart_send(USART_485_INDEX, abySendData,8);
163                         break;
164                     
165                     case 0x10:
166                             g_tCurRtuCmd.m_wRegNum = (pbydata[wStartOff + 4] << 8) + pbydata[wStartOff + 5];
167                             if((g_tCurRtuCmd.m_wRegNum + g_tCurRtuCmd.m_wStartAddr) <= g_tCurRtuCmd.m_wRegOffsetNum)
168                             {
169                                     for(i=0;i<g_tCurRtuCmd.m_wRegNum ;i++)
170                                     {
171                                                     Modbus_HoldReg[g_tCurRtuCmd.m_wStartAddr+i]=   pbydata[wStartOff + 7+i*2] <<8 ; //低字节在前               
172                                                     Modbus_HoldReg[g_tCurRtuCmd.m_wStartAddr+i]|=((uint16_t)pbydata[wStartOff + 8+i*2]); //高字节在后
173                                     }
174                                     abySendData[0] = pbydata[wStartOff];
175                                     abySendData[1] = pbydata[wStartOff + 1];
176                                     abySendData[2] = pbydata[wStartOff + 2];
177                                     abySendData[3] = pbydata[wStartOff + 3];
178                                     abySendData[4] = pbydata[wStartOff + 4];
179                                     abySendData[5] = pbydata[wStartOff + 5];
180                             
181                                     wCrc = crc16(abySendData,6);
182                                     abySendData[6]=(wCrc>>8)&0xFF;
183                                     abySendData[7]=(wCrc)&0xFF;
184                             
185                                     /* 如果配置信息发生改变就写入flash,不用做比较相等处理,写flash函数已经处理 */
186                                     writeConfigInfoToFlash(CONFIG_DATA_FLASH_ADDR,&g_tConfigInfo);
187                                                         
188                                     
189                                     usart_send(USART_485_INDEX, abySendData,8);
190                             }
191                             else
192                             {
193                                     g_tCurRtuCmd.m_byExceptionCode = 0x02;    //异常码,超出寄存范围
194                             }
195                         break;
196                     default:
197                                  g_tCurRtuCmd.m_byExceptionCode = 0x01;     //异常码,功能码错误或者不存在 
198                         break;
199                 }
200         
201         if(g_tCurRtuCmd.m_byExceptionCode != 0)
202         {
203             TCLX_PLATFORM_DIAG(("exception code[%d]\n",  g_tCurRtuCmd.m_byExceptionCode));
204             abySendData[0] = g_tConfigInfo.m_bydeviceAddr;
205             abySendData[1] = g_tCurRtuCmd.m_byFunCode + 0x80;
206             abySendData[2] = g_tCurRtuCmd.m_byExceptionCode;
207             wCrc = crc16(abySendData, 3);
208             abySendData[3] = wCrc & 0x00FF;
209             abySendData[4] = (wCrc >> 8) & 0x00FF;
210             usart_send(USART_485_INDEX, abySendData, 5);
211         }
212     
213       memset(&g_tCurRtuCmd, 0, sizeof(TRtuCommand));
214         
215       wIndex += (wStartOff + wRealLength - 1);
216      }/* switch(g_tCurRtuCmd.m_byFunCode) */
217 
218   }/* if modbusRtu do.... */
219         usartRcvRestore(USART_485_INDEX);
220 }
221 
222 
223 /************************************** flash opration *****************************************/
224 
225 
226 /*====================================================================
227   函数名:Read_FlashData
228   功  能:从flash读取配置信息
229   输入参数说明:
230 FlashReadBaseAdd:配置信息基地址
231   输出参数说明:
232 DeviceCfg      :配置参数
233   返回值说明:无
234     备 注: 
235  ====================================================================*/
236 void Read_FlashData(uint32_t FlashReadBaseAdd,DeviceConfigInfo_t *DeviceCfg)
237 {
238     if(NULL == DeviceCfg)
239     {
240         return;
241     }
242     __IO DeviceConfigInfo_t *ptPos = (__IO DeviceConfigInfo_t*)FlashReadBaseAdd;
243     
244     memcpy(DeviceCfg,(const char *)ptPos,sizeof(DeviceConfigInfo_t));
245 }
246 
247 /*====================================================================
248   函数名:writeConfigInfoToFlash
249   功  能:向flash写配置信息
250   输入参数说明:
251 FlashReadBaseAdd:配置信息基地址
252   输出参数说明:
253 DeviceCfg      :配置参数
254   返回值说明:无
255     备 注: 
256  ====================================================================*/
257 void writeConfigInfoToFlash(uint32_t FlashWriteBaseAdd,DeviceConfigInfo_t *DeviceCfg)
258 {
259       uint8_t  byIndex = 0;
260     uint16_t wIndex = 0;
261    
262     DeviceConfigInfo_t DeviceCfgTemp = {0};
263     
264     for(byIndex = 0;byIndex < 10;byIndex++)
265     {
266         Read_FlashData(FlashWriteBaseAdd,&DeviceCfgTemp);
267         
268         if(0 == memcmp(&DeviceCfg,&DeviceCfgTemp,sizeof(DeviceConfigInfo_t)))
269         {
270             TCLX_PLATFORM_DIAG(("write succeed: Data equal\r\n"));
271             return;   
272         }
273         else
274         {
275             HAL_Delay(500);
276             DIS_INT;
277             HAL_StatusTypeDef status = HAL_OK;
278             if(HAL_OK != (status = HAL_FLASH_Unlock()))
279             {
280                 TCLX_PLATFORM_DIAG((" falsh unlock err\r\n"));
281                 continue;
282             }
283             FLASH_EraseInitTypeDef f;
284             f.TypeErase = FLASH_TYPEERASE_PAGES;
285             f.PageAddress = (uint32_t)FlashWriteBaseAdd;
286             f.NbPages = 1;
287             uint32_t PageError = 0;
288             
289             if(HAL_OK != (status = HAL_FLASHEx_Erase(&f, &PageError)))
290             {
291                 if(0 != PageError)
292                 {
293                     TCLX_PLATFORM_DIAG(("HAL_FLASHEx_Erase:failed(%d-%d)\n",status,PageError));
294                     HAL_FLASH_Lock();
295                     continue;
296                 }
297             }
298             for(wIndex = 0; wIndex < (sizeof(DeviceConfigInfo_t) / sizeof(uint32_t)); wIndex ++)
299             {
300                 if(HAL_OK != (status = HAL_FLASH_Program(TYPEPROGRAM_WORD,FlashWriteBaseAdd + (wIndex * sizeof(uint32_t)) ,((uint32_t *)DeviceCfg)[wIndex])))
301                 {
302                     TCLX_PLATFORM_DIAG(("HAL_FLASH_Program:CONFIG_DATA_FLASH_ADDR failed(%d)\n",status));
303                     HAL_FLASH_Lock();
304                     continue;
305                 }
306             
307             }    
308             if(HAL_OK != (status = HAL_FLASH_Lock()))
309             {
310                 TCLX_PLATFORM_DIAG(("HAL_FLASH_Lock:HAL_FLASH_Lock(%d)\n",status));
311             }
312             EN_INT;
313             return ;
314         }
315     }
316 }

参考下面例程,此例程比较详细

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