
#include "modbus_m.h"

#ifdef MODBUS_M

#include "inout.h"
#include "delay.h"
#include "trigger.h"

#ifndef MODBUS_M_NUM
#define MODBUS_M_NUM		1
#endif

//-----------------------------------------------------------------
// 控制结构
ModbusCtrl g_modBusMCtrl[MODBUS_M_NUM];

//-----------------------------------------------------------------




/*-----------------------------------------------------------------
功能:初始化 Modbus 主设备串口
midx :		主站序号
baud :		波特率
dat:		数据长度
parity:		校验位
stop:		停止位
mode:		数据模式
*/

void InitModbus_m(int midx, int usart, BAUD_TypeDef baud, char dat, char parity, char stop, char mode)
{
	if (midx < 0 || midx >= MODBUS_M_NUM)
	{
		printf("init modbus_m, idx error\r\n");
		return;
	}

	memset(&g_modBusMCtrl[midx], 0, sizeof(ModbusCtrl));

	if (mode == 'A'	|| mode == 'a')
	{
		g_modBusMCtrl[midx].modBusMode = 1;
	}
	else
	{
		g_modBusMCtrl[midx].modBusMode = 0;
	}

	// 延时
	switch(baud)
	{
		// 数据位 + 1个起始位 + 奇偶校验位(无校验则没有) + 停止位
		// 根据波特率来计算 通讯延时, 单位us = 10000000 / baud(按照一个字节10位计算, 8 N 1)
		case B4800:
		{
			g_modBusMCtrl[midx].modBusWait = 2200;
			break;
		}
		case B9600:
		{
			g_modBusMCtrl[midx].modBusWait = 1100;
			break;
		}
		case B14400:
		{
			g_modBusMCtrl[midx].modBusWait = 750;
			break;
		}
		case B19200:
		{
			g_modBusMCtrl[midx].modBusWait = 600;
			break;
		}
		case B38400:
		{
			g_modBusMCtrl[midx].modBusWait = 300;
			break;
		}
		case B57600:
		{
			g_modBusMCtrl[midx].modBusWait = 200;
			break;
		}
		case B115200:
		{
			g_modBusMCtrl[midx].modBusWait = 100;
			break;
		}
		case B230400:
		{
			g_modBusMCtrl[midx].modBusWait = 50;
			break;
		}
		case B460800:
		{
			g_modBusMCtrl[midx].modBusWait = 25;
			break;
		}
		case B921600:
		{
			g_modBusMCtrl[midx].modBusWait = 13;
			break;
		}
		default:
		{
			g_modBusMCtrl[midx].modBusWait = 0;
		}
	}
	if ((dat == '8') && (parity != 'N') && (parity != 'n'))	// 7位数据过滤校验位
	{
		g_modBusMCtrl[midx].filterChk = 1;
	}
	else
	{
		g_modBusMCtrl[midx].filterChk = 0;
	}

#ifndef USART1_485OutEn
#define	USART1_485OutEn		DefOutEn
#endif

#ifndef USART1_485OutDis
#define	USART1_485OutDis	DefOutDis
#endif

#ifndef USART2_485OutEn
#define	USART2_485OutEn		DefOutEn
#endif

#ifndef USART2_485OutDis
#define	USART2_485OutDis	DefOutDis
#endif

#ifndef USART3_485OutEn
#define	USART3_485OutEn		DefOutEn
#endif

#ifndef USART3_485OutDis
#define	USART3_485OutDis	DefOutDis
#endif

#ifndef USART4_485OutEn
#define	USART4_485OutEn		DefOutEn
#endif

#ifndef USART4_485OutDis
#define	USART4_485OutDis	DefOutDis
#endif

#ifndef USART5_485OutEn
#define	USART5_485OutEn		DefOutEn
#endif

#ifndef USART5_485OutDis
#define	USART5_485OutDis	DefOutDis
#endif

#ifndef USART6_485OutEn
#define	USART6_485OutEn		DefOutEn
#endif

#ifndef USART6_485OutDis
#define	USART6_485OutDis	DefOutDis
#endif

	// 初始化串口
	switch(usart)
	{
#ifdef COMM_USART1
		case COMM_USART1:
		{
			InitUsart1(baud, dat, parity, stop);		// 串口1
			RegisterModbusCommFunc(&g_modBusMCtrl[midx], Usart1SendData, Usart1GetData, Usart1CleanRsBuf, IsUsart1SendOver, USART1_485OutEn, USART1_485OutDis);
			break;
		}
#endif
#ifdef COMM_USART2
		case COMM_USART2:
		{
			InitUsart2(baud, dat, parity, stop);		// 串口2
			RegisterModbusCommFunc(&g_modBusMCtrl[midx], Usart2SendData, Usart2GetData, Usart2CleanRsBuf, IsUsart2SendOver, USART2_485OutEn, USART2_485OutDis);
			break;
		}
#endif
#ifdef COMM_USART3
		case COMM_USART3:
		{
			InitUsart3(baud, dat, parity, stop);		// 串口3
			RegisterModbusCommFunc(&g_modBusMCtrl[midx], Usart3SendData, Usart3GetData, Usart3CleanRsBuf, IsUsart3SendOver, USART3_485OutEn, USART3_485OutDis);
			break;
		}
#endif
#ifdef COMM_USART4
		case COMM_USART4:
		{
			InitUsart4(baud, dat, parity, stop);		// 串口4
			RegisterModbusCommFunc(&g_modBusMCtrl[midx], Usart4SendData, Usart4GetData, Usart4CleanRsBuf, IsUsart4SendOver, USART4_485OutEn, USART4_485OutDis);
			break;
		}
#endif
#ifdef COMM_USART5
		case COMM_USART5:
		{
			InitUsart5(baud, dat, parity, stop);		// 串口5
			RegisterModbusCommFunc(&g_modBusMCtrl[midx], Usart5SendData, Usart5GetData, Usart5CleanRsBuf, IsUsart5SendOver, USART5_485OutEn, USART5_485OutDis);
			break;
		}
#endif
#ifdef COMM_USART6
		case COMM_USART6:
		{
			InitUsart6(baud, dat, parity, stop);		// 串口6
			RegisterModbusCommFunc(&g_modBusMCtrl[midx], Usart6SendData, Usart6GetData, Usart6CleanRsBuf, IsUsart6SendOver, USART6_485OutEn, USART6_485OutDis);
			break;
		}
#endif
		default:
		{
			RegisterModbusCommFunc(&g_modBusMCtrl[midx], DefSendData, DefGetData, DefCleanRsBuf, DefIsSendOver, DefOutEn, DefOutDis);
			break;
		}

	}

	g_modBusMCtrl[midx].CommOutDis();		// 关闭发送
}

