单片机modebus RTU通信实现,采用C语言,可适用于单片机,VC,安卓等(转)

源:单片机modebus RTU通信实现,采用C语言,可适用于单片机,VC,安卓等

//modebus_rtu.c

/*************************************************************************************************************
 * 文件名:        MODEBUS_RTU.c
 * 功能:        MODEBUS_RTU通信协议层
 * 作者:        cp1300@139.com
 * 创建时间:    2014-03-24
 * 最后修改时间:2014-11-17
 * 详细:        MODEBUS RTU通信协议层
*************************************************************************************************************/
#include "system.h"
#include "usart.h"
#include "delay.h"
#include "MODEBUS_RTU.h"






//调试开关
#define MODEBUS_RTU_DBUG    1
#if MODEBUS_RTU_DBUG
    #include "system.h"
    #define modebus_debug(format,...)    uart_printf(format,##__VA_ARGS__)
#else
    #define modebus_debug(format,...)    /\
/
#endif    //MODEBUS_RTU_DBUG





/*************************************************************************************************************************
* 函数    :    bool MODEBUS_Init(MODEBUS_HANDLE *pHandle, u8 UartCh, u32 BaudRate, u8 *pRxBuff,u8 *pTxBuff, u32 RxBuffSize, u32 TimeOut)
* 功能    :    MODEBUS 初始化
* 参数    :    pHandle:当前初始化的modebus句柄,UartCh:使用的串口通道;BaudRate:使用的波特率;pRxBuff:接收缓冲区指针;
            RxBuffSize:接收缓冲区大小;pTxBuff:发送缓冲区指针;TimeOut:接收超时,单位ms
* 返回    :    FALSE:初始化失败;TRUE:初始化成功
* 依赖    :    串口
* 作者    :    cp1300@139.com
* 时间    :    2014-09-25
* 最后修改时间 : 2014-11-10
* 说明    :     收发缓冲区可以与发送缓冲区使用同一缓冲区
            发送缓冲区必须大于最大数据包大小,否则会出现内存溢出
*************************************************************************************************************************/
bool MODEBUS_Init(MODEBUS_HANDLE *pHandle, u8 UartCh, u32 BaudRate, u8 *pRxBuff,u8 *pTxBuff, u32 RxBuffSize, u32 TimeOut)
{        
    if(pHandle == NULL) return FALSE;
    pHandle->TxPacketNum = 0;                                                    //发送数据包计数
    pHandle->RxPacketNum = 0;                                                    //接收数据包计数
    pHandle->ErrorNum = 0;                                                        //通信错误计数
    pHandle->ReturnTime = 0;                                                    //数据返回时间
    //设置串口
    if(MODEBUS_UartInit(UartCh, BaudRate) == FALSE)                                //初始化串口
    {
        pHandle->UartCh = 0xff;                                                    //通道无效
        pHandle->pRxBuff = pHandle->pTxBuff = NULL;                                //缓冲区无效
        pHandle->RxBuffSize = 0;                                                //缓冲区大小为0
    }
    MODEBUS_SetRxBuff(UartCh, pRxBuff, RxBuffSize);                    
    MODEBUS_DisableRx(UartCh);                                                    //关闭串口接收
    pHandle->UartCh = UartCh;                                                    //通道
    pHandle->pRxBuff = pRxBuff;
    pHandle->pTxBuff = pTxBuff;                                                    //缓冲区
    pHandle->RxBuffSize = RxBuffSize;                                            //缓冲区大小
    if(TimeOut == 0) TimeOut = 1;
    pHandle->TimeOut = TimeOut;
    pHandle->BaudRate = BaudRate;
    
    return TRUE;
}




#if(MODEBUS_RTU_HOST) //开启主机模式
/*************************************************************************************************************************
* 函数    :    MRTU_ERROR MODEBUS_HOST_ReadReg(MODEBUS_HANDLE *pHandle, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u16 *pRegData)
* 功能    :    主机读取从机一个指定寄存器
* 参数    :    pHandle:modebus句柄;RegType:读取的寄存器类型;SlaveAddr:从机地址;RegAddr:需读取的寄存器地址;pRegData:寄存器的值
* 返回    :    MRTU_ERROR:通信状态
* 依赖    :    底层通信驱动
* 作者    :    cp1300@139.com
* 时间    :    2014-03-24
* 最后修改时间 : 2014-11-16
* 说明    :     MOUEBUS RTU读取数据,读取一个寄存器
            输入输出的数据都为小端模式
*************************************************************************************************************************/
MRTU_ERROR MODEBUS_HOST_ReadReg(MODEBUS_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;
    u16 cnt1, cnt2=0;                //接收数据计数器
    u16 TimeOut;
    u16 TimeDelay = 0;                //用于计算数据接收延时

    
    if(pHandle == NULL) return MRTU_HANDLE_ERROR;    //句柄无效
    TimeOut = pHandle->TimeOut/10+1;                //超时初值
    pFrame = (MRTU_READ_FRAME *)pHandle->pTxBuff;
    //数据结构填充
    pFrame->addr = SlaveAddr;                        //从机地址
    pFrame->fun = (u8)RegType;                        //功能码,读取
    pFrame->StartReg = SWAP16(RegAddr);                //寄存器起始地址
    pFrame->RegNum = SWAP16(1);                        //需要读取的寄存器数量,1    
    crc16 = usMBCRC16(pHandle->pTxBuff, 6);            //计算CRC16
    pFrame->CRC16 = crc16;                            //crc16

#if MODEBUS_RTU_DBUG
    {
        u16 i;
        
        modebus_debug("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",8,crc16);
        for(i = 0;i < 8;i ++)
        {
            modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
        }
        modebus_debug("\r\n");
    }
#endif    //MODEBUS_RTU_DBUG
    
    MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, 6+2);    //发送数据
    MODEBUS_ClearRxCnt(pHandle->UartCh);                        //清除接收缓冲区
    MODEBUS_GetDataOver(pHandle->UartCh);                        //清除溢出标志
    MODEBUS_EnableRx(pHandle->UartCh);                            //使能接收
    //等待数据返回
    do
    {
        cnt1 = cnt2;
        MODEBUS_Delay10MS();                                    //延时10ms    
        if(MODEBUS_GetDataOver(pHandle->UartCh) == SET)            //查看是否发生溢出    
        {
            MODEBUS_DisableRx(pHandle->UartCh);                    //关闭接收
            MODEBUS_ClearRxCnt(pHandle->UartCh);                //清除接收缓冲区
            modebus_debug("接收溢出!\r\n");
            return MRTU_OVER_ERROR;                                //返回溢出错误
        }
        cnt2 = MODEBUS_GetDataCnt(pHandle->UartCh);                //获取接收数据计数器
        if(cnt1 == cnt2)                                        //完成接收数据了,退出等待
        {
            TimeOut --;
            if((cnt1 > 0)&&(TimeOut!=0)) TimeOut=1;                //数据接收完毕,退出
            TimeDelay ++;
        }
        else
        {
            TimeOut = pHandle->TimeOut/10+1;                    //有数据,计数器复位
        }
    }while(TimeOut);
    TimeDelay -= 1;
    //等待完毕
    MODEBUS_DisableRx(pHandle->UartCh);                            //关闭接收
    MODEBUS_ClearRxCnt(pHandle->UartCh);                        //清除接收缓冲区
    if(cnt1 == 0)                                                 //没有接收到数据
    {    
        modebus_debug("接收超时(%dmS)!\r\n",TimeDelay*10);
        pHandle->ReturnTime = 0xffff;                            //接收数据超时
        return MRTU_TIME_OUT;                //返回超时
    }
    pHandle->ReturnTime = TimeDelay*10;                            //数据返回时间
    
