zoukankan      html  css  js  c++  java
  • 单片机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("
    <- MODEBUS RTU TXD(%dB)(CRC:0x%04X):
    ",8,crc16);
            for(i = 0;i < 8;i ++)
            {
                modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
            }
            modebus_debug("
    ");
        }
    #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("接收溢出!
    ");
                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)!
    ",TimeDelay*10);
            pHandle->ReturnTime = 0xffff;                            //接收数据超时
            return MRTU_TIME_OUT;                //返回超时
        }
        pHandle->ReturnTime = TimeDelay*10;                            //数据返回时间
        
    #if MODEBUS_RTU_DBUG
        {
            u16 i;
            
            modebus_debug("
    -> MODEBUS RTU RXD(%dB)(ping:%dmS):
    ",cnt1,TimeDelay*10);
            for(i = 0;i < cnt1;i ++)
            {
                modebus_debug("0x%02X ", pHandle->pRxBuff[i]);
            }
            modebus_debug("
    ");
        }
    #endif    //MODEBUS_RTU_DBUG
        
        pReFrame = (MRTU_RETURN_FRAME *)pHandle->pRxBuff;
        //检查地址
        if(pReFrame->addr != SlaveAddr)
        {
            modebus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02X
    ",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
    ",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
    ", 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
    ", pReFrame->fun);
                return MRTU_FUNR_ERROR;
            }
        }
        //判断数据长度
        if(pReFrame->DataLen != 2)
        {
            modebus_debug("返回数据长度错误,读取%d个寄存器,共%dB,只返回了%dB
    ",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("
    <- MODEBUS RTU TXD(%dB)(CRC:0x%04X):
    ",8,crc16);
            for(i = 0;i < 8;i ++)
            {
                modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
            }
            modebus_debug("
    ");
        }
    #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("接收溢出!
    ");
                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)!
    ",TimeDelay*10);
            pHandle->ReturnTime = 0xffff;                            //接收数据超时
            return MRTU_TIME_OUT;                //返回超时
        }
        pHandle->ReturnTime = TimeDelay*10;                            //数据返回时间
        
    #if MODEBUS_RTU_DBUG
        {
            u16 i;
            
            modebus_debug("
    -> MODEBUS RTU RXD(%dB)(ping:%dmS):
    ",cnt1,TimeDelay*10);
            for(i = 0;i < cnt1;i ++)
            {
                modebus_debug("0x%02X ", pHandle->pRxBuff[i]);
            }
            modebus_debug("
    ");
        }
    #endif    //MODEBUS_RTU_DBUG
        
        pReFrame = (MRTU_RETURN_FRAME *)pHandle->pRxBuff;
        //检查地址
        if(pReFrame->addr != SlaveAddr)
        {
            modebus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02X
    ",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
    ",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
    ", 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
    ", pReFrame->fun);
                return MRTU_FUNR_ERROR;
            }
        }
        //判断数据长度
        if(pReFrame->DataLen != (RegNum*2))
        {
            modebus_debug("返回数据长度错误,读取%d个寄存器,共%dB,只返回了%dB
    ",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("
    <- MODEBUS RTU TXD(%dB)(CRC:0x%04X):
    ",8,crc16);
            for(i = 0;i < 8;i ++)
            {
                modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
            }
            modebus_debug("
    ");
        }
    #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("接收溢出!
    ");
                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)!
    ",TimeDelay*10);
            pHandle->ReturnTime = 0xffff;                            //接收数据超时
            return MRTU_TIME_OUT;                //返回超时
        }
        pHandle->ReturnTime = TimeDelay*10;                            //数据返回时间
        
    #if MODEBUS_RTU_DBUG
        {
            u16 i;
            
            modebus_debug("
    -> MODEBUS RTU RXD(%dB)(ping:%dmS):
    ",cnt1,TimeDelay*10);
            for(i = 0;i < cnt1;i ++)
            {
                modebus_debug("0x%02X ", pHandle->pRxBuff[i]);
            }
            modebus_debug("
    ");
        }
    #endif    //MODEBUS_RTU_DBUG
        
        pReFrame = (MRTU_WRITE_FRAME *)pHandle->pRxBuff;
        //检查地址
        if(pReFrame->addr != SlaveAddr)
        {
            modebus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02X
    ",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
    ",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
    ", 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
    ", pReFrame->fun);
                return MRTU_FUNR_ERROR;
            }
        }
        //判断数据是否写入
        if(SWAP16(pReFrame->StartReg) != RegAddr)    //返回的寄存器地址不一致
        {
            modebus_debug("返回寄存器地址错误,写入寄存器%d,返回寄存器%d
    ",RegAddr, pReFrame->StartReg);
            return MRTU_REG_ERROR;                    //返回寄存器错误
        }
        if(SWAP16(pReFrame->RegData) != RegData)
        {
            modebus_debug("数据写入错误,写入值:0x%04X,返回了:0x%04X
    ",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("
    <- MODEBUS RTU TXD(%dB)(CRC:0x%04X):
    ",7+pFrame->DataLen+2,crc16);
            for(i = 0;i < 7+pFrame->DataLen+2;i ++)
            {
                modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
            }
            modebus_debug("
    ");
        }
    #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("接收溢出!
    ");
                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)!
    ",TimeDelay*10);
            pHandle->ReturnTime = 0xffff;                            //接收数据超时
            return MRTU_TIME_OUT;                //返回超时
        }
        pHandle->ReturnTime = TimeDelay*10;                            //数据返回时间
        
    #if MODEBUS_RTU_DBUG
        {
            u16 i;
            
            modebus_debug("
    -> MODEBUS RTU RXD(%dB)(ping:%dmS):
    ",cnt1,TimeDelay*10);
            for(i = 0;i < cnt1;i ++)
            {
                modebus_debug("0x%02X ", pHandle->pRxBuff[i]);
            }
            modebus_debug("
    ");
        }
    #endif    //MODEBUS_RTU_DBUG
        
        pReFrame = (MRTU_WRIT_EMULT_RFRAME *)pHandle->pRxBuff;
        //检查地址
        if(pReFrame->addr != SlaveAddr)
        {
            modebus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02X
    ",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
    ",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
    ", 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
    ", pReFrame->fun);
                return MRTU_FUNR_ERROR;
            }
        }
        //判断数据是否写入
        if(SWAP16(pReFrame->StartReg) != RegAddr)    //返回的寄存器地址不一致
        {
            modebus_debug("返回寄存器地址错误,写入寄存器%d,返回寄存器%d
    ",RegAddr, pReFrame->StartReg);
            return MRTU_REG_ERROR;                    //返回寄存器错误
        }
        if(SWAP16(pReFrame->RegNum) != RegNum)
        {
            modebus_debug("写入寄存器数量错误,写入%d个寄存器,返回%d个寄存器
    ",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("
    <- MODEBUS RTU TXD(%dB)(CRC:0x%04X):
    ",5, crc16);
            for(i = 0;i < 5;i ++)
            {
                modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
            }
            modebus_debug("
    ");
        }
    #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;
    ", pHandle->pRxBuff[0], SlaveAddr);
            return MRTU_ADDR_ERROR;
        }
        //对接受的数据进行CRC校验
        crc16 = usMBCRC16(pHandle->pRxBuff, DataLen-2);            //计算CRC16    
        
    #if MODEBUS_RTU_DBUG
        {
            u16 i;
            
            modebus_debug("
    -> MODEBUS RTU RXD(%dB)(CRC:0x%04X):
    ",DataLen,crc16);
            for(i = 0;i < DataLen;i ++)
            {
                modebus_debug("0x%02X ",pHandle->pRxBuff[i]);
            }
            modebus_debug("
    ");
        }
    #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
    ", 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
    ", 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
    ", 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
    ", 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
    ",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("
    <- MODEBUS RTU TXD(%dB)(CRC:0x%04X):
    ",3+pFrame->DataLen+2,crc16);
            for(i = 0;i < 3+pFrame->DataLen+2;i ++)
            {
                modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
            }
            modebus_debug("
    ");
        }
    #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("
    <- MODEBUS RTU TXD(%dB)(CRC:0x%04X):
    ",8,crc16);
            for(i = 0;i < 8;i ++)
            {
                modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
            }
            modebus_debug("
    ");
        }
    #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("
    <- MODEBUS RTU TXD(%dB)(CRC:0x%04X):
    ",8,pFrame->crc16);
            for(i = 0;i < 8;i ++)
            {
                modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
            }
            modebus_debug("
    ");
        }
    #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
    ", Fun);
                    switch(Fun)
                    {
                        case MRTU_FUN_READ_HOLD        :                    //读保持寄存器,可读写寄存器为保持寄存器
                        case MRTU_FUN_READ_INPUT    :                    //读输入寄存器,为只读寄存器
                        {
                            //解析
                            MODEBUS_SLAVE_ReadUnpack(&ModebusHandle1, &FrameInfo);
                            //数据处理
                            uart_printf("主机读取从机(%d)从%d开始的寄存器,共需要读取%d个
    ", 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
    ", 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("
    ");
                            //返回
                            MODEBUS_SLAVE_ReturnWriteMultHoldReg(&ModebusHandle1, FrameInfo.SlaveAddr, FrameInfo.StartReg, FrameInfo.RegNum);
                        }break;
                        default:break;
                    }
                    
                }
                MODEBUS_EnableRx(ModebusHandle1.UartCh);    //使能接收
            }
            OSTimeDlyHMSM(0,0,0,200);
        }    
    }

    模拟主机

    从机接收处理

  • 相关阅读:
    shell getopt getopts获取参数
    apache+svn+ladp认证
    SVN 迁移项目分支
    iptables 优先级
    很实用的一篇HTTP状态码
    套路还在——矩阵计算估值
    CU上看到的一个简单的算法帖子
    linux下服务端实现公网数据转发
    c++接口实现与分离(转载)
    c++继承概念
  • 原文地址:https://www.cnblogs.com/LittleTiger/p/4681910.html
Copyright © 2011-2022 走看看