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

📄 uartwaitfunc.c

📁 Modbus串口通讯协议通讯源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
    INT32U dwReadNum = 1;                   //要读的次数
    INT32U dwReadNumBak;
    INT32U dwSingleReadSize;
    INT32S startadd,startbit;
    BOOL MultRead = FALSE;

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

    /**********************************************************
    检验传入参数的正确性,
    **********************************************************/
    if (pabyBitResult == NULL)
    {
        result =  ECOM_BADPARA;        //检验传入参数的正确性;
        goto  ReadBitError;            //错误返回
    }

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

    while (dwReadNum != 0)
    {
      /*确定每帧读的个数*/
      if (MultRead)
      {
        if (dwReadNum > 1)
        {
          dwSingleReadSize = g_iMaxFrameSize - 9;
        }
        else                    //最后一次发读命令
        {
          dwSingleReadSize = dwSize - (dwReadNumBak - 1) * (g_iMaxFrameSize - 9);
        }
      }
      else                      //单帧读
      {
        dwSingleReadSize = dwSize;
      }
     /*调用单帧读函数*/
     result = SingleFrameReadBitWait(byAddr, pabyBitResult + iRecvBitNum, DecTagBak, dwSingleReadSize);
     iRecvBitNum = iRecvBitNum + result;
     if (result > 0)       //读正确,修改相应DecTagBak
     {
       if (MultRead)
       {
         if (dwReadNum > 1)     //调用TagChange()函数,更改TAG地址
         {
           result = TagChange(DecTagBak, COMMAND_BIT_RD);
           if (result == 0)
           {
             result = ECOM_BADPARA;
             goto ReadBitError;
           }
         }
       }
       dwReadNum--;
       result = ECOM_NOERROR;
     }
     else                       //读错误,错误代码给result,错误返回
     {
       goto ReadBitError;
     }
   }                           //读位结束

   free(DecTagBak);
   return  iRecvBitNum;        //正确返回读取位个数

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

    /**********************************************************
    用来存储接收到的桢数据,dwSize乘2是因为字节返回时需要占据两个
    数据, 加6是因为要包括地址,命令和校验码6个字节的数据,加1是
    要增加字符串的结束符'\0'
    **********************************************************/
    INT8U* pszRecv = (INT8U *)malloc(dwSize * 2 + 6 + 1);
    
    /**********************************************************
    检验传入参数的正确性,dwSize乘2是因为字节返回时需要占据两个
    数据,加9是因为桢长还要包括前缀和后缀的长度
    **********************************************************/
    if ((pabyByteResult == NULL) || (dwSize * 2 + 9 > g_iMaxFrameSize))
    {
        result =  ECOM_BADPARA;   //检验传入参数的正确性;
        goto  SingleReadByteError;
    }

    if (!IsDecodeSuccess(pszDecTag, &(stcTagCmd.stcTag), TAG_COMMAND_READ))
    {
        result = ECOM_BADPARA;   //判断TAG是否有效,有效解码至stcTagCmd.stcTag;
        goto   SingleReadByteError;
    }

    if (ECOM_NOERROR == result)  //如果参数无错,则发送数据桢
    {
        stcTagCmd.byRange = dwSize;
        result = SendReadByteCommand(&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 SingleReadByteError;
    }

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

    if (result > 0)             //数据桢校验正确,则获取最终的解码后数据
    {
        iRecvByteNum = GetFinalByteData(pabyByteResult, pszRecv, iRecvTagLen);
        free(pszRecv);          //释放开辟的资源
        return iRecvByteNum;   //返回接受的字节个数
    }
    else
    {
        goto SingleReadByteError;
    }

SingleReadByteError:

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

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

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

EXAMPLE:  设备地址 = 22(DEC), TAG = MB14(DEC),读取连续5个字节,
          设正常情况下返加的5个值为十六进制{0x13, 0x12, 0x31, 0x33, 0xA5},
          若通讯错误,设错误代码为15(DEC)

发送帧
     帧头 设备地址  功能代码  TAG  MB14(DEC) = MB000E(HEX) 读取字节数  LRC校验
字节 _1__ ____2____ ____2____ ______________6______________ ____2____  ____2____
HEX  0x3A 0x31 0x36 0x30 0x33 0x4E 0x42 0x30 0x30 0x30 0x45 0x30 0x35  0x^^ 0x^^
ASC  ':'  '1'   '6'  '0' '3'   'M' 'B'  '0'   '0'  '0'  'E'   '0' '5'    '^' '^'
     结束符
     ____2____
HEX  0x0D 0x0A
ASC  '\r' '\n'

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

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

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

注意:0x^^代表实际运算的校验和, '^'表示对应的校验和的ASCII表达方式
*******************************************************************************/
/*******************************************************************************
函数名: COM_ReadByteWait
描  述: 读字节等待函数,执行该函数后可立即返回执行结果
输  入: byAddr        选用的下位机地址,如1,2等
        pabyByteResult 返回读取字节或字节区间的首地址,以16进制格式存放,如0x1F
        pszDecTag      读取字节的信息TAG,如MB14,详见通讯协议的方式
        dwSize         读取的字节的个数
输  出: pabyByteResult     存入读取的结果
返回值: INT32S 类型,其值表示函数的执行结果,为正数时表示读取的字节的个数,
        否则表示执行的错误代码,
*******************************************************************************/
INT32S WINAPI COM_ReadByteWait(INT8U byAddr, INT8U *pabyByteResult, const INT8S *pszDecTag, INT32U dwSize)
{
    INT32S result = ECOM_NOERROR;                          //记录错误返回值
    INT32S iRecvByteNum = 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 (pabyByteResult == NULL)
    {
        result =  ECOM_BADPARA;            //检验传入参数的正确性;
        goto  ReadByteError;
    }

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

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

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

    /**********************************************************
    用来存储接收到的桢数据,dwSize乘4是因为字返回时需要占据四个
    数据, 加6是因为要包括地址,命令和校验码6个字节的数据,加1是

⌨️ 快捷键说明

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