#if MODEBUS_RTU_DBUG
    {
        u16 i;
        
        modebus_debug("\r\n-> MODEBUS RTU RXD(%dB)(ping:%dmS):\r\n",cnt1,TimeDelay*10);
        for(i = 0;i < cnt1;i ++)
        {
            modebus_debug("0x%02X ", pHandle->pRxBuff[i]);
        }
        modebus_debug("\r\n");
    }
#endif    //MODEBUS_RTU_DBUG
    
    pReFrame = (MRTU_RETURN_FRAME *)pHandle->pRxBuff;
    //检查地址
    if(pReFrame->addr != SlaveAddr)
    {
        modebus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02X\r\n",SlaveAddr, pReFrame->addr);
        return MRTU_ADDR_ERROR;
    }
    //对接受的数据进行CRC校验
    crc16 = usMBCRC16(pHandle->pRxBuff, cnt1-2);//计算CRC16
    if((pHandle->pRxBuff[cnt1-1] != (crc16 >> 8)) || (pHandle->pRxBuff[cnt1-2] != (crc16 & 0xff)))
    {
        modebus_debug("CRC校验错误,计算CRC为:0x%04X,返回CRC为:0x%04X\r\n",crc16,(u16)(pHandle->pRxBuff[cnt1-2]<<8)|pHandle->pRxBuff[cnt1-1]);
        return MRTU_CRC_ERROR;                //返回CRC校验错误
    }
    //返回的功能码不一致
    if(pReFrame->fun != (u8)RegType)
    {
        pUnuFrame = (MRTU_UNU_FRAME *)pHandle->pRxBuff;        //异常数据帧
        if(pUnuFrame->ErrorFun == ((u8)RegType|0x80))        //返回有异常
        {
            modebus_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
        {
            modebus_debug("返回错误,返回功能码为0x%02X\r\n", pReFrame->fun);
            return MRTU_FUNR_ERROR;
        }
    }
    //判断数据长度
    if(pReFrame->DataLen != 2)
    {
        modebus_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];
    
    return MRTU_OK;                        //返回成功 
}




/*************************************************************************************************************************
* 函数    :    MRTU_ERROR MODEBUS_HOST_ReadMultReg(MODEBUS_HANDLE *pHandle, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[])
* 功能    :    主机读取从机指定多个连续寄存器
* 参数    :    pHandle:modebus句柄;RegType:读取的寄存器类型;SlaveAddr:从机地址;RegAddr:需读取的寄存器地址;RegNum:寄存器数量;pRegData:返回寄存器的值,至少为RegNum的2倍
            返回的寄存器的值按照循序存放在pRegData中
* 返回    :    MRTU_ERROR:通信状态
* 依赖    :    底层通信驱动
* 作者    :    cp1300@139.com
* 时间    :    2014-03-24
* 最后修改时间 : 2014-11-16
* 说明    :     MOUEBUS RTU读取数据,读取一个寄存器
            输入输出的数据都为小端模式
*************************************************************************************************************************/
MRTU_ERROR MODEBUS_HOST_ReadMultReg(MODEBUS_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;
    u16 cnt1, cnt2=0;                //接收数据计数器
    u16 TimeOut;
    u16 TimeDelay = 0;                //用于计算数据接收延时
    u8 i;

    
    if(pHandle == NULL) return MRTU_HANDLE_ERROR;    //句柄无效
    TimeOut = pHandle->TimeOut/10+1;                //超时初值
    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 = usMBCRC16(pHandle->pTxBuff, 6);            //计算CRC16
    pFrame->CRC16 = crc16;                            //crc16

#if MODEBUS_RTU_DBUG
    {
        u16 i;
        
        modebus_debug("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",8,crc16);
        for(i = 0;i < 8;i ++)
        {
            modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
        }
        modebus_debug("\r\n");
    }
#endif    //MODEBUS_RTU_DBUG
    
    MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, 6+2);    //发送数据
    MODEBUS_ClearRxCnt(pHandle->UartCh);                        //清除接收缓冲区
    MODEBUS_GetDataOver(pHandle->UartCh);                        //清除溢出标志
    MODEBUS_EnableRx(pHandle->UartCh);                            //使能接收
    //等待数据返回
    do
    {
        cnt1 = cnt2;
        MODEBUS_Delay10MS();                                    //延时10ms    
        if(MODEBUS_GetDataOver(pHandle->UartCh) == SET)            //查看是否发生溢出    
        {
            MODEBUS_DisableRx(pHandle->UartCh);                    //关闭接收
            MODEBUS_ClearRxCnt(pHandle->UartCh);                //清除接收缓冲区
            modebus_debug("接收溢出!\r\n");
            return MRTU_OVER_ERROR;                                //返回溢出错误
        }
        cnt2 = MODEBUS_GetDataCnt(pHandle->UartCh);                //获取接收数据计数器
        if(cnt1 == cnt2)                                        //完成接收数据了,退出等待
        {
            TimeOut --;
            if((cnt1 > 0)&&(TimeOut!=0)) TimeOut=1;                //数据接收完毕,退出
            TimeDelay ++;
        }
        else
        {
            TimeOut = pHandle->TimeOut/10+1;                    //有数据,计数器复位
        }
    }while(TimeOut);
    TimeDelay -= 1;
    //等待完毕
    MODEBUS_DisableRx(pHandle->UartCh);                            //关闭接收
    MODEBUS_ClearRxCnt(pHandle->UartCh);                        //清除接收缓冲区
    if(cnt1 == 0)                                                 //没有接收到数据
    {    
        modebus_debug("接收超时(%dmS)!\r\n",TimeDelay*10);
        pHandle->ReturnTime = 0xffff;                            //接收数据超时
        return MRTU_TIME_OUT;                //返回超时
    }
    pHandle->ReturnTime = TimeDelay*10;                            //数据返回时间
    
