📄 uartcomm.c
字号:
描 述: 发送读字节信息,供读字节函数调用
输 入: 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 + -