打印
[STM32MP1]

linux 串口modbus操作

[复制链接]
3739|23
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2021-10-1 18:40 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

标准modbus-RTU协议,分离底层接口,之前使用在STM32上,最近移植到linux,操作都是一样。

//uart.c 串口读写,没有特意去设置起始位什么的,通常都是8.N.1极少数使用奇偶校验了,毕竟通信协议上面都会带CRC。


/*

* Uart.c

*

*  Created on: 2018年8月2日

*      Author: cfan

*/

#include <stdio.h>

#include <unistd.h>

#include <unistd.h>

#include <stdlib.h>

#include <fcntl.h>

#include <unistd.h>

#include <stdint.h>

#include <termios.h>

#include "uart.h"

#include <errno.h>          // 包含errno所需要的头文件

#include <string.h>          // 包含strerror所需要的头文件

#include "typedef.h"

//串口初始化

int UART_Init(UART_HANDLE *pHandle, const char *pUartDeviceName, UART_BAUD_TYPE BaudRate)

{

        pHandle->fd = -1;

        if(pUartDeviceName == NULL || pHandle==NULL || strlen(pUartDeviceName)>UART_DEVICE_NAME_MAX_LEN)

        {

                printf("%s(%d)Check the input parameters!\r\n",__FILE__ , __LINE__);

                return -1;

        }

        strcpy(pHandle->UartDeviceName, pUartDeviceName);                                        //记录串口设备名称

        //打开串口

        pHandle->fd = open(pUartDeviceName, O_RDWR|O_NOCTTY|O_NDELAY);                //读写独占方式打开串口

        if (pHandle->fd < 0)

        {

                //打印错误信息

                printf("Can't Open Serial Port(%s) : %s(%d)\n",pUartDeviceName, strerror(errno), errno);

                return errno;

        }

        else

        {

                printf("Open Serial Port OK!\r\n");

                if(tcgetattr( pHandle->fd,&pHandle->options)  !=  0)//得到与pHandle->fd指向对象的相关参数

                {

                        close(pHandle->fd);

                        //打印错误信息

                        printf("Can't Get Serial Port : %s(%d)\n", strerror(errno), errno);

                        return errno;

                }

                //波特率

                //pHandle->options.c_ispeed = pHandle->options.c_ospeed = BaudRate;

                cfsetispeed(&pHandle->options, BaudRate);        //设置输入波特率

                cfsetospeed(&pHandle->options, BaudRate);         //设置输出波特率

                //修改控制模式,保证程序不会占用串口

                pHandle->options.c_cflag |= CLOCAL;

                //修改控制模式,使得能够从串口中读取输入数据

                pHandle->options.c_cflag |= CREAD;

                //不使用流控制

                //pHandle->options.c_cflag = 0;

                //设置数据位

                pHandle->options.c_cflag &= ~CSIZE;                 //屏蔽其他标志位

                pHandle->options.c_cflag |= CS8;

                //无奇偶校验位。

                pHandle->options.c_cflag &= ~PARENB;

                //输入模式标志-清除所有的软件流控标志,否则会在接收到特殊字符后出现一些问题

                pHandle->options.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);

                pHandle->options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

                //本地模式标志

                //pHandle->options.c_lflag = 0;

                //1个停止位

                pHandle->options.c_cflag &= ~CSTOPB;

                //修改输出模式,原始数据输出

                pHandle->options.c_oflag &= ~OPOST;

                //设置等待时间和最小接收字符

                pHandle->options.c_cc[VTIME] = 2;        //读取一个字符等待1*(1/10)s

                pHandle->options.c_cc[VMIN] = 0;        //读取字符的最少个数为1

                pHandle->options.c_oflag &= ~(ONLCR | OCRNL); //添加的

                pHandle->options.c_iflag &= ~(ICRNL | INLCR);

                pHandle->options.c_iflag &= ~(IXON | IXOFF | IXANY); //添加的

                //激活配置 (将修改后的termios数据设置到串口中)

                if (tcsetattr(pHandle->fd, TCSANOW, &pHandle->options) != 0)

                {

                        close(pHandle->fd);

                        //打印错误信息

                        printf("Can't Set Serial Port : %s(%d)\n", strerror(errno), errno);

                        return errno;

                }

        }

        return 0;

}

//串口发送数据

int UART_SendData(UART_HANDLE *pHandle, u8 *pDataBuff, u32 len)

{

        if(pHandle == NULL || pDataBuff == NULL)

        {

                printf("%s(%d)Check the input parameters!\r\n",__FILE__ , __LINE__);

                return -1;

        }

        if(write(pHandle->fd, pDataBuff, len) < 0)

        {

                printf("Uart(%s) Send Data Error : %s(%d)\n",pHandle->UartDeviceName,  strerror(errno), errno);

                return errno;

        }

        return 0;

}

//串口发送字符串

int UART_SendString(UART_HANDLE *pHandle, const char *pString)

{

        u32 len;

        if(pHandle == NULL || pString == NULL)

        {

                printf("%s(%d)Check the input parameters!\r\n",__FILE__ , __LINE__);

                return -1;

        }

        len = strlen(pString);

        if(write(pHandle->fd, (u8*)pString, len) <= 0)

        {

                printf("Uart(%s) Send String Error : %s(%d)\n",pHandle->UartDeviceName, strerror(errno), errno);

                return errno;

        }

        return 0;

}

//串口读取数据

int UART_ReadData(UART_HANDLE *pHandle, u8 *pDataBuff,u32 DataBuffSize)

{

        int len;

        if(pHandle == NULL || pDataBuff == NULL)

        {

                printf("%s(%d)Check the input parameters!\r\n",__FILE__ , __LINE__);

                return 0;

        }

        len = read(pHandle->fd, pDataBuff, DataBuffSize);

        if(len < 0) //非阻塞读取会返回Resource temporarily unavailable,错误11

        {

                //printf("Uart(%s) Read Data Error : %s(%d)\n",pHandle->UartDeviceName,  strerror(errno), errno);

                return 0;

        }

        return len;

}

//清除串口接收缓冲区

void UART_ClearRxData(UART_HANDLE *pHandle)

{

        int len;

        static u8 buff[512+4];

        int i = 0;

        if(pHandle == NULL)

        {

                printf("%s(%d)Check the input parameters!\r\n",__FILE__ , __LINE__);

                return;

        }

        for(i = 0;i < 100000;i ++)

        {

                len = read(pHandle->fd, buff, 512);

                if(len <= 0)//非阻塞读取会返回Resource temporarily unavailable,错误11

                {

                        //printf("Uart(%s) Clear Data Error : %s(%d)\n",pHandle->UartDeviceName,  strerror(errno), errno);

                        break;

                }

        }

}


使用特权

评论回复
沙发
tpgf|  楼主 | 2021-10-1 18:41 | 只看该作者
//uart.h


/*
* Uart.h
*
*  Created on: 2018年8月2日
*      Author: cfan
*/

#ifndef HARDWARE_UART_H_
#define HARDWARE_UART_H_
#include "termios.h"
#include "typedef.h"


#define UART_DEVICE_NAME_MAX_LEN        35        //串口名称最大长度

//波特率定义
typedef enum
{
        UART_B50                =        B50,
        UART_B75                =        B75,
        UART_B110                =        B110,
        UART_B134                =        B134,
        UART_B150                =        B150,
        UART_B200                =        B200,
        UART_B300                =        B300,
        UART_B600                =        B600,
        UART_B1200                =        B1200,
        UART_B1800                =        B1800,
        UART_B2400                =        B2400,
        UART_B4800                =        B4800,
        UART_B9600                =        B9600,
        UART_B19200                =        B19200,
        UART_B38400                =        B38400,
        UART_B57600                =        B57600,
        UART_B115200        =        B115200,
        UART_B230400        =        B230400,
        UART_B460800        =        B460800,
        UART_B500000        =        B500000,
        UART_B576000        =        B576000,
        UART_B921600        =        B921600,
        UART_B1000000        =        B1000000,
        UART_B1152000        =        B1152000,
        UART_B1500000        =        B1500000,
        UART_B2000000        =        B2000000,
        UART_B2500000        =        B2500000,
        UART_B3000000        =        B3000000,
        UART_B3500000        =        B3500000,
        UART_B4000000        =        B4000000,
}UART_BAUD_TYPE;

typedef struct
{
        int fd;
        char UartDeviceName[UART_DEVICE_NAME_MAX_LEN+1];                //串口名称
        struct termios options;
}UART_HANDLE;



//串口初始化
int UART_Init(UART_HANDLE *pHandle, const char *pUartDeviceName, UART_BAUD_TYPE BaudRate);
//串口发送数据
int UART_SendData(UART_HANDLE *pHandle, u8 *pDataBuff, u32 len);
//串口发送字符串
int UART_SendString(UART_HANDLE *pHandle, const char *pString);
//串口读取数据
int UART_ReadData(UART_HANDLE *pHandle, u8 *pDataBuff,u32 DataBuffSize);
//清除串口接收缓冲区
void UART_ClearRxData(UART_HANDLE *pHandle);

#endif /* HARDWARE_UART_H_ */

使用特权

评论回复
板凳
tpgf|  楼主 | 2021-10-1 18:41 | 只看该作者
//modbus_rtc.c




/*************************************************************************************************************
* 文件名:                MODBUS_RTU.c
* 功能:                MODBUS_RTU通信协议层
* 作者:                cp1300@139.com
* 创建时间:        2014-03-24
* 最后修改时间:2015-07-02
* 详细:                MODBUS RTU通信协议层
                                2015-04-27:添加发送延时,防止通信帧结束时产生干扰
                                2015-05-20:修复当接收字节小于2的时候进行CRC校验出现异常
                                2016-04-11:增加初始化标记,当没有初始化时,直接退出modbus,增加内存检测,如果内存指针为空,则退出
                                2017-03-06:增加底层接口支持,并且去掉了溢出检测
                                2017-03-23:修改名称错误,应为为modbus,增加所有接口回调模式,完全与底层通信解耦,移植性能更强
                                2018-01-27:增加延时接口
*************************************************************************************************************/
#include "typedef.h"
#include "stdio.h"
#include "modbus_rtu.h"



//调试开关
#define MODBUS_RTU_DBUG                 0
#if MODBUS_RTU_DBUG
        #include "system.h"
        #define modbus_debug(format,...)        uart_printf(format,##__VA_ARGS__)
#else
        #define modbus_debug(format,...)        /\
/

#endif        //MODBUS_RTU_DBUG





/*************************************************************************************************************************
* 函数                        :        bool MODBUS_Init(MODBUS_HANDLE *pHandle,u8 *pTxBuff, u16 TxBuffSize, u16 TxByteTimeUs, u16 RxTimeOutMs,
                                                bool (* pSendData)(u8 *pDataBuff, u16 DataLen),
                                                int (* pReadData)(u8 **pDataBuff, u8 ByteTimeOut, u16 TimeOut, u16 *pReceiveDelay),
                                                void (*pClearRxData)(void),
                                                void (*pDelayMS)(u8 ms))
* 功能                        :        MODBUS 初始化
* 参数                        :        pHandle:当前初始化的MODBUS句柄,pTxBuff:发送缓冲区指针;TxBuffSize:发送缓冲区大小;
                                        TxByteTimeUs:发送1个字节的时间(用于RS485收发切换,特别是发送后的切换延时)延时大小为0-65535(us);RxTimeOutMs:接收超时,单位ms,pReceiveDelay:返回接收延时,单位ms
                                        pSendCallBack:发送回调函数(pDataBuff:发送数据缓冲区,DataLen:发送数据长度)
                                        pReadCallBack:接收数据回调函数,会等待直到数据被写入到接收缓冲区(pDataBuff:接收数据缓冲区,ByteTimeOut:等待的字节超时时间,单位ms,TimeOut:数据包超时时间,单位ms)
                                        pClearRxData:清除接收数据缓冲区回调函数
                                        pDelayMS:系统ms延时接口
* 返回                        :        FALSE:初始化失败;TRUE:初始化成功
* 依赖                        :        底层回调接口
* 作者                        :        cp1300@139.com
* 时间                        :        2014-09-25
* 最后修改时间         :         2017-03-23
* 说明                        :         发送缓冲区必须大于最大数据包大小,否则会出现内存溢出
                                        2017-03-23:增加回调,抛离底层依赖
                                        2018-01-27:增加延时回调
*************************************************************************************************************************/
bool MODBUS_Init(MODBUS_HANDLE *pHandle,u8 *pTxBuff, u16 TxBuffSize, u16 TxByteTimeUs, u16 RxTimeOutMs,
                                bool (* pSendData)(u8 *pDataBuff, u16 DataLen),
                                int (* pReadData)(u8 **pDataBuff, u8 ByteTimeOut, u16 TimeOut, u16 *pReceiveDelay),
                                void (*pClearRxData)(void),
                                void (*pDelayMS)(u8 ms))
{               
        pHandle->ID = 0;
        if(pHandle == NULL) return FALSE;
        pHandle->WriteRegCnt = 0;                                                                                                        //写入寄存器次数
        pHandle->ReadRegCnt = 0;                                                                                                        //读取寄存器次数
        pHandle->ReturnTimeMs = 0;                                                                                                        //数据返回时间
        pHandle->pTxBuff = pTxBuff;                                                                                                        //发送缓冲区
        pHandle->TxBuffSize = TxBuffSize;                                                                                        //发送缓冲区大小
        pHandle->RxTimeOutMs = RxTimeOutMs;                                                                                        //接收超时时间
        if(pHandle->RxTimeOutMs < 20) pHandle->RxTimeOutMs = 20;                                        //限制最小为20ms
        pHandle->SlaveAddr = 0;                                                                                                                //从机地址无效
        pHandle->TxByteTimeUs = TxByteTimeUs;                                                                                //发送1个字节的时间(用于RS485收发切换,特别是发送后的切换延时)
        pHandle->pSendData = pSendData;                                                                                                //发送回调函数
        pHandle->pReadData = pReadData;                                                                                                //接收回调函数
        pHandle->pClearRxData = pClearRxData;                                                                                //清除接收回调函数
        pHandle->pDelayMS = pDelayMS;                                                                                                //系统毫秒延时接口
        pHandle->pSingleRegReadWriteCallBack = NULL;                                                                //初始化设置从机单寄存器读写回调函数为空
        if((pHandle->pSendData==NULL) || (pHandle->pClearRxData==NULL) || (pHandle->pDelayMS == NULL))       
                return FALSE;                                                                                                                        //底层通信接口没有实现,返回初始化失败
        pHandle->ID = MODBUS_INIT_ID;                                                                                                //modbus初始化标记
       
        return TRUE;
}