#if MODEBUS_RTU_DBUG
    {
        u16 i;
        
        modebus_debug("\r\n-> MODEBUS RTU RXD(%dB)(ping:%dmS):\r\n",cnt1,TimeDelay*10);
        for(i = 0;i < cnt1;i ++)
        {
            modebus_debug("0x%02X ", pHandle->pRxBuff[i]);
        }
        modebus_debug("\r\n");
    }
#endif    //MODEBUS_RTU_DBUG
    
    pReFrame = (MRTU_RETURN_FRAME *)pHandle->pRxBuff;
    //检查地址
    if(pReFrame->addr != SlaveAddr)
    {
        modebus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02X\r\n",SlaveAddr, pReFrame->addr);
        return MRTU_ADDR_ERROR;
    }
    //对接受的数据进行CRC校验
    crc16 = usMBCRC16(pHandle->pRxBuff, cnt1-2);//计算CRC16
    if((pHandle->pRxBuff[cnt1-1] != (crc16 >> 8)) || (pHandle->pRxBuff[cnt1-2] != (crc16 & 0xff)))
    {
        modebus_debug("CRC校验错误,计算CRC为:0x%04X,返回CRC为:0x%04X\r\n",crc16,(u16)(pHandle->pRxBuff[cnt1-2]<<8)|pHandle->pRxBuff[cnt1-1]);
        return MRTU_CRC_ERROR;                //返回CRC校验错误
    }
    //返回的功能码不一致
    if(pReFrame->fun != (u8)RegType)
    {
        pUnuFrame = (MRTU_UNU_FRAME *)pHandle->pRxBuff;        //异常数据帧
        if(pUnuFrame->ErrorFun == ((u8)RegType|0x80))        //返回有异常
        {
            modebus_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
        {
            modebus_debug("返回错误,返回功能码为0x%02X\r\n", pReFrame->fun);
            return MRTU_FUNR_ERROR;
        }
    }
    //判断数据长度
    if(pReFrame->DataLen != (RegNum*2))
    {
        modebus_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];
    }
    
    return MRTU_OK;                        //返回成功 
}






/*************************************************************************************************************************
* 函数    :    MRTU_ERROR MODEBUS_HOST_WriteReg(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u16 RegData)
* 功能    :    主机写从机一个指定寄存器
* 参数    :    pHandle:modebus句柄;SlaveAddr:从机地址;RegAddr:写寄存器地址;RegData:寄存器的值
* 返回    :    MRTU_ERROR:通信状态
* 依赖    :    底层通信驱动
* 作者    :    cp1300@139.com
* 时间    :    2014-03-24
* 最后修改时间 : 2014-11-16
* 说明    :     MOUEBUS RTU写从机一个保持寄存器
            输入输出的数据都为小端模式
            预置单个寄存器的发送与接收数据包格式完全一致,理论上发送与接收的数据都应该一致
*************************************************************************************************************************/
MRTU_ERROR MODEBUS_HOST_WriteReg(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u16 RegData)
{
    MRTU_WRITE_FRAME *pFrame, *pReFrame;//发送数据帧格式
    MRTU_UNU_FRAME    *pUnuFrame;            //返回的异常数据帧格式
    u16 crc16;
    u16 cnt1, cnt2=0;                    //接收数据计数器
    u16 TimeOut;
    u16 TimeDelay = 0;                    //用于计算数据接收延时

    
    if(pHandle == NULL) return MRTU_HANDLE_ERROR;    //句柄无效
    TimeOut = pHandle->TimeOut/10+1;                //超时初值
    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 = usMBCRC16(pHandle->pTxBuff, 6);    //计算CRC16

#if MODEBUS_RTU_DBUG
    {
        u16 i;
        
        modebus_debug("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",8,crc16);
        for(i = 0;i < 8;i ++)
        {
            modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
        }
        modebus_debug("\r\n");
    }
#endif    //MODEBUS_RTU_DBUG
    
    MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, 6+2);    //发送数据
    MODEBUS_ClearRxCnt(pHandle->UartCh);                        //清除接收缓冲区
    MODEBUS_GetDataOver(pHandle->UartCh);                        //清除溢出标志
    MODEBUS_EnableRx(pHandle->UartCh);                            //使能接收
    //等待数据返回
    do
    {
        cnt1 = cnt2;
        MODEBUS_Delay10MS();                                    //延时10ms    
        if(MODEBUS_GetDataOver(pHandle->UartCh) == SET)            //查看是否发生溢出    
        {
            MODEBUS_DisableRx(pHandle->UartCh);                    //关闭接收
            MODEBUS_ClearRxCnt(pHandle->UartCh);                //清除接收缓冲区
            modebus_debug("接收溢出!\r\n");
            return MRTU_OVER_ERROR;                                //返回溢出错误
        }
        cnt2 = MODEBUS_GetDataCnt(pHandle->UartCh);                //获取接收数据计数器
        if(cnt1 == cnt2)                                        //完成接收数据了,退出等待
        {
            TimeOut --;
            if((cnt1 > 0)&&(TimeOut!=0)) TimeOut=1;                //数据接收完毕,退出
            TimeDelay ++;
        }
        else
        {
            TimeOut = pHandle->TimeOut/10+1;                    //有数据,计数器复位
        }
    }while(TimeOut);
    TimeDelay -= 1;
    //等待完毕
    MODEBUS_DisableRx(pHandle->UartCh);                            //关闭接收
    MODEBUS_ClearRxCnt(pHandle->UartCh);                        //清除接收缓冲区
    if(cnt1 == 0)                                                 //没有接收到数据
    {    
        modebus_debug("接收超时(%dmS)!\r\n",TimeDelay*10);
        pHandle->ReturnTime = 0xffff;                            //接收数据超时
        return MRTU_TIME_OUT;                //返回超时
    }
    pHandle->ReturnTime = TimeDelay*10;                            //数据返回时间
    
#if MODEBUS_RTU_DBUG
    {
        u16 i;
        
        modebus_debug("\r\n-> MODEBUS RTU RXD(%dB)(ping:%dmS):\r\n",cnt1,TimeDelay*10);
        for(i = 0;i < cnt1;i ++)
        {
            modebus_debug("0x%02X ", pHandle->pRxBuff[i]);
        }
        modebus_debug("\r\n");
    }
