[STM32F1] STM32F103单片机modbus通信示例

[复制链接]
1340|36
 楼主| 花间一壶酒sd 发表于 2023-4-22 00:19 | 显示全部楼层
modbus.h
  1. #ifndef __MODBUS_H
  2. #define __MODBUS_H
  3. #include "sys.h"


  4. #define SlaveID  0x01     //从机地址

  5. void DisposeReceive( void );
  6. void Modbus_03_Slave(void);
  7. void Modbus_06_Slave(void);
  8. void Modbus_16_Slave(void);
  9. #endif
 楼主| 花间一壶酒sd 发表于 2023-4-22 00:19 | 显示全部楼层
modbus.c
  1. #include "modbus.h"
  2. #include "crc16.h"
  3. #include "uart2.h"

  4. #define HoldRegStartAddr    0x0000                                                                                                                                                                                //保持寄存器起始地址
  5. #define HoldRegCount        8                                                                                                                                                                                                        //保持寄存器数量
  6. #define HoldMaxValue        1000                                                                                                                                                    //寄存器最大值

  7. extern u8 data_backup[DMA_REC_LEN];                                                                                                                                                                   //modbus接收到数据备份
  8. extern u16 dataLen_backup;                                                                                                                                                                                                                //modbus接收数据长度备份


  9. u8 SendBuf[DMA_REC_LEN] = {0};
  10. u16 StartRegAddr = HoldRegStartAddr;
  11. u8 err = 0;                                                                                                        //错误代码

  12. u8 HoldReg[HoldRegCount*2] = {1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8}; //存储8路数据值


  13. /*
  14. 错误代码说明:
  15. 0x01  不是支持的功能码
  16. 0x02  起始地址不在规定范围内
  17. 0x03  寄存器数量不在规定范围内
  18. 0x04  数据校验错误
  19. */
  20. //处理接收到的数据
  21. // 接收: [地址][功能码][起始地址高][起始地址低][总寄存器数高][总寄存器数低][CRC低][CRC高]
  22. void DisposeReceive( void )
  23. {
  24.     u16 CRC16 = 0, CRC16Temp = 0;
  25.     if( data_backup[0] == SlaveID )                                                                         //地址等于本机地址 地址范围:1 - 32
  26.     {
  27.         CRC16 = App_Tab_Get_CRC16( data_backup, dataLen_backup - 2 );                                  //CRC校验 低字节在前 高字节在后 高字节为报文最后一个字节
  28.         CRC16Temp = ( ( u16 )( data_backup[dataLen_backup - 1] << 8 ) | data_backup[dataLen_backup - 2] );
  29.         if( CRC16 != CRC16Temp )
  30.         {
  31.             err = 4;                                                                                               //CRC校验错误
  32.         }
  33.         StartRegAddr = ( u16 )( data_backup[2] << 8 ) | data_backup[3];
  34.         if( StartRegAddr > (HoldRegStartAddr + HoldRegCount - 1) )
  35.         {
  36.             err = 2;                                                                                               //起始地址不在规定范围内 00 - 07    1 - 8号通道
  37.         }
  38.         if( err == 0 )
  39.         {
  40.             switch( data_backup[1] )                                                                        //功能码
  41.             {
  42.                 case 3:                                                                                            //读多个寄存器
  43.                 {
  44.                     Modbus_03_Slave();
  45.                     break;
  46.                 }
  47.                 case 6:                                                                                            //写单个寄存器
  48.                 {
  49.                     Modbus_06_Slave();
  50.                     break;
  51.                 }
  52.                 case 16:                                                                                           //写多个寄存器
  53.                 {
  54.                     Modbus_16_Slave();
  55.                     break;
  56.                 }
  57.                 default:
  58.                 {
  59.                     err = 1;                                                                                       //不支持该功能码
  60.                     break;
  61.                 }
  62.             }
  63.         }
  64.         if( err > 0 )
  65.         {
  66.             SendBuf[0] = data_backup[0];
  67.             SendBuf[1] = data_backup[1] | 0x80;
  68.             SendBuf[2] = err;                                                                                      //发送错误代码
  69.             CRC16Temp = App_Tab_Get_CRC16( SendBuf, 3 );                                                           //计算CRC校验值
  70.             SendBuf[3] = CRC16Temp & 0xFF;                                                                         //CRC低位
  71.             SendBuf[4] = ( CRC16Temp >> 8 );                                                                       //CRC高位
  72.             uart2_Send( SendBuf, 5 );                                       
  73.             err = 0;                                                                                               //发送完数据后清除错误标志
  74.         }
  75.     }
  76. }

  77. /*
  78. 函数功能:读保持寄存器  03
  79. 主站请求报文:      0x01 0x03   0x0000  0x0001  0x840A     读从0开始的1个保持寄存器
  80. 从站正常响应报文:  0x01 0x03   0x02    0x09C4  0xBF87     读到的2字节数据为 0x09C4
  81. */
  82. void Modbus_03_Slave( void )
  83. {
  84.     u16 RegNum = 0;
  85.     u16 CRC16Temp = 0;
  86.     u8 i = 0;
  87.     RegNum = ( u16 )( data_backup[4] << 8 ) | data_backup[5];                                                //获取寄存器数量
  88.     if( ( StartRegAddr + RegNum ) <= (HoldRegStartAddr + HoldRegCount) )      //寄存器地址+寄存器数量 在规定范围内 <=8
  89.     {
  90.         SendBuf[0] = data_backup[0];                                                                                                                                                                        //地址       
  91.         SendBuf[1] = data_backup[1];                                                                                                                                                                        //功能码
  92.         SendBuf[2] = RegNum * 2;                                                                                                                                                                                        //返回字节数量
  93.         for( i = 0; i < RegNum; i++ )                                                                             //循环读取保持寄存器内的值
  94.         {
  95.             SendBuf[3 + i * 2] = HoldReg[StartRegAddr * 2 + i * 2];
  96.             SendBuf[4 + i * 2] = HoldReg[StartRegAddr * 2 + i * 2 + 1];
  97.         }
  98.         CRC16Temp = App_Tab_Get_CRC16( SendBuf, RegNum * 2 + 3 );                                                  //获取CRC校验值
  99.         SendBuf[RegNum * 2 + 3] = CRC16Temp & 0xFF;                                                                //CRC低位
  100.         SendBuf[RegNum * 2 + 4] = ( CRC16Temp >> 8 );                                                              //CRC高位
  101.         uart2_Send( SendBuf, RegNum * 2 + 5 );
  102.     }
  103.     else
  104.     {
  105.         err = 3;                                                                                                   //寄存器数量不在规定范围内
  106.     }
  107. }
  108. /*
  109. 函数功能:写单个保持寄存器 06
  110. 主站请求报文:      0x01 0x06    0x0000  0x1388    0x849C   写0号寄存器的值为0x1388
  111. 从站正常响应报文:  0x01 0x06    0x0000  0x1388    0x849C    0号寄存器的值为0x1388
  112. */
  113. void Modbus_06_Slave( void )
  114. {
  115.     u16  RegValue = 0;
  116.     u16 CRC16Temp = 0;
  117.     RegValue = ( u16 )( data_backup[4] << 8 ) | data_backup[5];                                              //获取寄存器值
  118.     if( RegValue <= HoldMaxValue )                                                  //寄存器值不超过1000
  119.     {
  120.         HoldReg[StartRegAddr * 2] = data_backup[4];                                                         //存储寄存器值
  121.         HoldReg[StartRegAddr * 2 + 1] = data_backup[5];
  122.         SendBuf[0] = data_backup[0];                                                                                                                                                                        //地址
  123.         SendBuf[1] = data_backup[1];                                                                                                                                                                        //功能码
  124.         SendBuf[2] = data_backup[2];                                                                                                                                                                        //地址高位
  125.         SendBuf[3] = data_backup[3];                                                                                                                                                                        //地址低位
  126.         SendBuf[4] = data_backup[4];                                                                                                                                                                        //值高位
  127.         SendBuf[5] = data_backup[5];                                                                                                                                                                        //值低位
  128.         CRC16Temp = App_Tab_Get_CRC16( SendBuf, 6 );                                                              //获取CRC校验值
  129.         SendBuf[6] = CRC16Temp & 0xFF;                                                                            //CRC低位
  130.         SendBuf[7] = ( CRC16Temp >> 8 );                                                                          //CRC高位
  131.         uart2_Send( SendBuf, 8 );
  132.     }
  133.     else
  134.     {
  135.         err =  3;                                                                                                  //寄存器数值不在规定范围内
  136.     }
  137. }
  138. /*
  139. 函数功能:写多个连续保持寄存器值 16
  140. 主站请求报文:       0x01 0x10    0x7540  0x0002  0x04  0x0000 0x2710    0xB731  写从0x7540地址开始的2个保持寄存器值 共4字节
  141. 从站正常响应报文:   0x01 0x10    0x7540  0x0002  0x5A10                         写从0x7540地址开始的2个保持寄存器值
  142. */
  143. void Modbus_16_Slave( void )
  144. {
  145.     u16 RegNum = 0;
  146.     u16 CRC16Temp = 0;
  147.     u8 i = 0;
  148.     RegNum = ( u16 )( data_backup[4] << 8 ) | data_backup[5];                                                //获取寄存器数量
  149.     if( ( StartRegAddr + RegNum ) <= (HoldRegStartAddr + HoldRegCount) )             //寄存器地址+寄存器数量 在规定范围内 <=8
  150.     {
  151.         for( i = 0; i < RegNum; i++ )                                                                              //存储寄存器设置值
  152.         {
  153.             HoldReg[StartRegAddr * 2 + i * 2] = data_backup[i * 2 + 7];
  154.             HoldReg[StartRegAddr * 2 + 1 + i * 2] = data_backup[i * 2 + 8];
  155.         }
  156.         SendBuf[0] = data_backup[0];                                                                                                                                                                        //起始地址
  157.         SendBuf[1] = data_backup[1];                                                                                                                                                                        //功能码
  158.         SendBuf[2] = data_backup[2];                                                                                                                                                                        //地址高位
  159.         SendBuf[3] = data_backup[3];                                                                                                                                                                        //地址低位
  160.         SendBuf[4] = data_backup[4];                                                                                                                                                                        //寄存器数量高位
  161.         SendBuf[5] = data_backup[5];                                                                                                                                                                        //寄存器数量低位
  162.         CRC16Temp = App_Tab_Get_CRC16( SendBuf, 6 );                                                        //获取CRC校验值
  163.         SendBuf[6] = CRC16Temp & 0xFF;                                                                      //CRC低位
  164.         SendBuf[7] = ( CRC16Temp >> 8 );                                                                    //CRC高位
  165.         uart2_Send( SendBuf, 8 );
  166.     }
  167.     else
  168.     {
  169.         err = 3;                                                                                                   //寄存器数量不在规定范围内
  170.     }
  171. }
 楼主| 花间一壶酒sd 发表于 2023-4-22 00:19 | 显示全部楼层