使用特权

评论回复
地板
tpgf|  楼主 | 2021-10-1 18:42 | 只看该作者

#if(MODBUS_RTU_HOST) //开启主机模式
/*************************************************************************************************************************
* 函数                        :        MRTU_ERROR MODBUS_HOST_ReadOneReg(MODBUS_HANDLE *pHandle, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u16 *pRegData)
* 功能                        :        主机读取从机一个指定寄存器
* 参数                        :        pHandle:MODBUS句柄;RegType:读取的寄存器类型;SlaveAddr:从机地址;RegAddr:需读取的寄存器地址;pRegData:寄存器的值
* 返回                        :        MRTU_ERROR:通信状态
* 依赖                        :        底层通信驱动
* 作者                        :        cp1300@139.com
* 时间                        :        2014-03-24
* 最后修改时间         :         2017-03-23
* 说明                        :         MOUEBUS RTU读取数据,读取一个寄存器
                                        输入输出的数据都为小端模式
                                        2017-03-23:增加回调,抛离底层依赖
*************************************************************************************************************************/
MRTU_ERROR MODBUS_HOST_ReadOneReg(MODBUS_HANDLE *pHandle, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u16 *pRegData)
{
        MRTU_READ_FRAME *pFrame;                                                //发送数据帧格式
        MRTU_RETURN_FRAME *pReFrame;                                        //返回数据帧格式
        MRTU_UNU_FRAME        *pUnuFrame;                                                //返回的异常数据帧格式
        u16 crc16;
        u32 TimeDelay = 0;                                                                //用于计算数据接收延时
        int len;
        u8 *pRxBuff;                                                                        //接收数据缓冲区指针
       
        if(pHandle == NULL) return MRTU_HANDLE_ERROR;        //句柄无效
        if((pHandle->ID != MODBUS_INIT_ID)||(pHandle->pTxBuff==NULL)) return MRTU_HANDLE_ERROR;        //2016-04-11增加防止modbus未初始化异常
        if(pHandle->TxBuffSize < 8) return MRTU_OVER_ERROR;        //检查发送缓冲区
        pFrame = (MRTU_READ_FRAME *)pHandle->pTxBuff;
        //数据结构填充
        pFrame->addr = SlaveAddr;                                                //从机地址
        pFrame->fun = (u8)RegType;                                                //功能码,读取
        pFrame->StartReg = SWAP16(RegAddr);                                //寄存器起始地址
        pFrame->RegNum = SWAP16(1);                                                //需要读取的寄存器数量,1       
        crc16 = MODBUS_CRC16(pHandle->pTxBuff, 6);                //计算CRC16
        pFrame->CRC16 = crc16;                                                        //crc16

#if MODBUS_RTU_DBUG
        {
                u16 i;
               
                modbus_debug("\r\n<- MODBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",8,crc16);
                for(i = 0;i < 8;i ++)
                {
                        modbus_debug("0x%02X ",pHandle->pTxBuff);
                }
                modbus_debug("\r\n");
        }
#endif        //MODBUS_RTU_DBUG
       
        pHandle->pSendData(pHandle->pTxBuff, 6+2);                //发送数据
        TimeDelay = 8*pHandle->TxByteTimeUs;                        //计算发送所需时间,进行延时
        TimeDelay/=1000;                                                                //转换为ms
        TimeDelay += 1;
        pHandle->pDelayMS(TimeDelay);                                        //等待发送完毕,防止通信帧结束时产生干扰
        pHandle->pClearRxData();                                                //清除接收缓冲区
        //等待数据返回
        len = pHandle->pReadData(&pRxBuff, 10, pHandle->RxTimeOutMs, &pHandle->ReturnTimeMs);        //接收数据
        if(len <= 0)                                                                         //没有接收到数据
        {       
                modbus_debug("接收超时(%dms)!\r\n",pHandle->RxTimeOutMs);        //接收数据超时
                pHandle->ReturnTimeMs = 0xffff;                                //接收时间无效
                return MRTU_TIME_OUT;                                                //返回超时
        }

#if MODBUS_RTU_DBUG
        {
                u16 i;
               
                modbus_debug("\r\n-> MODBUS RTU RXD(%dB)(ping:%dms):\r\n",len,pHandle->ReturnTimeMs);
                for(i = 0;i < len;i ++)
                {
                        modbus_debug("0x%02X ", pRxBuff);
                }
                modbus_debug("\r\n");
        }
#endif        //MODBUS_RTU_DBUG
        if(len < MODBUS_MIN_RX_BYTE)
        {
                modbus_debug("返回数据长度错误\r\n");
                return MRTU_LEN_ERROR;
        }
        pReFrame = (MRTU_RETURN_FRAME *)pRxBuff;
        //检查地址
        if(pReFrame->addr != SlaveAddr)
        {
                modbus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02X\r\n",SlaveAddr, pReFrame->addr);
                return MRTU_ADDR_ERROR;
        }
        //对接受的数据进行CRC校验
        crc16 = MODBUS_CRC16(pRxBuff, len-2);                                        //计算CRC16
        if((pRxBuff[len-1] != (crc16 >> 8)) || (pRxBuff[len-2] != (crc16 & 0xff)))
        {
                modbus_debug("CRC校验错误,计算CRC为:0x%04X,返回CRC为:0x%04X\r\n",crc16,(u16)(pRxBuff[len-2]<<8)|pRxBuff[len-1]);
                return MRTU_CRC_ERROR;                                                                //返回CRC校验错误
        }
        //返回的功能码不一致
        if(pReFrame->fun != (u8)RegType)
        {
                pUnuFrame = (MRTU_UNU_FRAME *)pRxBuff;                                //异常数据帧
                if(pUnuFrame->ErrorFun == ((u8)RegType|0x80))                //返回有异常
                {
                        modbus_debug("返回异常,异常码%d\r\n", pUnuFrame->unu);
                        switch(pUnuFrame->unu)
                        {
                                case 1: return MRTU_UNUS1_ERROR;                        //异常码1
                                case 2: return MRTU_UNUS2_ERROR;                        //异常码2
                                case 3: return MRTU_UNUS3_ERROR;                        //异常码3
                                case 4: return MRTU_UNUS4_ERROR;                        //异常码4
                                case 5: return MRTU_UNUS5_ERROR;                        //异常码5
                                case 6: return MRTU_UNUS6_ERROR;                        //异常码6
                                default: return MRTU_OTHER_ERROR;
                        }
                }
                else
                {
                        modbus_debug("返回错误,返回功能码为0x%02X\r\n", pReFrame->fun);
                        return MRTU_FUNR_ERROR;
                }
        }
        //判断数据长度
        if(pReFrame->DataLen != 2)
        {
                modbus_debug("返回数据长度错误,读取%d个寄存器,共%dB,只返回了%dB\r\n",1, 1*2, pReFrame->DataLen);
                return MRTU_LEN_ERROR;                                //返回数据长度错误
        }
        //获取返回的寄存器的值
        *pRegData = pReFrame->DataBuff[0];
        *pRegData <<= 8;
        *pRegData |= pReFrame->DataBuff[1];
        pHandle->ReadRegCnt ++;                                        //读取寄存器次数增加
       
        return MRTU_OK;                                                        //返回成功
}



使用特权