#endif    //MODEBUS_RTU_DBUG
    
    pReFrame = (MRTU_WRITE_FRAME *)pHandle->pRxBuff;
    //检查地址
    if(pReFrame->addr != SlaveAddr)
    {
        modebus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02X\r\n",SlaveAddr, pReFrame->addr);
        return MRTU_ADDR_ERROR;
    }
    //对接受的数据进行CRC校验
    crc16 = usMBCRC16(pHandle->pRxBuff, cnt1-2);//计算CRC16
    if((pHandle->pRxBuff[cnt1-1] != (crc16 >> 8)) || (pHandle->pRxBuff[cnt1-2] != (crc16 & 0xff)))
    {
        modebus_debug("CRC校验错误,计算CRC为:0x%04X,返回CRC为:0x%04X\r\n",crc16,(u16)(pHandle->pRxBuff[cnt1-2]<<8)|pHandle->pRxBuff[cnt1-1]);
        return MRTU_CRC_ERROR;                //返回CRC校验错误
    }
    //返回的功能码不一致
    if(pReFrame->fun != (u8)MRTU_FUN_WRITE)
    {
        pUnuFrame = (MRTU_UNU_FRAME *)pHandle->pRxBuff;        //异常数据帧
        if(pUnuFrame->ErrorFun == ((u8)MRTU_FUN_WRITE|0x80))//返回有异常
        {
            modebus_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
        {
            modebus_debug("返回错误,返回功能码为0x%02X\r\n", pReFrame->fun);
            return MRTU_FUNR_ERROR;
        }
    }
    //判断数据是否写入
    if(SWAP16(pReFrame->StartReg) != RegAddr)    //返回的寄存器地址不一致
    {
        modebus_debug("返回寄存器地址错误,写入寄存器%d,返回寄存器%d\r\n",RegAddr, pReFrame->StartReg);
        return MRTU_REG_ERROR;                    //返回寄存器错误
    }
    if(SWAP16(pReFrame->RegData) != RegData)
    {
        modebus_debug("数据写入错误,写入值:0x%04X,返回了:0x%04X\r\n",RegData, pReFrame->RegData);
        return MRTU_WRITE_ERROR;                //写入数据错误
    }

    return MRTU_OK;                                //返回成功 
}




/*************************************************************************************************************************
* 函数    :    MRTU_ERROR MODEBUS_HOST_WriteMultReg(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[])
* 功能    :    主机写从机多个指定寄存器
* 参数    :    pHandle:modebus句柄;SlaveAddr:从机地址;RegAddr:写寄存器地址;RegNum:寄存器数量, pRegData:需要写入的寄存器的值
            写入寄存器的值按照循序排列,使用小端格式,大小必须为RegNum*2
* 返回    :    MRTU_ERROR:通信状态
* 依赖    :    底层通信驱动
* 作者    :    cp1300@139.com
* 时间    :    2014-03-24
* 最后修改时间 : 2014-11-16
* 说明    :     MOUEBUS RTU写从机一个保持寄存器
            输入输出的数据都为小端模式
            返回数据寄存器位置与寄存器数量
*************************************************************************************************************************/
MRTU_ERROR MODEBUS_HOST_WriteMultReg(MODEBUS_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;
    u16 cnt1, cnt2=0;                                //接收数据计数器
    u16 TimeOut;
    u16 TimeDelay = 0;                                //用于计算数据接收延时
    u8 i;
    
    if(pHandle == NULL) return MRTU_HANDLE_ERROR;    //句柄无效
    TimeOut = pHandle->TimeOut/10+1;                //超时初值
    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 = usMBCRC16(pHandle->pTxBuff, 7+pFrame->DataLen);    //计算CRC16,高低位对调过
    pFrame->DataBuff[pFrame->DataLen] = crc16&0xff;    //高位
    pFrame->DataBuff[pFrame->DataLen+1]=crc16>>8;    //低位
    
#if MODEBUS_RTU_DBUG
    {
        u16 i;
        
        modebus_debug("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",7+pFrame->DataLen+2,crc16);
        for(i = 0;i < 7+pFrame->DataLen+2;i ++)
        {
            modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
        }
        modebus_debug("\r\n");
    }
#endif    //MODEBUS_RTU_DBUG
    
    MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, 7+pFrame->DataLen+2);    //发送数据
    MODEBUS_ClearRxCnt(pHandle->UartCh);                        //清除接收缓冲区
    MODEBUS_GetDataOver(pHandle->UartCh);                        //清除溢出标志
    MODEBUS_EnableRx(pHandle->UartCh);                            //使能接收
    //等待数据返回
    do
    {
        cnt1 = cnt2;
        MODEBUS_Delay10MS();                                    //延时10ms    
        if(MODEBUS_GetDataOver(pHandle->UartCh) == SET)            //查看是否发生溢出    
        {
            MODEBUS_DisableRx(pHandle->UartCh);                    //关闭接收
            MODEBUS_ClearRxCnt(pHandle->UartCh);                //清除接收缓冲区
            modebus_debug("接收溢出!\r\n");
            return MRTU_OVER_ERROR;                                //返回溢出错误
        }
        cnt2 = MODEBUS_GetDataCnt(pHandle->UartCh);                //获取接收数据计数器
        if(cnt1 == cnt2)                                        //完成接收数据了,退出等待
        {
            TimeOut --;
            if((cnt1 > 0)&&(TimeOut!=0)) TimeOut=1;                //数据接收完毕,退出
            TimeDelay ++;
        }
        else
        {
            TimeOut = pHandle->TimeOut/10+1;                    //有数据,计数器复位
        }
    }while(TimeOut);
    TimeDelay -= 1;
    //等待完毕
    MODEBUS_DisableRx(pHandle->UartCh);                            //关闭接收
    MODEBUS_ClearRxCnt(pHandle->UartCh);                        //清除接收缓冲区
    if(cnt1 == 0)                                                 //没有接收到数据
    {    
        modebus_debug("接收超时(%dmS)!\r\n",TimeDelay*10);
        pHandle->ReturnTime = 0xffff;                            //接收数据超时
        return MRTU_TIME_OUT;                //返回超时
    }
    pHandle->ReturnTime = TimeDelay*10;                            //数据返回时间
    
#if MODEBUS_RTU_DBUG
    {
        u16 i;
        
        modebus_debug("\r\n-> MODEBUS RTU RXD(%dB)(ping:%dmS):\r\n",cnt1,TimeDelay*10);
        for(i = 0;i < cnt1;i ++)
        {
            modebus_debug("0x%02X ", pHandle->pRxBuff[i]);
        }
        modebus_debug("\r\n");
    }
