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

📄 uartwaitfunc.c

📁 Modbus串口通讯协议通讯源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
    要增加字符串的结束符'\0'
    **********************************************************/
    INT8U* pszRecv = (INT8U *)malloc(dwSize * 4 + 6 + 1);

    /**********************************************************
    检验传入参数的正确性,dwSize乘4是因为字节返回时需要占据两个
    数据,加9是因为桢长还要包括前缀和后缀的长度
    **********************************************************/
    if ((pawWordResult == NULL) || (dwSize * 4 + 9 > g_iMaxFrameSize))
    {
        result =  ECOM_BADPARA;   //检验传入参数的正确性;
        goto   SingleReadWordError;
    }

    if (!IsDecodeSuccess(pszDecTag, &(stcTagCmd.stcTag), TAG_COMMAND_READ))
    {
        result = ECOM_BADPARA;  //首先检验TAG正确,若正确解码至stcTagCmd.stcTag;
        goto   SingleReadWordError;
    }


    if (ECOM_NOERROR == result)                     //如果参数无错,则发送数据桢
    {
        stcTagCmd.byRange = dwSize;
        result = SendReadWordCommand(&stcTagCmd, byAddr);
    }

    if (result >= 0)                                //数据桢发送正确,则接收数据桢
    {
        #ifndef  FORTESTONLY                        //测试编译开关
          iRecvTagLen = GetModbusFrame(pszRecv);
        #else
          extern INT8U  testbuf[50];
          extern INT32S testsize;
          iRecvTagLen = testsize;
          pszRecv = testbuf;
        #endif
    }
    else
    {
        goto   SingleReadWordError;
    }

    if (iRecvTagLen < 0)
    {
        result = iRecvTagLen;   //接收不正确,错误代码给result
        goto   SingleReadWordError;
    }
    else                         //数据桢接收正确,则进行校验
    {
        result = ModbusFrameCheck(pszRecv, iRecvTagLen, byAddr, COMMAND_SHORT_RD, dwSize);
    }

    if (result > 0)             //数据桢校验正确,则获取最终的解码后数据
    {
        iRecvWordNum = GetFinalWordData(pawWordResult, pszRecv, iRecvTagLen);

        free(pszRecv);          //释放开辟的资源
        return iRecvWordNum;   //返回接受的字个数
    }
    else
    {
        goto  SingleReadWordError;
    }

SingleReadWordError:

    free(pszRecv);             //释放开辟的资源
    return result;
}

/*******************************************************************************
读字通讯帧, 协议为MODBUS协议ASCII方式

协议组成(':' + 设备地址 + 功能代码(0x05) + 字TAG地址 + 读取字数 + LRC校验 + 结束符)

EXAMPLE:  设备地址 = 22(DEC), TAG = MW22(DEC), MW22占用MB22和MB23两个字节,
          设读取连续2个字(读取值为MW22、MW24) 设MW22 = 0x1314, MW24 = 0x3245,
          若通讯错误,设错误代码为15(DEC)

发送帧
     帧头 设备地址  功能代码  TAG  MW22(DEC) = MW0016(HEX)  读取字数   LRC校验
字节 _1__ ____2____ ____2____ ______________6______________ ____2____  ____2____
HEX  0x3A 0x31 0x36 0x30 0x35 0x4E 0x57 0x30 0x30 0x31 0x36 0x30 0x32  0x^^ 0x^^
ASC  ':'  '1'   '6'  '0' '5'   'M' 'W'  '0'   '0'  '1'  '6'   '0' '2'    '^' '^'
     结束符
     ____2____
HEX  0x0D 0x0A
ASC  '\r' '\n'

正常返回帧
     帧头 设备地址  功能代码      读取值(占用字节数 = 读取字数 * 4)
字节 _1__ ____2____ ____2____ ___________________8____________________
HEX  0x3A 0x31 0x36 0x30 0x35 0x31 0x33 0x31 0x34 0x33 0x32 0x34  0x35
ASC  ':'  '1'   '6'  '0' '5'   '1'  '3'  '1'  '4'  '3'  '2'  '4'  '5'

     LRC校验     结束符
字节 ____2____   ____2____
HEX  0x^^ 0x^^   0x0D 0x0A
ASC  '^' '^'    '\r' '\n'

错误返回帧
     帧头 设备地址  功能代码  错误代码    LRC校验     结束符
字节 _1__ ____2____ ____2____ ____2____  ____2____   ____2____
HEX  0x3A 0x31 0x36 0x38 0x35 0x30 0x46  0x^^ 0x^^   0x0D 0x0A
ASC  ':'  '1'   '6'  '8' '5'   '0'  'F'   '^' '^'    '\r' '\n'

注意:0x^^代表实际运算的校验和, '^'表示对应的校验和的ASCII表达方式
*******************************************************************************/

/*******************************************************************************
函数名: COM_ReadWordWait
描  述: 读字等待函数,执行该函数后可立即返回执行结果
输  入: byAddr        选用的下位机地址,如1,2等
        pawWordResult  返回读取字或字区间的首地址,以16进制存放,如0x131A
        pszDecTag      读取字的信息TAG,如MW22,详见通讯协议的方式
        dwSize         读取的字的个数
输  出: pawResult      存入读取的结果
返回值: INT32S 类型,其值表示函数的执行结果,为正数时表示读取的字的个数,
        否则表示执行的错误代码,
*******************************************************************************/
INT32S WINAPI COM_ReadWordWait(INT8U byAddr, INT16U *pawWordResult,const INT8S *pszDecTag, INT32U dwSize)
{
    INT32S result = ECOM_NOERROR;                          //记录返回值
    INT32S iRecvWordNum = 0;                               //记录读取的字的个数

    INT32U dwReadNum = 1;                                 //要读的次数
    INT32U dwReadNumBak;
    INT32U dwSingleReadSize;
    INT32S startadd;                                      //起始偏移地址
    BOOL MultRead = FALSE;

    /*存操作的tag,以便修改*/
    INT8S *DecTagBak = (INT8S *)malloc(10);
    sscanf(pszDecTag + 2, "%d", &startadd);
    sprintf(DecTagBak,"%C%C%01d",*pszDecTag, *(pszDecTag + 1), startadd);

    /**********************************************************
    检验传入参数的正确性,
    **********************************************************/
    if (pawWordResult == NULL)
    {
        result =  ECOM_BADPARA;             //检验传入参数的正确性;
        goto   ReadWordError;
    }

    if (dwSize * 4 + 9 > g_iMaxFrameSize)  //多帧读
    {
      MultRead = TRUE;
      if (dwSize % ((g_iMaxFrameSize - 9) / 4))
        dwReadNum = dwSize / ((g_iMaxFrameSize - 9) / 4) + 1;
      else
        dwReadNum = dwSize / ((g_iMaxFrameSize - 9) / 4);
    }
    dwReadNumBak = dwReadNum;

    while (dwReadNum != 0)
    {
      /*确定每帧读的个数*/
      if (MultRead)
      {
        if (dwReadNum > 1)
        {
          dwSingleReadSize = (g_iMaxFrameSize - 9) / 4;
        }
        else                      //最后一次发读命令
        {
          dwSingleReadSize = dwSize - (dwReadNumBak - 1) * ((g_iMaxFrameSize - 9) / 4);
        }
      }
      else                        //单帧读
      {
        dwSingleReadSize = dwSize;
      }
      /*调用单帧读函数*/
      result = SingleFrameReadWordWait(byAddr, pawWordResult + iRecvWordNum, DecTagBak, dwSingleReadSize);
      iRecvWordNum = iRecvWordNum + result;
      if (result > 0)             //读正确,修改相应TAG地址
      {
        if (MultRead)
        {
          if (dwReadNum > 1)    //调用TagChange()函数,更改TAG地址
          {
            result = TagChange(DecTagBak, COMMAND_SHORT_RD);
            if (result == 0)
            {
             result = ECOM_BADPARA;
             goto ReadWordError;
            }
          }
        }
        dwReadNum--;
        result = ECOM_NOERROR;
      }
      else
      {
        goto  ReadWordError;
      }
    }
    free(DecTagBak);          //释放开辟的资源
    return iRecvWordNum;      //返回接受的字个数

ReadWordError:
    free(DecTagBak);          //释放开辟的资源
    return result;
}