//-----------------------------------------------------------------
// 功能:发送一条MODBUS协议的命令
// 参数:
//		midx:		主站序号
//		index:		站号:			1~247(十进制)
//		cmd:		命令码:		03H (读操作)
//								06H	(写操作)
//								10H (写操作)
//		addr:		数据起始地址: 0000H~FFFFH
//		pData:		数据内容:	单位:字(读操作时内容为读取个数,写操作时为写入内容)
//		len:		数据长度,单位字,读操作时固定为1,写操作时为写入长度
//		steps， 发送步骤
// 返回值:	0:发送正常
//		-1: 参数错误
//		-2: 发送超时

// 可以分4个步骤(1--4)发送, 如果步骤执行正确，返回步骤号码;
// steps == -1, 非分步调用（同步调用）
/*
 * 步骤1:打开发送允许
 * 步骤2:数据发送
 * 步骤3:等待发送完成
 * 步骤4:关闭发送
 */
int ModbusTx(int midx, u8 index, u8 cmd, u16 addr, u16 * pData, u16 len, int steps)
{
	int i, j, timeout, result;
	u8 checksum, temp;		// 和校验(ASCII)
	u16 checkcrc;			// CRC 校验(RTU)
	u16 data;
	u8 pSDat[MDBSRXLEN_MAX];	// 待发送数据

	if ((cmd != MODBUS_RD) && (cmd != MODBUS_WR) && (cmd != MODBUS_WR_MULTI))
	{
		result = -1;
		return result;
	}

	if (pData == NULL || len == 0)
	{
		result = -1;
		return result;		
	}

	if (midx < 0 || midx >= MODBUS_M_NUM)
	{
		result = -1;
		return result;
	}

	if (steps == 1 || steps == -1)
	{
		g_modBusMCtrl[midx].CommOutEn();			// 打开发送

		if (steps == -1)
		{
			DelayUs(100);
		}
	}

	if (steps == 2 || steps == -1)
	{
		i = 0;
		result = 0;

		if (g_modBusMCtrl[midx].modBusMode != 0)	// ASCII码模式
		{
			checksum = 0;
			pSDat[i++] = ':';					//':'

			temp = HIHFBYTE(index);
			pSDat[i++] = RtuToAscii(temp);		// IDX_H
			temp = LOHFBYTE(index);
			pSDat[i++] = RtuToAscii(temp);		// IDX_L
			checksum += index;

			temp = HIHFBYTE(cmd);
			pSDat[i++] = RtuToAscii(temp);		// CMD_H
			temp = LOHFBYTE(cmd);
			pSDat[i++] = RtuToAscii(temp);		// CMD_L
			checksum += cmd;

			temp = HIHFBYTE(HIBYTE(addr));
			pSDat[i++] = RtuToAscii(temp);		// ADDR_HH
			temp = LOHFBYTE(HIBYTE(addr));
			pSDat[i++] = RtuToAscii(temp);		// ADDR_HL
			checksum += HIBYTE(addr);

			temp = HIHFBYTE(LOBYTE(addr));
			pSDat[i++] = RtuToAscii(temp);		// ADDR_LH
			temp = LOHFBYTE(LOBYTE(addr));
			pSDat[i++] = RtuToAscii(temp);		// ADDR_LL
			checksum += LOBYTE(addr);

			if (cmd == MODBUS_WR_MULTI)
			{//多字 发送
				// 数据个数,单位字
				temp = HIHFBYTE(HIBYTE(len));
				pSDat[i++] = RtuToAscii(temp);		// LEN_HH
				temp = LOHFBYTE(HIBYTE(len));
				pSDat[i++] = RtuToAscii(temp);		// LEN_HL
				checksum += HIBYTE(len);

				temp = HIHFBYTE(LOBYTE(len));
				pSDat[i++] = RtuToAscii(temp);		// LEN_LH
				temp = LOHFBYTE(LOBYTE(len));
				pSDat[i++] = RtuToAscii(temp);		// LEN_LL
				checksum += LOBYTE(len); 
				
				// 数据区字节数
				temp = HIHFBYTE(LOBYTE(len)<<1);
				pSDat[i++] = RtuToAscii(temp);		// LEN_H
				temp = LOHFBYTE(LOBYTE(len)<<1);
				pSDat[i++] = RtuToAscii(temp);		// LEN_L
				checksum += LOBYTE(len<<1);  
			}

			for (j = 0; j < len; j++)
			{
				data = pData[j];
				temp = HIHFBYTE(HIBYTE(data));
				pSDat[i++] = RtuToAscii(temp);		// DATA_HH
				temp = LOHFBYTE(HIBYTE(data));
				pSDat[i++] = RtuToAscii(temp);		// DATA_HL
				checksum += HIBYTE(data);

				temp = HIHFBYTE(LOBYTE(data));
				pSDat[i++] = RtuToAscii(temp);		// DATA_LH
				temp = LOHFBYTE(LOBYTE(data));
				pSDat[i++] = RtuToAscii(temp);		// DATA_LL

				checksum += LOBYTE(data);
			}

			checksum = ~checksum + 1;
			pSDat[i++] = RtuToAscii(HIHFBYTE(checksum));	// LRC_H
			pSDat[i++] = RtuToAscii(LOHFBYTE(checksum));	// LRC_L
			pSDat[i++] = '\r';								// CR
			pSDat[i++] = '\n';								// LF
		}
		else		// RTU模式
		{
			pSDat[i++] = index;		// IDX
			pSDat[i++] = cmd;		// CMD
			pSDat[i++] = HIBYTE(addr);		// ADDR_H
			pSDat[i++] = LOBYTE(addr);		// ADDR_L

			if (cmd == MODBUS_WR_MULTI)
			{//多字 发送
				// 数据个数,单位字
				pSDat[i++] = HIBYTE(len);		// LEN_H
				pSDat[i++] = LOBYTE(len);		// LEN_L

				// 数据区字节数
				pSDat[i++] = LOBYTE(len)<<1;	// LEN_L * 2 
			}

			for (j = 0; j < len; j++)
			{
				data = pData[j];

				pSDat[i++] = HIBYTE(data);		// DATA_H
				pSDat[i++] = LOBYTE(data);		// DATA_L
			}

			checkcrc = ModbusCrc16(&pSDat[0], i);

			pSDat[i++] = HIBYTE(checkcrc);	// CRC_H
			pSDat[i++] = LOBYTE(checkcrc);	// CRC_L
		}

		g_modBusMCtrl[midx].ModBusCommSend(pSDat, i);
	}

	if (steps == 3)
	{
		if (g_modBusMCtrl[midx].IsModBusCommSendOver() != TRUE)
		{
			return 0;
		}
	}
	else if (steps == -1)	// 同步发送
	{
		timeout = (i*2 + 4) * g_modBusMCtrl[midx].modBusWait;	// 超时时间,单位us((字节数*2 + 4) * 每个字节发送时长)

		do	//等待发送完成
		{
			if (g_modBusMCtrl[midx].IsModBusCommSendOver() == TRUE)
			{
				break;
			}

			DelayUs(1);

			if (timeout-- <= 0)
			{
				result = -2;
				break;
			}
		}while(1);

		DelayUs(g_modBusMCtrl[midx].modBusWait);	// 等待最后一个字节发送完成
	}

	if (steps == 4 || steps == -1)
	{
		g_modBusMCtrl[midx].ModBusCommCleanRsBuf();	// 清空已接收的数据
		g_modBusMCtrl[midx].CommOutDis();				// 关闭发送
	}

	if (steps != -1)
	{
		return steps;
	}

	return result;
}

