⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 uartcomm.c

📁 Modbus串口通讯协议通讯源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
描  述: 发送读字节信息,供读字节函数调用
输  入: byAddr    选用的下位机地址,如1,2等
        pstcTagCmd 读取字节的信息TAG,详见通讯协议的方式
输  出: 无
返回值: INT32S 类型,其值表示函数的执行结果,为0时则操作成功,否则表示执行的错误
        代码,调用GetErrMsg可获得详细的错误信息
*******************************************************************************/
INT32S SendReadByteCommand(const STC_TAG_COMMAND *pstcTagCmd, INT8U byAddr)
{
    INT32S len, result;
    INT8S* buf = (INT8S *)malloc(g_iMaxFrameSize + 1);  //用来存储发送的数据

    /*将buf中的数据格式化为通讯协议所要求的格式*/
    len = sprintf(buf, ":%02X%02X%C%C%04X%02X", byAddr, COMMAND_CHAR_RD,
                  pstcTagCmd->stcTag.cFlag, 'B',
                  pstcTagCmd->stcTag.addr, pstcTagCmd->byRange);

    len += sprintf(&buf[len], "%02X\x0d\x0a", CaculateLRC(buf + 1, len - 1));

    result =  sio_write(g_iComNumber, buf, len);

    free(buf); //释放buf指针

    #ifndef  FORTESTONLY                           //测试编译开关
      return result;
    #else
      return len;                                //for test only
    #endif

}
/*******************************************************************************
函数名: SendReadWordCommand
描  述: 发送读字信息,供读字函数调用
输  入: byAddr    选用的下位机地址,如1,2等
        pstcTagCmd 读取字的信息TAG,详见通讯协议的方式
输  出: 无
返回值: INT32S 类型,其值表示函数的执行结果,为0时则操作成功,否则表示执行的错误
        代码,调用GetErrMsg可获得详细的错误信息
*******************************************************************************/
INT32S SendReadWordCommand(const STC_TAG_COMMAND *pstcTagCmd, INT8U byAddr)
{
    INT32S len, result;
    INT8S* buf = (INT8S *)malloc(g_iMaxFrameSize + 1);  //用来存储发送的数据

    /*将buf中的数据格式化为通讯协议所要求的格式*/
    len = sprintf(buf, ":%02X%02X%C%C%04X%02X", byAddr, COMMAND_SHORT_RD,
                  pstcTagCmd->stcTag.cFlag, 'W',
                  pstcTagCmd->stcTag.addr, pstcTagCmd->byRange);

    len += sprintf(&buf[len], "%02X\x0d\x0a", CaculateLRC(buf + 1, len - 1));

    result =  sio_write(g_iComNumber, buf, len);

    free(buf); //释放buf指针

    #ifndef  FORTESTONLY                           //测试编译开关
      return result;
    #else
      return len;                                //for test only
    #endif

}

/*******************************************************************************
函数名: SendReadDWordCommand
描  述: 发送读双字信息,供读双字函数调用
输  入: byAddr    选用的下位机地址,如1,2等
        pstcTagCmd 读取字的信息TAG,详见通讯协议的方式
输  出: 无
返回值: INT32S 类型,其值表示函数的执行结果,为0时则操作成功,否则表示执行的错误
        代码,调用GetErrMsg可获得详细的错误信息
*******************************************************************************/
INT32S SendReadDWordCommand(const STC_TAG_COMMAND *pstcTagCmd, INT8U byAddr)
{
    INT32S len, result;
    INT8S* buf = (INT8S *)malloc(g_iMaxFrameSize + 1);  //用来存储发送的数据

    /*将buf中的数据格式化为通讯协议所要求的格式*/
    len = sprintf(buf, ":%02X%02X%C%C%04X%02X", byAddr, COMMAND_LONG_RD,
                  pstcTagCmd->stcTag.cFlag, 'D',
                  pstcTagCmd->stcTag.addr, pstcTagCmd->byRange);

    len += sprintf(&buf[len], "%02X\x0d\x0a", CaculateLRC(buf + 1, len - 1));

    result =  sio_write(g_iComNumber, buf, len);

    free(buf); //释放buf指针

    #ifndef  FORTESTONLY                           //测试编译开关
      return result;
    #else
      return len;                                //for test only
    #endif

}

