MODBUS MASTER RTU在STM32上的实现

MODBUS MASTER RTU在STM32上的实现

1.概述

  • 最近需要将几个信号采集模块通过总线串联起来,这样便于系统模块化。故将目光关注到了工业上经常使用的modbus协议。

  • modbus协议是一种一主多从的拓扑结构,主要是应用层软件协议,有关modbus的相关信息,可以自行google、百度。

  • STM32实现的Master工程代码在github上,点击获取

2.开发环境

  • STM32F042单片机
  • MDK-KEIL5
  • STM32-CUBE库
  • Modbus slaver测试软件

3.移植来源

  • 信号采集模块作为slaver,采用的是开源的freemodbus协议。关于其的实现大家百度一下都能发现,相关的移植过程介绍也很多,不再一一赘述。值得注意的是:这个freemodbus的源码值得一看,其判断对一帧数据包的接受采用的是定时器判断超时。大体思路是中断接收函数在接收每一个字节数据时会重置定时器,如果定时器发生定时溢出中断,则说明没有新数据到来,代表一个数据包接收完整。

  • 然后就是master的设计实现。freemodbus并没有开源的master实现代码,故这部分需要我们自己开发完成。在github上发现有人发布了ardunio版本的master,但是ardunio的代码采C++语言编写,需要我们做一些C语言的移植和一些硬件底层接口的移植。

    ardunio master的github源工程链接点击,感谢他的分享。

4.移植过程

  • 了解ardunio modbus库的实现思路——很简单明了。打开源工程,里面有源代码和例程代码,不过例程代码需要用ardunio的IDE打开。其大体思路就是每个Modbus Function都用一个函数实现,如

      uint8_t ModbusMaster::readDiscreteInputs(uint16_t u16ReadAddress,uint16_t u16BitQty)
      {
        _u16ReadAddress = u16ReadAddress;
        _u16ReadQty = u16BitQty;
        return ModbusMasterTransaction(ku8MBReadDiscreteInputs);
       }

    这个ModbusMasterTransaction函数就是根据用户选择的功能模块填充数组并且发送,然后等待从机回应的数据(带超时检测),接着解析接收到的数据包,如果成功则将数据放在_u16ResponseBuffer数组中

  • 将ardunio的C++代码移植为C语言

  • 将ardunio相关的serial等函数使用自己的代码实现,serial函数其实就是硬件层接口的函数封装,这也是移植到其他平台必须要根据自身平台做相应的改变。
    (1). 在ModbusMasterTransaction函数中涉及到_serial->read()、_serial->write()、_serial->flush()、_serial->available()、millis()、bitWrite()、bitRead()、word()几种函数,从名字中我们就可以知道什么意思,故我们需要在我们的系统中重新实现这几个函数。
    (2). 我们底层串口的设计思路如下,数据发送采用数据的发送直接采用循环发送,而STM32cube库已经将这个功能封装好API接口,我们再封装一层即可,如下所示

      /**
        * @brief  将数据包发送出去
        * @param
        * @note
        * @retval void
        * @author xiaodaqi
        */
      uint8_t Modbus_Master_Write(uint8_t *buf,uint8_t length)
      {
       if(HAL_UART_Transmit(&huart2 ,(uint8_t *)buf,length,0xff))
       {
         return HAL_ERROR;
       }
          else
          {
            return HAL_OK;
          }
      }

    这句函数就相当于_serial->write()的实现。
    (3). 串口数据的接收:我们采用中断接收的方式,并且在中断处理函数中将接收到的字节采用循环队压人缓冲区,这样子就能实现ardunio的功能代码。

    跟底层相关的移植代码可到我的工程 Modbus_Master--trans_recieve_buff_control.c .h文件查看。

    _serial->read()的变体为:

      /**
        * @brief  读出缓冲区的数据
        * @param
        * @note
        * @retval void
        * @author xiaodaqi
        */
      uint8_t Modbus_Master_Read(void)
      {
          uint8_t cur =0xff;
          if( !rbIsEmpty(&m_Modbus_Master_RX_RingBuff))
          {
                cur = rbPop(&m_Modbus_Master_RX_RingBuff);
          }
          return cur;
      }

    _serial->flush()的变体为:

      /**
        * @brief  清除环形队列
        * @param
        * @note
        * @retval void
        * @author xiaodaqi
        */
      uint8_t Modbus_Master_Rece_Flush(void)
      {
        rbClear(&m_Modbus_Master_RX_RingBuff);
      }

    _serial->available()的变体为:

      /**
        * @brief  判断ringbuffer里面是否有尚未处理的字节
        * @param
        * @note
        * @retval void
        * @author xiaodaqi
        */
      uint8_t Modbus_Master_Rece_Available(void)
      {
          /*如果数据包buffer里面溢出了,则清零,重新计数*/
              if(m_Modbus_Master_RX_RingBuff.flagOverflow==1)
              {
                  rbClear(&m_Modbus_Master_RX_RingBuff);
              }
          return !rbIsEmpty(&m_Modbus_Master_RX_RingBuff);
      }

    millis()的变体为:

      /**
        * @brief  1ms周期的定时器
        * @param
        * @note
        * @retval void
        * @author xiaodaqi
        */
      uint32_t Modbus_Master_Millis(void)
      {
        return HAL_GetTick();
      }

    其余几个函数的变体如下:

      /*模拟ardunio函数*************************************************/
      static inline uint8_t lowByte(uint16_t ww)
      {
        return (uint8_t) ((ww) & 0x00FF);
      }
    
      static inline uint8_t highByte(uint16_t ww)
      {
        return (uint8_t) ((ww) >> 8);
      }
    
      static inline uint16_t word(uint8_t H_Byte,uint8_t L_Byte)
      {
          uint16_t word;
          word = (uint16_t)(H_Byte<<8);
          word = word + L_Byte;
        return word;
      }
    
      #define bitSet(value, bit)  ((value) |= (1UL << (bit))) 
      #define bitClear(value, bit)  ((value) &= ~(1UL << (bit)))
    
      #define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))
      #define bitRead(value, bit)  (((value) >> (bit)) & 0x01) 

    (4).剩下的就是串口的一些硬件初始化,如波特率等,这些配置由于我是用CUBEMX配置直接生成的代码,这里不做过多阐述。