/*******************************************************************************
函数名: SingleFrameReadDWordWait
描  述: 读双字等待函数,执行该函数后可立即返回执行结果
输  入: byAddr          选用的下位机地址,如1,2等
        padwDWordResult  返回读取字或字区间的首地址,该参数应传递数据格式的参数
        pszDecTag        读取字的信息TAG,详见通讯协议的方式
        dwSize           读取的字的个数
输  出: padwResult       存入读取的结果
返回值: INT32S 类型,其值表示函数的执行结果,为正数时表示读取的双字的个数,
        否则表示执行的错误代码,
*******************************************************************************/
static SingleFrameReadDWordWait(INT8U byAddr, INT32U *padwDWordResult, const INT8S *pszDecTag, INT32U dwSize)
{
    STC_TAG_COMMAND stcTagCmd;                             //定义操作变量stcTagCmd
    INT32S result = ECOM_NOERROR;                          //记录返回值
    INT32S iRecvDWordNum;                                 //记录读取的双字的个数
    INT32S iRecvTagLen = 0;                               //返回桢的字节数

    /**********************************************************
    用来存储接收到的桢数据,dwSize乘8是因为双字返回时需要占据八个
    数据, 加6是因为要包括地址,命令和校验码6个字节的数据,加1是
    要增加字符串的结束符'\0'
    **********************************************************/
    INT8U* pszRecv = (INT8U *)malloc(dwSize * 8 + 6 + 1);

    /**********************************************************
    检验传入参数的正确性,dwSize乘8是因为字节返回时需要占据两个
    数据,加9是因为桢长还要包括前缀和后缀的长度
    **********************************************************/
    if ((padwDWordResult == NULL) || (dwSize * 8 + 9 > g_iMaxFrameSize))
    {
        result =  ECOM_BADPARA;   //检验传入参数的正确性;
        goto   SingleReadDWordError;
    }

    if (!IsDecodeSuccess(pszDecTag, &(stcTagCmd.stcTag), TAG_COMMAND_READ))
    {
        result = ECOM_BADPARA;  //首先检验TAG正确,若正确解码至stcTagCmd.stcTag;
        goto   SingleReadDWordError;
    }


    if (ECOM_NOERROR == result)                    //如果参数无错,则发送数据桢
    {
        stcTagCmd.byRange = dwSize;

        result = SendReadDWordCommand(&stcTagCmd, byAddr);
    }

    if (result >= 0)                              //数据桢发送正确,则接收数据桢
    {
        #ifndef  FORTESTONLY                      //测试编译开关
          iRecvTagLen = GetModbusFrame(pszRecv);
        #else
          extern INT8U  testbuf[50];
          extern INT32S testsize;
          iRecvTagLen = testsize;
          pszRecv = testbuf;
        #endif
    }
    else
    {
        goto   SingleReadDWordError;
    }

    if (iRecvTagLen < 0)
    {
        result = iRecvTagLen;   //接收不正确,错误代码给result
        goto   SingleReadDWordError;
    }
    else                         //数据桢接收正确,则进行校验
    {
        result = ModbusFrameCheck(pszRecv, iRecvTagLen, byAddr, COMMAND_LONG_RD, dwSize);
    }

    if (result > 0)             //数据桢校验正确,则获取最终的解码后数据
    {
        iRecvDWordNum = GetFinalDWordData(padwDWordResult, pszRecv, iRecvTagLen);

        free(pszRecv);          //释放开辟的资源
        return iRecvDWordNum;  //返回接受的字个数
    }
    else
    {
        goto  SingleReadDWordError;
    }

SingleReadDWordError:

    free(pszRecv);             //释放开辟的资源
    return result;
}
/*******************************************************************************
读双字通讯帧, 协议为MODBUS协议ASCII方式

协议组成(':' + 设备地址 + 功能代码(0x07) + 双字TAG地址 + 读取双字数 + LRC校验 + 结束符)

EXAMPLE:  设备地址 = 22(DEC), TAG = MD22(DEC), MD22占用MB22和MB23,MB24,MB26共四个字节,
          设读取连续1个双字,设MD22 = 0x13143245,
          若通讯错误,设错误代码为15(DEC)

发送帧
     帧头 设备地址  功能代码  TAG  MW22(DEC) = MW0016(HEX)  读取字数   LRC校验
字节 _1__ ____2____ ____2____ ______________6______________ ____2____  ____2____
HEX  0x3A 0x31 0x36 0x30 0x37 0x4E 0x44 0x30 0x30 0x31 0x36 0x30 0x31  0x^^ 0x^^
ASC  ':'  '1'   '6'  '0' '7'   'M' 'D'  '0'   '0'  '1'  '6'   '0' '1'    '^' '^'
     结束符
     ____2____
HEX  0x0D 0x0A
ASC  '\r' '\n'

正常返回帧
     帧头 设备地址  功能代码      读取值(占用字节数 = 读取双字数 * 8)
字节 _1__ ____2____ ____2____ ___________________8____________________
HEX  0x3A 0x31 0x36 0x30 0x37 0x31 0x33 0x31 0x34 0x33 0x32 0x34  0x35
ASC  ':'  '1'   '6'  '0' '7'   '1'  '3'  '1'  '4'  '3'  '2'  '4'  '5'

     LRC校验     结束符
字节 ____2____   ____2____

⌨️ 快捷键说明

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