#endif    //MODEBUS_RTU_DBUG
    
    pReFrame = (MRTU_WRIT_EMULT_RFRAME *)pHandle->pRxBuff;
    //检查地址
    if(pReFrame->addr != SlaveAddr)
    {
        modebus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02X\r\n",SlaveAddr, pReFrame->addr);
        return MRTU_ADDR_ERROR;
    }
    //对接受的数据进行CRC校验
    crc16 = usMBCRC16(pHandle->pRxBuff, cnt1-2);//计算CRC16
    if((pHandle->pRxBuff[cnt1-1] != (crc16 >> 8)) || (pHandle->pRxBuff[cnt1-2] != (crc16 & 0xff)))
    {
        modebus_debug("CRC校验错误,计算CRC为:0x%04X,返回CRC为:0x%04X\r\n",crc16,(u16)(pHandle->pRxBuff[cnt1-2]<<8)|pHandle->pRxBuff[cnt1-1]);
        return MRTU_CRC_ERROR;                //返回CRC校验错误
    }
    //返回的功能码不一致
    if(pReFrame->fun != (u8)MRTU_FUN_MWRITE)
    {
        pUnuFrame = (MRTU_UNU_FRAME *)pHandle->pRxBuff;        //异常数据帧
        if(pUnuFrame->ErrorFun == ((u8)MRTU_FUN_MWRITE|0x80))//返回有异常
        {
            modebus_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
        {
            modebus_debug("返回错误,返回功能码为0x%02X\r\n", pReFrame->fun);
            return MRTU_FUNR_ERROR;
        }
    }
    //判断数据是否写入
    if(SWAP16(pReFrame->StartReg) != RegAddr)    //返回的寄存器地址不一致
    {
        modebus_debug("返回寄存器地址错误,写入寄存器%d,返回寄存器%d\r\n",RegAddr, pReFrame->StartReg);
        return MRTU_REG_ERROR;                    //返回寄存器错误
    }
    if(SWAP16(pReFrame->RegNum) != RegNum)
    {
        modebus_debug("写入寄存器数量错误,写入%d个寄存器,返回%d个寄存器\r\n",RegNum, pReFrame->RegNum);
        return MRTU_WRITE_ERROR;                //写入数据错误
    }

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



#if(MODEBUS_RTU_SLAVE) //开启从机模式
/*************************************************************************************************************************
* 函数    :    bool MODEBUS_SLAVE_RetrunUnu(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u8 Fun, MRTU_UNUS Unus)
* 功能    :    从机返回异常编码
* 参数    :    pHandle:modebus句柄;SlaveAddr:从机地址;Fun:来自主机的功能码;Unus:异常码,见MRTU_UNUS
* 返回    :    TRUE:发送成功;FALSE:发送失败
* 依赖    :    底层通信驱动
* 作者    :    cp1300@139.com
* 时间    :    2014-03-24
* 最后修改时间 : 2014-11-17
* 说明    :     从机返回异常码给主机,异常码见:MRTU_UNUS
            MRTU_UNUS1    异常码1,无效的操作码
            MRTU_UNUS2    异常码2,无效的数据地址
            MRTU_UNUS3    异常码3,无效的数据值
            MRTU_UNUS4    异常码4,无效操作
            MRTU_UNUS5    异常码5
            MRTU_UNUS6    异常码6
*************************************************************************************************************************/
bool MODEBUS_SLAVE_RetrunUnu(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u8 Fun, MRTU_UNUS Unus)
{
    MRTU_UNU_FRAME *pFrame;                //返回异常数据包
    u16 crc16;
    
    if(pHandle == NULL) return FALSE;    //句柄无效
    //数据结构填充
    pFrame = (MRTU_UNU_FRAME *)pHandle->pTxBuff;
    pFrame->addr = SlaveAddr;                        //从机地址
    pFrame->ErrorFun = (u8)Fun|0x80;                //功能码+0x80,出现异常
    pFrame->unu = (u8)Unus;                            //异常编码
    crc16 = usMBCRC16(pHandle->pTxBuff, 3);            //计算CRC16,高低位对调过
    pFrame->crc16H = crc16 & 0xff;
    pFrame->crc16L = crc16>>8;
#if MODEBUS_RTU_DBUG
    {
        u16 i;
        
        modebus_debug("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",5, crc16);
        for(i = 0;i < 5;i ++)
        {
            modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
        }
        modebus_debug("\r\n");
    }
#endif    //MODEBUS_RTU_DBUG
    
    MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, 5);    //发送数据
    
    return TRUE;
}