/*******************************************************************************
函数名: GetModbusFrame
描  述: 获得返回桢的数据
输  入: pbyResult    返回读取位或位域的首地址,该参数应传递数据格式的参数
输  出: pbyResult     存入读取的结果
返回值: INT32S 类型,其值表示函数的执行结果,操作成功返回读取的字节数(不包括':'和
        '0x0d','0x0a'),否则表示执行的错误代码,调用GetErrMsg可获得详细的错误信息
*******************************************************************************/
INT32S GetModbusFrame(INT8U* const pbyResult)
{
    INT32S i;                           //pbyResult数组的索引
    INT32S step;                        //用来记录执行的情况
    INT32S ch;                          //从返回桢中读取的数据
    INT32U timeout = g_iMaxOutTime;  //设定最长等待时间
    INT32U currtime = GetTickCount();   //获取当前的时间

    i = step = 0;
    /******************************************************
    在给定等待的时间内,检测桢数据是否接收完整,如果接收完整
    (step = 3),则跳出循环,否则继续判断等待,直到到达给定的
    最大等待时间(g_iMaxOutTime).
    ******************************************************/
    while (step < 3 && GetTickCount() - currtime < timeout)
    {
        ch = sio_getch(g_iComNumber);

        if (ch < 0 || ch >= 256)
        {
            continue;
        }

        /*如果接收到':',则完成第一步*/
        if (0 == step && ':' == ch)
        {                                        
            i = 0;
            step = 1;
            continue;
        }

        /*完成第一步后,读取数据,直到读取到0x0d*/
        if (1 == step && ch != 0x0d)
        {
            if(!isxdigit(ch) || islower(ch))
            {
                step = 0;
            }
            else
            {
                pbyResult[i] = ch;
                i++;
            }
            continue;
        }
        else if(1 == step && 0x0d == ch)  //接收到0x0d,则完成第二步
        {
            step = 2;
            continue;
        }

        /*完成第二步后,如果接收0x0a,则读取完成,否则跳回起始情况*/
        if (2 == step && ch == 0x0a) 
        {
            pbyResult[i] = '\0';
            step = 3;
            break;
        }
        else if(2 == step && ch != 0x0a)
        {
            step = 0;
        }
    }

    /*如果接收完成,则返回接受的字符数目,否则返回ECOM_TIMEOUT错误*/
    return (step == 3) ? i : ECOM_TIMEOUT;
}

/*******************************************************************************
函数名: IsHexFrame
描  述: 查看返回桢是否是16进制格式
输  入: byArray        待检查的字符串
        len             字符串的长度
输  出:
返回值: BOOL 类型,true表示字符串符合要求,false则不符合
*******************************************************************************/
static BOOL IsHexFrame(const INT8U *byArray, INT32U len)
{
    INT32U i = 0;
    for (; i < len; i++)
    {
        if (isalpha(byArray[i]) || isdigit(byArray[i]))
        {
            continue;
        }
        else
        {
            return FALSE;    
        }
    }
    return TRUE;
}

/*******************************************************************************
函数名: CheckModbusLRC
描  述: 校验返回桢的校验码是否与发送时一致
输  入: pbyResult      待检查的字符串
        len             字符串的长度
输  出:
返回值: BOOL 类型,true表示字符串符合要求,false则不符合
*******************************************************************************/
static BOOL CheckModbusLRC(const INT8U *pbyResult, INT32U len)
{
    INT8U lrc;
    lrc = CaculateLRC(pbyResult, len - 2);
    return (lrc == AsciiToByte(pbyResult + len - 2)) ? TRUE : FALSE;
}

/*******************************************************************************
函数名: ModbusFrameCheck
描  述: 校验返回桢的数据
输  入: pbyResult    待校验桢的首地址,该参数应传递数据格式的参数(不包括':'和
                      '0x0d','0x0a')
        len           待校验桢的长度,不包括':'和'0x0d','0x0a'
        byAddr        下位机的地址,1,2等
        enumCmd       命令类型格式,COMMAND_BIT_WR,COMMAND_CHAR_WR等
        dwSize        读取的数据(位,字节,字等)个数
输  出:
返回值: INT32S 类型,其值表示函数的执行结果,操作成功返回读取的字节数(不包括':'和
        '0x0d','0x0a'),否则表示执行的错误代码,调用GetErrMsg可获得详细的错误信息
*******************************************************************************/
INT32S ModbusFrameCheck(const INT8U *pbyResult, INT32U len, INT8U byAddr, EUartCommand enumCmd, INT32U dwSize)
{
    INT8U byPlcAddr = AsciiToByte(pbyResult);
    INT8U byPlcCmd = AsciiToByte(pbyResult + 2);

    /*检测返回的数据是否有误*/
    if (len < 6 || !IsHexFrame(pbyResult, len))
    {
        return ECOM_RECVDATA;
    }
    else if (!CheckModbusLRC(pbyResult, len))
    {
        return ECOM_RECVDATA;
    }

    if (byPlcAddr != byAddr) return ECOM_RECVDATA;
    if ((byPlcCmd & 0x7F) != enumCmd) return ECOM_RECVDATA;

    /***********************************************
    如果byPlcCmd大于0x80,则返回错误代码加0x80,该错误
    代码由下位机给出,加0x80是以区别上位机设定的错误
    ***********************************************/
    if (byPlcCmd >= 0x80)
    {
        return -(0x80 + AsciiToByte(pbyResult + 4));
    }

    switch (enumCmd)
    {
    case COMMAND_BIT_RD:
        if (len - 6 != dwSize) return ECOM_RECVDATA;
        else break;
    case COMMAND_CHAR_RD:
        /*范围乘2是因为字节返回时是用两个ASCII码来表示的*/
        if (len - 6 != dwSize * 2) return ECOM_RECVDATA;
        else break;
    case COMMAND_SHORT_RD:
        /*范围乘4是因为字返回时是用四个ASCII码来表示的*/
        if (len - 6 != dwSize * 4) return ECOM_RECVDATA;
        else break;
    case COMMAND_LONG_RD:
    case COMMAND_PROGRAM_RD:
        /*范围乘8是因为双字返回时是用八个ASCII码来表示的*/

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -