标准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;
}
}
}
//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_ */
//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;
}
#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[i]);
}
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[i]);
}
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; //返回成功
}
/*************************************************************************************************************************
* 函数 : 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[i]);
}
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[i]);
}
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[i] = pReFrame->DataBuff[i*2];
pRegData[i] <<= 8;
pRegData[i] |= pReFrame->DataBuff[i*2+1];
}
pHandle->ReadRegCnt ++; //读取寄存器次数增加
return MRTU_OK; //返回成功
}
/*************************************************************************************************************************
* 函数 : 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[i]);
}
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[i]);
}
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; //返回成功
}
/*************************************************************************************************************************
* 函数 : 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[i]>>8; //高位
pFrame->DataBuff[2*i+1] = pRegData[i]&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[i]);
}
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[i]);
}
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
#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[i]);
}
modbus_debug("\r\n");
}
#endif //MODBUS_RTU_DBUG
pHandle->pSendData(pHandle->pTxBuff, 5); //发送数据
return TRUE;
}
/*************************************************************************************************************************
* 函数 : 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[i]);
}
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校验错误
}
}
/*************************************************************************************************************************
* 函数 : 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[i]);
}
modbus_debug("\r\n");
}
#endif //MODBUS_RTU_DBUG
pHandle->pSendData(pHandle->pTxBuff, 3+pFrame->DataLen+2); //发送数据
return MRTU_OK; //返回成功
}
/*************************************************************************************************************************
* 函数 : 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[i]);
}
modbus_debug("\r\n");
}
#endif //MODBUS_RTU_DBUG
pHandle->pSendData(pHandle->pTxBuff, 8); //发送数据
return MRTU_OK; //返回成功
}
/*************************************************************************************************************************
* 函数 : 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[i]);
}
modbus_debug("\r\n");
}
#endif //MODBUS_RTU_DBUG
pHandle->pSendData(pHandle->pTxBuff, 8); //发送数据
return MRTU_OK; //返回成功
}
/*************************************************************************************************************************
* 函数 : 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); //需要写入的寄存器的值
}
/*************************************************************************************************************************
* 函数 : 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[i] = pWriteMultRegFrame->DataBuff[2*i];
pDataBuff[i] <<= 8;
pDataBuff[i] |= 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;
}
/*
//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 );
}
*/
/*************************************************************************************************************************
* 函数 : 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;
}
//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_*/
//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; //设置从机通信地址
}
/*************************************************************************************************************************
* 函数 : 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
//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_*/
//测试线程
/*
* UartThread.c
*
* Created on: 2018年8月5日
* Author: cfan
*/
#include "UserThread.h"
#include "Uart.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include "modbus_rtu.h"
#include "SystemLog.h"
#include "modbus_rtu.h"
MODBUS_HANDLE g_ModbusHandle; //modbus句柄
UART_HANDLE g_UartHandle1; //串口1句柄
#define MODBUS_BUFF_SIZE 512
static u8 ModbusBuff[MODBUS_BUFF_SIZE];
//MODBUS1发送数据接口
bool MODBUS1_SendData(u8 *pDataBuff, u16 DataLen)
{
if(UART_SendData(&g_UartHandle1, pDataBuff, DataLen) <= 0) return FALSE;
usleep(1000);
return TRUE;
}
//MODBUS1接收数据接口
int MODBUS1_ReadData(u8 **pDataBuff, u8 ByteTimeOut, u16 TimeOut, u16 *pReceiveDelay)
{
u8 *p = ModbusBuff;
u32 cnt = 0;
u32 TimeOutCnt;
u32 i;
int len;
u16 ReceiveDelay = 0;
if(ByteTimeOut < 1) ByteTimeOut = 1;
TimeOutCnt = TimeOut/ByteTimeOut+1;
for(i = 0;i < TimeOutCnt;i ++)
{
usleep((u32)ByteTimeOut*1000); //字节延时-帧间隔延时
len = UART_ReadData(&g_UartHandle1, &p[cnt], MODBUS_BUFF_SIZE-1-cnt); //读取数据
//if(len < 0) return -1; //读取错误-非阻塞没有读取到数据也会返回-1,此处不做处理
if(len < 0)len = 0;
cnt += len; //加上收到的数据
ReceiveDelay += ByteTimeOut; //记录接收延时
if((cnt >= (MODBUS_BUFF_SIZE-1)) || ((cnt > 0) && (len==0))) //接收完成-接收缓冲区满或 接收数据长度大于0,并且当前周期没有接收到数据则认为接收完成
{
*pDataBuff = p; //返回接收数据指针
if(pReceiveDelay != NULL) *pReceiveDelay = ReceiveDelay;//返回接收延时
return cnt;
}
else if((cnt > 0) && (len>0)) //如果一直有数据返回,则等待缓冲区满为止
{
if(i > 1) i --;
}
}
return 0;
}
//清除MODBUS1接收数据接口
void MODBUS1_ClearRxData(void)
{
UART_ClearRxData(&g_UartHandle1);
}
//ms延时接口
void DelayMS(u8 ms)
{
usleep(1000*ms);
}
//串口线程
void *UartThread(void *arg)
{
int error;
u16 RegData[16];
printf("UartThread 启动...\r\n");
error = UART_Init(&g_UartHandle1, "/dev/ttyS1", UART_B115200); //初始化串口
if(error != 0)
{
plog("%s(%s) 串口初始化失败,错误%d.\r\n",__FILE__, __LINE__, error);
return NULL;
}
//初始化MODBUS
if(MODBUS_Init(&g_ModbusHandle, ModbusBuff, 128-1, 10, 500,
MODBUS1_SendData, MODBUS1_ReadData, MODBUS1_ClearRxData, DelayMS) == FALSE) //延时接口
{
printf("初始化Modbus协议栈失败!\r\n");
plog("%s(%s) 初始化Modbus协议栈失败.\r\n",__FILE__, __LINE__);
}
while(1)
{
error = MODBUS_HOST_ReadMultReg(&g_ModbusHandle, HOLD_REG, 1, 0, 8, RegData);
if(error == MRTU_OK) //读取成功
{
printf("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t\r\n",RegData[0],RegData[1],RegData[2],RegData[3],RegData[4],RegData[5],RegData[6],RegData[7]);
}
else
{
//printf("MODBUS读取错误:%d\r\n",error);
}
sleep(3);
}
}
//UserThread.h
/*
* UserThread.h
*
* Created on: 2018年8月5日
* Author: cfan
*/
#ifndef USERTHREAD_USERTHREAD_H_
#define USERTHREAD_USERTHREAD_H_
void *UartThread(void *arg); //串口线程
#endif /* USERTHREAD_USERTHREAD_H_ */
示例代码:https://download.youkuaiyun.com/download/cp1300/10611732