评论回复
5
tpgf|  楼主 | 2021-10-1 18:42 | 只看该作者
/*************************************************************************************************************************
* 函数                        :        MRTU_ERROR MODBUS_HOST_ReadMultReg(MODBUS_HANDLE *pHandle, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[])
* 功能                        :        主机读取从机指定多个连续寄存器
* 参数                        :        pHandle:MODBUS句柄;RegType:读取的寄存器类型;SlaveAddr:从机地址;RegAddr:需读取的寄存器地址;RegNum:寄存器数量;pRegData:返回寄存器的值,至少为RegNum的2倍
                                        返回的寄存器的值按照循序存放在pRegData中
* 返回                        :        MRTU_ERROR:通信状态
* 依赖                        :        底层通信驱动
* 作者                        :        cp1300@139.com
* 时间                        :        2014-03-24
* 最后修改时间         :         2017-03-23
* 说明                        :         MOUEBUS RTU读取数据,读取一个寄存器
                                        输入输出的数据都为小端模式
                                        2017-03-23:增加回调,抛离底层依赖
*************************************************************************************************************************/
MRTU_ERROR MODBUS_HOST_ReadMultReg(MODBUS_HANDLE *pHandle, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[])
{
        MRTU_READ_FRAME *pFrame;                //发送数据帧格式
        MRTU_RETURN_FRAME *pReFrame;        //返回数据帧格式
        MRTU_UNU_FRAME        *pUnuFrame;                //返回的异常数据帧格式
        u16 crc16;
        u32 TimeDelay = 0;                                //用于计算数据接收延时
        int len;
        u8 *pRxBuff;                                        //接收数据缓冲区指针
        u8 i;

       
        if(pHandle == NULL) return MRTU_HANDLE_ERROR;                //句柄无效
        if((pHandle->ID != MODBUS_INIT_ID)||(pHandle->pTxBuff==NULL)) return MRTU_HANDLE_ERROR;        //2016-04-11增加防止modbus未初始化异常
        if(pHandle->TxBuffSize < 8) return MRTU_OVER_ERROR;        //检查发送缓冲区
        pFrame = (MRTU_READ_FRAME *)pHandle->pTxBuff;
        //数据结构填充
        pFrame->addr = SlaveAddr;                                                        //从机地址
        pFrame->fun = (u8)RegType;                                                        //功能码,读取
        pFrame->StartReg = SWAP16(RegAddr);                                        //寄存器起始地址
        if((RegNum > 127) || (RegNum == 0))        return MRTU_REGN_ERROR;        //寄存器数量错误
        pFrame->RegNum = SWAP16(RegNum);                                        //需要读取的寄存器数量
        crc16 = MODBUS_CRC16(pHandle->pTxBuff, 6);                        //计算CRC16
        pFrame->CRC16 = crc16;                                                                //crc16

#if MODBUS_RTU_DBUG
        {
                u16 i;
               
                modbus_debug("\r\n<- MODBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",8,crc16);
                for(i = 0;i < 8;i ++)
                {
                        modbus_debug("0x%02X ",pHandle->pTxBuff);
                }
                modbus_debug("\r\n");
        }
#endif        //MODBUS_RTU_DBUG
       
        pHandle->pSendData(pHandle->pTxBuff, 6+2);                        //发送数据
        TimeDelay = 8*pHandle->TxByteTimeUs;                                //计算发送所需时间,进行延时
        TimeDelay/=1000;                                                                        //转换为ms
        TimeDelay += 1;
        pHandle->pDelayMS(TimeDelay);                                                //等待发送完毕,防止通信帧结束时产生干扰
        pHandle->pClearRxData();                                                        //清除接收缓冲区
        //等待数据返回
        len = pHandle->pReadData(&pRxBuff, 10, pHandle->RxTimeOutMs, &pHandle->ReturnTimeMs);        //接收数据
        if(len <= 0)                                                                                //没有接收到数据
        {       
                modbus_debug("接收超时(%dms)!\r\n",pHandle->RxTimeOutMs);
                pHandle->ReturnTimeMs = 0xffff;                                        //接收时间无效
                return MRTU_TIME_OUT;                                                        //返回超时
        }
       
#if MODBUS_RTU_DBUG
        {
                u16 i;
               
                modbus_debug("\r\n-> MODBUS RTU RXD(%dB)(ping:%dms):\r\n",len,pHandle->ReturnTimeMs);
                for(i = 0;i < len;i ++)
                {
                        modbus_debug("0x%02X ", pRxBuff);
                }
                modbus_debug("\r\n");
        }
#endif        //MODBUS_RTU_DBUG
       
        if(len < MODBUS_MIN_RX_BYTE)
        {
                modbus_debug("返回数据长度错误\r\n");
                return MRTU_LEN_ERROR;
        }
       
        pReFrame = (MRTU_RETURN_FRAME *)pRxBuff;
        //检查地址
        if(pReFrame->addr != SlaveAddr)
        {
                modbus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02X\r\n",SlaveAddr, pReFrame->addr);
                return MRTU_ADDR_ERROR;
        }
        //对接受的数据进行CRC校验
        crc16 = MODBUS_CRC16(pRxBuff, len-2);//计算CRC16
        if((pRxBuff[len-1] != (crc16 >> 8)) || (pRxBuff[len-2] != (crc16 & 0xff)))
        {
                modbus_debug("CRC校验错误,计算CRC为:0x%04X,返回CRC为:0x%04X\r\n",crc16,(u16)(pRxBuff[len-2]<<8)|pRxBuff[len-1]);
                return MRTU_CRC_ERROR;                                                                //返回CRC校验错误
        }
        //返回的功能码不一致
        if(pReFrame->fun != (u8)RegType)
        {
                pUnuFrame = (MRTU_UNU_FRAME *)pRxBuff;                                //异常数据帧
                if(pUnuFrame->ErrorFun == ((u8)RegType|0x80))                //返回有异常
                {
                        modbus_debug("返回异常,异常码%d\r\n", pUnuFrame->unu);
                        switch(pUnuFrame->unu)
                        {
                                case 1: return MRTU_UNUS1_ERROR;                        //异常码1
                                case 2: return MRTU_UNUS2_ERROR;                        //异常码2
                                case 3: return MRTU_UNUS3_ERROR;                        //异常码3
                                case 4: return MRTU_UNUS4_ERROR;                        //异常码4
                                case 5: return MRTU_UNUS5_ERROR;                        //异常码5
                                case 6: return MRTU_UNUS6_ERROR;                        //异常码6
                                default: return MRTU_OTHER_ERROR;
                        }
                }
                else
                {
                        modbus_debug("返回错误,返回功能码为0x%02X\r\n", pReFrame->fun);
                        return MRTU_FUNR_ERROR;
                }
        }
        //判断数据长度
        if(pReFrame->DataLen != (RegNum*2))
        {
                modbus_debug("返回数据长度错误,读取%d个寄存器,共%dB,只返回了%dB\r\n",RegNum, RegNum*2, pReFrame->DataLen);
                return MRTU_LEN_ERROR;                                                                //返回数据长度错误
        }
        //获取返回的寄存器的值
        for(i = 0;i < RegNum;i ++)
        {
                pRegData = pReFrame->DataBuff[i*2];
                pRegData <<= 8;
                pRegData |= pReFrame->DataBuff[i*2+1];
        }
        pHandle->ReadRegCnt ++;                                                                        //读取寄存器次数增加
       
        return MRTU_OK;                                                                                        //返回成功
}



使用特权

评论回复
6
tpgf|  楼主 | 2021-10-1 18:42 | 只看该作者
/*************************************************************************************************************************
* 函数                        :        MRTU_ERROR MODBUS_HOST_WriteReg(MODBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u16 RegData)
* 功能                        :        主机写从机一个指定寄存器
* 参数                        :        pHandle:MODBUS句柄;SlaveAddr:从机地址;RegAddr:写寄存器地址;RegData:寄存器的值
* 返回                        :        MRTU_ERROR:通信状态
* 依赖                        :        底层通信驱动
* 作者                        :        cp1300@139.com
* 时间                        :        2014-03-24
* 最后修改时间         :         2017-03-23
* 说明                        :         MOUEBUS RTU写从机一个保持寄存器
                                        输入输出的数据都为小端模式
                                        预置单个寄存器的发送与接收数据包格式完全一致,理论上发送与接收的数据都应该一致
                                        2017-03-23:增加回调,抛离底层依赖
*************************************************************************************************************************/
MRTU_ERROR MODBUS_HOST_WriteOneReg(MODBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u16 RegData)
{
        MRTU_WRITE_FRAME *pFrame, *pReFrame;                        //发送数据帧格式
        MRTU_UNU_FRAME        *pUnuFrame;                                                //返回的异常数据帧格式
        u16 crc16;
        u32 TimeDelay = 0;                                                                //用于计算数据接收延时
        int len;
        u8 *pRxBuff;                                                                        //接收数据缓冲区指针

       
        if(pHandle == NULL) return MRTU_HANDLE_ERROR;        //句柄无效
        if((pHandle->ID != MODBUS_INIT_ID)||(pHandle->pTxBuff==NULL)) return MRTU_HANDLE_ERROR;        //2016-04-11增加防止modbus未初始化异常
        if(pHandle->TxBuffSize < 8) return MRTU_OVER_ERROR;        //检查发送缓冲区
        pFrame = (MRTU_WRITE_FRAME *)pHandle->pTxBuff;
        //数据结构填充
        pFrame->addr = SlaveAddr;                                                //从机地址
        pFrame->fun = (u8)MRTU_FUN_WRITE;                                //功能码,预置单个寄存器
        pFrame->StartReg = SWAP16(RegAddr);                                //寄存器起始地址
        pFrame->RegData = SWAP16(RegData);                                //写入寄存器内容
        pFrame->crc16 = MODBUS_CRC16(pHandle->pTxBuff, 6);        //计算CRC16

#if MODBUS_RTU_DBUG
        {
                u16 i;
               
                modbus_debug("\r\n<- MODBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",8,crc16);
                for(i = 0;i < 8;i ++)
                {
                        modbus_debug("0x%02X ",pHandle->pTxBuff);
                }
                modbus_debug("\r\n");
        }
#endif        //MODBUS_RTU_DBUG
       
        pHandle->pSendData(pHandle->pTxBuff, 6+2);                //发送数据
        TimeDelay = 8*pHandle->TxByteTimeUs;                                //计算发送所需时间,进行延时
        TimeDelay/=1000;                                                                //转换为ms
        TimeDelay += 1;
        pHandle->pDelayMS(TimeDelay);                                                //等待发送完毕,防止通信帧结束时产生干扰
        pHandle->pClearRxData();                                                //清除接收缓冲区
        //等待数据返回
        len = pHandle->pReadData(&pRxBuff, 10, pHandle->RxTimeOutMs, &pHandle->ReturnTimeMs);        //接收数据
        if(len <= 0)         //没有接收到数据
        {       
                modbus_debug("接收超时(%dms)!\r\n",pHandle->RxTimeOutMs);        //接收数据超时
                pHandle->ReturnTimeMs = 0xffff;                                //接收数据超时
                return MRTU_TIME_OUT;                                                //返回超时
        }
        pHandle->ReturnTimeMs = TimeDelay*10;                        //数据返回时间
       
#if MODBUS_RTU_DBUG
        {
                u16 i;
               
                modbus_debug("\r\n-> MODBUS RTU RXD(%dB)(ping:%dms):\r\n",len,pHandle->ReturnTimeMs);
                for(i = 0;i < len;i ++)
                {
                        modbus_debug("0x%02X ", pRxBuff);
                }
                modbus_debug("\r\n");
        }
#endif        //MODBUS_RTU_DBUG
        if(len < MODBUS_MIN_RX_BYTE)
        {
                modbus_debug("返回数据长度错误\r\n");
                return MRTU_LEN_ERROR;
        }
       
        pReFrame = (MRTU_WRITE_FRAME *)pRxBuff;
        //检查地址
        if(pReFrame->addr != SlaveAddr)
        {
                modbus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02X\r\n",SlaveAddr, pReFrame->addr);
                return MRTU_ADDR_ERROR;
        }
        //对接受的数据进行CRC校验
        crc16 = MODBUS_CRC16(pRxBuff, len-2);//计算CRC16
        if((pRxBuff[len-1] != (crc16 >> 8)) || (pRxBuff[len-2] != (crc16 & 0xff)))
        {
                modbus_debug("CRC校验错误,计算CRC为:0x%04X,返回CRC为:0x%04X\r\n",crc16,(u16)(pRxBuff[len-2]<<8)|pRxBuff[len-1]);
                return MRTU_CRC_ERROR;                                //返回CRC校验错误
        }
        //返回的功能码不一致
        if(pReFrame->fun != (u8)MRTU_FUN_WRITE)
        {
                pUnuFrame = (MRTU_UNU_FRAME *)pRxBuff;                                //异常数据帧
                if(pUnuFrame->ErrorFun == ((u8)MRTU_FUN_WRITE|0x80))//返回有异常
                {
                        modbus_debug("返回异常,异常码%d\r\n", pUnuFrame->unu);
                        switch(pUnuFrame->unu)
                        {
                                case 1: return MRTU_UNUS1_ERROR;                        //异常码1
                                case 2: return MRTU_UNUS2_ERROR;                        //异常码2
                                case 3: return MRTU_UNUS3_ERROR;                        //异常码3
                                case 4: return MRTU_UNUS4_ERROR;                        //异常码4
                                case 5: return MRTU_UNUS5_ERROR;                        //异常码5
                                case 6: return MRTU_UNUS6_ERROR;                        //异常码6
                                default: return MRTU_OTHER_ERROR;
                        }
                }
                else
                {
                        modbus_debug("返回错误,返回功能码为0x%02X\r\n", pReFrame->fun);
                        return MRTU_FUNR_ERROR;
                }
        }
        //判断数据是否写入
        if(SWAP16(pReFrame->StartReg) != RegAddr)                                //返回的寄存器地址不一致
        {
                modbus_debug("返回寄存器地址错误,写入寄存器%d,返回寄存器%d\r\n",RegAddr, pReFrame->StartReg);
                return MRTU_REG_ERROR;                                                                //返回寄存器错误
        }
        if(SWAP16(pReFrame->RegData) != RegData)
        {
                modbus_debug("数据写入错误,写入值:0x%04X,返回了:0x%04X\r\n",RegData, pReFrame->RegData);
                return MRTU_WRITE_ERROR;                                                        //写入数据错误
        }
        pHandle->WriteRegCnt ++;                                                                //主机写入寄存器次数增加

        return MRTU_OK;                                                                                        //返回成功
}



使用特权

评论回复
7
tpgf|  楼主 | 2021-10-1 18:42 | 只看该作者
/*************************************************************************************************************************
* 函数                        :        MRTU_ERROR MODBUS_HOST_WriteMultReg(MODBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[])
* 功能                        :        主机写从机多个指定寄存器
* 参数                        :        pHandle:MODBUS句柄;SlaveAddr:从机地址;RegAddr:写寄存器地址;RegNum:寄存器数量, pRegData:需要写入的寄存器的值
                                        写入寄存器的值按照循序排列,使用小端格式,大小必须为RegNum*2
* 返回                        :        MRTU_ERROR:通信状态
* 依赖                        :        底层通信驱动
* 作者                        :        cp1300@139.com
* 时间                        :        2014-03-24
* 最后修改时间         :         2017-03-23
* 说明                        :         MOUEBUS RTU写从机一个保持寄存器
                                        输入输出的数据都为小端模式
                                        返回数据寄存器位置与寄存器数量
                                        2017-03-23:增加回调,抛离底层依赖
*************************************************************************************************************************/
MRTU_ERROR MODBUS_HOST_WriteMultReg(MODBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[])
{
        MRTU_WRITE_MULT_FRAME *pFrame;                                        //发送数据帧格式
        MRTU_WRIT_EMULT_RFRAME *pReFrame;                                //返回数据帧格式
        MRTU_UNU_FRAME        *pUnuFrame;                                                //返回的异常数据帧格式
        u16 crc16;
        u32 TimeDelay = 0;                                                                //用于计算数据接收延时
        int len;
        u8 *pRxBuff;                                                                        //接收数据缓冲区指针
        u8 i;
       
        if(pHandle == NULL) return MRTU_HANDLE_ERROR;        //句柄无效
        if((pHandle->ID != MODBUS_INIT_ID)||(pHandle->pTxBuff==NULL)) return MRTU_HANDLE_ERROR;        //2016-04-11增加防止modbus未初始化异常
        if(pHandle->TxBuffSize < 7+2*RegNum+2) return MRTU_OVER_ERROR;        //检查发送缓冲区
        pFrame = (MRTU_WRITE_MULT_FRAME *)pHandle->pTxBuff;
        //数据结构填充
        pFrame->addr = SlaveAddr;                                                //从机地址
        pFrame->fun = (u8)MRTU_FUN_MWRITE;                                //功能码,预置多个寄存器
        pFrame->StartReg = SWAP16(RegAddr);                                //寄存器起始地址
        if((RegNum > 127) || (RegNum == 0))        return MRTU_REGN_ERROR;        //寄存器数量错误
        pFrame->RegNum = SWAP16(RegNum);                                //写入寄存器数量
        pFrame->DataLen = 2*RegNum;                                                //数据长度
        //循环写入数据
        for(i = 0;i < RegNum;i ++)
        {
                pFrame->DataBuff[2*i] = pRegData>>8;                //高位
                pFrame->DataBuff[2*i+1] = pRegData&0xff;        //低位
        }
        crc16 = MODBUS_CRC16(pHandle->pTxBuff, 7+pFrame->DataLen);        //计算CRC16,高低位对调过
        pFrame->DataBuff[pFrame->DataLen] = crc16&0xff;        //高位
        pFrame->DataBuff[pFrame->DataLen+1]=crc16>>8;        //低位
       
#if MODBUS_RTU_DBUG
        {
                u16 i;
               
                modbus_debug("\r\n<- MODBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",7+pFrame->DataLen+2,crc16);
                for(i = 0;i < 7+pFrame->DataLen+2;i ++)
                {
                        modbus_debug("0x%02X ",pHandle->pTxBuff);
                }
                modbus_debug("\r\n");
        }
#endif        //MODBUS_RTU_DBUG
       
        pHandle->pSendData(pHandle->pTxBuff, 7+pFrame->DataLen+2);                                                                //发送数据
        TimeDelay = 8*pHandle->TxByteTimeUs;                        //计算发送所需时间,进行延时
        TimeDelay/=1000;                                                                //转换为ms
        TimeDelay += 1;
        pHandle->pDelayMS(TimeDelay);                                        //等待发送完毕,防止通信帧结束时产生干扰
        pHandle->pClearRxData();                                                //清除接收缓冲区
        //等待数据返回
        len = pHandle->pReadData(&pRxBuff, 10, pHandle->RxTimeOutMs, &pHandle->ReturnTimeMs);        //接收数据
        if(len <= 0)                                                                         //没有接收到数据                                                //没有接收到数据
        {       
                modbus_debug("接收超时(%dms)!\r\n",pHandle->RxTimeOutMs);                                                        //接收数据超时
                pHandle->ReturnTimeMs = 0xffff;                                //接收数据超时
                return MRTU_TIME_OUT;                                                //返回超时
        }
        pHandle->ReturnTimeMs = TimeDelay*10;                        //数据返回时间
       
#if MODBUS_RTU_DBUG
        {
                u16 i;
               
                modbus_debug("\r\n-> MODBUS RTU RXD(%dB)(ping:%dms):\r\n",len,pHandle->ReturnTimeMs);
                for(i = 0;i < len;i ++)
                {
                        modbus_debug("0x%02X ", pRxBuff);
                }
                modbus_debug("\r\n");
        }
#endif        //MODBUS_RTU_DBUG
        if(len < MODBUS_MIN_RX_BYTE)
        {
                modbus_debug("返回数据长度错误\r\n");
                return MRTU_LEN_ERROR;
        }
       
        pReFrame = (MRTU_WRIT_EMULT_RFRAME *)pRxBuff;
        //检查地址
        if(pReFrame->addr != SlaveAddr)
        {
                modbus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02X\r\n",SlaveAddr, pReFrame->addr);
                return MRTU_ADDR_ERROR;
        }
        //对接受的数据进行CRC校验
        crc16 = MODBUS_CRC16(pRxBuff, len-2);//计算CRC16
        if((pRxBuff[len-1] != (crc16 >> 8)) || (pRxBuff[len-2] != (crc16 & 0xff)))
        {
                modbus_debug("CRC校验错误,计算CRC为:0x%04X,返回CRC为:0x%04X\r\n",crc16,(u16)(pRxBuff[len-2]<<8)|pRxBuff[len-1]);
                return MRTU_CRC_ERROR;                                                                //返回CRC校验错误
        }
        //返回的功能码不一致
        if(pReFrame->fun != (u8)MRTU_FUN_MWRITE)
        {
                pUnuFrame = (MRTU_UNU_FRAME *)pRxBuff;                                //异常数据帧
                if(pUnuFrame->ErrorFun == ((u8)MRTU_FUN_MWRITE|0x80))//返回有异常
                {
                        modbus_debug("返回异常,异常码%d\r\n", pUnuFrame->unu);
                        switch(pUnuFrame->unu)
                        {
                                case 1: return MRTU_UNUS1_ERROR;                        //异常码1
                                case 2: return MRTU_UNUS2_ERROR;                        //异常码2
                                case 3: return MRTU_UNUS3_ERROR;                        //异常码3
                                case 4: return MRTU_UNUS4_ERROR;                        //异常码4
                                case 5: return MRTU_UNUS5_ERROR;                        //异常码5
                                case 6: return MRTU_UNUS6_ERROR;                        //异常码6
                                default: return MRTU_OTHER_ERROR;
                        }
                }
                else
                {
                        modbus_debug("返回错误,返回功能码为0x%02X\r\n", pReFrame->fun);
                        return MRTU_FUNR_ERROR;
                }
        }
        //判断数据是否写入
        if(SWAP16(pReFrame->StartReg) != RegAddr)                                //返回的寄存器地址不一致
        {
                modbus_debug("返回寄存器地址错误,写入寄存器%d,返回寄存器%d\r\n",RegAddr, pReFrame->StartReg);
                return MRTU_REG_ERROR;                                                                //返回寄存器错误
        }
        if(SWAP16(pReFrame->RegNum) != RegNum)
        {
                modbus_debug("写入寄存器数量错误,写入%d个寄存器,返回%d个寄存器\r\n",RegNum, pReFrame->RegNum);
                return MRTU_WRITE_ERROR;                                                        //写入数据错误
        }
        pHandle->WriteRegCnt ++;                                                                //主机写入寄存器次数增加

        return MRTU_OK;                                                                                        //返回成功
}
#endif //MODBUS_RTU_HOST



使用特权

评论回复
8
tpgf|  楼主 | 2021-10-1 18:43 | 只看该作者

#if(MODBUS_RTU_SLAVE) //开启从机模式
/*************************************************************************************************************************
* 函数                        :        bool MODBUS_SLAVE_RetrunUnu(MODBUS_HANDLE *pHandle,u8 SlaveAddr, u8 Fun, MRTU_UNUS Unus)
* 功能                        :        从机返回异常编码
* 参数                        :        pHandle:MODBUS句柄;SlaveAddr:从机地址;Fun:来自主机的功能码;Unus:异常码,见MRTU_UNUS
* 返回                        :        TRUE:发送成功;FALSE:发送失败
* 依赖                        :        底层通信驱动
* 作者                        :        cp1300@139.com
* 时间                        :        2014-03-24
* 最后修改时间         :         2017-03-23
* 说明                        :         从机返回异常码给主机,异常码见:MRTU_UNUS
                                        MRTU_UNUS1        异常码1,无效的操作码
                                        MRTU_UNUS2        异常码2,无效的数据地址
                                        MRTU_UNUS3        异常码3,无效的数据值
                                        MRTU_UNUS4        异常码4,无效操作
                                        MRTU_UNUS5        异常码5
                                        MRTU_UNUS6        异常码6
                                        2017-03-23:增加回调,抛离底层依赖
*************************************************************************************************************************/
bool MODBUS_SLAVE_RetrunUnu(MODBUS_HANDLE *pHandle,u8 SlaveAddr, u8 Fun, MRTU_UNUS Unus)
{
        MRTU_UNU_FRAME *pFrame;                                                                        //返回异常数据包
        u16 crc16;
       
        if(pHandle == NULL) return FALSE;                                                //句柄无效
        if((pHandle->ID != MODBUS_INIT_ID)||(pHandle->pTxBuff==NULL)) return FALSE;        //2016-04-11增加防止modbus未初始化异常
        if(pHandle->TxBuffSize < 5) return FALSE;                                //检查发送缓冲区
        //数据结构填充
        pFrame = (MRTU_UNU_FRAME *)pHandle->pTxBuff;
        pFrame->addr = SlaveAddr;                                                                //从机地址
        pFrame->ErrorFun = (u8)Fun|0x80;                                                //功能码+0x80,出现异常
        pFrame->unu = (u8)Unus;                                                                        //异常编码
        crc16 = MODBUS_CRC16(pHandle->pTxBuff, 3);                                //计算CRC16,高低位对调过
        pFrame->crc16H = crc16 & 0xff;
        pFrame->crc16L = crc16>>8;
#if MODBUS_RTU_DBUG
        {
                u16 i;
               
                modbus_debug("\r\n<- MODBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",5, crc16);
                for(i = 0;i < 5;i ++)
                {
                        modbus_debug("0x%02X ",pHandle->pTxBuff);
                }
                modbus_debug("\r\n");
        }
#endif        //MODBUS_RTU_DBUG
       
        pHandle->pSendData(pHandle->pTxBuff, 5);                                //发送数据
       
        return TRUE;
}



使用特权

