STM32/STM8技术论坛
直播中

胖子的逆袭

13年用户 1184经验值
私信 关注
[资料]

modbus-rtu 通讯如何实现(环境:STM32)


编程思路:
    1.串口中断允许自动接收总线上的信息,当接收的 字节后超过3.5个字节时间没有新的字节认为本次
    接收完成,接收完成标志置1;如果接收完成标志已 经置1又有数据进来则丢弃新来的数据。
    2.串口接收数据的处理, 当接收完成标志置1进入接收数据处理,
  • (1)首先判断接收的第一位数据与本机地址是否相同,如果不相同清空接收缓存不发送任何信息;
  • (2)接收的第一位数据与本机地址相同,则对接收缓存中的数据进行crc16校验,如果接 收的校验位与本校验结果不相同清空接收缓存不发送任何信息;
  • (3)如果crc16校验正确则根据数据串中的命令码进行相应的处理。


  1. ******************************************************/
  2. #include "modbus.h"
  3. u8 Com0_id = 0x05;//本机串口0的通讯地址
  4. u8 Uart0_rev_buff[100];//com0串口接收缓冲区
  5. u8 Uart0_send_buff[100];//com0串口发送缓冲区
  6. vu8 Uart0_rev_count;
  7. vs8 Uart0_send_counter = 0;
  8. vu8 Uart0_rev_comflag;
  9. vu8 Crc_counter = 0;//com0校验计数器
  10. vu8 *Uart0_send_pointer = Uart0_send_buff;//com0串口发送指针
  11. vu16 Mkgz_bz = 0;//模块故障标志1:输入异常,2:过压,3:欠压,4:过温
  12. vu16 Out_current = 50;//输出电流
  13. vu16 Out_voltage = 240;//输出电压
  14. vu16 Mkzt_bz = 0;//模块状态标志
  15. vu16 OutX_current = 1000;//输出限流
  16. vu16 Jc_voltage = 2530;//均充电压
  17. vu16 Fc_voltage = 2400;//浮充电压
  18. vu16 user_day = 1825;//使用天数

  19. void Delay(vu32 nCount);
  20. unsigned short getCRC16(volatile unsigned char *ptr,unsigned char len) ;
  21. void mov_data(u8 a[100],u8 b[100],u8 c);
  22. void Modbus_Function_3(void);
  23. void Modbus_Function_6(void);      
  24. /***************************************
  25. 函数名称:crc16校验
  26. 函数功能:crc16校验
  27. 函数输入:字节指针*ptr,数据长度len
  28. 函数返回:双字节crc
  29. 函数编写:孙可
  30. 编写日期:2008年6月9日
  31. 函数版本:v0.2
  32. ****************************************/
  33. unsigned short getCRC16(volatile unsigned char *ptr,unsigned char len)
  34. {
  35.     unsigned char i;
  36.     unsigned short crc = 0xFFFF;
  37.     if(len==0)
  38.     {
  39.         len = 1;
  40.     }
  41.     while(len--)  
  42.     {   
  43.         crc ^= *ptr;
  44.         for(i=0; i<8; i++)  
  45.      {
  46.             if(crc&1)
  47.          {
  48.                 crc >>= 1;  
  49.                 crc ^= 0xA001;
  50.          }  
  51.          else
  52.       {
  53.                 crc >>= 1;
  54.          }
  55.         }         
  56.         ptr++;
  57.     }
  58.     return(crc);
  59. }
  60. /***************************************
  61.     块数据复制数据函数
  62. 功能:把数组a的c个数据复制到数组b中
  63. 输入:指针a,指针b,数据个数c
  64. 返回:无
  65. 编写:孙可
  66. 编写日期:2008年3月28日
  67. 版本:v0.1
  68. ****************************************/
  69. void mov_data(u8 a[100],u8 b[100],u8 c)
  70. {
  71.     u8 i;
  72.     for(i=c; i>0; i--)
  73.     {
  74.             a = b;
  75.     }
  76. }
  77. ///////////////////////////////////////////////////////////////////////
  78. void Modbus_Function_3(void)
  79. {
  80.     u16 tempdress = 0;
  81.     u8 i = 3;
  82.     u16 crcresult;         
  83.     tempdress = (Uart0_rev_buff[2] << 8) + Uart0_rev_buff[3];
  84.     if((tempdress >= 0x0120) & (tempdress + Uart0_rev_buff[5] < 0x0132))
  85.     {
  86.         Uart0_send_buff[0] = Com0_id;
  87.         Uart0_send_buff[1] = 0x03;
  88.         Uart0_send_buff[2] = 2 * Uart0_rev_buff[5];
  89.         Uart0_send_counter = 2 * Uart0_rev_buff[5] + 3;
  90.         switch(tempdress)
  91.      {
  92.             case 0x0120:
  93.       {
  94.                 Uart0_send_buff = Mkgz_bz & 0xff;
  95.           i++;
  96.                 Uart0_send_buff = (Mkgz_bz >> 8) & 0xff;
  97.              i++;
  98.             }//后面不放break的目的是继续往下执行
  99.             case 0x0122:
  100.       {
  101.                 Uart0_send_buff = Out_voltage & 0xff;
  102.           i++;
  103.                 Uart0_send_buff = (Out_voltage >> 8) & 0xff;
  104.              i++;
  105.       }
  106.             case 0x0124:
  107.       {
  108.                 Uart0_send_buff = Out_current & 0xff;
  109.           i++;
  110.                 Uart0_send_buff = (Out_current >> 8) & 0xff;
  111.              i++;
  112.       }
  113.             case 0x0126:
  114.       {
  115.                 Uart0_send_buff = Mkzt_bz & 0xff;
  116.           i++;
  117.                 Uart0_send_buff = (Mkzt_bz >> 8) & 0xff;
  118.              i++;
  119.       }
  120.             case 0x0128://这个地址是备用的里面的数据没有意义
  121.       {
  122.                 Uart0_send_buff = 0x00;
  123.           i++;
  124.                 Uart0_send_buff = 0x00;
  125.              i++;
  126.       }
  127.             case 0x012A:
  128.       {
  129.                 Uart0_send_buff = OutX_current & 0xff;
  130.           i++;
  131.                 Uart0_send_buff = (OutX_current >> 8) & 0xff;
  132.           i++;
  133.       }
  134.             case 0x012C:
  135.       {
  136.                 Uart0_send_buff = Jc_voltage & 0xff;
  137.           i++;
  138.                 Uart0_send_buff = (Jc_voltage >> 8) & 0xff;
  139.           i++;
  140.       }
  141.             case 0x012E:
  142.       {
  143.                 Uart0_send_buff = Fc_voltage & 0xff;
  144.           i++;
  145.                 Uart0_send_buff = (Fc_voltage >> 8) & 0xff;
  146.           i++;
  147.       }
  148.             case 0x0130:
  149.       {
  150.                 Uart0_send_buff = 0x00;
  151.           i++;
  152.                 Uart0_send_buff = 0x00;
  153.           i++;
  154.       }
  155.      }
  156.         //UCSRB |= (1<
  157.         crcresult = getCRC16(Uart0_send_buff,Uart0_send_counter);
  158.         Uart0_send_buff[Uart0_send_counter] = crcresult & 0xff;
  159.         Uart0_send_buff[Uart0_send_counter+1] = (crcresult >> 8) & 0xff;
  160.         Uart0_send_counter = Uart0_send_counter+2;
  161.         Uart0_send_pointer = Uart0_send_buff;
  162.                 USART_SendData(USART1, *Uart0_send_pointer++);
  163.                 USART_ITConfig(USART1, USART_IT_TXE, ENABLE);               
  164.     }
  165. }
  166. /////////////////////////////////////////////////////////////
  167. void Modbus_Function_6(void)
  168. {
  169.     u16 tempdress = 0;
  170.     u8 tx_flat = 0;
  171.     u16 crcresult;
  172.     tempdress = (Uart0_rev_buff[2]<<8) + Uart0_rev_buff[3];
  173.     switch(tempdress)
  174.     {
  175.         case 0x0126:
  176.      {
  177.             Mkzt_bz = (Uart0_rev_buff[4]<<8) + Uart0_rev_buff[5];
  178.             if(user_day > 0)
  179.       {
  180.                 tx_flat = 1;
  181.       }
  182.         }break;
  183.         case 0x012A:
  184.      {
  185.             OutX_current = (Uart0_rev_buff[4]<<8) + Uart0_rev_buff[5];
  186.             if(user_day > 0)
  187.       {
  188.                 tx_flat = 1;
  189.       }
  190.         }break;
  191.         case 0x012C:
  192.      {
  193.             Jc_voltage = (Uart0_rev_buff[4]<<8) + Uart0_rev_buff[5];
  194.             if(user_day > 0)
  195.       {
  196.                 tx_flat = 1;
  197.       }
  198.         }break;
  199.         case 0x012E:
  200.      {
  201.             Fc_voltage = (Uart0_rev_buff[4]<<8) + Uart0_rev_buff[5];
  202.             if(user_day > 0)
  203.       {
  204.                 tx_flat = 1;
  205.       }
  206.         }break;
  207.         case 0x01EE:
  208.      {
  209.             user_day = (Uart0_rev_buff[4]<<8) + Uart0_rev_buff[5];
  210.             tx_flat = 1;
  211.             //eeprom_write_word (&user_day_eep,user_day);
  212.         }break;
  213.         default: //命令码无效不应答
  214.                 {
  215.                     tx_flat = 0;
  216.                 }
  217.     }
  218.     if(tx_flat == 1)
  219.     {
  220.         Uart0_send_buff[0] = Com0_id;
  221.         Uart0_send_buff[1] = 0x06;
  222.         Uart0_send_buff[2] = Uart0_rev_buff[2];
  223.         Uart0_send_buff[3] = Uart0_rev_buff[3];
  224.         Uart0_send_buff[4] = Uart0_rev_buff[4];
  225.         Uart0_send_buff[5] = Uart0_rev_buff[5];
  226.         Uart0_send_counter = 6;
  227.         //UCSRB |= (1<
  228.         crcresult = getCRC16(Uart0_send_buff,Uart0_send_counter);
  229.         Uart0_send_buff[Uart0_send_counter] = crcresult & 0xff;
  230.         Uart0_send_buff[Uart0_send_counter+1] = (crcresult >> 8) & 0xff;
  231.         Uart0_send_counter = Uart0_send_counter+2;
  232.         Uart0_send_pointer = Uart0_send_buff;
  233.             USART_SendData(USART1, *Uart0_send_pointer++);
  234.                 USART_ITConfig(USART1, USART_IT_TXE, ENABLE);               
  235.     }
  236. }
  237. /////////////////////////////////////////////////////////////
  238. void Com0_Communication(void)
  239. {
  240.     s8 i =0;        
  241.     if(Uart0_rev_comflag == 1)//接收完成标志=1处理,否则退出
  242.     {
  243.         if(Uart0_rev_buff[0] == Com0_id)//地址错误不应答
  244.      {
  245.             unsigned short crcresult;
  246.             unsigned char temp[2];
  247.             crcresult = getCRC16(Uart0_rev_buff,Crc_counter-2);
  248.             temp[1] = crcresult & 0xff;
  249.             temp[0] = (crcresult >> 8) & 0xff;
  250.             if((Uart0_rev_buff[Crc_counter-1] == temp[0])&&(Uart0_rev_buff[Crc_counter-2] == temp[1]))//crc校验错误不应答
  251.          {
  252.                 //SETBIT(PORTC,PC6);
  253.                 Delay(1);
  254.                     switch(Uart0_rev_buff[1])
  255.                  {
  256.                             case 0x03:
  257.                       {
  258.                         if(user_day > 0)
  259.             {
  260.                             Modbus_Function_3();
  261.                   }      
  262.            }
  263.                             break;  
  264.                             case 0x06:
  265.                          {
  266.                         Modbus_Function_6();
  267.            }
  268.                             break;            
  269.                     }      
  270.       }
  271.      }
  272.         Uart0_rev_comflag = 0;
  273.         for(i = 100;i > -1;i--)
  274.      {
  275.                     Uart0_rev_buff = 0;
  276.      }
  277.     }
  278.                  
  279. }
  280. /*******************************************************************************
  281. * Function Name  : Delay
  282. * Description    : Inserts a delay time.
  283. * Input          : nCount: specifies the delay time length.
  284. * Output         : None
  285. * Return         : None
  286. *******************************************************************************/
  287. void Delay(vu32 nCount)
  288. {
  289.   for(; nCount != 0; nCount--);
  290. }




回帖(2)

xi416547162q

2015-1-20 11:39:21
有点意思,编码再规范点就更加完美了
举报

杨达里

2017-3-11 09:23:08
这种零零碎碎的知识,没有办法学习啊。。。

看样子楼主只是简单的了解了一些modbus基础知识,但是不够系统和全面。
其实,modbus虽然比较简单,但是如果不注意有很多坑,
特别是寄存器的位数,大小端处理,浮点数,长整数的处理等等。
目前也没有什么合适的书籍系统介绍,帮助理解的。

刚刚搜了一下,只有最近清华出版的《Modbus软件开发实战指南》不错,
应该是Modbus开发方面第一书,很系统很全面,各种代码都是开源的。
内容着重讲述如何快速入门并精通Modbus软件开发技术,
适用于初学Modbus通信协议的读者,可以看看。

京东或者淘宝搜索:Modbus软件开发实战指南
应该能看到吧。.
举报

更多回帖

发帖
×
20
完善资料,
赚取积分