//-----------------------------------------------------------------
// 功能:接收一条MODBUS协议的应答
// 参数:
//		midx:		主站序号
//		index:		本条应该接收的站号:			1~247(十进制)
//		cmd:		本条应该接收的命令码:		03H (读操作)
//											06H	(写操作)
//											10H (写操作)
//		addr:		本条应该接收的数据起始地址: 0000H~FFFFH
//		*pData:		接收数据内容:	单位:字
//		len:		本条应该接收的数据长度:	单位:字
//		steps:		接收步骤
// 返回值:	>0: 接收正常,返回值为接收到的数据长度
//			-1: 参数错误
//			-2: 接收超时
//			-3: 接收错误
//			-4:串口接收错误
// 可以分步骤接收
// steps == -1, 同步调用，直到收到数据或超时
// steps == 1, 异步调用标志，正确得到数据结果为长度 len, 0为没有数据，其他为错误

int ModbusRx(int midx, u8 index, u8 cmd, u16 addr, u16 * pData, u16 len, int steps)
{
	int i, j, result, exlen, timeout;
	u8 pRDat[MDBSRXLEN_MAX];		// 待接收数据缓冲区
	u16 checkcrc;					// crc校验(RTU)
	u8 checksum;					// 和校验(ASCII)
	u8 temp_h, temp_l, tempH, tempL;		//

	if ((cmd != MODBUS_RD) && (cmd != MODBUS_WR) && (cmd != MODBUS_WR_MULTI))
	{
		return -1;		//参数错误
	}

	if ((len > MODBUS_LENMAX) || (len == 0))
	{
		return -1;		//参数错误
	}

	if (midx < 0 || midx >= MODBUS_M_NUM)
	{
		result = -1;
		return result;
	}

	if (g_modBusMCtrl[midx].modBusMode != 0)	// ASCII码模式
	{
		if (cmd == MODBUS_RD)	// 读数据
		{
			exlen = len * 4 + 11;		//包长度
		}
		else if (cmd == MODBUS_WR) 		// 写数据
		{
			exlen = 17;		//包长度
		}
		else if (cmd == MODBUS_WR_MULTI) 	// 写多个字数据
		{
			exlen = 17;		//包长度
		}
		else
		{
			exlen = 17;		//包长度
		}

		exlen -= 1;			// 减去起始符 :
	}
	else		// RTU 模式
	{
		if (cmd == MODBUS_RD)	// 读数据
		{
			exlen = len * 2 + 5;		//包长度
		}
		else if (cmd == MODBUS_WR) 		// 写数据
		{
			exlen = 8;		//包长度
		}
		else if (cmd == MODBUS_WR_MULTI) 	// 写多个字数据
		{
			exlen = 8;		//包长度
		}
		else 
		{
			exlen = 8;		//包长度
		}
	}

	if (steps == 1 || steps == -1)
	{
		memset(pRDat, 0, MDBSRXLEN_MAX);
		if (g_modBusMCtrl[midx].modBusMode != 0)	// ASCII码模式
		{
			timeout = exlen*2 + 400;	// 超时时间
			do
			{
				result = g_modBusMCtrl[midx].ModBusCommReceive(pRDat, 1);	// 从缓冲区读取数据

				if (result == 1)
				{
					if (pRDat[0] == ':')		// 找到开始标志
					{
						result = 0;
						break;
					}
					else
					{
						if (steps == 1)
						{
							result = 1;		// 异步调用,返回执行步骤
							break;
						}
					}
				}

				if (steps == -1)
				{
					DelayUs(g_modBusMCtrl[midx].modBusWait);

					if (timeout-- <= 0)
					{
						printf("1. call ModbusRx timeout\r\n");
						result = -2;		//接收超时
						break;
					}
				}
			}while(1);

			if (result != 0)
			{
				return result;
			}
		}
	}

	if (steps != -1 && g_modBusMCtrl[midx].modBusMode == 0)	// RTU 码模式
	{
		steps = 2;
	}

	if (steps == 2 || steps == -1)
	{
		timeout = exlen*2 + 400;	// 超时时间

		do
		{
			result = g_modBusMCtrl[midx].ModBusCommReceive(pRDat, exlen);	// 从缓冲区读取数据
			if (result >= exlen)
			{
				if (g_modBusMCtrl[midx].modBusMode != 0)	// ASCII码模式
				{
					if (g_modBusMCtrl[midx].filterChk != 0)	// 7位数据过滤校验位
					{
						for (i = 0; i < result; i++)
						{
							pRDat[i] &= 0x7F;
						}
					}

					// 处理数据包
					result = 0;
					i = 0;
					checksum = 0;

					if (pRDat[i] != RtuToAscii(HIHFBYTE(index)))	// INDEX_H
					{
						result = -3;		// 接收错误
						break;
					}
					temp_h = AsciiToRtu(pRDat[i++]);
					if (pRDat[i] != RtuToAscii(LOHFBYTE(index)))	// INDEX_L
					{
						result = -3;		// 接收错误
						break;
					}
					temp_l = AsciiToRtu(pRDat[i++]);
					checksum += MAKEBYTE(temp_l,temp_h);

					if (pRDat[i] != RtuToAscii(HIHFBYTE(cmd)))		// CMD_H
					{
						result = -3;		// 接收错误
						break;
					}

					temp_h = AsciiToRtu(pRDat[i++]);
					if (pRDat[i] != RtuToAscii(LOHFBYTE(cmd)))		// CMD_L
					{
						result = -3;		// 接收错误
						break;
					}
					temp_l = AsciiToRtu(pRDat[i++]);
					checksum += MAKEBYTE(temp_l,temp_h);

					if (cmd == MODBUS_RD)		// 读数据
					{
						if (pRDat[i] != RtuToAscii(HIHFBYTE(LOBYTE(len) << 1)))	// LEN_H
						{
							result = -3;	// 接收错误
							break;
						}
						temp_h = AsciiToRtu(pRDat[i++]);
						if (pRDat[i] != RtuToAscii(LOHFBYTE(LOBYTE(len) << 1)))	// LEN_L
						{
							result = -3;	// 接收错误
							break;
						}
						temp_l = AsciiToRtu(pRDat[i++]);
						checksum += MAKEBYTE(temp_l,temp_h);
					}
					else if (cmd == MODBUS_WR)		// 写数据
					{
						if (pRDat[i] !=  RtuToAscii(HIHFBYTE(HIBYTE(addr))))		// ADDR_HH
						{
							result = -3;		//接收错误
							break;
						}
						temp_h = AsciiToRtu(pRDat[i++]);
						if (pRDat[i] !=  RtuToAscii(LOHFBYTE(HIBYTE(addr))))		// ADDR_HL
						{
							result = -3;		//接收错误
							break;
						}
						temp_l = AsciiToRtu(pRDat[i++]);
						checksum += MAKEBYTE(temp_l,temp_h);

						if (pRDat[i] !=  RtuToAscii(HIHFBYTE(LOBYTE(addr))))		// ADDR_LH
						{
							result = -3;		//接收错误
							break;
						}
						temp_h = AsciiToRtu(pRDat[i++]);
						if (pRDat[i] !=  RtuToAscii(LOHFBYTE(LOBYTE(addr))))		// ADDR_LL
						{
							result = -3;		//接收错误
							break;
						}
						temp_l = AsciiToRtu(pRDat[i++]);
						checksum += MAKEBYTE(temp_l,temp_h);
					}
					else if (cmd == MODBUS_WR_MULTI)	// 写多个字数据
					{
						if (pRDat[i] !=  RtuToAscii(HIHFBYTE(HIBYTE(addr))))		// ADDR_HH
						{
							result = -3;		//接收错误
							break;
						}
						temp_h = AsciiToRtu(pRDat[i++]);
						if (pRDat[i] !=  RtuToAscii(LOHFBYTE(HIBYTE(addr))))		// ADDR_HL
						{
							result = -3;		//接收错误
							break;
						}
						temp_l = AsciiToRtu(pRDat[i++]);
						checksum += MAKEBYTE(temp_l,temp_h);

						if (pRDat[i] !=  RtuToAscii(HIHFBYTE(LOBYTE(addr))))		// ADDR_LH
						{
							result = -3;		//接收错误
							break;
						}
						temp_h = AsciiToRtu(pRDat[i++]);
						if (pRDat[i] !=  RtuToAscii(LOHFBYTE(LOBYTE(addr))))		// ADDR_LL
						{
							result = -3;		//接收错误
							break;
						}
						temp_l = AsciiToRtu(pRDat[i++]);
						checksum += MAKEBYTE(temp_l,temp_h);
						
						if (pRDat[i] !=  RtuToAscii(HIHFBYTE(HIBYTE(len))))		// LEN_HH
						{
							result = -3;		//接收错误
							break;
						}
						temp_h = AsciiToRtu(pRDat[i++]);
						if (pRDat[i] !=  RtuToAscii(LOHFBYTE(HIBYTE(len))))		// LEN_HL
						{
							result = -3;		//接收错误
							break;
						}
						temp_l = AsciiToRtu(pRDat[i++]);
						checksum += MAKEBYTE(temp_l,temp_h);

						if (pRDat[i] !=  RtuToAscii(HIHFBYTE(LOBYTE(len))))		// LEN_LH
						{
							result = -3;		//接收错误
							break;
						}
						temp_h = AsciiToRtu(pRDat[i++]);
						if (pRDat[i] !=  RtuToAscii(LOHFBYTE(LOBYTE(len))))		// LEN_LL
						{
							result = -3;		//接收错误
							break;
						}
						temp_l = AsciiToRtu(pRDat[i++]);
						checksum += MAKEBYTE(temp_l,temp_h);
					}

					if (cmd == MODBUS_RD ||		// 读数据
							cmd == MODBUS_WR ||		// 写数据
							0)
					{
						for (j = 0; j < len; j++)
						{
							temp_h = AsciiToRtu(pRDat[i++]);
							temp_l = AsciiToRtu(pRDat[i++]);
							tempH = MAKEBYTE(temp_l, temp_h);
							checksum += tempH;

							temp_h = AsciiToRtu(pRDat[i++]);
							temp_l = AsciiToRtu(pRDat[i++]);
							tempL = MAKEBYTE(temp_l, temp_h);

							checksum += tempL;
							pData[j] = MAKEWORD(tempL, tempH);
						}
					}
					
					checksum = ~checksum + 1;

					if (pRDat[i++] != RtuToAscii(HIHFBYTE(checksum)))		// LRC_H
					{
						result = -3;		//接收错误
						break;
					}
					if (pRDat[i++] != RtuToAscii(LOHFBYTE(checksum)))		// LRC_L
					{
						result = -3;		//接收错误
						break;
					}
					if (pRDat[i++] != '\r')		// CR
					{
						// result = -3;		//接收错误
						break;
					}
					if (pRDat[i++] != '\n')		// LF
					{
						//result = -3;		//接收错误
						break;
					}

					if (result == 0)
					{
						result = len;
					}
					break;
				}
				else		// RTU 模式
				{
					// 处理数据包
					result = 0;
					i = 0;

					if (pRDat[i++] != index) // 从站地址
					{
						result = -3;		// 接收错误
						break;
					}

					if (pRDat[i++] != cmd)
					{
						result = -3;		// 接收错误
						break;
					}

					if (cmd == MODBUS_RD)	// 读数据
					{
						if (pRDat[i++] != (LOBYTE(len) << 1))	// LEN
						{
							result = -3;		// 接收错误
							break;
						}
					}
					else if (cmd == MODBUS_WR)		// 写数据
					{
						if (pRDat[i++] != HIBYTE(addr))		// ADDR_H
						{
							result = -3;		// 接收错误
							break;
						}

						if (pRDat[i++] != LOBYTE(addr))		// ADDR_L
						{
							result = -3;		// 接收错误
							break;
						}
					}
					else if (cmd == MODBUS_WR_MULTI)	// 写多个字数据
					{
						if (pRDat[i++] != HIBYTE(addr))		// ADDR_H
						{
							result = -3;		// 接收错误
							break;
						}

						if (pRDat[i++] != LOBYTE(addr))		// ADDR_L
						{
							result = -3;		// 接收错误
							break;
						}

						if (pRDat[i++] != HIBYTE(len))		// LEN_H
						{
							result = -3;		// 接收错误
							break;
						}

						if (pRDat[i++] != LOBYTE(len))		// LEN_L
						{
							result = -3;		// 接收错误
							break;
						}
					}

					if (cmd == MODBUS_RD ||		// 读数据
							cmd == MODBUS_WR ||		// 写数据
							0)
					{
						for (j = 0; j < len; j++)
						{
							pData[j] = MAKEWORD(pRDat[i+1], pRDat[i]);
							i += 2;
						}
					}

					checkcrc = ModbusCrc16(&pRDat[0], i);

					if (pRDat[i++] != HIBYTE(checkcrc))		// LRC_H
					{
						result = -3;		// 接收错误
						break;
					}

					if (pRDat[i++] != LOBYTE(checkcrc))		// LRC_L
					{
						result = -3;		// 接收错误
						break;
					}

					if (result == 0)
					{
						result = len;
					}
					break;
				}
			}
			else if (result < 0)
			{
				if (result == -1)		// 输入参数错误
				{
					printf("para error\r\n");
				}
				else if (result == -2)	// 串口没有初始化
				{
					printf("usart is not inited\r\n");
				}
				else if (result == -3)	// 接收缓冲区控制错误
				{
					printf("usart receive buf ctrl error\r\n");
				}
				else
				{
				}
				result = -4;
				break;
			}
			else if (result > 0)
			{
				printf("receive data len error\r\n");
				result = -3;
				break;
			}
			else	// result == 0
			{
			}

			if (steps == -1)
			{
				// 等待接收完成
				DelayUs(g_modBusMCtrl[midx].modBusWait);

				if (timeout-- <= 0)
				{
					printf("2. call ModbusRx timeout, index=%d\r\n", index);
					result = -2;		//接收超时
					break;
				}
			}
			else
			{
				result = 0;
				break;
			}

		}while(1);
	}
	
	if (result == -3)	// 接收错误
	{
		printf("Rx data error, index=%d\r\n", index);
	}

	return result;
}

//-----------------------------------------------------------------
// 功能:发送一条MODBUS协议的命令,并等待回包
// 参数:
//		index:		站号:			1~247(十进制)
//		cmd:		命令码:		03H (读操作)
//							06H	(写操作)
//		addr:		数据起始地址: 0000H~FFFFH
//		pWrDat:	写入数据
//		wLen:	写入长度			单位:字
//		pRdBuf:	回包数据指针:
//		rLen:	读取(回包)数据长度:		单位:字

// 返回值:	>=0:接收正常(无回包,则返回0;有回包,则返回数据长度)
//			-1: 参数错误
//			-2: 接收超时
//			-3: 接收错误

int ModbusComm(int midx, u8 index, u8 cmd, u16 addr, u16 * pWrDat, u16 wLen, u16 * pRdBuf, u16 rLen)
{
	int result;
	u16 rdatabuf[MDBSRXLEN_MAX];

    if (cmd != MODBUS_RD)	// 20231027
    {
		if (pWrDat == NULL || wLen == 0)
		{
			return -1;
		}
    }

    if (cmd == MODBUS_RD)
    {
		result = ModbusTx(midx, index, cmd, addr, &rLen, 1, -1);
	}
	else
	{
		result = ModbusTx(midx, index, cmd, addr, pWrDat, wLen, -1);
	}

	if (result == 0)
	{
		// 等待回包
		if (index != 0 &&
				((index & 0x80) == 0 || cmd == MODBUS_RD) &&
				1)
		{
			if (pRdBuf == NULL)
			{
				result = ModbusRx(midx, index, cmd, addr, rdatabuf, rLen, -1);
			}
			else
			{
				result = ModbusRx(midx, index, cmd, addr, pRdBuf, rLen, -1);
			}
		}
	}
	
	return result;
}

//-----------------------------------------------------------------
// 功能:16位数据读写

int ModbusComm16(int midx, u8 index, u8 cmd, u16 addr, u16 wrDat, u16 * pData)
{
	int i,rst;
	
	for (i = 0; i < 3; i++)
	{
		rst = ModbusComm(midx, index, cmd, addr, &wrDat, 1, pData, 1);
		DelayMs(1);
		if (rst >= 0)
		{
			break;
		}
	}
	return rst;
}

//-----------------------------------------------------------------
// 功能:32位数据读写
int ModbusComm32(int midx, u8 index, u8 cmd, u16 addr, u32 wrDat, u32 * pData)
{
	int i,rst;
	
	if (cmd == MODBUS_WR)
	{//多字写入命令转换
		cmd = MODBUS_WR_MULTI;
	}
	
	for (i = 0; i < 3; i++)
	{
		rst = ModbusComm(midx, index, cmd, addr, (u16*)(&wrDat), 2, (u16*)pData, 2);
		DelayMs(1);
		if (rst >= 0)
		{
			break;
		}
	}
	
	return rst;
}

int ModbusCommMulti(int midx, u8 index, u8 cmd, u16 addr, u16 * pWrDat, u8 wlen, u16 * pRdDat, u8 rlen)
{
	int i,rst;

	if (cmd == MODBUS_WR)
	{//多字写入命令转换
		cmd = MODBUS_WR_MULTI;
	}

	for (i = 0; i < 3; i++)
	{
		rst = ModbusComm(midx, index, cmd, addr, pWrDat, wlen, pRdDat, rlen);
		DelayMs(1);
		if (rst >= 0)
		{
			break;
		}
	}

	return rst;
}

//-----------------------------------------------------------------

Rs485Ctrl g_rs485Ctrl;

void InitRs485Ctrl(int rsvtimout)
{
	memset(&g_rs485Ctrl, 0, sizeof(Rs485Ctrl));

	// 读取超时时间
	if (rsvtimout < 50)
	{
		g_rs485Ctrl.modrsvTimout = 50;
	}
	else if (rsvtimout > 500)
	{
		g_rs485Ctrl.modrsvTimout = 500;
	}
	else
	{
		g_rs485Ctrl.modrsvTimout = rsvtimout;
	}
}

void RegRsCmdProc(Resv485ExProc proc)
{
	g_rs485Ctrl.resv485exproc = proc;
}

int AddRs485Cmd(Rs485Cmd * pCmd)
{
	Rs485Ctrl * p485Ctrl = &g_rs485Ctrl;

	if (p485Ctrl != NULL && pCmd != NULL)
	{
		if (p485Ctrl->bufCmdNum < MAX_485CMD && p485Ctrl->bufTail < MAX_485CMD)
		{
			memcpy(&(p485Ctrl->cmdbuf[p485Ctrl->bufTail]), pCmd, sizeof(Rs485Cmd));
			p485Ctrl->bufTail++;
			if (p485Ctrl->bufTail >= MAX_485CMD)
			{
				p485Ctrl->bufTail = 0;
			}
			p485Ctrl->bufCmdNum++;
			return p485Ctrl->bufCmdNum;
		}
		return -2; 
	}

	return -1;
}

