linux 串口modbus操作

本文介绍了一种在Linux环境下实现标准Modbus-RTU协议的方法,重点讲解了串口读写接口的设置过程及MODBUS从机通信处理流程。通过具体的代码示例展示了如何进行串口初始化、数据发送与接收等关键步骤。

标准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

评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cp1300

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值