[STM32WB]

STM32CubeMX | Modbus RTU 主机协议栈实现

[复制链接]
979|87
手机看帖
扫描二维码
随时随地手机跟帖
实际测量不符|  楼主 | 2024-1-31 22:52 | 显示全部楼层 |阅读模式
国产单片机、FreeModbus无缝使用前言
         ~~~~~~~~        modbus rtu在嵌入式方面非常的常见和使用,嵌入式linux中可以使用libmodbus这个库,但是对于嵌入式单片机,开源的有FreeModbus这个库,但是只是从机,对于modbus rtu主机的实现,网上却找不到开源的库,或者找到了但是不方便移植,使用者想要去使用还要去搞明白是怎么实现的,本博客基于以上原因,实现了一套modbus rtu主机协议栈。

本主机协议栈优点如下:

接口明确清晰,使用者无需关心协议栈内部实现
面向对象编程思想,使用C语言的struct作为一个modbus rtu主机的控制接口,此方法的好处是可以灵活的实现多个主机,例如:实现一个多主机的modbus pdu。
支持RTOS
可搭配FreeModbus协议栈无缝使用
移植简单、可很方便的移植到其他单片机如GD32、MM32等
源码简单、只有一个头文件、一个源文件、一个移植接口示例文件



使用特权

评论回复
实际测量不符|  楼主 | 2024-1-31 22:53 | 显示全部楼层
协议栈API介绍
2.1 控制结构
typedef struct
{
        //
        // 收发数据缓存
        //
        uint8_t ucBuf[128];
       
        //
        // 收发数据状态
        //
        uint16_t usStatus;
       
        //
        // 如果使用了RTOS需要进行互斥,那么需要实现以下两个函数的绑定
        //
        void (*lock)(void);
        void (*unlock)(void);
       
        //
        // 微秒延时函数,用于等待超时
        //
        void (*delayms)(uint32_t nms);
       
        //
        // 定时器启动和停止函数
        //
        void (*timerStop)(void);
        void (*timerStart)(void);
       
        //
        // 发送数据函数,可以是串口、TCP等
        //
        uint32_t (*sendData)(const void* buf, uint32_t len);

}MBRTUMaterTypeDef;


使用特权

评论回复
实际测量不符|  楼主 | 2024-1-31 22:53 | 显示全部楼层
2.2 主机读线圈状态(CMD1)
/**
* 主机读取线圈状态
* @param  ucSlaveAddress 从机地址
* @param  usAddress      要读取的线圈起始地址
* @param  usNum          要读取的线圈数量
* @param  usTimeout      超时时间,单位毫秒
* @param  pucCoilsBuffer 存储读取到的线圈状态,一个字节代表一个线圈状态,值范围:0/1
* @return                0:成功  <0:执行失败
*/
int MBRTUMasterReadCoils(MBRTUMaterTypeDef* psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, uint16_t usTimeout, uint8_t* pucCoilsBuffer)

使用特权

评论回复
实际测量不符|  楼主 | 2024-1-31 22:53 | 显示全部楼层
2.2 主机读离散量输入(CMD2)

/**
* 主机读取离散量输入
* @param  ucSlaveAddress 从机地址
* @param  usAddress      要读取的离散量起始地址
* @param  usNum          要读取的离散量数量
* @param  usTimeout      超时时间,单位毫秒
* @param  pucDiscBuffer  存储读取到的离散量输入状态,一个字节代表一个离散量的状态,值范围:0/1
* @return                0:成功  <0:执行失败
*/
int MBRTUMasterReadDiscreteInputs(MBRTUMaterTypeDef* psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, uint16_t usTimeout, uint8_t* pucDiscBuffer)

使用特权

评论回复
实际测量不符|  楼主 | 2024-1-31 22:53 | 显示全部楼层
2.2 主机读保持寄存器(CMD3)

/**
* 主机读取保持寄存器
* @param  ucSlaveAddress 从机地址
* @param  usAddress      要读取的保持寄存器起始地址
* @param  usNum          要读取的保持寄存器数量
* @param  usTimeout      超时时间,单位毫秒
* @param  pusRegBuffer   存储读取到的寄存器值
* @return                0:成功  <0:执行失败
*/
int MBRTUMasterReadHoldingRegisters(MBRTUMaterTypeDef* psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, uint16_t usTimeout, uint16_t* pusRegBuffer)