/*************************************************************************************************************************
* 函数    :    MRTU_ERROR MODEBUS_SLAVE_FramesUnpack(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u32 DataLen, u8 *pFun)
* 功能    :    从机模式接收数据拆包
* 参数    :    pHandle:modebus句柄;SlaveAddr:从机地址;DataLen:接收数据长度;pFun:来自主机的功能码
* 返回    :    MRTU_ERROR:状态,只有MRTU_OK:才是有效数据包
* 依赖    :    底层通信驱动
* 作者    :    cp1300@139.com
* 时间    :    2014-03-24
* 最后修改时间 : 2014-11-17
* 说明    :     需要等数据接收完毕后拆包
*************************************************************************************************************************/
MRTU_ERROR MODEBUS_SLAVE_FramesUnpack(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u32 DataLen, u8 *pFun)
{
    u16 crc16;
    MRTU_READ_FRAME *pReadFrame;                //来自主机的读取数据帧格式
    MRTU_WRITE_MULT_FRAME *pWriteMultFrame;        //来自主机的写多个保持寄存器

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






/*************************************************************************************************************************
* 函数    :    MRTU_ERROR MODEBUS_SLAVE_ReturnReadReg(MODEBUS_HANDLE *pHandle, u8 Fun, u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[])
* 功能    :    从机返回主机读取的寄存器
* 参数    :    pHandle:modebus句柄;Fun:读取的功能码;SlaveAddr:从机地址;RegAddr:需读取的寄存器地址;RegNum:寄存器数量;pRegData:返回寄存器的值,至少为RegNum的2倍
            返回的寄存器的值按照循序存放在pRegData中
* 返回    :    MRTU_ERROR:通信状态
* 依赖    :    底层通信驱动
* 作者    :    cp1300@139.com
* 时间    :    2014-03-24
* 最后修改时间 : 2014-11-16
* 说明    :     MOUEBUS RTU主机读取从机的指定寄存器,可以为保持寄存器,也可以为输入寄存器,可以一次读取多个
            输入输出的数据都为小端模式
            注意:如果直接使用数据帧的寄存器数量以及地址,必须高地位交换
*************************************************************************************************************************/
MRTU_ERROR MODEBUS_SLAVE_ReturnReadReg(MODEBUS_HANDLE *pHandle, u8 Fun, u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[])
{
    MRTU_RETURN_FRAME *pFrame;        //返回数据帧格式
    u16 crc16;
    u8 i;

    
    if(pHandle == NULL) return MRTU_HANDLE_ERROR;    //句柄无效
    if((Fun != MRTU_FUN_READ_INPUT) && (Fun != MRTU_FUN_READ_HOLD)) return MRTU_FUNR_ERROR;    //功能码错误
    if((RegNum > 127) || (RegNum == 0))    return MRTU_REGN_ERROR;    //寄存器数量错误
    pFrame = (MRTU_RETURN_FRAME *)pHandle->pTxBuff;
    //数据结构填充
    pFrame->addr = SlaveAddr;                        //从机地址
    pFrame->fun = Fun;                                //功能码,读取
    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 = usMBCRC16(pHandle->pTxBuff, 3+pFrame->DataLen);//计算CRC16
    pFrame->DataBuff[pFrame->DataLen] = crc16&0xff;    //数据发送交换过
    pFrame->DataBuff[pFrame->DataLen+1] = crc16>>8;    //数据发送交换过

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

    return MRTU_OK;                        //返回成功 
}










/*************************************************************************************************************************
* 函数    :    MRTU_ERROR MODEBUS_SLAVE_ReturnWriteHoldReg(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u16 RegData)
* 功能    :    从机返回主机预置单个保持寄存器
* 参数    :    pHandle:modebus句柄;Fun:读取的功能码;SlaveAddr:从机地址;RegAddr:需读取的寄存器地址;RegData:返回寄存器的值
* 返回    :    MRTU_ERROR:通信状态
* 依赖    :    底层通信驱动
* 作者    :    cp1300@139.com
* 时间    :    2014-03-24
* 最后修改时间 : 2014-11-16
* 说明    :     MOUEBUS RTU主机写单个寄存器成功后返回
            输入输出的数据都为小端模式
            注意:如果直接使用数据帧的寄存器数量以及地址,必须高地位交换
*************************************************************************************************************************/
MRTU_ERROR MODEBUS_SLAVE_ReturnWriteHoldReg(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u16 RegData)
{
    MRTU_WRITE_FRAME *pFrame;        //返回数据帧格式
    u16 crc16;
    
    if(pHandle == NULL) return MRTU_HANDLE_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 = usMBCRC16(pHandle->pTxBuff, 6);//计算CRC16

#if MODEBUS_RTU_DBUG
    {
        u16 i;
        
        modebus_debug("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",8,crc16);
        for(i = 0;i < 8;i ++)
        {
            modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
        }
        modebus_debug("\r\n");
    }
#endif    //MODEBUS_RTU_DBUG
    
    MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, 8);    //发送数据

    return MRTU_OK;                        //返回成功 
}







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

    if(pHandle == NULL) return MRTU_HANDLE_ERROR;    //句柄无效
    if((RegNum > 127) || (RegNum == 0))    return MRTU_REGN_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 = usMBCRC16(pHandle->pTxBuff, 6);    //计算CRC16
#if MODEBUS_RTU_DBUG
    {
        u16 i;
        
        modebus_debug("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",8,pFrame->crc16);
        for(i = 0;i < 8;i ++)
        {
            modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
        }
        modebus_debug("\r\n");
    }
#endif    //MODEBUS_RTU_DBUG
    
    MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, 8);    //发送数据

    return MRTU_OK;                        //返回成功 
}




/*************************************************************************************************************************
* 函数    :    MRTU_ERROR MODEBUS_SLAVE_ReadUnpack(MODEBUS_HANDLE *pHandle, MRTU_SLAVE_INFO *pFrameInfo)
* 功能    :    解析来自主机的读取寄存器命令
* 参数    :    pHandle:modebus句柄;pFrameInfo:解析的信息结构
* 返回    :    MRTU_ERROR:通信状态
* 依赖    :    底层通信驱动
* 作者    :    cp1300@139.com
* 时间    :    2014-11-17
* 最后修改时间 : 2014-11-17
* 说明    :     用于将modebus的大端模式解析为小端模式
            支持 MRTU_FUN_READ_HOLD,MRTU_FUN_READ_INPUT 命令解析
*************************************************************************************************************************/
void MODEBUS_SLAVE_ReadUnpack(MODEBUS_HANDLE *pHandle, MRTU_SLAVE_INFO *pFrameInfo)
{
    MRTU_READ_FRAME *pReadRegFrame;                            //主机读取从机数据帧
    
    pReadRegFrame = (MRTU_READ_FRAME *)pHandle->pRxBuff;
    pFrameInfo->SlaveAddr = pReadRegFrame->addr;            //从机地址
    pFrameInfo->fun = pReadRegFrame->fun;                    //功能码
    pFrameInfo->StartReg = SWAP16(pReadRegFrame->StartReg);    //寄存器起始地址
    pFrameInfo->RegNum = SWAP16(pReadRegFrame->RegNum);        //寄存器数量
}




/*************************************************************************************************************************
* 函数    :    void MODEBUS_SLAVE_WriteUnpack(MODEBUS_HANDLE *pHandle, MRTU_SLAVE_INFO *pFrameInfo, u16 *pData)
* 功能    :    解析来自主机的预置单个寄存器命令
* 参数    :    pHandle:modebus句柄;pFrameInfo:解析的信息结构;pData:需要写入从机的值
* 返回    :    MRTU_ERROR:通信状态
* 依赖    :    底层通信驱动
* 作者    :    cp1300@139.com
* 时间    :    2014-11-17
* 最后修改时间 : 2014-11-17
* 说明    :     用于将modebus的大端模式解析为小端模式
            支持 MRTU_FUN_WRITE 命令解析
*************************************************************************************************************************/
void MODEBUS_SLAVE_WriteUnpack(MODEBUS_HANDLE *pHandle, MRTU_SLAVE_INFO *pFrameInfo, u16 *pData)
{
    MRTU_WRITE_FRAME *pWriteRegFrame;                            //主机预置单个保持寄存器
    
    pWriteRegFrame = (MRTU_WRITE_FRAME *)pHandle->pRxBuff;
    pFrameInfo->SlaveAddr = pWriteRegFrame->addr;                //从机地址
    pFrameInfo->fun = pWriteRegFrame->fun;                        //功能码
    pFrameInfo->StartReg = SWAP16(pWriteRegFrame->StartReg);    //寄存器起始地址
    pFrameInfo->RegNum = 1;                                        //寄存器数量
    *pData = SWAP16(pWriteRegFrame->RegData);                    //需要写入的寄存器的值
}