评论回复
9
tpgf|  楼主 | 2021-10-1 18:43 | 只看该作者
/*************************************************************************************************************************
* 函数                        :        MRTU_ERROR MODBUS_SLAVE_FramesUnpack(MODBUS_HANDLE *pHandle,u8 SlaveAddr,u8 *pRxBuff, u16 DataLen, u8 *pFun)
* 功能                        :        从机模式接收数据拆包
* 参数                        :        pHandle:MODBUS句柄;SlaveAddr:从机地址;pRxBuff:接收数据缓冲区;DataLen:接收数据长度;pFun:来自主机的功能码
* 返回                        :        MRTU_ERROR:状态,只有MRTU_OK:才是有效数据包
* 依赖                        :        底层通信驱动
* 作者                        :        cp1300@139.com
* 时间                        :        2014-03-24
* 最后修改时间         :         2017-03-24
* 说明                        :         需要等数据接收完毕后拆包
                                        0:为广播地址
                                        2017-03-23:增加回调,抛离底层依赖
*************************************************************************************************************************/
MRTU_ERROR MODBUS_SLAVE_FramesUnpack(MODBUS_HANDLE *pHandle,u8 SlaveAddr,u8 *pRxBuff, u16 DataLen, u8 *pFun)
{
        u16 crc16;
        MRTU_READ_FRAME *pReadFrame;                                                //来自主机的读取数据帧格式
        MRTU_WRITE_MULT_FRAME *pWriteMultFrame;                                //来自主机的写多个保持寄存器

        *pFun = 0xff;                                                                                //功能码无效
        if((pRxBuff[0] != SlaveAddr) && (pRxBuff[0] != MODBUS_BROAD_ADDR))
        {
                modbus_debug("地址不符,丢弃;目标地址:0x%02X;本机地址:0x%02X;\r\n", pRxBuff[0], SlaveAddr);
                return MRTU_ADDR_ERROR;
        }
        if(DataLen < MODBUS_MIN_RX_BYTE) return MRTU_LEN_ERROR;
        //对接受的数据进行CRC校验
        crc16 = MODBUS_CRC16(pRxBuff, DataLen-2);                        //计算CRC16       
       
#if MODBUS_RTU_DBUG
        {
                u16 i;
               
                modbus_debug("\r\n-> MODBUS RTU RXD(%dB)(CRC:0x%04X):\r\n",DataLen,crc16);
                for(i = 0;i < DataLen;i ++)
                {
                        modbus_debug("0x%02X ",pRxBuff);
                }
                modbus_debug("\r\n");
        }
#endif        //MODBUS_RTU_DBUG
       
        pHandle->RxDataLen = DataLen;                                                //记录接受数据的长度
        if((pRxBuff[DataLen-1] == (crc16 >> 8)) && (pRxBuff[DataLen-2] == (crc16 & 0xff)))
        {
                //判断功能码
                switch(pRxBuff[1])
                {
                        case MRTU_FUN_READ_HOLD                :                                //0x03读保持寄存器,可读写寄存器为保持寄存器
                        case MRTU_FUN_READ_INPUT        :                                //0x04读输入寄存器,为只读寄存器       
                        {
                                pReadFrame = (MRTU_READ_FRAME *)pRxBuff;
                                if((SWAP16(pReadFrame->RegNum) > 127) || (SWAP16(pReadFrame->RegNum) == 0))       
                                {
                                        modbus_debug("读取寄存器数量错误,读取寄存器数量为:%d\r\n", SWAP16(pReadFrame->RegNum));
                                        if(pRxBuff[0] != MODBUS_BROAD_ADDR)        //非广播地址才返回
                                        {
                                                MODBUS_SLAVE_RetrunUnu(pHandle, pRxBuff[0], pRxBuff[1], MRTU_UNUS2);        //返回异常2
                                        }
                                        return MRTU_REGN_ERROR;                                //寄存器数量错误
                                }
                                pHandle->ReadRegCnt ++;                                        //读取寄存器次数增加
                        }break;
                        case MRTU_FUN_WRITE        :pHandle->WriteRegCnt ++;break;                //0x06写单个保持寄存器
                        case MRTU_FUN_MWRITE                :                                //0x10写多个保持寄存器
                        {
                                pWriteMultFrame = (MRTU_WRITE_MULT_FRAME *)pRxBuff;
                                if((SWAP16(pWriteMultFrame->RegNum) > 127) || (SWAP16(pWriteMultFrame->RegNum) == 0))       
                                {
                                        modbus_debug("写寄存器数量错误,读取寄存器数量为:%d\r\n", SWAP16(pWriteMultFrame->RegNum));
                                        if(pRxBuff[0] != MODBUS_BROAD_ADDR)        //非广播地址才返回
                                        {
                                                MODBUS_SLAVE_RetrunUnu(pHandle, pRxBuff[0], pRxBuff[1], MRTU_UNUS2);        //返回异常2
                                        }
                                        return MRTU_REGN_ERROR;                                //寄存器数量错误
                                }
                                else if(pWriteMultFrame->DataLen != (2*SWAP16(pWriteMultFrame->RegNum)))
                                {
                                        modbus_debug("写寄存器数据长度错误,需要写入%d个寄存器,长度为:%dB,收到数据长度为:%dB\r\n", pWriteMultFrame->RegNum, 2*pWriteMultFrame->RegNum, pWriteMultFrame->DataLen);
                                        if(pRxBuff[0] != MODBUS_BROAD_ADDR)        //非广播地址才返回
                                        {
                                                MODBUS_SLAVE_RetrunUnu(pHandle, pRxBuff[0], pRxBuff[1], MRTU_UNUS3);        //返回异常3
                                        }
                                        return MRTU_REGN_ERROR;                                //寄存器数量错误
                                }
                                pHandle->WriteRegCnt ++;                                //主机写入寄存器次数增加
                        }break;
                        default:                                                                        //不支持的功能码,返回异常1
                        {
                                modbus_debug("不支持的操作码:0x%02X\r\n", pRxBuff[1]);
                                if(pRxBuff[0] != MODBUS_BROAD_ADDR)                //非广播地址才返回
                                {
                                        MODBUS_SLAVE_RetrunUnu(pHandle, pRxBuff[0], pRxBuff[1], MRTU_UNUS1);        //返回异常1
                                }
                                return MRTU_FUNR_ERROR;
                        }
                }
               
                *pFun = pRxBuff[1];                                                                //返回功能码
                return MRTU_OK;                                                                        //返回成功
        }
        else
        {
                modbus_debug("CRC校验错误,计算CRC为:0x%04X,返回CRC为:0x%04X\r\n",crc16,(u16)(pRxBuff[DataLen-2]<<8)|pRxBuff[DataLen-1]);
                return MRTU_CRC_ERROR;                                                        //返回CRC校验错误
        }
}



使用特权

评论回复
10
tpgf|  楼主 | 2021-10-1 18:43 | 只看该作者

/*************************************************************************************************************************
* 函数                        :        MRTU_ERROR MODBUS_SLAVE_ReturnReadReg(MODBUS_HANDLE *pHandle, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u8 RegNum)
* 功能                        :        从机返回主机读取的寄存器
* 参数                        :        pHandle:MODBUS句柄;RegType:要读取的寄存器类型;SlaveAddr:从机地址;RegAddr:需读取的寄存器地址;RegNum:寄存器数量;
                                        返回的寄存器的值按照循序存放在pRegData中
* 返回                        :        MRTU_ERROR:通信状态
* 依赖                        :        底层通信驱动
* 作者                        :        cp1300@139.com
* 时间                        :        2014-03-24
* 最后修改时间         :         2017-03-23
* 说明                        :         MOUEBUS RTU主机读取从机的指定寄存器,可以为保持寄存器,也可以为输入寄存器,可以一次读取多个
                                        输入输出的数据都为小端模式
                                        注意:如果直接使用数据帧的寄存器数量以及地址,必须高地位交换
                                        2017-03-23:增加回调,抛离底层依赖
                                        2018-01-27:增加底层读写寄存器回调
*************************************************************************************************************************/
MRTU_ERROR MODBUS_SLAVE_ReturnReadReg(MODBUS_HANDLE *pHandle, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u8 RegNum)
{
        MRTU_RETURN_FRAME *pFrame;                //返回数据帧格式
        u16 crc16;
        u8 i;
        u16 data;
        MRTU_UNUS Unus;
       
        if(pHandle == NULL) return MRTU_HANDLE_ERROR;        //句柄无效
        if((pHandle->ID != MODBUS_INIT_ID)||(pHandle->pTxBuff==NULL)) return MRTU_HANDLE_ERROR;        //2016-04-11增加防止modbus未初始化异常
        if((RegType != INPUT_REG) && (RegType != HOLD_REG)) return MRTU_FUNR_ERROR;                                //寄存器类型错误
        if((RegNum > 127) || (RegNum == 0))        return MRTU_REGN_ERROR;                                                                //寄存器数量错误
        if(pHandle->TxBuffSize < (3+2*RegNum+2)) return MRTU_OVER_ERROR;                                                //检查发送缓冲区
        pFrame = (MRTU_RETURN_FRAME *)pHandle->pTxBuff;
        //数据结构填充
        pFrame->addr = SlaveAddr;                                                //从机地址
        pFrame->fun = RegType;                                                        //功能码,读取
        pFrame->DataLen = 2*RegNum;                                                //数据长度
        //循环写入要返回的数据
        for(i = 0;i < RegNum;i ++)
        {
                Unus = pHandle->pSingleRegReadWriteCallBack(RegAddr+i,&data,RegType,TRUE);                                //调用回调,读取数据
                switch(Unus)
                {
                        case MRTU_NOT_UNUS        : break;//无异常
                        default:                                        //有异常则返回对应的异常码,并退出通信
                        {
                                MODBUS_SLAVE_RetrunUnu(pHandle, SlaveAddr,  RegType, Unus);                                                //返回异常码
                                switch (Unus)
                                {
                                        case MRTU_UNUS1        :        return MRTU_UNUS1_ERROR;        //异常码1,无效的操作码
                                        case MRTU_UNUS2        :        return MRTU_UNUS2_ERROR;        //异常码2,无效的数据地址
                                        case MRTU_UNUS3        :        return MRTU_UNUS3_ERROR;        //异常码3,无效的数据值
                                        case MRTU_UNUS4        :        return MRTU_UNUS4_ERROR;        //异常码4,无效操作
                                        case MRTU_UNUS5        :        return MRTU_UNUS5_ERROR;        //异常码5
                                        case MRTU_UNUS6        :        return MRTU_UNUS6_ERROR;        //异常码6
                                        default: return MRTU_OTHER_ERROR;                                //其它错误
                                }
                        }
                }
               
                pFrame->DataBuff[2*i] = data>>8;                        //数据高位
                pFrame->DataBuff[2*i+1] = data&0xff;                //数据低位
        }
        crc16 = MODBUS_CRC16(pHandle->pTxBuff, 3+pFrame->DataLen);//计算CRC16
        pFrame->DataBuff[pFrame->DataLen] = crc16&0xff;        //数据发送交换过
        pFrame->DataBuff[pFrame->DataLen+1] = crc16>>8;        //数据发送交换过

#if MODBUS_RTU_DBUG
        {
                u16 i;
               
                modbus_debug("\r\n<- MODBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",3+pFrame->DataLen+2,crc16);
                for(i = 0;i < 3+pFrame->DataLen+2;i ++)
                {
                        modbus_debug("0x%02X ",pHandle->pTxBuff);
                }
                modbus_debug("\r\n");
        }
#endif        //MODBUS_RTU_DBUG
       
        pHandle->pSendData(pHandle->pTxBuff, 3+pFrame->DataLen+2);        //发送数据

        return MRTU_OK;                                                                        //返回成功
}




使用特权

评论回复
11
tpgf|  楼主 | 2021-10-1 18:43 | 只看该作者

/*************************************************************************************************************************
* 函数                        :        MRTU_ERROR MODBUS_SLAVE_ReturnWriteOneHoldReg(MODBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u16 RegData)
* 功能                        :        从机返回主机预置单个保持寄存器
* 参数                        :        pHandle:MODBUS句柄;Fun:读取的功能码;SlaveAddr:从机地址;RegAddr:需读取的寄存器地址;RegData:返回寄存器的值
* 返回                        :        MRTU_ERROR:通信状态
* 依赖                        :        底层通信驱动
* 作者                        :        cp1300@139.com
* 时间                        :        2014-03-24
* 最后修改时间         :         2017-03-23
* 说明                        :         MOUEBUS RTU主机写单个寄存器成功后返回
                                        输入输出的数据都为小端模式
                                        注意:如果直接使用数据帧的寄存器数量以及地址,必须高地位交换
                                        2017-03-23:增加回调,抛离底层依赖
*************************************************************************************************************************/
MRTU_ERROR MODBUS_SLAVE_ReturnWriteOneHoldReg(MODBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u16 RegData)
{
        MRTU_WRITE_FRAME *pFrame;                                                //返回数据帧格式
       
        if(pHandle == NULL) return MRTU_HANDLE_ERROR;        //句柄无效
        if((pHandle->ID != MODBUS_INIT_ID)||(pHandle->pTxBuff==NULL)) return MRTU_HANDLE_ERROR;        //2016-04-11增加防止modbus未初始化异常
        if(pHandle->TxBuffSize < 6) return MRTU_OVER_ERROR;        //检查发送缓冲区
        pFrame = (MRTU_WRITE_FRAME *)pHandle->pTxBuff;
        //数据结构填充
        pFrame->addr = SlaveAddr;                                                //从机地址
        pFrame->fun = MRTU_FUN_WRITE;                                        //功能码,预置单个寄存器
        pFrame->StartReg = SWAP16(RegAddr);                                //寄存器地址
        pFrame->RegData = SWAP16(RegData);                                //寄存器的值
        pFrame->crc16 = MODBUS_CRC16(pHandle->pTxBuff, 6);//计算CRC16

#if MODBUS_RTU_DBUG
        {
                u16 i;
               
                modbus_debug("\r\n<- MODBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",8,pFrame->crc16);
                for(i = 0;i < 8;i ++)
                {
                        modbus_debug("0x%02X ",pHandle->pTxBuff);
                }
                modbus_debug("\r\n");
        }
#endif        //MODBUS_RTU_DBUG
       
        pHandle->pSendData(pHandle->pTxBuff, 8);                //发送数据

        return MRTU_OK;                                                                        //返回成功
}



使用特权

评论回复
12
tpgf|  楼主 | 2021-10-1 18:43 | 只看该作者