使用特权

评论回复
实际测量不符|  楼主 | 2024-1-31 22:53 | 显示全部楼层
2.2 主机读输入寄存器(CMD4)

/**
* 主机读取输入寄存器
* @param  ucSlaveAddress 从机地址
* @param  usAddress      要读取的输入寄存器起始地址
* @param  usNum          要读取的输入寄存器数量
* @param  usTimeout      超时时间,单位毫秒
* @param  pusRegBuffer   存储读取到的寄存器值
* @return                0:成功  <0:执行失败
*/
int MBRTUMasterReadInputRegisters(MBRTUMaterTypeDef* psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, uint16_t usTimeout, uint16_t* pusRegBuffer)

使用特权

评论回复
实际测量不符|  楼主 | 2024-1-31 22:54 | 显示全部楼层
2.2 主机写单个线圈(CMD5)

5506065ba5f0ba3e44.png

使用特权

评论回复
实际测量不符|  楼主 | 2024-1-31 22:54 | 显示全部楼层
2.2 主机写单个寄存器(CMD6)

6002565ba5f16aefaf.png

使用特权

评论回复
实际测量不符|  楼主 | 2024-1-31 22:54 | 显示全部楼层
2.2 主机写多个线圈(CMD15)

7424465ba5f20bbef0.png

使用特权

评论回复
实际测量不符|  楼主 | 2024-1-31 22:54 | 显示全部楼层
2.2 主机写多个寄存器(CMD16)

645665ba5f2c6dfe4.png

使用特权

评论回复
实际测量不符|  楼主 | 2024-1-31 22:54 | 显示全部楼层
3、移植前的基础工程生成
基础工程这里我使用STM32CubeMX生成,使用的是STM32F103C8单片机,配置步骤如下,首先将时钟配置到72M:
5992265ba5f3c386b7.png

使用特权

评论回复
实际测量不符|  楼主 | 2024-1-31 22:55 | 显示全部楼层
配置串口1用于调试打印,配置串口3用于modbus主机通信:
8552265ba5f49b9bb8.png

使用特权

评论回复
实际测量不符|  楼主 | 2024-1-31 22:55 | 显示全部楼层
6480265ba5f519685b.png

使用特权

评论回复
实际测量不符|  楼主 | 2024-1-31 22:55 | 显示全部楼层
配置用于检测3.5个字符超时时间的定时器,我配置成了5ms超时。

这里需要跟你实际使用的波特率进行超时时间的计算,以:波特率9600、8bit数据位、1bit停止位,奇校验、无流控为例,那么1s内就可以传输9600bits÷(8+1+1)=960bytes,那么3.5个字节的时间就是1000ms÷960×3.5≈3.65ms,所以,我设置5ms的超时时间是没有问题的。

使用特权

评论回复
实际测量不符|  楼主 | 2024-1-31 22:55 | 显示全部楼层
6059765ba5f611de62.png

使用特权

评论回复
实际测量不符|  楼主 | 2024-1-31 22:55 | 显示全部楼层
开启定时器和串口中断,注意:串口的中断要比定时器中断等级高:
8459965ba5f771bc51.png

使用特权

评论回复
实际测量不符|  楼主 | 2024-1-31 22:56 | 显示全部楼层
最后输出工程就可以了:

1689665ba5f8459b84.png

使用特权

评论回复
实际测量不符|  楼主 | 2024-1-31 22:56 | 显示全部楼层
5582965ba5f8ca0dba.png

使用特权

评论回复
实际测量不符|  楼主 | 2024-1-31 22:56 | 显示全部楼层
4、移植主机协议栈
主机协议栈源码就只有三个文件:
2669265ba5f9d9f11f.png

使用特权

评论回复
实际测量不符|  楼主 | 2024-1-31 22:56 | 显示全部楼层
其中,mbrtu_master.h和mbrtu_master.c是协议栈实现,无需动,mbrtu_master_example.c是移植参考示例。

下面讲解一下移植过程。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

37

主题

560

帖子

0

粉丝