int GetRs485CmdBufLen(void)
{
	return g_rs485Ctrl.bufCmdNum;
}

int Rs485Task(void)
{
	// 分步骤执行
	int rslt;
	u32 curIime;

	Rs485Cmd *pCmd;

	Rs485Ctrl * p485Ctrl = &g_rs485Ctrl;

	if (p485Ctrl == NULL)
	{
		return -1;
	}

	pCmd = &(p485Ctrl->cmdbuf[p485Ctrl->bufHead]);

	// 读取当前时间
	curIime = GetUsSoftTimer();

	if ((curIime - p485Ctrl->stepTime) > g_modBusMCtrl[pCmd->midx].modBusWait)
	{
		p485Ctrl->stepTime = curIime;

		if (p485Ctrl->working == 0 &&
			p485Ctrl->bufCmdNum > 0 &&
			p485Ctrl->steps == 0)
		{
			p485Ctrl->working = 1;
			p485Ctrl->steps = 1;
		}

		if (p485Ctrl->working == 0)
		{
			p485Ctrl->steps = 0;
			return 0;
		}

		if (p485Ctrl->steps > 0 && p485Ctrl->steps < 5)
		{
			if (pCmd->cmd == MODBUS_RD)
			{
				rslt = ModbusTx(pCmd->midx, pCmd->index, pCmd->cmd, pCmd->addr, &(pCmd->rlen), 1, p485Ctrl->steps);
			}
			else
			{
				rslt = ModbusTx(pCmd->midx, pCmd->index, pCmd->cmd, pCmd->addr, pCmd->wrdat, pCmd->wlen, p485Ctrl->steps);
			}

			if (rslt == p485Ctrl->steps)
			{
				p485Ctrl->steps++;
			}
		}
		else if (p485Ctrl->steps >= 5 && p485Ctrl->steps < p485Ctrl->modrsvTimout)
		{
			// 等待回包
			if (pCmd->index != 0 &&
					((pCmd->index & 0x80) == 0 || pCmd->cmd == MODBUS_RD) &&
					1)
			{
				if (pCmd->cmd == MODBUS_RD)
				{
					rslt = ModbusRx(pCmd->midx, pCmd->index, pCmd->cmd, pCmd->addr, pCmd->rddat, pCmd->rlen, 1);
				}
				else
				{
					rslt = ModbusRx(pCmd->midx, pCmd->index, pCmd->cmd, pCmd->addr, pCmd->rddat, 1, 1);
				}
				if (rslt == pCmd->rlen)	// 读到正确数据
				{
					// 执行读到数据函数
					if (p485Ctrl->resv485exproc != NULL)
					{
						p485Ctrl->resv485exproc(pCmd);
					}
					p485Ctrl->steps = p485Ctrl->modrsvTimout;
				}
				else
				{
					p485Ctrl->steps++;
				}
			}
			else
			{
				p485Ctrl->steps = p485Ctrl->modrsvTimout;
			}
		}
		else
		{
			p485Ctrl->steps++;
		}

		if (p485Ctrl->steps >= p485Ctrl->modrsvTimout)
		{
			if (p485Ctrl->bufCmdNum > 0)
			{
				p485Ctrl->bufCmdNum--;
			}
			p485Ctrl->steps = 0;
			p485Ctrl->working = 0;
			p485Ctrl->bufHead++;
			if (p485Ctrl->bufHead >= MAX_485CMD)
			{
				p485Ctrl->bufHead = 0;
			}
		}
	}
	return p485Ctrl->steps;
}

//-----------------------------------------------------------------

#endif