测试效果

测试功能码 0x03  读一个或者多个保持寄存器值

897776442b7aa3c8bf.png
 楼主| 花间一壶酒sd 发表于 2023-4-22 00:20 | 显示全部楼层
测试功能码 0x10 写多个保持寄存器值

313626442b7b88bef9.png
 楼主| 花间一壶酒sd 发表于 2023-4-22 00:20 | 显示全部楼层
再次读取所有寄存器值

781846442b7c63a4cd.png

说明寄存器值修改成功。

工程下载地址 https://download.csdn.net/download/qq_20222919/12937240

串口调试软件下载地址: https://download.csdn.net/download/qq_20222919/18798579?spm=1001.2014.3001.5501
周半梅 发表于 2024-6-6 07:03 | 显示全部楼层

需要在做项目的过程中经历磨难
Pulitzer 发表于 2024-6-6 08:06 | 显示全部楼层

硬件设计和软件设计本来就是鱼和熊掌的关系,两者不可兼得
童雨竹 发表于 2024-6-6 10:02 | 显示全部楼层

结构化模块化的程序设计的思想,使最基本的要求
Wordsworth 发表于 2024-6-6 11:05 | 显示全部楼层

确定好硬件原理图,硬件布线,最后才是软件的开发
Clyde011 发表于 2024-6-6 12:08 | 显示全部楼层

没有发现有哪本是介绍设计思想的
公羊子丹 发表于 2024-6-6 13:01 | 显示全部楼层

写程序不难,但是程序怎么样才能写的好,写的快,那是需要点经验积累的
万图 发表于 2024-6-6 14:04 | 显示全部楼层

在实际的项目应用当中,单片机引脚的复用相当厉害
Uriah 发表于 2024-6-6 15:07 | 显示全部楼层

但是如果不懂程序设计的思想的话,会给你做项目的过程中带来很多很多的困惑。
帛灿灿 发表于 2024-6-6 17:03 | 显示全部楼层

时间片轮的设计思想
Bblythe 发表于 2024-6-6 18:06 | 显示全部楼层

事实上很多做项目的工程师本身自己也会在用
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 在线客服 返回列表 返回顶部