5.代码调试

完成上述关键代码的移植后就可以进行调试了。

  • 在main函数中增加如下的测试代码

        while (1)
        {
              uint8_t result;
              //测试read input registers功能
              //从机地址0x01 ,连续都2个地址为0x2的寄存器
              result = ModbusMaster_readInputRegisters(0x01,0x2, 2);
              if (result == 0x00)
              {
                  Input_Result[0] = ModbusMaster_getResponseBuffer(0x00);
                  Input_Result[1] = ModbusMaster_getResponseBuffer(0x01);
              }
                  HAL_Delay(1000);
        }
  • 我们使用Modbus Slave软件来模拟从机。并且设置相应的地址里面的数值为1和2。软件使用方法可自行百度、google。下图为软件设置及运行结果。
    FHz8VgM.png

  • 然后我们用keil仿真查看结果Input_Result数组的结果。如下图所示,结果显示modbus通信正确。
    S9kJUOc.png

6.总结

以上即为本次移植的过程,有需要的朋友可以直接使用我的代码,亦可使用ardunio代码自行移植。

转载于:https://www.cnblogs.com/littlebigqi/p/7736945.html

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
课程背景Modbus 协议是工业自动化控制系统中常见的通信协议,协议的全面理解是个痛点。本课程主讲老师集10多年在Modbus协议学习、使用中的经验心得,结合当前物联网浪潮下Modbus协议开发的痛点,推出这套面向Modbus 协议初学者的课程。本课程不同于以往市面课程只是协议讲解无实现代码,而是采用讲解与实践并重的方式,结合STM32F103ZET6开发板进行手把手编程实践,十分有利于初学者学习。涵盖了学习者在Modbus协议方面会遇到的方方面面的问题,是目前全网首个对Modbus协议进行全面总结的课程。课程名称   协议讲解及实现>>课程内容1、Modbus 协议的基础。2、Modbus协议栈函数编程实现。3、Modbus协议在串行链路编程实现。4、Modbus协议在以太网链路编程实现。5、常见问题的解决方法。带给您的价值通过学习本课程,您可以做到如下:1、全面彻底的理解Modbus协议。2、理解在串行链路,以太网链路的实现。3、掌握Modbus协议解析的函数编程方法,调试工具的使用。4、掌握多个串口,网口同时运行同一个Modbus协议栈的方法。5、掌握Modbus协议下,负数,浮点数等处理方法。讲师简介许孝刚,山东大学工程硕士,副高职称,技术总监。10多年丰富嵌入式系统开发经验,国家软考“嵌入式系统设计师”。2017年获得“华为开发者社区杰出贡献者”奖励。
### 回答1: Modbus-RTU是一种串行通信协议,常用于工业自动化领域。在STM32微控制器中,可以使用Modbus-RTU例程来实现Modbus通信。 首先,需要在STM32的开发环境中创建一个新的工程。接下来,需要导入一个支持Modbus-RTU的库文件,例如FreeModbus。然后,可以在工程中引入Modbus-RTU的头文件,并进行相应的配置。 在代码中,需要初始化串口以实现Modbus设备的通信。可以使用STM32的UART模块来实现串口通信,并设置适当的波特率、数据位、校验位和停止位。然后,需要配置Modbus设备的地址,该地址用于唯一标识每个Modbus设备。 接下来,需要实现Modbus的处理函数。当Modbus主站发送请求时,STM32将读取请求并做出响应。可以使用相应的函数来读取和写入寄存器或线圈。同时,还需要处理异常情况,并发送合适的错误码。 最后,需要在主函数中初始化Modbus并启动主循环。主循环中可以不断接收并处理Modbus请求。 需要注意的是,对于不同的STM32型号和开发环境,可能会有不同的具体实现方法和配置步骤。因此,在实际开发中,可能需要参考具体的开发文档和示例代码来实现Modbus-RTUSTM32例程。 总结起来,通过使用Modbus-RTU例程,可以在STM32微控制器上实现Modbus通信。这种通信方式在工业自动化中广泛应用,可以实现设备之间的数据交换和控制操作。 ### 回答2: Modbus-RTU是一种串行通信协议,常用于工业自动化领域。STM32是一系列基于ARM Cortex-M内核的微控制器。modbus-rtu stm32例程指的是在STM32微控制器上实现Modbus-RTU协议的示例程序。 在STM32微控制器上实现Modbus-RTU协议,可以通过以下几个步骤来完成: 1. 首先,需要在STM32上配置串口通信。选择一个合适的USART(串口)通信接口,并设置波特率、数据位、停止位和校验位等参数,以满足Modbus-RTU协议的要求。 2. 接下来,需要编写程序来处理Modbus-RTU协议的数据传输。可以利用STM32的GPIO(通用输入输出)接口来连接Modbus-RTU所需要的引脚,例如DE/RE(使能/接收使能)引脚和RX/TX引脚。通过GPIO接口配置,可以实现数据的发送和接收功能。 3. 在程序中,需要实现Modbus-RTU协议的相关功能,例如数据的读取、写入、寄存器的读取和写入等。可以编写针对Modbus命令功能码的函数来实现这些功能。 4. 最后,通过调用上述函数以及适当的分析和处理,将Modbus-RTU的数据进行解析和处理,并根据协议规定作出相应的响应。 通过上述步骤,在STM32微控制器上实现Modbus-RTU协议的示例程序就完成了。可以根据具体应用需求和硬件资源,对示例程序进行修改和优化,以实现更复杂的功能。同时,需要注意调试和测试过程中的错误排查和修复,以确保程序的正确性和稳定性。 ### 回答3: modbus-rtu是一种常用的串行通信协议,用于在工业控制领域中实现设备之间的通信。在stm32微控制器上实现modbus-rtu通信的例程,可以帮助我们快速理解并应用该协议。 首先,需要创建一个stm32工程,并配置相应的串口和GPIO引脚用于通信。然后,需导入modbus库,包括寄存器读写操作的函数。可以从网上下载并添加相应的库文件。 接下来,需要初始化串口,确定通信的波特率、数据位数、停止位数等参数,并设置stm32的USART寄存器为相应的模式。 然后,需要调用modbus库中提供的函数进行modbus的配置。例如,调用modbus_init_slave函数来初始化modbus从站,设置从站ID等参数。然后调用modbus_function_read_holding_registers和modbus_function_write_holding_registers函数来读写保持寄存器的数据。 在实际的代码中,还需要根据应用的需求,编写相应的逻辑和算法来处理modbus通信。例如,可以使用定时中断来定时发送和接收数据。在接收到数据后,可以使用modbus库中提供的函数来解析数据,并根据解析结果进行相应的控制和操作。 最后,进行编译、烧录和测试。通过串口调试助手等工具,可以观察和验证modbus通信的数据传输和功能是否正常。 总之,通过实现modbus-rtu stm32例程,我们可以学习和掌握在stm32微控制器上使用modbus通信的方法和技巧,为工业控制领域的应用提供基础和参考。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值