/*************************************************************************************************************************
* 函数    :    void MODEBUS_SLAVE_WriteMultUnpack(MODEBUS_HANDLE *pHandle, MRTU_SLAVE_INFO *pFrameInfo, u16 *pDataBuff)
* 功能    :    解析来自主机的预置多个寄存器命令
* 参数    :    pHandle:modebus句柄;pFrameInfo:解析的信息结构;pDataBuff:需要写入从机寄存器值的数组,必须足够大,防止溢出
* 返回    :    MRTU_ERROR:通信状态
* 依赖    :    底层通信驱动
* 作者    :    cp1300@139.com
* 时间    :    2014-11-17
* 最后修改时间 : 2014-11-17
* 说明    :     用于将modebus的大端模式解析为小端模式
            支持 MRTU_FUN_MWRITE 命令解析
*************************************************************************************************************************/
void MODEBUS_SLAVE_WriteMultUnpack(MODEBUS_HANDLE *pHandle, MRTU_SLAVE_INFO *pFrameInfo, u16 *pDataBuff)
{
    MRTU_WRITE_MULT_FRAME *pWriteMultRegFrame;                    //主机预置多个保持寄存器
    u8 i;
    
    pWriteMultRegFrame = (MRTU_WRITE_MULT_FRAME *)pHandle->pRxBuff;
    pFrameInfo->SlaveAddr = pWriteMultRegFrame->addr;                //从机地址
    pFrameInfo->fun = pWriteMultRegFrame->fun;                        //功能码
    pFrameInfo->StartReg = SWAP16(pWriteMultRegFrame->StartReg);    //寄存器起始地址
    pFrameInfo->RegNum = SWAP16(pWriteMultRegFrame->RegNum);        //寄存器数量
    //需要写入的寄存器的值
    for(i = 0;i < pFrameInfo->RegNum;i ++)
    {
        pDataBuff[i] = pWriteMultRegFrame->DataBuff[2*i];
        pDataBuff[i] <<= 8;
        pDataBuff[i] |= pWriteMultRegFrame->DataBuff[2*i+1];
    }            
}
#endif //MODEBUS_RTU_SLAVE