/*************************************************************************************************************************
* 函数                        :        MRTU_ERROR MODBUS_SLAVE_ReturnWriteMultHoldReg(MODBUS_HANDLE *pHandle, u8 SlaveAddr, u16 RegAddr, u8 RegNum)
* 功能                        :        从机返回主机预置多个保持寄存器
* 参数                        :        pHandle:MODBUS句柄;SlaveAddr:从机地址;RegAddr:需读取的寄存器地址;RegNum:需要读取的寄存器数量
* 返回                        :        MRTU_ERROR:通信状态
* 依赖                        :        底层通信驱动
* 作者                        :        cp1300@139.com
* 时间                        :        2014-03-24
* 最后修改时间         :        2017-03-23
* 说明                        :         MOUEBUS RTU主机写单个寄存器成功后返回
                                        输入输出的数据都为小端模式
                                        注意:如果直接使用数据帧的寄存器数量以及地址,必须高地位交换
                                        2017-03-23:增加回调,抛离底层依赖
*************************************************************************************************************************/
MRTU_ERROR MODBUS_SLAVE_ReturnWriteMultHoldReg(MODBUS_HANDLE *pHandle, u8 SlaveAddr, u16 RegAddr, u8 RegNum)
{
        MRTU_WRIT_EMULT_RFRAME *pFrame;                                        //返回数据帧格式

        if(pHandle == NULL) return MRTU_HANDLE_ERROR;        //句柄无效
        if((pHandle->ID != MODBUS_INIT_ID)||(pHandle->pTxBuff==NULL)) return MRTU_HANDLE_ERROR;        //2016-04-11增加防止modbus未初始化异常
        if((RegNum > 127) || (RegNum == 0))        return MRTU_REGN_ERROR;        //寄存器数量错误
        if(pHandle->TxBuffSize < 8) return MRTU_OVER_ERROR;                        //检查发送缓冲区
        pFrame = (MRTU_WRIT_EMULT_RFRAME *)pHandle->pTxBuff;
        //数据结构填充
        pFrame->addr = SlaveAddr;                                                //从机地址
        pFrame->fun = MRTU_FUN_MWRITE;                                        //功能码,预置多个寄存器
        pFrame->StartReg = SWAP16(RegAddr);                                //寄存器起始地址
        pFrame->RegNum = SWAP16(RegNum);                                //寄存器数量
        pFrame->crc16 = MODBUS_CRC16(pHandle->pTxBuff, 6);        //计算CRC16
#if MODBUS_RTU_DBUG
        {
                u16 i;
               
                modbus_debug("\r\n<- MODBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",8,pFrame->crc16);
                for(i = 0;i < 8;i ++)
                {
                        modbus_debug("0x%02X ",pHandle->pTxBuff);
                }
                modbus_debug("\r\n");
        }
#endif        //MODBUS_RTU_DBUG
       
        pHandle->pSendData(pHandle->pTxBuff, 8);        //发送数据

        return MRTU_OK;                                                                //返回成功
}



使用特权

评论回复
13
tpgf|  楼主 | 2021-10-1 18:44 | 只看该作者
/*************************************************************************************************************************
* 函数                        :        void MODBUS_SLAVE_ReadUnpack(MODBUS_HANDLE *pHandle, u8 *pRxBuff, MRTU_SLAVE_INFO *pFrameInfo)
* 功能                        :        解析来自主机的读取寄存器命令
* 参数                        :        pHandle:MODBUS句柄;pRxBuff:接收缓冲区,长度不进行验证;pFrameInfo:解析的信息结构
* 返回                        :        MRTU_ERROR:通信状态
* 依赖                        :        底层通信驱动
* 作者                        :        cp1300@139.com
* 时间                        :        2014-11-17
* 最后修改时间         :         2017-03-23
* 说明                        :         用于将MODBUS的大端模式解析为小端模式
                                        支持 MRTU_FUN_READ_HOLD,MRTU_FUN_READ_INPUT 命令解析
                                        不进行接收数据长度验证,这个会在调用次函数前进行验证的
                                        2017-03-23:增加回调,抛离底层依赖
*************************************************************************************************************************/
void MODBUS_SLAVE_ReadUnpack(MODBUS_HANDLE *pHandle, u8 *pRxBuff, MRTU_SLAVE_INFO *pFrameInfo)
{
        MRTU_READ_FRAME *pReadRegFrame;                                                        //主机读取从机数据帧
       
        pReadRegFrame = (MRTU_READ_FRAME *)pRxBuff;
        pFrameInfo->SlaveAddr = pReadRegFrame->addr;                        //从机地址
        pFrameInfo->fun = pReadRegFrame->fun;                                        //功能码
        pFrameInfo->StartReg = SWAP16(pReadRegFrame->StartReg);        //寄存器起始地址
        pFrameInfo->RegNum = SWAP16(pReadRegFrame->RegNum);                //寄存器数量
}




/*************************************************************************************************************************
* 函数                        :        void MODBUS_SLAVE_WriteOneUnpack(MODBUS_HANDLE *pHandle, u8 *pRxBuff, MRTU_SLAVE_INFO *pFrameInfo, u16 *pData)
* 功能                        :        解析来自主机的预置单个寄存器命令
* 参数                        :        pHandle:MODBUS句柄;pRxBuff:接收缓冲区,长度不进行验证;pFrameInfo:解析的信息结构;pData:需要写入从机的值
* 返回                        :        MRTU_ERROR:通信状态
* 依赖                        :        底层通信驱动
* 作者                        :        cp1300@139.com
* 时间                        :        2014-11-17
* 最后修改时间         :         2014-11-17
* 说明                        :         用于将MODBUS的大端模式解析为小端模式
                                        支持 MRTU_FUN_WRITE 命令解析
                                        不进行接收数据长度验证,这个会在调用次函数前进行验证的
                                        2017-03-23:增加回调,抛离底层依赖
*************************************************************************************************************************/
void MODBUS_SLAVE_WriteOneUnpack(MODBUS_HANDLE *pHandle, u8 *pRxBuff, MRTU_SLAVE_INFO *pFrameInfo, u16 *pData)
{
        MRTU_WRITE_FRAME *pWriteRegFrame;                                                        //主机预置单个保持寄存器
       
        pWriteRegFrame = (MRTU_WRITE_FRAME *)pRxBuff;
        pFrameInfo->SlaveAddr = pWriteRegFrame->addr;                                //从机地址
        pFrameInfo->fun = pWriteRegFrame->fun;                                                //功能码
        pFrameInfo->StartReg = SWAP16(pWriteRegFrame->StartReg);        //寄存器起始地址
        pFrameInfo->RegNum = 1;                                                                                //寄存器数量
        *pData = SWAP16(pWriteRegFrame->RegData);                                        //需要写入的寄存器的值
}



使用特权

评论回复
14
tpgf|  楼主 | 2021-10-1 18:44 | 只看该作者
/*************************************************************************************************************************
* 函数                        :        void MODBUS_SLAVE_WriteMultUnpack(MODBUS_HANDLE *pHandle, u8 *pRxBuff, MRTU_SLAVE_INFO *pFrameInfo, u8 **pDataBuff)
* 功能                        :        解析来自主机的预置多个寄存器命令
* 参数                        :        pHandle:MODBUS句柄;pRxBuff:接收缓冲区,长度不进行验证;pFrameInfo:解析的信息结构;pDataBuff:原始的写入数据区(高低位交换过)
* 返回                        :        MRTU_ERROR:通信状态
* 依赖                        :        底层通信驱动
* 作者                        :        cp1300@139.com
* 时间                        :        2014-11-17
* 最后修改时间         :         2017-03-23
* 说明                        :         用于将MODBUS的大端模式解析为小端模式
                                        支持 MRTU_FUN_MWRITE 命令解析
                                        不进行接收数据长度验证,这个会在调用次函数前进行验证的
                                        2017-03-23:增加回调,抛离底层依赖
                                        2018-01-28:修改直接返回延时的数据包指针,降低内存消耗
*************************************************************************************************************************/
void MODBUS_SLAVE_WriteMultUnpack(MODBUS_HANDLE *pHandle, u8 *pRxBuff, MRTU_SLAVE_INFO *pFrameInfo, u8 **pDataBuff)
{
        MRTU_WRITE_MULT_FRAME *pWriteMultRegFrame;                                                //主机预置多个保持寄存器
        u8 MaxRegCnt;
        //u8 i;
       
        pWriteMultRegFrame = (MRTU_WRITE_MULT_FRAME *)pRxBuff;
        pFrameInfo->SlaveAddr = pWriteMultRegFrame->addr;                                //从机地址
        pFrameInfo->fun = pWriteMultRegFrame->fun;                                                //功能码
        pFrameInfo->StartReg = SWAP16(pWriteMultRegFrame->StartReg);        //寄存器起始地址
        pFrameInfo->RegNum = SWAP16(pWriteMultRegFrame->RegNum);                //寄存器数量
        //需要写入的寄存器的值
        MaxRegCnt = (pHandle->RxDataLen-8)/2;                                                                //计算理论最大写入的寄存器数量
        if(pFrameInfo->RegNum > MaxRegCnt) pFrameInfo->RegNum = MaxRegCnt;        //限制寄存器数量不能超过系统支持的数量
        *pDataBuff = pWriteMultRegFrame->DataBuff;                                                        //直接返回指针
       
        /*for(i = 0;i < pFrameInfo->RegNum;i ++)
        {
                pDataBuff = pWriteMultRegFrame->DataBuff[2*i];
                pDataBuff <<= 8;
                pDataBuff |= pWriteMultRegFrame->DataBuff[2*i+1];
        }*/       
       
}
#endif //MODBUS_RTU_SLAVE



/*******************************************************************************
*函数名称:CRC16
*函数功能:CRC16效验函数
*参数说明:*p效验帧的指针   帧长 datalen ,除了校验位
*返 回 值:效验字
*注意事项:多项式码0xA001,输出结果为大端模式
*******************************************************************************/
u16 MODBUS_CRC16(unsigned char *p, unsigned short datalen)
{
    unsigned char CRC16Lo,CRC16Hi,CL,CH,SaveHi,SaveLo;
    unsigned short i,Flag;

    CRC16Lo = 0xFF;     CRC16Hi = 0xFF;
    CL = 0x01;          CH = 0xA0;
    for(i = 0;i < datalen; i++)
    {
        CRC16Lo ^= *(p+i);                  //每一个数据与CRC寄存器进行异或
        for(Flag = 0; Flag < 8; Flag++)
        {
            SaveHi = CRC16Hi;  SaveLo = CRC16Lo;
            CRC16Hi >>= 1; CRC16Lo >>= 1;   //高位右移一位,低位右移一位
            if((SaveHi & 0x01) == 0x01)     //如果高位字节最后一位为1
            CRC16Lo  |=0x80 ;                             //则低位字节右移后前面补1否则自动补0
            if((SaveLo & 0x01) == 0x01)     //如果LSB为1,则与多项式码进行异或
            { CRC16Hi ^= CH;  CRC16Lo ^= CL; }
        }
    }
    return (unsigned short)(CRC16Hi<<8)|CRC16Lo;
}



使用特权

评论回复
15
tpgf|  楼主 | 2021-10-1 18:44 | 只看该作者

/*
//MODBUS CRC16计算
//结果为大端模式
BIG_U16 MODBUS_CRC16( u8 * pucFrame, u16 usLen )
{
        static const u8 aucCRCHi[] = {
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40
        };
        static const u8 aucCRCLo[] = {
    0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
    0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
    0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,
    0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
    0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
    0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
    0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
    0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
    0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,
    0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
    0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
    0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
    0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB,
    0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,
    0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
    0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
    0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,
    0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,
    0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89,
    0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
    0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
    0x41, 0x81, 0x80, 0x40
        };
    u8           ucCRCHi = 0xFF;
    u8           ucCRCLo = 0xFF;
    int             iIndex;
       
    while( usLen-- )
    {
        iIndex = ucCRCLo ^ *( pucFrame++ );
        ucCRCLo = ( u8 )( ucCRCHi ^ aucCRCHi[iIndex] );
        ucCRCHi = aucCRCLo[iIndex];
    }
    return ( u16 )( ucCRCHi << 8 | ucCRCLo );
}
*/


使用特权

评论回复
16
tpgf|  楼主 | 2021-10-1 18:44 | 只看该作者
/*************************************************************************************************************************
* 函数        :        void MODBUS_32TO16(u16 *Out16H, u16 *Out16L, u32 In32)
* 功能        :        将32bit数据拆分为高低16位,并且使用大端模式,兼容MODBUS
* 参数        :        Out16H:拆分的高16位,大端模式;Out16L:拆分的低16位,大端模式;In32:需要拆分的数据,小端模式,兼容STM32
* 返回        :        无
* 依赖        :        无
* 作者        :        cp1300@139.com
* 时间        :        2014-05-27
* 最后修改时间 : 2014-05-27
* 说明        :         将STM32 32位数据拆分为兼容MODBUS 大端模式
*************************************************************************************************************************/
void MODBUS_32TO16(u16 *Out16H, u16 *Out16L, u32 In32)
{
        *Out16H = SWAP16(In32 >> 16);
        *Out16L = SWAP16(In32 & 0xffff);
}


/*************************************************************************************************************************
* 函数        :        u32 MODBUS_16TO32(u16 In16H, u16 In16L)
* 功能        :        将MODBUS高低16位转换为小端模式的32位数
* 参数        :        In16H:大端模式的高16位数;In16L:大端模式的低16位数
* 返回        :        32bit的整形数据
* 依赖        :        无
* 作者        :        cp1300@139.com
* 时间        :        2014-05-27
* 最后修改时间 : 2014-05-27
* 说明        :         将MODBUS的2个16bit寄存器组成一个兼容STM32的32bit整形数
*************************************************************************************************************************/
u32 MODBUS_16TO32(u16 In16H, u16 In16L)
{
        u32 temp;
       
        temp = SWAP16(In16H);
        temp <<= 16;
        temp |= SWAP16(In16L);
       
        return temp;
}



