stm32编写Modbus步骤

2024-06-10 18:52
文章标签 步骤 编写 stm32 modbus

本文主要是介绍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步骤的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1048981

相关文章

零基础STM32单片机编程入门(一)初识STM32单片机

文章目录 一.概要二.单片机型号命名规则三.STM32F103系统架构四.STM32F103C8T6单片机启动流程五.STM32F103C8T6单片机主要外设资源六.编程过程中芯片数据手册的作用1.单片机外设资源情况2.STM32单片机内部框图3.STM32单片机管脚图4.STM32单片机每个管脚可配功能5.单片机功耗数据6.FALSH编程时间,擦写次数7.I/O高低电平电压表格8.外设接口

BD错误集锦8——在集成Spring MVC + MyBtis编写mapper文件时需要注意格式 You have an error in your SQL syntax

报错的文件 <?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.yuan.dao.YuanUserDao"><!

如何利用echarts编写立体的柱状图表

1、引入 import * as echarts from 'echarts' 2、创建图标容器 3、调用渲染 <template><div ref="eachrtsBox" style="width: 200px;height: 200px;"></div></template><script>import * as echarts from 'echarts'export d

Modbus初学者教程,第三章:modbus寄存器说明

第三章:modbus寄存器说明 寄存器种类 Modbus协议中一个重要的概念是寄存器,所有的数据均存放于寄存器中。Modbus寄存器是指一块内存区域。Modbus寄存器根据存放的数据类型以及各自读写特性,将寄存器分为4个部分,这4个部分可以连续也可以不连续,由开发者决定。寄存器的意义如下表所示。 Modbus协议定义了设备间的数据传输方式,包括数据格式和通信规则。Modbus寄存器是协议中用

STM32单片机PWR电源控制详解

文章目录 1. PWR概述 2. 电源结构框图 3. 上电复位和掉电复位 4. 可编程电压监测器 5. 低功耗模式 6. 模式选择 6.1 睡眠模式 6.2 停止模式 6.3 待机模式 7. 代码示例 1. PWR概述 PWR(Power Control)电源控制,负责管理STM32内部的电源供电部分,可以实现可编程电压监测器和低功耗模式的功能。 可编程电压监测器

spring mvc完整项目创建步骤记录

快速创建一个spring mvc项目(只有页面调用→到controller→到页面) 1、首先创建Dynamic Web Project 2、创建jsp页面index.jsp以及成功(/WEB-INF/view/success.jsp)和失败页面(/WEB-INF/view/error.jsp) index.jsp <%@ page language="java" contentType=

ssh在本地虚拟机中的应用——解决虚拟机中编写和阅读代码不方便问题的一个小技巧

虚拟机中编程小技巧分享——ssh的使用 事情的起因是这样的:前几天一位工程师过来我这边,他看到我在主机和虚拟机运行了两个vscode环境,不经意间提了句:“这么艰苦的环境写代码啊”。 后来我一想:确实。 我长时间以来都是直接在虚拟机里写的代码,但是毕竟是虚拟机嘛,有时候编辑器没那么流畅,在文件比较多的时候跳转很麻烦,容易卡住。因此,我当晚简单思考了一下,想到了一个可行的解决方法——即用ssh

简单 使用 的makefile编写 框架

1、指定编译器,如海思平台:CROSS_COMPILE=arm-hisiv100nptl-linux-; 2、指定编译工具:GCC=$(CROSS_COMPILE)gcc   CC=$(CROSS_COMPILE)g++; 3、使用 export 导出 子makefile 要用的变量; 4、定义变量的形式  指定 工程源文件 需要使用到的 “宏”,在后面的 LDFLAGS 里面使用 -D将其

STM32学习之一:什么是STM32

目录 1.什么是STM32 2.STM32命名规则 3.STM32外设资源 4. STM32的系统架构 5. 从0到1搭建一个STM32工程 学习stm32已经很久了,因为种种原因,也有很久一段时间没接触过stm32了。等我捡起来的时候,发现很多都已经忘记了,重新捡起来吧。 每次谈到stm32如何如何,那么该如何解释什么是stm32呢? 1.什么是STM32 stm32

STM32学习 修改系统主频

前面时钟树的学习说明单片机的主频是可以修改的,那么怎么更改系统的主频,这里做一个简单的介绍。首先要明白,单片机的程序是如何运行,这里简单说明一下。 对应的代码在startup_stm32....文件里面,这里是复位程序的汇编代码。 复位子程序是系统上电后第一个执行的程序,调用 SystemInit 函数初始化系统时钟,然后调用 C 库函数 _mian,最终调用 main 函数去到 C