//MODEBUS CRC16计算
//结果为大端模式
BIG_U16 usMBCRC16( 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 MODEBUS_32TO16(u16 *Out16H, u16 *Out16L, u32 In32)
* 功能    :    将32bit数据拆分为高低16位,并且使用大端模式,兼容modebus
* 参数    :    Out16H:拆分的高16位,大端模式;Out16L:拆分的低16位,大端模式;In32:需要拆分的数据,小端模式,兼容STM32
* 返回    :    无
* 依赖    :    无
* 作者    :    cp1300@139.com
* 时间    :    2014-05-27
* 最后修改时间 : 2014-05-27
* 说明    :     将STM32 32位数据拆分为兼容MODEBUS 大端模式
*************************************************************************************************************************/
void MODEBUS_32TO16(u16 *Out16H, u16 *Out16L, u32 In32)
{
    *Out16H = SWAP16(In32 >> 16);
    *Out16L = SWAP16(In32 & 0xffff);
}


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

 

//modebus_rtu.h

/*************************************************************************************************************
 * 文件名:        MODEBUS_RTU.c
 * 功能:        MODEBUS_RTU通信协议层
 * 作者:        cp1300@139.com
 * 创建时间:    2014-03-24
 * 最后修改时间:2014-11-17
 * 详细:        MODEBUS RTU通信协议层
*************************************************************************************************************/
#ifndef _MODEBUS_RTU_H_
#define _MODEBUS_RTU_H_

#include "system.h"
#include "ucos_ii.h"


/***********************配置相关************************/
#define MODEBUS_RTU_HOST        1            //1:开启主机模式;0:关闭主机模式
#define MODEBUS_RTU_SLAVE        1            //1:开启从机模式;0:关闭从机模式
/*********************************************************/


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



/***********************关接口函数************************/
/**********************移植需要修改***********************/
#define MODEBUS_UartInit(ch,Speed)                     UARTx_Init((UART_CH_Type)ch, Speed, ENABLE)                    //串口初始化
#define MODEBUS_GetDataCnt(ch)                        UARTx_GetRxCnt((UART_CH_Type)ch)                            //获取接收数据计数器
#define MODEBUS_ClearRxCnt(ch)                        UARTx_ClearRxCnt((UART_CH_Type)ch)                            //清除接收数据计数器
#define MODEBUS_GetDataOver(ch)                        UARTx_GetRxBuffFullFlag((UART_CH_Type)ch)                    //获取数据溢出标志
#define MODEBUS_SendData(ch,pbuff,len)                 UARTx_SendData((UART_CH_Type)ch, pbuff, len)                //数据发送
#define MODEBUS_SetRxBuff(ch, RxBuff, RxBuffSize)     UARTx_SetRxBuff((UART_CH_Type)ch, RxBuff, RxBuffSize)        //设置串口接收缓冲区
#define MODEBUS_DisableRx(ch)                        (UARTx_EnableRx((UART_CH_Type)ch, DISABLE))                    //串口接收关闭
#define MODEBUS_EnableRx(ch)                        (UARTx_EnableRx((UART_CH_Type)ch, ENABLE))                    //串口接收使能
#define MODEBUS_SetBaudRate(ch, x)                    (UARTx_SetBaudRate((UART_CH_Type)ch, x))                    //设置串口波特率
//系统延时函数,根据实际修改,如果使用ucos建议使用ucos系统延时
#define MODEBUS_Delay10MS()                            OSTimeDlyHMSM(0,0,0,10)                                        //10ms延时,字节超时固定为10ms    
/*********************************************************/


//支持的功能码
#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;


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



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





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


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


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


//从机返回的异常数据帧,从机返回的异常数据帧
typedef __packed 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_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;




//MODEBUS句柄结构
typedef struct
{
    u8    UartCh;            //串口通道
    u32 BaudRate;        //通信波特率
    u8 *pRxBuff;        //接收缓冲区
    u8 *pTxBuff;        //发送缓冲区
    u32 RxBuffSize;        //接收缓冲区大小
    u32    TxPacketNum;    //发送数据包计数
    u32 RxPacketNum;    //接收数据包计数
    u32 ErrorNum;        //通信错误计数
    u16 TimeOut;        //通信超时时间,单位ms
    u16 ReturnTime;        //数据返回时间,单位ms,只对于主机有效
} MODEBUS_HANDLE;



bool MODEBUS_Init(MODEBUS_HANDLE *pHandle, u8 UartCh, u32 BaudRate, u8 *pRxBuff,u8 *pTxBuff, u32 RxBuffSize, u32 TimeOut);                    //初始化modebus
#if(MODEBUS_RTU_HOST) //开启主机模式
MRTU_ERROR MODEBUS_HOST_ReadReg(MODEBUS_HANDLE *pHandle, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u16 *pRegData);                    //主机读取从机指定单个寄存器
MRTU_ERROR MODEBUS_HOST_ReadMultReg(MODEBUS_HANDLE *pHandle, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[]);    //主机读取从机多个指定寄存器
MRTU_ERROR MODEBUS_HOST_WriteReg(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u16 RegData);                                            //主机写从机单个保持寄存器
MRTU_ERROR MODEBUS_HOST_WriteMultReg(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[]);                            //主机写从机单个保持寄存器
#endif

#if(MODEBUS_RTU_SLAVE) //开启从机模式
bool MODEBUS_SLAVE_RetrunUnu(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u8 Fun, MRTU_UNUS Unus);                                                    //从机返回异常码
MRTU_ERROR MODEBUS_SLAVE_FramesUnpack(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u32 DataLen, u8 *pFun);                                            //从机解析数据包
MRTU_ERROR MODEBUS_SLAVE_ReturnReadReg(MODEBUS_HANDLE *pHandle, u8 Fun, u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[]);                //从机返回主机读取的寄存器
MRTU_ERROR MODEBUS_SLAVE_ReturnWriteHoldReg(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u16 RegData);                                //从机返回主机写入单个寄存器命令
MRTU_ERROR MODEBUS_SLAVE_ReturnWriteMultHoldReg(MODEBUS_HANDLE *pHandle, u8 SlaveAddr, u16 RegAddr, u8 RegNum);                                //从机返回主机写多个寄存器命令
void MODEBUS_SLAVE_ReadUnpack(MODEBUS_HANDLE *pHandle, MRTU_SLAVE_INFO *pFrameInfo);                                                        //从机解析主机读取命令
void MODEBUS_SLAVE_WriteUnpack(MODEBUS_HANDLE *pHandle, MRTU_SLAVE_INFO *pFrameInfo, u16 *pData);                                            //从机解析主机写单个寄存器命令
void MODEBUS_SLAVE_WriteMultUnpack(MODEBUS_HANDLE *pHandle, MRTU_SLAVE_INFO *pFrameInfo, u16 *pDataBuff);                                    //从机解析主机多个寄存器命令
#endif


u16 usMBCRC16( u8 * pucFrame, u16 usLen );                                //crc计算
void MODEBUS_32TO16(u16 *Out16H, u16 *Out16L, u32 In32);                //将32bit数据拆分为高低16位,并且使用大端模式,兼容modebus
u32 MODEBUS_16TO32(u16 In16H, u16 In16L);                                //将modebus高低16位转换为小端模式的32位数
    
#endif /*_MODEBUS_RTU_H_*/

 

//从机测试

//主机比较简单就不写了

//任务2:
//ModeBus
u8 ModebusBuff[128];
void TaskModeBus(void *pdata)
{
    MODEBUS_HANDLE ModebusHandle1;
    MRTU_SLAVE_INFO FrameInfo;    //modebus读写信息
    
    u16 data;
    u16 RegBuff[20];
    u8 i;
    u32 cnt1 = 0,cnt2 = 0;
    u8 Fun;
    
    
    for(i = 0;i < 10;i ++)
    {
        RegBuff[i] = i+1;
    }
    MODEBUS_Init(&ModebusHandle1, UART_CH2, 115200, ModebusBuff, ModebusBuff, 128-1, 200);
    MODEBUS_EnableRx(ModebusHandle1.UartCh);    //使能接收
    MODEBUS_ClearRxCnt(ModebusHandle1.UartCh);    //清除接收
    while(1)
    {
        cnt2 = cnt1;
        cnt1 = MODEBUS_GetDataCnt(ModebusHandle1.UartCh);
        if((cnt1 == cnt2) && (cnt1 != 0))
        {
            MODEBUS_DisableRx(ModebusHandle1.UartCh);    //关闭接收
            MODEBUS_ClearRxCnt(ModebusHandle1.UartCh);    //清除接收
            if(MODEBUS_SLAVE_FramesUnpack(&ModebusHandle1, 1, cnt1, &Fun) == MRTU_OK)
            {
                uart_printf("收到数据,功能码:0x%02X\r\n", Fun);
                switch(Fun)
                {
                    case MRTU_FUN_READ_HOLD        :                    //读保持寄存器,可读写寄存器为保持寄存器
                    case MRTU_FUN_READ_INPUT    :                    //读输入寄存器,为只读寄存器
                    {
                        //解析
                        MODEBUS_SLAVE_ReadUnpack(&ModebusHandle1, &FrameInfo);
                        //数据处理
                        uart_printf("主机读取从机(%d)从%d开始的寄存器,共需要读取%d个\r\n", FrameInfo.SlaveAddr,  FrameInfo.StartReg, FrameInfo.RegNum);
                        //返回
                        MODEBUS_SLAVE_ReturnReadReg(&ModebusHandle1, FrameInfo.fun, FrameInfo.SlaveAddr, FrameInfo.StartReg, FrameInfo.RegNum, RegBuff);
                    }break;
                    case MRTU_FUN_WRITE            :                    //写单个保持寄存器
                    {
                        //解析
                        MODEBUS_SLAVE_WriteUnpack(&ModebusHandle1, &FrameInfo, &data);
                        //数据处理
                        uart_printf("主机写从机(%d)的寄存器%d值为:0x%02X\r\n", FrameInfo.SlaveAddr,  FrameInfo.StartReg, data);
                        //返回
                        MODEBUS_SLAVE_ReturnWriteHoldReg(&ModebusHandle1, FrameInfo.SlaveAddr, FrameInfo.StartReg, data);
                    }break;
                    case MRTU_FUN_MWRITE        :                    //写多个保持寄存器
                    {
                        //解析
                        MODEBUS_SLAVE_WriteMultUnpack(&ModebusHandle1, &FrameInfo, RegBuff);
                        //数据处理
                        uart_printf("主机写从机(%d),从%d开始的寄存器,共%d个,数据为:", FrameInfo.SlaveAddr,  FrameInfo.StartReg, FrameInfo.RegNum);
                        for(i = 0;i < FrameInfo.RegNum;i ++)
                        {
                            uart_printf("0x%04X ", RegBuff[i]);    //打印数据
                        }
                        uart_printf("\r\n");
                        //返回
                        MODEBUS_SLAVE_ReturnWriteMultHoldReg(&ModebusHandle1, FrameInfo.SlaveAddr, FrameInfo.StartReg, FrameInfo.RegNum);
                    }break;
                    default:break;
                }
                
            }
            MODEBUS_EnableRx(ModebusHandle1.UartCh);    //使能接收
        }
        OSTimeDlyHMSM(0,0,0,200);
    }    
}

 

模拟主机

 

从机接收处理

posted @ 2015-07-28 09:42  酒醉的Tiger  阅读(1332)  评论(0编辑  收藏  举报