使用特权

评论回复
17
tpgf|  楼主 | 2021-10-1 18:45 | 只看该作者
//modbus_rtu.h


/*************************************************************************************************************
* 文件名:                MODBUS_RTU.c
* 功能:                MODBUS_RTU通信协议层
* 作者:                cp1300@139.com
* 创建时间:        2014-03-24
* 最后修改时间:2015-07-02
* 详细:                MODBUS RTU通信协议层
                                2015-04-27:添加发送延时,防止通信帧结束时产生干扰
                                2017-03-23:增加回调,抛离底层依赖
                                2018-01-28:修改直接返回延时的数据包指针,降低内存消耗
*************************************************************************************************************/
#ifndef _MODBUS_RTU_H_
#define _MODBUS_RTU_H_
#include "typedef.h"


/***********************配置相关************************/
#define MODBUS_RTU_HOST                        1                        //1:开启主机模式;0:关闭主机模式
#define MODBUS_RTU_SLAVE                1                        //1:开启从机模式;0:关闭从机模式
#define MODBUS_BROAD_ADDR                255                        //广播地址,不带返回
#define MODBUS_MIN_RX_BYTE                4                        //定义最少接收字符数量建议设置为4
/*********************************************************/


//16位整形数高低对调
#define SWAP16(x)   (((x & 0xff00) >> 8) | ((x & 0xff) << 8))

       
//支持的功能码
#define MRTU_FUN_READ_HOLD                0x03                        //读保持寄存器,可读写寄存器为保持寄存器
#define MRTU_FUN_READ_INPUT                0x04                        //读输入寄存器,为只读寄存器
#define MRTU_FUN_WRITE                        0x06                        //写单个保持寄存器
#define MRTU_FUN_MWRITE                        0x10                        //写多个保持寄存器


//大端数据标记
#define BIG_U16                u16                                                        //16位整形数,需要转换为大端模式,兼容modubus


//读取寄存器类型选择
typedef enum
{
        HOLD_REG         =         MRTU_FUN_READ_HOLD,                        //保持寄存器
        INPUT_REG        =        MRTU_FUN_READ_INPUT,                //输入寄存器
} READ_REG_TYPE;


//数据读取 主机数据帧,主机读取从机的数据帧
#pragma pack(8)
typedef  struct
{
        u8        addr;                                //地址 address
        u8        fun;                                //功能码 function
        BIG_U16        StartReg;                //数据起始地址
        BIG_U16        RegNum;                        //需要读取的寄存器个数
        BIG_U16        CRC16;                        //CRC16
} MRTU_READ_FRAME;                        //MODBUS RTU master Read Reg Frame



//预置单个保持寄存器,主机写从机单个寄存器的数据帧
//从机返回数据帧与主机预置单个寄存器数据帧一样
#pragma pack(8)
typedef  struct
{
        u8        addr;                                //地址 address
        u8        fun;                                //功能码 function
        BIG_U16        StartReg;                //数据起始地址
        BIG_U16        RegData;                //数据值
        BIG_U16 crc16;                        //CRC校验值
} MRTU_WRITE_FRAME;                        //MODBUS RTU master Write Reg Frame





//预置多个保持寄存器,主机写从机多个寄存器的数据帧
#pragma pack(8)
typedef  struct
{
        u8        addr;                                //地址 address
        u8        fun;                                //功能码 function
        BIG_U16        StartReg;                //数据起始地址
        BIG_U16        RegNum;                        //寄存器数量
        u8        DataLen;                        //数据长度
        u8        DataBuff[2];                //寄存器的值       
} MRTU_WRITE_MULT_FRAME;                       


//预置多个保持寄存器后返回数据帧,从机返回主机的数据帧
#pragma pack(8)
typedef  struct
{
        u8        addr;                                //地址 address
        u8        fun;                                //功能码 function
        BIG_U16        StartReg;                //数据起始地址
        BIG_U16        RegNum;                        //寄存器数量
        BIG_U16 crc16;                        //CRC校验值
} MRTU_WRIT_EMULT_RFRAME;                       


//读取从机返回数据帧格式,从机返回给主机的数据帧
#pragma pack(8)
typedef  struct
{
        u8        addr;                                //地址 address
        u8        fun;                                //功能码 function
        u8        DataLen;                        //数据长度
        u8        DataBuff[2];                //数据区,CRC16放在最后结尾处
        //MRTU_REG16        CRC16;        //CRC16
} MRTU_RETURN_FRAME;        //MODBUS RTU master Read Reg Frame


//从机返回的异常数据帧,从机返回的异常数据帧
#pragma pack(8)
typedef  struct
{
        u8        addr;                                //地址 address
        u8        ErrorFun;                        //错误功能码 function+0x80
        u8        unu;                                //异常码
        u8        crc16H;                                //CRC16放在最后结尾处
        u8        crc16L;                                //CRC16放在最后结尾处
} MRTU_UNU_FRAME;       


//从机数据包解析后的相关信息
typedef struct
{
        u8        SlaveAddr;        //主机发送的从机地址
        u8         RegNum;                //主机需要读取从机的寄存器数量
        u8        fun;                //主机发送给从机的功能码
        u16 StartReg;        //主机需要读写的从机寄存器地址
} MRTU_SLAVE_INFO;


//异常码定义
typedef enum
{
        MRTU_NOT_UNUS        =        0x00,        //无异常
        MRTU_UNUS1                =        0x01,        //异常码1,无效的操作码
        MRTU_UNUS2                =        0x02,        //异常码2,无效的数据地址
        MRTU_UNUS3                =        0x03,        //异常码3,无效的数据值
        MRTU_UNUS4                =        0x04,        //异常码4,无效操作
        MRTU_UNUS5                =        0x05,        //异常码5
        MRTU_UNUS6                =        0x06,        //异常码6
} MRTU_UNUS;


//错误状态
typedef enum
{
        MRTU_OK                                 =         0,                //OK
        MRTU_TIME_OUT                         =         1,                //超时
        MRTU_OVER_ERROR                 =         2,                //溢出,缓冲区大小不足
        MRTU_CRC_ERROR                        =        3,                //CRC错误
        MRTU_ADDR_ERROR                        =        4,                //地址错误,返回地址不一致
        MRTU_REG_ERROR                        =        5,                //寄存器地址错误,返回寄存器地址不一致
        MRTU_FUNR_ERROR                        =        6,                //功能码错误,返回功能码不一致或者不支持的功能码
        MRTU_HANDLE_ERROR                =        7,                //句柄错误,句柄为空
        MRTU_REGN_ERROR                        =        8,                //寄存器数量错误
        MRTU_LEN_ERROR                        =        9,                //返回数据长度错误
        MRTU_WRITE_ERROR                =        10,                //写寄存器错误,写入与读取不一致
        MRTU_UNUS1_ERROR                =        0x81,        //异常码1,无效的操作码
        MRTU_UNUS2_ERROR                =        0x82,        //异常码2,无效的数据地址
        MRTU_UNUS3_ERROR                =        0x83,        //异常码3,无效的数据值
        MRTU_UNUS4_ERROR                =        0x84,        //异常码4,无效操作
        MRTU_UNUS5_ERROR                =        0x85,        //异常码5
        MRTU_UNUS6_ERROR                =        0x86,        //异常码6
        MRTU_OTHER_ERROR = 0xff
} MRTU_ERROR;


//MODBUS句柄结构
typedef struct
{
        bool (* pSendData)(u8 *pDataBuff, u16 DataLen);                        //发送回调指针,        发送数据回调函数
        int (* pReadData)(u8 **pDataBuff, u8 ByteTimeOut, u16 TimeOut, u16 *pReceiveDelay);        //读取数据回调指针,>0返回接收到的数据长度,否则失败
        void (*pClearRxData)(void);                                                                //清除接收数据缓冲区
        void (*pDelayMS)(u8 ms);                                                                //延时接口
        MRTU_UNUS (*pSingleRegReadWriteCallBack)(u16 RegAddr,u16 *pRegData,READ_REG_TYPE RegType, bool isReadReg);        //单个从机寄存器读写回调函数(RegAddr:寄存器地址;pRegData:输入输出寄存器值;RegType:寄存器类型;isReadReg读取还是写入)
        u8 *pTxBuff;                                                                                        //发送缓冲区
        u32        WriteRegCnt;                                                                                //写入寄存器次数
        u32 ReadRegCnt;                                                                                        //读取寄存器次数
        u32 ID;                                                                                                        //标记ID,如果标记不合法,则认为没有初始化,直接退出
        u16 TxBuffSize;                                                                                        //发送缓冲区大小
        u16 RxTimeOutMs;                                                                                //通信接收超时的时间,单位ms
        u16 ReturnTimeMs;                                                                                //数据返回时间,单位ms,用于记录从机返回数据的延时时间,只在主机模式下有效
        u16 TxByteTimeUs;                                                                                //发送1个字节的时间,单位us(用于RS485收发切换,特别是发送后的切换延时)
        u16 RxDataLen;                                                                                        //记录当前接收的数据长度-从机模式下放置写入或读取超过内存限制的数据
        u8        SlaveAddr;                                                                                        //从机地址,只对从机有效
} MODBUS_HANDLE;

#define MODBUS_INIT_ID        0x128497AC                                                        //MODBUS初始化标记


//初始化MODBUS
bool MODBUS_Init(MODBUS_HANDLE *pHandle,u8 *pTxBuff, u16 TxBuffSize, u16 TxByteTimeUs, u16 RxTimeOutMs,
                                bool (* pSendData)(u8 *pDataBuff, u16 DataLen),
                                int (* pReadData)(u8 **pDataBuff, u8 ByteTimeOut, u16 TimeOut, u16 *pReceiveDelay),
                                void (*pClearRxData)(void),
                                void (*pDelayMS)(u8 ms));                                                                //延时接口
#if(MODBUS_RTU_HOST) //开启主机模式
MRTU_ERROR MODBUS_HOST_ReadOneReg(MODBUS_HANDLE *pHandle, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u16 *pRegData);                                        //主机读取从机指定单个寄存器
MRTU_ERROR MODBUS_HOST_ReadMultReg(MODBUS_HANDLE *pHandle, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[]);        //主机读取从机多个指定寄存器
MRTU_ERROR MODBUS_HOST_WriteOneReg(MODBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u16 RegData);                                                                                        //主机写从机单个保持寄存器
MRTU_ERROR MODBUS_HOST_WriteMultReg(MODBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[]);                                                        //主机写从机单个保持寄存器
#endif


#if(MODBUS_RTU_SLAVE) //开启从机模式
MRTU_ERROR MODBUS_SLAVE_FramesUnpack(MODBUS_HANDLE *pHandle,u8 SlaveAddr,u8 *pRxBuff, u16 DataLen, u8 *pFun);                                                                //从机解析数据包
bool MODBUS_SLAVE_RetrunUnu(MODBUS_HANDLE *pHandle,u8 SlaveAddr, u8 Fun, MRTU_UNUS Unus);                                                                                                        //从机返回异常码
MRTU_ERROR MODBUS_SLAVE_ReturnReadReg(MODBUS_HANDLE *pHandle, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u8 RegNum);                                        //从机返回主机读取的寄存器
MRTU_ERROR MODBUS_SLAVE_ReturnWriteOneHoldReg(MODBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u16 RegData);                                                                //从机返回主机写入单个寄存器命令
MRTU_ERROR MODBUS_SLAVE_ReturnWriteMultHoldReg(MODBUS_HANDLE *pHandle, u8 SlaveAddr, u16 RegAddr, u8 RegNum);                                                                //从机返回主机写多个寄存器命令
void MODBUS_SLAVE_ReadUnpack(MODBUS_HANDLE *pHandle, u8 *pRxBuff, MRTU_SLAVE_INFO *pFrameInfo);                                                                                                //从机解析主机读取命令
void MODBUS_SLAVE_WriteOneUnpack(MODBUS_HANDLE *pHandle, u8 *pRxBuff, MRTU_SLAVE_INFO *pFrameInfo, u16 *pData);                                                                //从机解析主机写单个寄存器命令
void MODBUS_SLAVE_WriteMultUnpack(MODBUS_HANDLE *pHandle, u8 *pRxBuff, MRTU_SLAVE_INFO *pFrameInfo, u8 **pDataBuff);                                                //从机解析主机多个寄存器命令
#endif


u16 MODBUS_CRC16(unsigned char *p, unsigned short datalen);                                //crc计算
void MODBUS_32TO16(u16 *Out16H, u16 *Out16L, u32 In32);                                        //将32bit数据拆分为高低16位,并且使用大端模式,兼容MODBUS
u32 MODBUS_16TO32(u16 In16H, u16 In16L);                                                                //将MODBUS高低16位转换为小端模式的32位数
       
#endif /*_MODBUS_RTU_H_*/

使用特权

评论回复
18
tpgf|  楼主 | 2021-10-1 18:45 | 只看该作者
//MODBUS_SLAVE.c


/*************************************************************************************************************
* 文件名:                        MODBUS_SLAVE.c
* 功能:                        MODBUS_SLAVE 子机通信处理
* 作者:                        cp1300@139.com
* 创建时间:                2015-02-01
* 最后修改时间:        2014-02-01
* 详细:                        MODBUS RTU 子机通信处理
                                        2017-03-24:底层通信解耦,全部使用回调
                                        2018-01-27:增加底层读写寄存器回调
*************************************************************************************************************/
#include "typedef.h"
#include "stdio.h"
#include "MODBUS_RTU.h"
#include "MODBUS_SLAVE.h"

#if(MODBUS_RTU_SLAVE)        //支持从机
//调试开关
#define MODBUS_SLAVE_DBUG        0
#if MODBUS_SLAVE_DBUG
        #include "system.h"
        #define slave_debug(format,...)        uart_printf(format,##__VA_ARGS__)
#else
        #define slave_debug(format,...)        /\
/

#endif        //MODBUS_SLAVE_DBUG


/*************************************************************************************************************************
* 函数                        :        void MODBUS_SLAVE_Init(MODBUS_HANDLE *pHandle, u8 SlaveAddr,
                                                MRTU_UNUS *pSingleRegReadWriteCallBack(u16 RegAddr,u16 *pRegData,READ_REG_TYPE RegType, bool isReadReg))
* 功能                        :        从机模式初始化
* 参数                        :        pHandle:MODBUS句柄;SlaveAddr:从机地址
                                        pSingleRegReadWriteCallBack:单个从机寄存器读写回调函数(RegAddr:寄存器地址;pRegData:输入输出寄存器值;RegType:寄存器类型;isReadReg读取还是写入)
* 返回                        :        无
* 依赖                        :        底层通信驱动
* 作者                        :        cp1300@139.com
* 时间                        :        2014-11-17
* 最后修改时间                 :         2017-03-23
* 说明                        :         2017-03-23:增加回调,抛离底层依赖
                                        2018-01-27:增加底层读写寄存器回调
*************************************************************************************************************************/
void MODBUS_SLAVE_Init(MODBUS_HANDLE *pHandle, u8 SlaveAddr,
        MRTU_UNUS (*pSingleRegReadWriteCallBack)(u16 RegAddr,u16 *pRegData,READ_REG_TYPE RegType, bool isReadReg))
{
        MODBUS_SLAVE_SetAddr(pHandle, SlaveAddr);                                                                //设置通信监听地址
        pHandle->pSingleRegReadWriteCallBack = pSingleRegReadWriteCallBack;                //单个寄存器读写回调函数
}



/*************************************************************************************************************************
* 函数                        :        void MODBUS_SLAVE_SetAddr(MODBUS_HANDLE *pHandle, u8 SlaveAddr)
* 功能                        :        设置从机通信地址
* 参数                        :        pHandle:句柄;SlaveAddr:从机地址
* 返回                        :        无
* 依赖                        :        底层通信驱动
* 作者                        :        cp1300@139.com
* 时间                        :        2014-11-17
* 最后修改时间         :         2017-03-23
* 说明                        :         2017-03-23:增加回调,抛离底层依赖
*************************************************************************************************************************/
void MODBUS_SLAVE_SetAddr(MODBUS_HANDLE *pHandle, u8 SlaveAddr)
{
        pHandle->SlaveAddr = SlaveAddr;        //设置从机通信地址
}




使用特权

评论回复
19
tpgf|  楼主 | 2021-10-1 18:45 | 只看该作者
/*************************************************************************************************************************
* 函数                        :        bool MODBUS_SLAVE_Handle(MODBUS_HANDLE *pHandle, u8 *pRxData, u16 DataLen)
* 功能                        :        MODBUS 从机通信处理
* 参数                        :        pHandle:句柄;pRxData:接收数据缓冲区;DataLen:接收数据长度
* 返回                        :        TRUE:数据包有效;FALSE:无效
* 依赖                        :        底层通信驱动
* 作者                        :        cp1300@139.com
* 时间                        :        2014-11-17
* 最后修改时间                 :         2017-03-23
* 说明                        :         2016-02-19:增加写入的保持寄存器地址记录
                                        2017-03-23:增加回调,抛离底层依赖
*************************************************************************************************************************/
bool MODBUS_SLAVE_Handle(MODBUS_HANDLE *pHandle, u8 *pRxData, u16 DataLen)
{
        MRTU_SLAVE_INFO FrameInfo;                        //MODBUS读写信息
        u8 Fun;
        bool Status = TRUE;
        u8 i;

        if(MODBUS_SLAVE_FramesUnpack(pHandle, pHandle->SlaveAddr, pRxData, DataLen, &Fun) == MRTU_OK)
        {
                switch(Fun)
                {
                        case MRTU_FUN_READ_HOLD:        //读保持寄存器
                        {
                                MODBUS_SLAVE_ReadUnpack(pHandle, pRxData, &FrameInfo);        //解析读取
                                //如果是广播地址则忽略
                                if(FrameInfo.SlaveAddr == MODBUS_BROAD_ADDR)                       
                                {
                                        slave_debug("广播地址不响应(读取保持寄范围%d~%d)!\r\n",FrameInfo.StartReg, FrameInfo.StartReg + FrameInfo.RegNum);
                                        break;
                                }
                                //返回保持寄存器
                                MODBUS_SLAVE_ReturnReadReg(pHandle, HOLD_REG, FrameInfo.SlaveAddr, FrameInfo.StartReg, FrameInfo.RegNum);
                        }break;
                       
                        case MRTU_FUN_READ_INPUT:        //读输入寄存器
                        {
                                MODBUS_SLAVE_ReadUnpack(pHandle, pRxData, &FrameInfo);                        //解析读取
                                if(FrameInfo.SlaveAddr == MODBUS_BROAD_ADDR)                                        //如果是广播地址则忽略
                                {
                                        slave_debug("广播地址不响应(读取输入寄范围%d~%d)!\r\n",FrameInfo.StartReg, FrameInfo.StartReg + FrameInfo.RegNum);
                                        break;        //广播地址不响应
                                }
                                //返回输入寄存器
                                MODBUS_SLAVE_ReturnReadReg(pHandle, INPUT_REG, FrameInfo.SlaveAddr, FrameInfo.StartReg, FrameInfo.RegNum);
                        }break;
                       
                        case MRTU_FUN_WRITE:                                                                                                //写单个保持寄存器
                        {
                                u16 RegData;
                                MRTU_UNUS Unus;
                               
                                MODBUS_SLAVE_WriteOneUnpack(pHandle, pRxData, &FrameInfo, &RegData);//解析要写入的寄存器
                               
                                //循环写入要返回的数据
                                for(i = 0;i < 1;i ++)
                                {
                                        Unus = pHandle->pSingleRegReadWriteCallBack(FrameInfo.StartReg+i,&RegData, HOLD_REG, FALSE);                                //调用回调,写入数据
                                        switch(Unus)
                                        {
                                                case MRTU_NOT_UNUS        : break;//无异常
                                                default:                                        //有异常则返回对应的异常码,并退出通信
                                                {
                                                        if(FrameInfo.SlaveAddr == MODBUS_BROAD_ADDR)                        //如果是广播地址则忽略
                                                        {
                                                                slave_debug("广播地址不响应(写入寄存器%d)!\r\n",FrameInfo.StartReg);
                                                                break;        //广播地址不响应
                                                        }
                                                        MODBUS_SLAVE_RetrunUnu(pHandle, FrameInfo.SlaveAddr,  HOLD_REG, Unus);                                                                //返回异常码
                                                        switch (Unus)
                                                        {
                                                                default: return FALSE;                                        //错误
                                                        }
                                                }
                                        }
                                }
                                if(FrameInfo.SlaveAddr == MODBUS_BROAD_ADDR)                        //如果是广播地址则忽略
                                {
                                        slave_debug("广播地址不响应(写入寄存器%d)!\r\n",FrameInfo.StartReg);
                                }
                                else MODBUS_SLAVE_ReturnWriteOneHoldReg(pHandle, FrameInfo.SlaveAddr, FrameInfo.StartReg, RegData);
                        }break;
                        case MRTU_FUN_MWRITE:                                                //写多个保持寄存器
                        {
                                u8 *pRegBuff;
                                u16 RegData;
                                MRTU_UNUS Unus;
                                u8 i;
                               
                                MODBUS_SLAVE_WriteMultUnpack(pHandle, pRxData, &FrameInfo, &pRegBuff);        //解析要写入的寄存器
                                //循环写入要返回的数据
                                for(i = 0;i < FrameInfo.RegNum;i ++)
                                {
                                        //一个一个的读取寄存器的值
                                        RegData = pRegBuff[2*i];
                                        RegData <<= 8;
                                        RegData |= pRegBuff[2*i+1];
                                        Unus = pHandle->pSingleRegReadWriteCallBack(FrameInfo.StartReg+i,&RegData, HOLD_REG, FALSE);                                //调用回调,写入数据
                                        switch(Unus)
                                        {
                                                case MRTU_NOT_UNUS        : break;//无异常
                                                default:                                        //有异常则返回对应的异常码,并退出通信
                                                {
                                                        if(FrameInfo.SlaveAddr == MODBUS_BROAD_ADDR)                        //如果是广播地址则忽略
                                                        {
                                                                slave_debug("广播地址不响应(写入寄存器%d)!\r\n",FrameInfo.StartReg);
                                                                break;        //广播地址不响应
                                                        }
                                                        MODBUS_SLAVE_RetrunUnu(pHandle, FrameInfo.SlaveAddr,  HOLD_REG, Unus);                                                                //返回异常码
                                                        switch (Unus)
                                                        {
                                                                default: return FALSE;                                        //错误
                                                        }
                                                }
                                        }
                                }

                                if(FrameInfo.SlaveAddr == MODBUS_BROAD_ADDR)                        //如果是广播地址则忽略
                                {
                                        slave_debug("广播地址不响应(写入寄存器%d)!\r\n",FrameInfo.StartReg);
                                }
                                else MODBUS_SLAVE_ReturnWriteMultHoldReg(pHandle, FrameInfo.SlaveAddr, FrameInfo.StartReg,FrameInfo.RegNum);       
                        }break;
                       
                        default: Status = FALSE; break;                                //其它命令,不返回
                }
        }
        else
        {
                 Status = FALSE;
        }
       
        return Status;
}
#endif


使用特权

评论回复
20
tpgf|  楼主 | 2021-10-1 18:45 | 只看该作者
//MODBUS_SLAVE.h


/*************************************************************************************************************
* 文件名:                        MODBUS_SLAVE.h
* 功能:                        MODBUS_SLAVE 子机通信处理
* 作者:                        cp1300@139.com
* 创建时间:                2015-02-01
* 最后修改时间:        2014-02-01
* 详细:                        MODBUS RTU 子机通信处理
                                        2018-01-27:增加底层读写寄存器回调
*************************************************************************************************************/
#ifndef _MODBUS_SLAVE_H_
#define _MODBUS_SLAVE_H_
#include "typedef.h"
#include "MODBUS_RTU.h"



#if(MODBUS_RTU_SLAVE)        //支持从机

//初始化
void MODBUS_SLAVE_Init(MODBUS_HANDLE *pHandle, u8 SlaveAddr,                                                                                                        //从机模式初始化
        MRTU_UNUS (*pSingleRegReadWriteCallBack)(u16 RegAddr,u16 *pRegData,READ_REG_TYPE RegType, bool isReadReg));        //单个从机寄存器读写回调函数(RegAddr:寄存器地址;pRegData:输入输出寄存器值;RegType:寄存器类型;isReadReg读取还是写入));
void MODBUS_SLAVE_SetAddr(MODBUS_HANDLE *pHandle, u8 SlaveAddr);                                                                                                //设置从机通信地址
bool MODBUS_SLAVE_Handle(MODBUS_HANDLE *pHandle, u8 *pRxData, u16 DataLen);                                                                                //MODBUS 从机通信处理

#endif        //#if(MODBUS_RTU_SLAVE)        //支持从机
#endif /*_MODBUS_SLAVE_H_*/

使用特权

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

本版积分规则

2028

主题

15903

帖子

14

粉丝