CANBUS高层协议-周立功公司推出的iCAN协议学习笔记
点击此处下载 ourdev_429047.pdf(文件大小:1.31M) (原文件名:iCAN协议(1.0).pdf) 点击此处下载 ourdev_429048.rar(文件大小:3K) (原文件名:ican.rar) ////////////////////////////////////////////////////////////////////////////////////FuncID 功能 描述
//0x00 Reserve 保留功能码
//0x01 连续写端口 用于对单个或者多个资源节点的数据写入
//0x02 连续读端口 用于读取单个或者多个资源节点的数据
//0x03 事件触发传送 用于从站主动向已建立连接的主站传送数据
//0x04 建立连接 用于和iCAN从站建立通讯连接
//0x05 删除连接 用于删除与iCAN从站的通讯连接
//0x06 设备复位 用于复位iCAN从站
//0x07 MACID检测 用于检测网络上是指定MACID从站是否存在
//0x08-0x0e Reserve 保留功能码
//0x0f 异常响应 从站不能正确处理接收到的命令帧时,发送异常响应帧
////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
//函数名称:=ICAN_SourceTest
//功能描述:资源检测
///////////////////////////////////////////////////////////////////////////////////////////
//入口参数:
// order=
// sourceid=资源节点地址
// sourcelenth=资源节点数量
// readorwrite=
// 0:读资源标志
// 1:写资源标志
//返回参数:
// 1=失败
// 0=成功
////////////////////////////////////////////////////////////////////////////////
uchar ICAN_SourceTest(uchar order,uchar sourceid,uchar sourcelenth,uchar readorwrite)
{
uchar data i;
//-----------------------------------------------------
//检测要读写的资源是否落在DI区域(0x00~0x1F)
//-----------------------------------------------------
if (sourceid < 0x20) // DI区域
{
i = SourceType; //取出当前从站的《开关量输入总数》
if (readorwrite) // 写资源请求吗?
return 1; // 返回失败
}
//-----------------------------------------------------
//检测要读写的资源是否落在DO区域(0x20~0x3F)
//-----------------------------------------------------
else if (sourceid < 0x40) // DO区域
{
i= SourceType ; //取出当前从站的《开关量输出总数》
sourceid -= 0x20;//????????????
if (readorwrite == 0) // 读资源请求吗?
return 1; // 返回失败
}
//-----------------------------------------------------
//检测要读写的资源是否落在AI区域(0x40~0x5F)
//-----------------------------------------------------
else if (sourceid < 0x60) // AI区域
{
i= SourceType ; //取出当前从站的《模拟量输入总数》
sourceid -= 0x40;//???????????
if (readorwrite) // 写资源请求吗?
return 1; // 返回失败
}
//-----------------------------------------------------
//检测要读写的资源是否落在AI区域(0x60~0x7F)
//-----------------------------------------------------
else if (sourceid < 0x80) // AO区域
{
i= SourceType; //取出当前从站的《模拟量输出总数》
sourceid -= 0x60;//?????????????????????
if (readorwrite == 0) // 读资源请求吗?
return 1; // 返回失败
}
//-----------------------------------------------------
//其他区域为可读写了
//-----------------------------------------------------
else
{
return 0; // 可读写区域(此处为什么不检查资源节点数量是否超出区域呢?)
}
if (i==0) // 如果当前从站的资源类型数量(DI/DO/AI/AO)=0
return 1; // 返回失败
if (sourcelenth + sourceid > i) // 如果读取资源节点地址+资源节点数量>从站的资源类型数量(DI/DO/AI/AO)
return 1; // 返回失败
return 0; // 返回成功
}
///////////////////////////////////////////////////////////////////////////////////////////
//函数名称:=ICAN_ObjIDManage
//功能描述:ICAN报文标识码生成
// 本子程序的目的是将结构体指针ICAN_ObjID中保存的上述信息,
// 合并到一个long型变量ulObjId中。
///////////////////////////////////////////////////////////////////////////////////////////
//入口参数:
// ICAN_ObjID=指向iCAN报文标识码的结构体指针
//返回参数:
// ulObjId=合成后的iCAN报文标识码
//说明:iCAN协议的报文格式如下:
// (1)、ID28-ID21=SrcMACID (源节点地址))
// (2)、ID20-ID13=DestMACID((目标节点地址)
// (3)、ID12=ACK
// (4)、ID11-ID8=FuncID(功能码)
// (5)、ID7-ID0=Source ID(资源节点地址)
///////////////////////////////////////////////////////////////////////////////////////////
uint32 ICAN_ObjIDManage(ICAN_OBJ* ICAN_ObjID)
{
uint32 xdata ulObjId;
ulObjId = 0;
ulObjId = (ICAN_ObjID->SrcMACID << 21) + // 源节点编号
(ICAN_ObjID->DestMACID << 13) + // 目标节点编号
(ICAN_ObjID->ACK << 12) + // 应答标志
(ICAN_ObjID->FUNCID << 8) + // 功能码
(ICAN_ObjID->SourceID); // 资源节点编号
return (ulObjId);
} ///////////////////////////////////////////////////////////////////////////////////////////
//函数名称:=ICAN_ObjIDRestore
//功能描述:ICAN报文标识码还原
// 将long型变量Receive_ObjID中保存的iCAN报文标识码分离到结构体指针*ICAN_ObjID。
///////////////////////////////////////////////////////////////////////////////////////////
//入口参数:
// Receive_ObjID=ICAN报文标识码
//出口参数:
// * ICAN_ObjID=结构体指针
///////////////////////////////////////////////////////////////////////////////////////////
void ICAN_ObjIDRestore(ICAN_OBJ *ICAN_ObjID, uint32 Receive_ObjID)
{
ICAN_ObjID->SrcMACID = (Receive_ObjID >> 21) & 0x000000FF; // 源节点编号(ID28-ID21)
ICAN_ObjID->DestMACID = (Receive_ObjID >> 13) & 0x000000FF; // 目标节点编号(ID20-ID13)
ICAN_ObjID->ACK = (Receive_ObjID >> 12) & 0x00000001; // 应答标志(ID12)
ICAN_ObjID->FUNCID = (Receive_ObjID >> 8) & 0x000000F; // 功能码(ID11-ID8)
ICAN_ObjID->SourceID = (Receive_ObjID) & 0x000000FF; // 资源节点编号(ID7-ID0)
}
///////////////////////////////////////////////////////////////////////////////////////////
//函数名称:=ICAN_ObjByte0Manage
//功能描述:生成iCAN报文数据段0字节BYTE0(SegFlag,多帧传输及单帧传输)
///////////////////////////////////////////////////////////////////////////////////////////
//入口参数:
// TxCtr=读数据指针????????????????????????????????????
// Lenth=报文长度(数据场长度)
//出口参数:
// CAN报文数据段0字节(BYTE0)=SegFlag
///////////////////////////////////////////////////////////////////////////////////////////
uchar ICAN_ObjByte0Manage(uint TxCtr, uint Lenth)
{
uchar ucTemp;
//--------------------------------------------------------------------------
//小于8字节,本次数据传输没有分段,分段标志SegFlag=0(iCAN报文一次最多只能传输7个字节)
//--------------------------------------------------------------------------
if (Lenth < 8)
{
ucTemp = 0; // BYTE0=0x00
}
else
{
//--------------------------------------------------------------------------
//批量数据的第一个分段(SegPolo=01=0x40《bit7,bit6》,批量数据传输的第1个分段;此时,SegNum=0x00值)
//--------------------------------------------------------------------------
if (TxCtr == 0) // 如果读数据指针=0
{
ucTemp = 0x40; // BYTE0=0x40
}
//--------------------------------------------------------------------------
//批量数据的最后一个分段(SegPolo=11=0xC0《bit7,bit6》,最后分段)
//--------------------------------------------------------------------------
else if (TxCtr + 7 >= Lenth) // 如果读数据指针+7>=报文长度
{
ucTemp = 0xC0; // BYTE0=0xC0
}
else
{
if (TxCtr > 448) // 发送数据超过448字节
{
return (0xFF);
}
//--------------------------------------------------------------------------
//中间数据段(中间分段SegPolo=10。SegNum值从0x01起,每次加1,以区分段数)
//--------------------------------------------------------------------------
ucTemp = TxCtr / 7 + 0x80; // 计数对应的段数,并标识为中间段
} // BYTE0=0x80+分段编号(读数据指针/7)
}
return (ucTemp); // 生成标识符
}
///////////////////////////////////////////////////////////////////////////////////////////
//函数名称:=CAN_ObjByte0Restore
//功能描述:iCAN报文数据段0字节生成(获取分段标识)
// 应用于连续写端口命令。根据接收报文中的第一个字节(分段码)来区分多帧数据中到底是
// 哪一帧。(参见iCAN协议)
///////////////////////////////////////////////////////////////////////////////////////////
//入口参数:
// *RxCtr=接收缓冲区数据接收指针
// segflag=接收报文第一字节=分段码
//出口参数:
// *RxCtr=数据接收指针
// ucTemp:
// 0=接收的报文中没有分段
// 1=当前是接收报文的第一分段
// segflag & 0x3f=当前是接收报文的中间分段(因为SegPolo占用bit7,bit6,分段号占用bit5,bit4,bit3,bit2,bit1,bit0,因此用0x3F分离出分段号)
// 65=当前是接收报文的最后一个分段
//-----------------------------------------------------------------------------
uchar ICAN_ObjByte0Restore(uint * RxCtr, uchar segflag)
{
uchar ucTemp;
//-----------------------------------------------------------------------------
//无分段标识(SegPolo=00)
//-----------------------------------------------------------------------------
if (segflag == 0) // 没有分段
{
*RxCtr = 0x00; // 接收数据指针指向零
ucTemp = 0x00; // 0=没有分段
}
//-----------------------------------------------------------------------------
//最后分段标识(SegPolo=11)
//-----------------------------------------------------------------------------
else if ((segflag & 0xC0) == 0xC0) // 批量数据的最后一个分段
{
ucTemp = 65; // 65=最后一个分段
}
//-----------------------------------------------------------------------------
//第一分段标识(SegPolo=01)
//-----------------------------------------------------------------------------
elseif ((segflag & 0x40) == 0x40) // 启始段,批量数据的第一个分段
{
*RxCtr = 0x00; // 接收数据指针指向零
ucTemp = 0x01; // 1=第一分段
}
//-----------------------------------------------------------------------------
//中间分段标识(SegPolo=10)
//-----------------------------------------------------------------------------
else // 中间数据段
{
ucTemp = segflag & 0x3f; // 返回中间分段号
*RxCtr = (segflag&0x3f)*7; // 接收数据指针
}
return (ucTemp); // 生成标识符
}
//*********************************************************************************************************
//定义iCAN帧ID码节点结构体
// ********************************************************************************************************
//typestruct { // 帧ID码
//
// uint32SrcMACID;
// uint32DestMACID;
// uint32ACK;
// uint32FUNCID;
// uint32SourceID;
//} ICAN_OBJ;
//typestruct { // ICAN读写缓冲
//
// uint32 ulID; // 发送的ID代码
// uintucDataLenth; // 数据场长度
// uintucDataCtr; // 读数据指针
// uchar ucXID; //0 标准帧;1 扩展帧
// uchar TrBegin;
// uchar ucDataBuff; // 数据缓冲(32)字节
//} tICAN_TRBUFF;
///////////////////////////////////////////////////////////////////////////////////////////
//函数名称=ICAN_ErrIDACK(uchar order)
//功能描述=填写错误报文
//-----------------------------------------------------------------------------------------
// 1、从iCAN帧头结构体(ICANSlav_ObjID)取出“资源节点编号”-------
// “源节点编号” |------>填写到结构体ICAN_ObjIDTmp
// “目标节点编号”-------
//
// “应答”=1 -------
// |------>填写到结构体ICAN_ObjIDTmp
// “功能码”=0x0F-------
// 其中结构体ICAN_ObjIDTmp保存iCAN帧头信息,包括如下:
// 源节点编号、目标节点编号、应答位、功能码、资源节点编号
// 2、填写iCAN读写缓冲结构体
// (1)、填写发送的ID代码=(源节点编号、目标节点编号、应答位、功能码、资源节点编号)
// (2)、填写扩展帧
// (3)、填写数据缓冲区=错误代码(只填写一个字节)
// (4)、填写发送报文长度=2字节(一个字节用来标识单帧/多帧,另一个字节=错误码)
// (5)、填写读数据指针=0
// (6)、填写发送开始标志
//-----------------------------------------------------------------------------------------
//入口参数:
// order=
// ErrID=错误号
///////////////////////////////////////////////////////////////////////////////////////////
void ICAN_ErrorIDACK(uchar order,uchar ErrID)
{
ICAN_OBJ xdata ICAN_ObjIDTmp;
ICAN_ObjIDTmp.SourceID = ICANSlav_ObjID.SourceID; // 资源节点编号
ICAN_ObjIDTmp.FUNCID = 0x0F; // 功能码
ICAN_ObjIDTmp.ACK = 1; // 应答标志
ICAN_ObjIDTmp.DestMACID = ICANSlav_ObjID.SrcMACID; // 目标节点编号
ICAN_ObjIDTmp.SrcMACID = ICANSlav_ObjID.DestMACID; // 源节点编号
ICAN_TrBuff.ulID = ICAN_ObjIDManage(&ICAN_ObjIDTmp); // 生成ICAN报文ID
ICAN_TrBuff.ucXID = 1; //0 标准帧;1 扩展帧
ICAN_TrBuff.ucDataBuff = ErrID; //返回对应的错误代码
ICAN_TrBuff.ucDataLenth = 2; //数据场长度1字节
ICAN_TrBuff.ucDataCtr = 0; //读数据指针指0字节
ICAN_TrBuff.TrBegin = 1; // 发送开始标志置位
}
uchar function = 0;
//*********************************************************************************************************
//定义CAN报文结构体
// ********************************************************************************************************
//typestruct _tCANFrame {
// uchar ucXID; // 0 标准帧(CAN2.0A);1 扩展帧(CAN2.0B)
// uchar ucDLC; // 数据场长度(DLC)
// uint32 ulID; // CAN报文ID(iCAN帧标识符)
// uchar ucDataBuff; // 报文数据场
} tCANFrame;
///////////////////////////////////////////////////////////////////////////////////////////
//函数名称=iCAN_ConnectACK(uchar order, tCANFrame *pCANFrame)
//功能描述=建立连接命令(功能码=0x04,用于和iCAN从站建立通讯连接)
//★★★命令帧格式
// CAN帧ID CAN帧数据部分
//----------------------------------------------------------------------------------------------------------------
// 源节点目标节点 ACK=0 功能码=0x04 资源节点=0xF7 | 分段码=0x00 MasterMACID CyclicMaster
//
//★★★正常响应帧格式
// CAN帧ID CAN帧数据部分
//----------------------------------------------------------------------------------------------------------------
// 源节点目标节点 ACK=1 功能码=0x04 资源节点=0xF7 | 分段码=0x00 DILen DOLenAILenAOLen
//
//★★★异常响应帧格式
// CAN帧ID CAN帧数据部分
//----------------------------------------------------------------------------------------------------------------
// 源节点目标节点 ACK=1 功能码=0x0F 资源节点=0xF7 | 分段码=0x00 ERRID
//说明:
//(1)、
// 在“建立连接”命令帧中源节点地址为主站 MACID,报文数据长度为 3 个字节。报文数据部分第一个字节表示分段码
// (固定为 0x00) ;报文数据第二个字节表示主站 MACID;报文数据的第三个字节表示主站定时循环参数(CyclicMaster)
//(2)、CyclicMaster的单位为 10ms,当 CyclicMaster > 0时, (CyclicMaster*4)时间为从站判断主站发送通讯报文
// 的是否超时的时间间隔,在通讯过程中,如果从站在(CyclicMaster*4)时间内未收到主站的命令报文,将自动删除连接,
// 退出通讯。当CyclicMaster为 0 时,从站不检测通讯超时,直到收到正确的“删除连接”命令帧时,才删除与主站的连接。
//----------------------------------------------------------------------------------------------------------------
//入口参数:
// order=
// *pCANFrame=CAN报文结构体指针(保存DLC、iCAN帧头、iCAN报文)
//出口参数:
// 无
//-----------------------------------------------------------------------------
void iCAN_ConnectACK(uchar order, tCANFrame *pCANFrame)
{
ICAN_OBJ xdata ICAN_ObjIDTmp; // iCAN帧头结构体
//----------------------------------------------------------------------------
//检测之前是否已经建立连接成功
//----------------------------------------------------------------------------
if (function >= 1) // 如果之前已经建立过连接,两次建立连接则报错
{
ICAN_ErrorIDACK(order,ErrID_NotCommand);
}
//----------------------------------------------------------------------------
//检测功能码=0x04
//----------------------------------------------------------------------------
else if (pCANFrame->ulID&0x00000400!=0x00000400)
{
ICAN_ErrorIDACK(order,ErrID_NotFunctionID); // 功能码不对;
}
/* else if(ICAN_Source.Area!=pCANFrame->ucDataBuff)
{
ICAN_ErrorIDACK(order,ErrID_NotCommand);//错误代码返回报文(命令不技持,主站ID不对);
}
*/
//----------------------------------------------------------------------------
//检测分段码=0(主站请求报文的第一个字节必须=0,即分段码=0)
//----------------------------------------------------------------------------
else if ((pCANFrame->ucDataBuff!=0))
{
ICAN_ErrorIDACK(order,ErrID_ParameterError); // 错误代码返回报文(参数非法)
}
//----------------------------------------------------------------------------
//填写应答报文给主站分三步骤:
//(1)、从报文中读取主站节点的MACID和通信节拍数
//(2)、填写iCAN报文帧头结构体ICAN_ObjIDTmp
// SrcMACID=源结点编号
// DestMACID=目标结点编号
// ACK=1
// FUNCID=0x04=功能码
// SourceID=0xF7=资源节点编号
//(3)、填写iCAN读写缓冲区结构体ICAN_TrBuff
// ulID=iCAN帧头格式合成(ID28-ID0)
// ucXID = 1(扩展帧),iCAN只支持扩展帧
// ucDataLenth=5,发送报文长度(DLC=5);分段码占用一字节,DIlen占用一字节,DOlen占用一字节,AIlen占用一字节,AOlen占用一字节(共计5字节)
// ucDataBuff=报文数据场2字节(DI资源数量)
// ucDataBuff=报文数据场3字节(DO资源数量)
// ucDataBuff=报文数据场4字节(AI资源数量)
// ucDataBuff=报文数据场5字节(AO资源数量)
// TrBegin=发送报文标志
//----------------------------------------------------------------------------
else
{
ICAN_Source.Area = pCANFrame->ucDataBuff; // 读取主站节点的MACID
ICAN_Source.Area = pCANFrame->ucDataBuff; // 读取主站节点定时通信节拍数
//----------------------------------
//填写iCAN帧头结构体信息
//----------------------------------
ICAN_ObjIDTmp.SourceID = 0xF7; // 资源节点编号
ICAN_ObjIDTmp.FUNCID = 0x04; // 功能码
ICAN_ObjIDTmp.ACK = 1; // 应答标志(从站返回应答报文)
ICAN_ObjIDTmp.DestMACID = ICAN_Source.Area; // 目标节点编号
ICAN_ObjIDTmp.SrcMACID = ICAN_Source.Area; // 源节点编号
//------------------------------------
//填写iCAN读写缓冲区结构体
//------------------------------------
ICAN_TrBuff.ulID = ICAN_ObjIDManage(&ICAN_ObjIDTmp); // 生成ICAN报文ID
ICAN_TrBuff.ucXID = 1; //0 标准帧;1 扩展帧
ICAN_TrBuff.ucDataLenth = 5; //数据场长度5字节
ICAN_TrBuff.ucDataCtr = 0; //读数据指针指0字节
ICAN_TrBuff.ucDataBuff = SourceType; //报文数据场2字节(DI资源数量)
ICAN_TrBuff.ucDataBuff = SourceType; //报文数据场3字节(DO资源数量)
ICAN_TrBuff.ucDataBuff = SourceType; //报文数据场4字节(AI资源数量)
ICAN_TrBuff.ucDataBuff = SourceType; //报文数据场5字节(AO资源数量)
ICAN_TrBuff.TrBegin = 1; // 发送开始标志置位
}
//----------------------------------------------------------------------------
//设置建立连接成功
//----------------------------------------------------------------------------
function++;
}
///////////////////////////////////////////////////////////////////////////////////////////
//函数名称=iCAN_MACIDTest(uchar order, uchar ack)
//功能描述=MACID检测命令(从站上电发送MACID检测命令),功能码=0x07
// “MAC ID检测”命令帧用于检测网络中是否存在指定“节点地址”的从站。在 iCAN
// 网络中,具有该“节点地址”的从站在接收到“MAC ID检测”命令帧后给出响应,告
// 知命令发送节点,该“节点地址”已经存在。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//★★★命令帧格式
// CAN帧ID CAN帧数据部分
//----------------------------------------------------------------------------------------------------------------
// 源节点目标节点 ACK=0 功能码=0x07 资源节点=随机数 | 分段码=0x00 MAC ID 序列号(4字节)
//
//★★★响应帧格式
// CAN帧ID CAN帧数据部分
//----------------------------------------------------------------------------------------------------------------
// 源节点目标节点 ACK=1 功能码=0x07 资源节点=随机数 | 分段码=0x00 MAC ID 序列号(4字节)
//说明:
//(1)、在“MAC ID检测”命令帧中资源节点为随机数。报文数据长度为 6 个字节,报文数据第一个字节表示分段码(固定为 0) ;
// 第二个字节表示要检测的“节点地址” ;第三至六字节为发送命令节点的设备序列号。
//(2)、从站在收到格式正确的“MACID检测”命令帧后,如果命令帧中的 MAC ID与自身的“节点地址”相同,从站发送“MAC ID
// 检测”响应帧。对格式不正确的“MACID检测” 命令帧或命令帧中的 MAC ID与自身的 “节点地址”不同,从站忽略该 “MAC
// ID检测”命令帧,不产生任何响应报文。
//(3)、“MAC ID检测”响应帧数据部分的“MAC ID”表示发送响应报文节点的“节点地址” ,4 字节的“产品序列号”为发送
// 响应的节点的设备序列号。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//入口参数:
// order=
// ack=
// 0:
// 1:
//出口参数:
// 无
//-----------------------------------------------------------------------------
void iCAN_MACIDTest(uchar order, uchar ack)
{
ICAN_OBJ xdata ICAN_ObjIDTmp; // iCAN帧头结构体
ICAN_ObjIDTmp.SourceID = 0xEE; // 资源节点编号(随机)
ICAN_ObjIDTmp.FUNCID = 0x07; // 功能码
ICAN_ObjIDTmp.ACK = ack; // 应答标志
//------------------------------------------------------------------------------------------------
//为了探测网络上是否有相同的节点编号,故源节点编号=目标节点编号,都从0xEE中获取
//------------------------------------------------------------------------------------------------
ICAN_ObjIDTmp.DestMACID = ICAN_Source.Area; // 目标节点编号
ICAN_ObjIDTmp.SrcMACID = ICAN_Source.Area; // 源节点编号
ICAN_TrBuff.ulID = ICAN_ObjIDManage(&ICAN_ObjIDTmp); // 生成ICAN报文ID
ICAN_TrBuff.ucXID = 1; //0 标准帧;1 扩展帧
ICAN_TrBuff.ucDataLenth = 6; //数据场长度6字节
ICAN_TrBuff.ucDataCtr = 0; //读数据指针指0字节
ICAN_TrBuff.ucDataBuff = ICAN_Source.Area; //报文数据场2字节(MACID)
ICAN_TrBuff.ucDataBuff = ICAN_Source.Area; //报文数据场3字节(SN第1字节)
ICAN_TrBuff.ucDataBuff = ICAN_Source.Area; //报文数据场4字节(SN第2字节)
ICAN_TrBuff.ucDataBuff = ICAN_Source.Area; //报文数据场5字节(SN第3字节)
ICAN_TrBuff.ucDataBuff = ICAN_Source.Area; //报文数据场5字节(SN第4字节)
ICAN_TrBuff.TrBegin = 1; // 发送开始标志置位
}
///////////////////////////////////////////////////////////////////////////////////////////
//函数名称=iCAN_UnConnectACK(uchar order, tCANFrame *pCANFrame)
//功能描述=删除连接命令(功能码=0x05)
// “删除连接”命令帧用于主站撤销与从站建立的通讯。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//★★★命令帧格式
// CAN帧ID CAN帧数据部分
//----------------------------------------------------------------------------------------------------------------
// 源节点目标节点 ACK=0 功能码=0x05 资源节点=0xF7 | 分段码=0x00 Master MACID
//
//★★★正常响应帧格式
// CAN帧ID CAN帧数据部分
//----------------------------------------------------------------------------------------------------------------
// 源节点目标节点 ACK=1 功能码=0x05 资源节点=0xF7 | 分段码=0x00
//★★★异常响应帧格式
// CAN帧ID CAN帧数据部分
//----------------------------------------------------------------------------------------------------------------
// 源节点目标节点 ACK=1 功能码=0x0F 资源节点=0xF7 | 分段码=0x00 ERRID
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//入口参数:
// order=
// *pCANFrame=CAN报文结构体(保存iCAN帧头、DLC、报文数据场数据)
//出口参数:
// 无
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void iCAN_UnConnectACK(uchar order, tCANFrame *pCANFrame)
{
ICAN_OBJ xdata ICAN_ObjIDTmp;
//----------------------------------------------------------------------------------------------------------------
//检测之前是否未建立连接成功,若是,则返回错误报文
//----------------------------------------------------------------------------------------------------------------
if (function ==0 )
{
ICAN_ErrorIDACK(order, ErrID_NotConnect) ; // 错误代码返回报文连接不存在
}
//----------------------------------------------------------------------------------------------------------------
//检测命令帧报文第二字节的Master MACID是否和从站保存的Master MACID相同,若不相同,则返回错误报文
//----------------------------------------------------------------------------------------------------------------
else if (pCANFrame->ucDataBuff != ICAN_Source.Area)
{
ICAN_ErrorIDACK(order, ErrID_NotConnect) ; // 错误代码返回报文连接不存在
return;
}
//----------------------------------------------------------------------------------------------------------------
//检测命令帧报文第一字节的分段码=0,若不等于0,则返回错误报文
//----------------------------------------------------------------------------------------------------------------
else if ((pCANFrame->ucDataBuff!=0))
{
ICAN_ErrorIDACK(order,ErrID_ParameterError); // 错误代码返回报文(参数非法)
}
//----------------------------------------------------------------------------------------------------------------
//取消建立连接,发送正常响应帧报文给主站
//----------------------------------------------------------------------------------------------------------------
else
{
function=0; // 取消建立连接
ICAN_ObjIDTmp.SourceID = 0xF7; // 资源节点编号
ICAN_ObjIDTmp.FUNCID = 0x05; // 功能码
ICAN_ObjIDTmp.ACK = 1; // 应答标志
ICAN_ObjIDTmp.DestMACID = ICAN_Source.Area; // 目标节点编号
ICAN_ObjIDTmp.SrcMACID = ICAN_Source.Area; // 源节点编号
ICAN_Source.Area = NOMASTER; // 清除目标主站节点的MACID
ICAN_Source.Area = 0; // 清除目标主站节点定时通信节拍数
ICAN_TrBuff.ulID = ICAN_ObjIDManage(&ICAN_ObjIDTmp); // 生成ICAN报文ID
ICAN_TrBuff.ucXID = 1; //0 标准帧;1 扩展帧
ICAN_TrBuff.ucDataLenth = 1; //数据场长度1字节
ICAN_TrBuff.ucDataCtr = 0; //读数据指针指0字节
ICAN_TrBuff.TrBegin = 1; // 发送开始标志置位
}
}
///////////////////////////////////////////////////////////////////////////////////////////
//函数名称=iCAN_ResetACK(uchar order, tCANFrame *pCANFrame)
//功能描述=设备复位命令(功能码=0x06)
// “设备复位”命令帧用于复位从站设备。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//★★★命令帧格式
// CAN帧ID CAN帧数据部分
//----------------------------------------------------------------------------------------------------------------
// 源节点目标节点 ACK=0 功能码=0x06 资源节点=随机数 | 分段码=0x00 Master MACID
//
//★★★正常响应帧格式
// CAN帧ID CAN帧数据部分
//----------------------------------------------------------------------------------------------------------------
// 源节点目标节点 ACK=1 功能码=0x06 资源节点=随机数 | 分段码=0x00
//★★★异常响应帧格式
// CAN帧ID CAN帧数据部分
//----------------------------------------------------------------------------------------------------------------
// 源节点目标节点 ACK=1 功能码=0x0F 资源节点=随机数 | 分段码=0x00 ERRID
//
//说明:
//(1)、在“设备复位”命令帧中资源节点可以为任意地址,报文数据长度为 2 个字节,报文数据第一个字节表示分段码,
// 报文数据第二个字节表示目标节点 MAC ID。
//(2)、从站收到正确的“设备复位”命令后,如果从站支持命令复位功能,返回正常响应,随后设备重启。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//入口参数:
// order=
// *pCANFrame=CAN报文结构体(保存iCAN帧头、DLC、报文数据场数据)
//出口参数:
// 无
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void iCAN_ResetACK(uchar order, tCANFrame *pCANFrame)
{
ICAN_OBJ xdata ICAN_ObjIDTmp;
//----------------------------------------------------------------------------------------------------------------
//检测之前是否未建立连接成功,若是,则返回错误报文
//----------------------------------------------------------------------------------------------------------------
if (function==0)
{
ICAN_ErrorIDACK(order, ErrID_NotConnect) ; // 错误代码返回报文连接不存在
return;
}
//----------------------------------------------------------------------------------------------------------------
//ICANSlav_ObjID=iCAN帧头的结构体(包含:源节点、目的节点、功能码、ACK、资源节点地址)
//如果iCAN帧头结构体中包含的源结点编号≠从站保存的Master MACID ,则返回错误报文
//----------------------------------------------------------------------------------------------------------------
//这段指令的含义是判断连接是否存在,因为删除连接时,会将从站保存的Master MACID设置为不可能的节点编号(120)
//因此可能通过检测从站内部保存的Master MACID和iCAN帧头中的源节点编号是否相同来检测连接是否已经建立。
//----------------------------------------------------------------------------------------------------------------
if (ICAN_Source.Area!= ICANSlav_ObjID.SrcMACID)
{
ICAN_ErrorIDACK(order, ErrID_NotConnect) ; // 错误代码返回报文连接不存在
return;
}
//----------------------------------------------------------------------------------------------------------------
//检测命令帧报文第二字节的Master MACID是否和从站保存的Master MACID相同,若不相同,则返回错误报文
//----------------------------------------------------------------------------------------------------------------
if ((pCANFrame->ucDataBuff != ICAN_Source.Area))
{
ICAN_ErrorIDACK(order, ErrID_ParameterError) ; // 错误代码返回报文参数非法(参数非法 错误码命令处理)
return;
}
//----------------------------------------------------------------------------------------------------------------
//如果不支持复位命令,则返回错误报文
//----------------------------------------------------------------------------------------------------------------
if (FlagSystemResetEn)
{
ICAN_ErrorIDACK(order, ErrID_NotCommand) ; // 错误代码返回不支持复位命令
return;
}
ICAN_ObjIDTmp.SourceID = 0xF7; // 资源节点编号
ICAN_ObjIDTmp.FUNCID = 0x06; // 功能码
ICAN_ObjIDTmp.ACK = 1; // 应答标志
ICAN_ObjIDTmp.DestMACID = ICAN_Source.Area; // 目标节点编号
ICAN_ObjIDTmp.SrcMACID = ICAN_Source.Area; // 源节点编号
ICAN_TrBuff.ulID = ICAN_ObjIDManage(&ICAN_ObjIDTmp); // 生成ICAN报文ID
ICAN_TrBuff.ucXID = 1; //0 标准帧;1 扩展帧
ICAN_TrBuff.ucDataLenth = 1; //数据场长度1字节
ICAN_TrBuff.ucDataCtr = 0; //读数据指针指0字节
ICAN_TrBuff.TrBegin = 1; // 发送开始标志置位
//复位的节点是本节点
//SystemReset();//系统复位标志
}
///////////////////////////////////////////////////////////////////////////////////////////
//函数名称=iCAN_ReadACK(uchar order, uchar sourceID, tCANFrame *pCANFrame)
//功能描述=连续读端口命令(功能码=0x02)
// “连续读端口”命令帧用于读取指定资源节点中的数据。在命令帧中指定了所要读取的资源
// 节点首地址和读取的数据长度。从站接收到“连续读端口”命令后,如果判断命令帧有效,
// 就会将资源中的数据返回给主站。。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//★★★访问资源节点命令帧格式
// CAN帧ID CAN帧数据部分
//----------------------------------------------------------------------------------------------------------------
// 源节点目标节点 ACK=0 功能码=0x02 资源节点 | 分段码=0x00 Length
//
//说明:
//(1)、访问资源节点的“连续读端口”命令用于访问资源节点 0x00~0xF8。
//(2)、在访问资源节点的“连续读端口”命令帧中,报文数据部分第一个字节表示分段码(固定为 0x00) ,
// 第二个字节为所要读出的数据长度(Length<=32) 。使用“连续读端口”命令访问资源节点时,最
// 多允许读出 32个资源节点的数据。如果所读的字节数据超过 7 个字节,则从站使用分帧响应。
//
//★★★访问资源子节点命令帧格式
// CAN帧ID CAN帧数据部分
//----------------------------------------------------------------------------------------------------------------
// 源节点目标节点 ACK=0 功能码=0x02 资源节点 | 分段码=0x00 SubIndexLength
//
//说明:
//(1)、访问资源子节点的“连续读端口”命令用于访问资源节点 0xF9~0xFF处的资源子节点。
//(2)、在访问资源子节点的“连续读端口”命令帧中,报文数据部分第一个字节表示分段码(固定为 0x00) ;
// 第二个字节为所要读的资源子节点;第三个字节为所要读出的数据长度(Length<=32) 。使用“连续
// 读端口”命令访问资源子节点时,最多允许读出 32个资源子节点的数据。如果所读的字节数据超过7个
// 字节,则从站使用分帧响应。
//
//★★★正常响应帧格式
// CAN帧ID CAN帧数据部分
//----------------------------------------------------------------------------------------------------------------
// 源节点目标节点 ACK=1 功能码=0x02 资源节点 | 分段码 1-7个字节
//
//说明:
//(1)、如果“连续读端口”命令帧中指定的资源段范围在从站中有效,从站使用正常响应返回主站读取的数据;
// 当主站请求的数据超过7个字节时,从站使用分段传输。“连续读端口”命令最多允许读出 32个资源节
// 点的数据。
//
//★★★异常响应帧格式
// CAN帧ID CAN帧数据部分
//----------------------------------------------------------------------------------------------------------------
// 源节点目标节点 ACK=1 功能码=0x0F 资源节点 | 分段码=0x00 ERRID
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//入口参数:
// order=
// *pCANFrame=CAN报文结构体(保存iCAN帧头、DLC、报文数据场数据)
//出口参数:
// 无
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void iCAN_ReadACK(uchar order, uchar sourceID, tCANFrame *pCANFrame)
{
ICAN_OBJ xdata ICAN_ObjIDTmp; // iCAN帧头的结构体
uchar i, j, k, l;
//----------------------------------------------------------------------------------------------------------------
//ICANSlav_ObjID=iCAN帧头的结构体(包含:源节点、目的节点、功能码、ACK、资源节点地址)
//如果iCAN帧头结构体中包含的源结点编号≠从站保存的Master MACID ,则返回错误报文
//----------------------------------------------------------------------------------------------------------------
//这段指令的含义是判断连接是否存在,因为删除连接时,会将从站保存的Master MACID设置为不可能的节点编号(120)
//因此可能通过检测从站内部保存的Master MACID和iCAN帧头中的源节点编号是否相同来检测连接是否已经建立。
//----------------------------------------------------------------------------------------------------------------
if (ICAN_Source.Area!= ICANSlav_ObjID.SrcMACID) // 连接不存在 错误码命令处理
{
ICAN_ErrorIDACK(order, ErrID_NotConnect) ; // 错误代码返回报文连接不存在
return;
}
//----------------------------------------------------------------------------------------------------------------
//检测之前是否未建立连接成功,若是,则返回错误报文
//----------------------------------------------------------------------------------------------------------------
if (function==0) // 连接不存在 错误码命令处理
{
ICAN_ErrorIDACK(order, ErrID_NotConnect) ; // 错误代码返回报文连接不存在
return;
}
//---------------------------------------------------------------------------------------------------------
//资源检测,返回1=资源非法,返回0=资源合法
//sourceID=资源节点编号
//pCANFrame->ucDataBuff=iCAN命令帧第二字节(=访问资源节点Length)
//0=连续读端口标志
//---------------------------------------------------------------------------------------------------------
if (ICAN_SourceTest(order,sourceID,pCANFrame->ucDataBuff,0)) // 资源检测
{
ICAN_ErrorIDACK(order, ErrID_NotSourceID) ; // 错误代码返回报文资源不存在
return;
}
//-----------------------------------------------------------------------------------
//填写iCAN帧头结构体相关信息
//-----------------------------------------------------------------------------------
ICAN_ObjIDTmp.SourceID = ICANSlav_ObjID.SourceID; // 资源节点编号
ICAN_ObjIDTmp.FUNCID = 0x02; // 读功能码
ICAN_ObjIDTmp.ACK = 1; // 应答标志
ICAN_ObjIDTmp.DestMACID = ICAN_Source.Area; // 目标节点编号???????????????
ICAN_ObjIDTmp.SrcMACID = ICAN_Source.Area; // 源节点编号?????????????????
//-----------------------------------------------------------------------------------
//填写iCAN读写缓存结构体
//-----------------------------------------------------------------------------------
ICAN_TrBuffR.ulID = ICAN_ObjIDManage(&ICAN_ObjIDTmp); // 生成ICAN报文ID(iCAN帧头合成)
ICAN_TrBuffR.ucXID = 1; //0 标准帧;1 扩展帧
i = sourceID; // 获取资源节点的地址
//------------------------------------------------------------------------------------------
//根据接收报文的帧头中的资源节点地址来判断当前是“连续读资源节点”还是“连续读资源子节点”
//------------------------------------------------------------------------------------------
if (i >= 0xF9)
{
k = pCANFrame->ucDataBuff; // 读取资源节点的子地址(SubIndex)
j = pCANFrame->ucDataBuff; // 读取资源长度(Length)
if (j >= 32) // 一次最多能读32单元超过则为无效,参数非法
{
ICAN_ErrorIDACK(order, ErrID_ParameterError); // 错误代码返回报文
return;
}
if ((i+j) > 0xFF) // 要读取的资源节点数量超过255,参数非法
{
ICAN_ErrorIDACK(order, ErrID_NotSourceID) ; // 错误代码返回报文
return;
}
//---------------------------------------------------------------------------------------------
//#define IOParameter 0xF8 // 输入/输出通道参数
//---------------------------------------------------------------------------------------------
if (i==IOParameter) // IO参数
{
if (j < 4) // 读取资源长度(Length)<4吗?
{ // 因为输入/输出通道参数只包含DILen,DOLen,AILen,AOLen=4
for (l = 0,k=0; l < j; l++, k++)
{
//将数据送入接收缓冲
ICAN_TrBuffR.ucDataBuff = SourceType; //读取指定单元的地址
}
}
else
{
ICAN_ErrorIDACK(order, ErrID_NotSourceID); // 错误代码返回报文,参数非法
return;
}
//---------------------------------------------------------------------------------------------
//#define IOConfigure 0xF9 // 输入/输出配置参数
//---------------------------------------------------------------------------------------------
//“配置资源” 用于设备标识、 通讯参数以及 I/O单元的配置, 占用资源节点 0xE0~0xFF。“配置资源”
//包括设备标识、通讯参数、I/O 属性和 I/O 配置,通过配置资源节点可以获取iCAN 从站设备的各种信息。
//---------------------------------------------------------------------------------------------
}
else if (i == IOConfigure) // IO配置
{
if (j < 32)
{
for (l = 0; l < j; l++, k++)
{
//将数据送入接收缓冲
ICAN_TrBuffR.ucDataBuff = ICAN_IOconfig; //读取指定单元的地址
}
}
else
{
ICAN_ErrorIDACK(order, ErrID_NotSourceID); // 错误代码返回报文,参数非法
return;
}
}
//---------------------------------------------------------------------------------------------
//读资源子节点的保留数据
//---------------------------------------------------------------------------------------------
else
{
}
}
//---------------------------------------------------------------------------------------------
//读取正常数据
//---------------------------------------------------------------------------------------------
else
{
j = pCANFrame->ucDataBuff; // 读取资源节点长度
if (j >= 32) // 一次最多能读32单元超过则为无效,参数非法
{
ICAN_ErrorIDACK(order, ErrID_ParameterError); // 错误代码返回报文,参数非法
return;
}
if ((i+j) >0xFF) // 资源节点超过255,参数非法
{
ICAN_ErrorIDACK(order, ErrID_NotSourceID) ; // 错误代码返回报文,参数非法
return;
}
for (l = 0; l < j; l++, i++) // 将数据送入接收缓冲
{
ICAN_TrBuffR.ucDataBuff = ICAN_Source.Area; //读取指定单元的地址
}
}
ICAN_TrBuffR.ucDataLenth = j + 1; //数据场长度k字节
ICAN_TrBuffR.ucDataCtr = 0; //读数据指针指0字节
ICAN_TrBuffR.TrBegin = 1; // 发送开始标志置位
}
///////////////////////////////////////////////////////////////////////////////////////////
//函数名称=iCAN_WriteACK(uchar order, uchar sourceID, tCANFrame *pCANFrame)
//功能描述=连续写端口命令(功能码=0x01)
// “连续写端口”命令帧用于修改指定资源节点中的数据。在命令帧中指定了所要进行操
// 作的资源的首地址以及数据。从站接收到“连续写端口”命令后,如果判断命令帧有效,
// 就会保存命令中的数据到相应的资源节点位置。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//★★★访问资源节点命令帧格式
// CAN帧ID CAN帧数据部分
//----------------------------------------------------------------------------------------------------------------
// 源节点目标节点 ACK=0 功能码=0x01 资源节点 | 分段码 1~7个字节
//
//说明:
//(1)、访问资源节点的“连续写端口”命令用于访问资源节点 0x00~0xF8。
//(2)、在访问资源节点的“连续写端口”命令帧中,报文数据部分第一个字节为分段码,第二个字节
// 开始为所要写入的数据,当所要写入的数据超过 7 个字节时,则要使用分段传输。使用连续写
// 端口命令访问资源节点时,最多允许修改 32 个资源节点的数据。
//
//★★★访问资源子节点命令帧格式
// CAN帧ID CAN帧数据部分
//----------------------------------------------------------------------------------------------------------------
// 源节点目标节点 ACK=0 功能码=0x01 资源节点 | 分段码 资源子节点1~6个字节
//
//说明:
//(1)、访问资源子节点的“连续写端口”命令用于访问资源节点 0xF9~0xFF处的资源子节点。
//(2)、在访问资源子节点的“连续写端口”命令帧中,报文数据部分第一个字节为分段码,第二个字节为
// 要访问的资源子节点,第三个字节开始为所要写入的数据。当所要写入的数据超过 6个字节时,则
// 要使用分段传输,使用分段传输时,只有第一段命令帧的数据部分包含资源子节点,中间和结尾段
// 的命令帧的数据部分只包含分段码和传输的数据。使用连续写端口命令访问资源子节点时,最多允
// 许修改 32个资源子节点的数据。
//
//★★★正常响应帧格式
// CAN帧ID CAN帧数据部分
//----------------------------------------------------------------------------------------------------------------
// 源节点目标节点 ACK=1 功能码=0x01 资源节点 | 分段码=0x00
//
//说明:
//(1)、 如果“连续写端口”命令帧中指定的资源段范围在从站中有效,从站在正确处理接收到的数据后
// 返回正常响应。使用多段传输时,从站只有在接收完最后一段命令帧后才作出响应。
//
//★★★异常响应帧格式
// CAN帧ID CAN帧数据部分
//----------------------------------------------------------------------------------------------------------------
// 源节点目标节点 ACK=1 功能码=0x0F 资源节点 | 分段码=0x00 ERRID
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//入口参数:
// order=
// *pCANFrame=CAN报文结构体(保存iCAN帧头、DLC、报文数据场数据)
//出口参数:
// 无
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void iCAN_WriteACK(uchar order, uchar sourceID, tCANFrame *pCANFrame)
{
static uchar WriteTest=0; // 全局变量,记录多帧传输前一分段编号
ICAN_OBJ xdata ICAN_ObjIDTmp; // iCAN帧头结构体
uchar i, j, l;
//----------------------------------------------------------------------------------------------------------------
//ICANSlav_ObjID=iCAN帧头的结构体(包含:源节点、目的节点、功能码、ACK、资源节点地址)
//如果iCAN帧头结构体中包含的源结点编号≠从站保存的Master MACID ,则返回错误报文
//----------------------------------------------------------------------------------------------------------------
//这段指令的含义是判断连接是否存在,因为删除连接时,会将从站保存的Master MACID设置为不可能的节点编号(120)
//因此可能通过检测从站内部保存的Master MACID和iCAN帧头中的源节点编号是否相同来检测连接是否已经建立。
//----------------------------------------------------------------------------------------------------------------
if (ICAN_Source.Area!= ICANSlav_ObjID.SrcMACID) // 连接不存在,错误码命令处理
{
ICAN_ErrorIDACK(order, ErrID_NotConnect) ; // 错误代码返回报文连接不存在
return;
}
//----------------------------------------------------------------------------------------------------------------
//检测之前是否未建立连接成功,若是,则返回错误报文
//----------------------------------------------------------------------------------------------------------------
if (function == 0) // 还没有建立连接, 错误码命令处理
{
ICAN_ErrorIDACK(order, ErrID_NotConnect); // 错误代码返回报文连接不存在
return;
}
//----------------------------------------------------------------------------------------------------------------
//获取分段标识子程序,入口参数如下:
//ICAN_ReBuff.ucDataCtr=接收缓冲区结构体读数据指针
//pCANFrame->ucDataBuff=CAN报文结构体第一字节(存储分段码)
//返回:0=无分段
// 65=最后分段
// 1=第一分段
// 其它=中间分段
//----------------------------------------------------------------------------------------------------------------
i = ICAN_ObjByte0Restore(&ICAN_ReBuff.ucDataCtr, pCANFrame->ucDataBuff); // 获取分段标识
//----------------------------------------------------------------------------------------------------------------
//数据接收完毕或者没有分段报文
//----------------------------------------------------------------------------------------------------------------
if (i == 65 || i == 0)
{
for (l = 1; l < pCANFrame->ucDLC; l++) // 将最后一帧或唯一的数据送入接收缓冲
{
ICAN_ReBuff.ucDataBuff.ucDataCtr++] =pCANFrame->ucDataBuff; // 读取CAN结构体中的数据到接收缓冲区
}
if (ICAN_ReBuff.ucDataCtr >= 32) //一次最多能读32单元超过则为无效,资源节点超过255也是非法
{
ICAN_ErrorIDACK(order, ErrID_ParameterError); // 错误代码返回报文,参数非法
return;
}
if (ICAN_SourceTest(order,ICANSlav_ObjID.SourceID,ICAN_ReBuff.ucDataCtr,1))
{ // 写端口时,资源检测
ICAN_ErrorIDACK(order, ErrID_NotSourceID); // 错误代码返回报文资源不存在
return;
}
if (ICANSlav_ObjID.SourceID >= 0xF9) // 写资源字节点地址
{
l = ICAN_ReBuff.ucDataBuff;//资源地址的子地址
if (i == IOParameter)
{//IO参数
if (ICAN_ReBuff.ucDataCtr < 5)
{//资源配置数量为4字节
for (j=1;j<ICAN_ReBuff.ucDataCtr;j++,l++){
//将接收缓冲数据送入目标资源
SourceType = ICAN_TrBuff.ucDataBuff; //读取指定单元的地址
}
}else{
//参数非法
ICAN_ErrorIDACK(order, ErrID_ParameterError) ;//错误代码返回报文//参数非法
return;
}
}else if(i==IOConfigure){//IO配置
if(ICAN_ReBuff.ucDataCtr<32){//资源配置数量为4字节
for(j=1;j<ICAN_ReBuff.ucDataCtr;j++,l++){
//将接收缓冲数据送入目标资源
ICAN_IOconfig = ICAN_TrBuff.ucDataBuff; //读取指定单元的地址
}
}else{
//参数非法
ICAN_ErrorIDACK(order, ErrID_ParameterError) ;//错误代码返回报文//参数非法
return;
}
}else{//其他资源子地址无效
//参数非法
ICAN_ErrorIDACK(order, ErrID_NotSourceID) ;//错误代码返回报文//参数非法
return;
}
}else{//写普通资源节点
i = sourceID; //获取资源节点的地址
if((i<0x20)||((i>=40)&&(i<60))){//只读区进行写
ICAN_ErrorIDACK(order, ErrID_OprationInvalid) ;//错误代码返回报文//操作无效
return;
}
for(j=0;j<ICAN_ReBuff.ucDataCtr;j++,i++){
ICAN_Source.Area=ICAN_ReBuff.ucDataBuff;//将数据写入对应的资源区
}
}
ICAN_ObjIDTmp.SourceID = ICANSlav_ObjID.SourceID; //资源节点编号
ICAN_ObjIDTmp.FUNCID = 0x01; //写功能码
ICAN_ObjIDTmp.ACK = 1; //应答标志
ICAN_ObjIDTmp.DestMACID = ICAN_Source.Area; //目标节点编号
ICAN_ObjIDTmp.SrcMACID = ICAN_Source.Area; //源节点编号
ICAN_TrBuff.ulID = ICAN_ObjIDManage(&ICAN_ObjIDTmp); //生成ICAN报文ID
ICAN_TrBuff.ucXID = 1; //0 标准帧;1 扩展帧//
ICAN_TrBuff.ucDataLenth = 1; //数据场长度1字节//
ICAN_TrBuff.ucDataCtr = 0; //读数据指针指0字节//
ICAN_TrBuff.TrBegin = 1; //发送开始标志置位
WriteTest=0;//保护标志无效
}
//----------------------------------------------------------------------------------------------------------------
//接收到第一个段
//----------------------------------------------------------------------------------------------------------------
else if (i == 1)
{
if (WriteTest!=0) // 接收过程被重刷新
{
ICAN_ErrorIDACK(order, ErrID_WriteError) ; //错误代码返回报文,参数非法
return;
}
for (l = 1; l < 8; l++)
{
ICAN_ReBuff.ucDataBuff.ucDataCtr++] =pCANFrame->ucDataBuff; //读取缓冲区中的数据
}
WriteTest = 2; // 下一段为02段
}
//----------------------------------------------------------------------------------------------------------------
//数据接收中间段
//----------------------------------------------------------------------------------------------------------------
else
{
if (WriteTest != i) // 接收过程帧格式不正确
{
ICAN_ErrorIDACK(order, ErrID_ParameterError); // 错误代码返回报文//参数非法
return;
}
WriteTest++; // 指向下一个帧
for (l = 1; l < 8; l++) // 将数据送入接收缓冲
{
ICAN_ReBuff.ucDataBuff.ucDataCtr++] =pCANFrame->ucDataBuff; //读取缓冲区中的数据
}
if (ICAN_ReBuff.ucDataCtr >= 32) // 一次最多能读32单元超过则为无效,资源节点超过255也是非法
{
ICAN_ErrorIDACK(order, ErrID_ParameterError); // 错误代码返回报文,参数非法
return;
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////
//函数名称=iCAN_CyclicACK(uchar order)
//功能描述=事件触发传送命令(功能码=0x03)
// 事件触发传送”命令用于 iCAN 从站主动向已建立连接的主站发送数据。iCAN 从站的
// “定时循环传送”和“事件触发传送”都使用该命令帧格式。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//★★★命令帧格式
// CAN帧ID CAN帧数据部分
//----------------------------------------------------------------------------------------------------------------
// 源节点目标节点 ACK=1 功能码=0x03 资源节点 | 分段码 输入端口数据
//
//说明:
//(1)、iCAN 从站根据输入端口的数据长度,决定是否采用分段传输。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//入口参数:
// order=
//出口参数:
// 无
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void iCAN_CyclicACK(uchar order)
{
ICAN_OBJ xdata ICAN_ObjIDTmp;
uchar i, j, l;
//----------------------------------------------------------------------------------------------------------------
//这段指令的含义是判断连接是否存在,因为删除连接时,会将从站保存的Master MACID设置为不可能的节点编号(120)
//因此可能通过检测从站内部保存的Master MACID和iCAN帧头中的源节点编号是否相同来检测连接是否已经建立。
//----------------------------------------------------------------------------------------------------------------
if (ICAN_Source.Area == NOMASTER )
{
ICAN_ErrorIDACK(order, ErrID_NotConnect); // 错误代码返回报文连接不存在
return;
}
//i=0x00; // (DI区)
// j = SourceType; // 获取资源节点的地址(DI区)
i=0x40; // (AI区)
j = SourceType; // 获取资源节点的地址(AI区)
ICAN_ObjIDTmp.SourceID =i; // 资源节点编号(AI区)
ICAN_ObjIDTmp.FUNCID = 0x03; // 事件触发传送命令功能码
ICAN_ObjIDTmp.ACK = 1; // 应答标志
ICAN_ObjIDTmp.DestMACID = ICAN_Source.Area; // 目标节点编号
ICAN_ObjIDTmp.SrcMACID = ICAN_Source.Area; // 源节点编号
ICAN_TrBuffC.ulID = ICAN_ObjIDManage(&ICAN_ObjIDTmp); // 生成ICAN报文ID
ICAN_TrBuffC.ucXID = 1; //0 标准帧;1 扩展帧
for (l = 0; l < j; l++, i++) // 读取正常数据,最多读取32字节,将数据送入接收缓冲
{
ICAN_TrBuffC.ucDataBuff = ICAN_Source.Area; // 读取指定单元的地址
}
ICAN_TrBuffC.ucDataLenth = j + 1; //数据场长度k字节
ICAN_TrBuffC.ucDataCtr = 0; //读数据指针指0字节
ICAN_TrBuffC.TrBegin = 1; // 发送开始标志置位
}
///////////////////////////////////////////////////////////////////////////////////////////
//ICAN模块从机通信接收子程序(Explain=解释, 说明)
///////////////////////////////////////////////////////////////////////////////////////////
void ICANRxMsgObjExplain(uchar order)
{
tCANFrame xdata CANFrame; // CAN报文结构体(存储CAN报文信息:DLC、DATA-DATA,扩展帧=1,iCAN报文帧头合成)
//----------------------------------------------------------------------------------------------------------------
//有数据处理,需要处理数据
//----------------------------------------------------------------------------------------------------------------
if (ICAN_ControlInfo.ReDataCtr)
{
ICAN_ControlInfo.ReDataCtr--;
//----------------------------------------------------------------------------------------------------------------
//需要加入你的代码======
//----------------------------------------------------------------------------------------------------------------
// ?????需要加入你的代码
// ?????需要加入你的代码
// ?????需要加入你的代码
if (CAN_ucReadBuffer(pReceFrameBuff,&CANFrame) != EMPTY) //
{
// if (1)
{//如果到数据
//需要加入你的代码===================================================================
//如果数据不为空则处理
//----------------------------------------------------------------------------------------------------------------
//结构体CANFrame中保存的iCAN帧头合成信息--->结构体指针ICANSlav_ObjID
//----------------------------------------------------------------------------------------------------------------
ICAN_ObjIDRestore(&ICANSlav_ObjID, CANFrame.ulID); // ICAN报文标识码还原
switch (ICANSlav_ObjID.FUNCID)
{
case 0x01: // 用于对单个或者多个资源节点的数据写入
iCAN_WriteACK(order, ICANSlav_ObjID.SourceID, &CANFrame); // 写数据命令命令
break;
case 0x02: // 用于对单个或者多个资源节点的数据读取
iCAN_ReadACK(order, ICANSlav_ObjID.SourceID, &CANFrame); // 读数据命令命令
break;
case 0x03: // 返回0X0F出错响应
ICAN_ErrorIDACK(order, ErrID_NotFunctionID); // 错误代码返回报文
//iCAN_CyclicACK(order);
break;
case 0x04: // 用于和iCAN节点建立连接
iCAN_ConnectACK(order, &CANFrame); // 建立连接命令
break;
case 0x05: // 用于删除与iCAN节点建立的连接
iCAN_UnConnectACK(order, &CANFrame); // 建立连接命令
break;
case 0x06: // 用于复位iCAN节点
iCAN_ResetACK(order, &CANFrame); // 复位设备命令
break;
case 0x07: // MACID检测命令
if (ICANSlav_ObjID.ACK == 0) // ACK == 0(接收到检测命令)
{ // 用于响应网络上是否有相同MACID节点的检测
iCAN_MACIDTest(order,1); // 接收到MACID检测命令并返回响应命令
}
else // ACK == 1(接收到响应命令)
{
MACIDTemp = ICAN_Source.Area; // 记录当前的MACID
ICAN_ControlInfo.MACIDTestStep = 9; // 检测到相同的MACID节点
}
break;
default:
ICAN_ErrorIDACK(order, ErrID_NotFunctionID) ; // 错误代码返回报文
break;
}
}
}
}
//-----------------------------------------------------------------------------
// ICAN模块从机通信处理程序
//-----------------------------------------------------------------------------
/*
void ICANSlaveISR(uchar order, unsigned long ulMsgObjID)
{
tCANFrame xdata CANFrame;
if (ulMsgObjID) {
//有中断产生
// 中断接收数据
for (i = 0; i < 4; i++) {
//对4个接收邮箱进行读数据处理
if (ulMsgObjID & (0x00000001 << (ulICANRxMsgNbrTbl - 1))) {
//只要有一个邮箱接收数据则进行处理
MsgObjectBuf.pucMsgData = CANFrame.ucDataBuff; //获取数据指针
CANMessageGet(pCAN_Node_Info->ulChNr, ulICANRxMsgNbrTbl, &MsgObjectBuf, 1); //读取邮箱数据
if (pReceFrameBuff->ucBufFull != 0) {
//接收缓冲区未满
CANFrame.ucXID = 1; //扩展ID
CANFrame.ulID = (MsgObjectBuf.ulMsgID & 0x1FFFFFFF); //读取ID内容
CANFrame.ucDLC = MsgObjectBuf.ulMsgLen; //获取数据长度
CAN_ucWriteBuffer(pReceFrameBuff, &CANFrame);//把接收的数据写入指定的接收缓存
}
ICAN_ControlInfo.ReDataCtr++;
}
}
*/
/*
*中断发送数据
for (i = 0; i < 4; i++) {
//对4个发送邮箱进行读数据处理
if ((ulMsgObjID & (0x00000001 << (pCAN_Node_Info->ulTxMsgObjNr - 1)))) {//有中断生产
ClearIntFlag(pCAN_Node_Info, pCAN_Node_Info->ulTxMsgObjNr);//清除发送中断标志
// if ((SendBuffToBus((void*)pCAN_Node_Info)) == 0) {
// //总线不忙
ICAN_ControlInfo.Idle = 0;
// }
}
}
}
} */
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//ICAN模块从机通信发送子程序(单帧发送报文)
//-----------------------------------------------------------------------------------------------------------------
//(1)、ICAN_TrBuff=通用报文发送缓冲区
//(2)、ICAN_TrBuffR=读应答报文发送缓冲区(专门应用于连续读端口命令,功能码=0x02)
//(3)、ICAN_TrBuffC=循环应答报文发送(专门应用于事件触发传送命令,功能码=0x03)
//(4)、ICAN_ControlInfo.Idle = 0(发送模块空闲)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ICANTxMsgObjSend(uchar order)
{
tCANFrame xdata CANFrame; // 定义CAN报文结构体
tICAN_RWBUFF *pICAN_TrBuff; // 定义一个从机ICAN节点资源读缓冲
uchar i;
if ((ICAN_TrBuff.TrBegin || ICAN_TrBuffR.TrBegin || ICAN_TrBuffC.TrBegin) && (ICAN_ControlInfo.Idle == 0))
{
//-----------------------------------------------------------------------------------------------------------------
//通用报文发送
//-----------------------------------------------------------------------------------------------------------------
if (ICAN_TrBuff.TrBegin)
{
pICAN_TrBuff = (tICAN_RWBUFF *)(&ICAN_TrBuff);
}
//-----------------------------------------------------------------------------------------------------------------
//读应答报文发送
//-----------------------------------------------------------------------------------------------------------------
else if(ICAN_TrBuffR.TrBegin)
{
pICAN_TrBuff = &ICAN_TrBuffR;
}
//-----------------------------------------------------------------------------------------------------------------
//循环应答报文发送
//-----------------------------------------------------------------------------------------------------------------
else
{
pICAN_TrBuff = &ICAN_TrBuffC;
}
//-----------------------------------------------------------------------------------------------------------------
//下面开始将
//-----------------------------------------------------------------------------------------------------------------
//发送标志有效并且总组空闲
CANFrame.ulID = pICAN_TrBuff->ulID; // 生成ICAN报文ID(iCAN报文帧头)
CANFrame.ucXID = pICAN_TrBuff->ucXID; //0 标准帧;1 扩展帧
CANFrame.ucDataBuff = ICAN_ObjByte0Manage(pICAN_TrBuff->ucDataCtr, pICAN_TrBuff->ucDataLenth); // 生成分段码
if ((CANFrame.ulID & 0x00000F00) != 0x00000F00) // 如果当前不是发送《异常响应帧》
{
ICAN_ControlInfo.ConnectTOTimer = 0; // 连接超时定时清零(为正常功能码时,定时清0;为异常功能码时,不清0)
}
for (i = 1; i < 8; i++)
{
CANFrame.ucDataBuff = pICAN_TrBuff->ucDataBuff; // 获取8个字节(读取报文数据场1字节)
pICAN_TrBuff->ucDataCtr++; // 数据指针加1
if (pICAN_TrBuff->ucDataCtr >= pICAN_TrBuff->ucDataLenth)
{
//发送缓冲的数据没读取完毕
pICAN_TrBuff->TrBegin = 0; // 数据发送完毕
break; //退出
}
}
CANFrame.ucDLC = i; //写入数据场长度
//===================================================================================================================
//加入你的发送缓冲代码
//===================================================================================================================
CAN_ucWriteBuffer(pSendFrameBuff, &CANFrame); // 将数据帧写入发送缓冲
if (SendBuffToBus((void*)pSendFrameBuff))
{//数据发送
ICAN_ControlInfo.CyclicTimer = 0; // 此句为自己添加的,
//循环传送定时器清零,如果是循环发送数据的话,将从发送完应答信息之后开始;
ICAN_ControlInfo.Idle = 0; // 发送成功总线忙
SFRPAGE = CONFIG_PAGE;
P7MDOUT=0x03;
led0 =~led0; // 通信时,灯(P7.0)闪烁;
}
else
{//没有发送成功则继续
ICAN_ControlInfo.Idle = 2;
}
//===================================================================================================================
//===================================================================================================================
}
if (ICAN_ControlInfo.Idle == 2)
{//没有发送成功继续发送
if (SendBuffToBus((void*)pCAN_Node_Info))
{//数据发送
ICAN_ControlInfo.Idle = 0; // 发送成功总线忙
SFRPAGE = CONFIG_PAGE;
P7MDOUT=0x03;
led0 =~led0;//通信时,灯(P7.0)闪烁;
}
}
}
uchar ICANTime1ms; // 处理通信延时计数器
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 定时器处理程序
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ICAN_DlyCtr(uchar order)
{
ICANTime1ms++; // 毫秒计数器加1
if (ICANTime1ms >= 10)
{
//累积到10MS(ICAN时间片单位)
ICANTime1ms -= 10; // 毫秒计数器减10
//ICANTime1ms = 0;
//----------------------------------------------------------------------------------------------------------------
//ICAN_ControlInfo.MACIDTestStep = 10,表示:MACID检测完成,正常通信
//----------------------------------------------------------------------------------------------------------------
if (ICAN_ControlInfo.MACIDTestStep == 10)
{
//----------------------------------------------------------------------------------------------------------------
//CyclicMaster的单位为 10ms,当 CyclicMaster > 0时, (CyclicMaster*4)时间为从站判断主站发送通讯报文的是否超时的时
//间间隔,在通讯过程中,如果从站在(CyclicMaster*4)时间内未收到主站的命令报文,将自动删除连接,退出通讯。当
//CyclicMaster为 0 时,从站不检测通讯超时,直到收到正确的“删除连接”命令帧时,才删除与主站的连接。
//----------------------------------------------------------------------------------------------------------------
if (ICAN_Source.Area) // IF CyclicMaster > 0(如果不为0,则有连接超时)
{
if ((ICAN_ControlInfo.ConnectTOTimer >> 2) > ICAN_Source.Area)
{
//超过连接超时时间//连接定时器
ICAN_ControlInfo.ConnectTOFlag = 1; // 连接超时标志置位
}
else
{//连接未超时
ICAN_ControlInfo.ConnectTOFlag = 0; // 连接超时标志清零
ICAN_ControlInfo.ConnectTOTimer++; // 连接定时器+1
}
}
else // 没有连接超时
{
ICAN_ControlInfo.ConnectTOFlag = 0; // 连接超时标志清零
ICAN_ControlInfo.ConnectTOTimer = 0; // 连接超时定时器清零
}
//----------------------------------------------------------------------------------------------------------------
// CyclicParameter:定时循环参数, 当 CyclicParameter>0 时,按照 CyclicParameter设定定时参数,循环传送 I/O数据值;
// CyclicMaster:主站通讯定时参数,当 CyclicMaster>0 时, (CyclicMaster*4)时间为从站判断主站发送通讯报文的是否超时的时间间隔;
//----------------------------------------------------------------------------------------------------------------
if (ICAN_Source.Area)
{
//基于时间触发的通信//循环传送定时器
ICAN_ControlInfo.CyclicTimer++; // 循环传送定时器+1
if (ICAN_ControlInfo.CyclicTimer > ICAN_Source.Area)
{
//循环传送时间//循环传送定时器
ICAN_ControlInfo.CyclicParameterFlag = 1;// 循环传送标志置位
ICAN_ControlInfo.CyclicTimer = 0; // 循环传送定时器清零
}
}
else
{
//不存在基于时间触发的通信
ICAN_ControlInfo.CyclicParameterFlag = 0;// 循环传送标志清零
ICAN_ControlInfo.CyclicTimer = 0; // 循环传送定时器清零
}
}
else if (ICAN_ControlInfo.MACIDTestStep < 10)
{
//检测MACID两次
if (ICAN_ControlInfo.MACIDTestTimer < 101)
{
//MACID检测定时器小于1S
ICAN_ControlInfo.MACIDTestTimer++; // MACID检测定时器+1
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ican通信调度程序
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ICAN_Schedul(uchar order)
{
//----------------------------------------------------------------------------------------
//ICAN_ControlInfo.MACIDTestStep = 10(进入正常通信)
//----------------------------------------------------------------------------------------
if (ICAN_ControlInfo.MACIDTestStep == 10)
{
if (ICAN_ControlInfo.CyclicParameterFlag) // 循环传送标志置位(定时传送时间到)
{
iCAN_CyclicACK(order); // 输入端口定时循环或者状态改变传送命令
ICAN_ControlInfo.CyclicParameterFlag = 0;
}
ICANRxMsgObjExplain(order); // ICAN模块从机通信接收子程序
ICANTxMsgObjSend(order); // ICAN模块从机通信发送子程序
}
//----------------------------------------------------------------------------------------
//ICAN_ControlInfo.MACIDTestStep = 0(第一次正在生成MACID检测报文)
//----------------------------------------------------------------------------------------
else if (ICAN_ControlInfo.MACIDTestStep == 0) // 目的=检测有没有与自己相同的MACID
{
iCAN_MACIDTest(order,0); // 第一次生成MACID检测报文(发送方)
ICANTxMsgObjSend(order); // ICAN模块从机通信发送子程序
if (ICAN_ControlInfo.Idle == 0) // 如果发送成功
{
ICAN_ControlInfo.MACIDTestTimer=0; // MACID定时器清零
ICAN_ControlInfo.MACIDTestStep = 1; // 第一次等待超时
}
}
//----------------------------------------------------------------------------------------
//ICAN_ControlInfo.MACIDTestStep = 1(第一次正在等等接收MACID应答报文过程)
//----------------------------------------------------------------------------------------
else if (ICAN_ControlInfo.MACIDTestStep == 1)
{
if (ICAN_ControlInfo.MACIDTestTimer > 100) // 超时时间到没有检测到响应报文
{
ICAN_ControlInfo.MACIDTestStep = 2; // 再检测一次
}
}
//----------------------------------------------------------------------------------------
//ICAN_ControlInfo.MACIDTestStep = 2(第二次正在生成MACID检测报文)
//----------------------------------------------------------------------------------------
else if (ICAN_ControlInfo.MACIDTestStep == 2)
{
iCAN_MACIDTest(order,0); // 第二次生成MACID检测报文(发送方)
ICANTxMsgObjSend(order); // ICAN模块从机通信发送子程序
if (ICAN_ControlInfo.Idle == 0) // 如果发送成功
{
ICAN_ControlInfo.MACIDTestTimer=0; // MACID定时器清零
ICAN_ControlInfo.MACIDTestStep = 3; // 第二次等待超时
}
}
//----------------------------------------------------------------------------------------
//ICAN_ControlInfo.MACIDTestStep = 3(第二次正在等等接收MACID应答报文过程)
//----------------------------------------------------------------------------------------
else if (ICAN_ControlInfo.MACIDTestStep == 3)
{
if (ICAN_ControlInfo.MACIDTestTimer > 100) // 超时时间到没有检测到响应报文
{
ICAN_ControlInfo.MACIDTestStep = 10; // 进入正常通信
}
}
//----------------------------------------------------------------------------------------
//其他情况(预操作状态)????????????????????????????????????????????????????????????????????
//----------------------------------------------------------------------------------------
else
{
if (MACIDTemp != ICAN_Source.Area) // 等待MACID的改变
{
ICAN_ControlInfo.MACIDTestStep = 0; // 继续检测是否相同的MACID
}
}
} 兄弟,怎么你学的和我学的都一样啊?能不能交流一下? 呵呵,我正在弄一个总线系统,中层协议是PROFIBUS总线,下层协议是CANBUS协议。
由于CANBUS协议不太熟悉,因此下层协议先用MODBUS协议开发出来。(即PROFIBUS协议+MODBUS协议)
楼上的是在远方论坛的霸王猫?
你用profibus转can,用的是dpv0周期性报文吧? 是的。
目前:AT90CAN128已经调试通过。PROFIBUS协议转换芯片用的是北京一家公司的。CAN高层协议准备用iCAN。
http://cache.amobbs.com/bbs_upload782111/files_13/ourdev_432469.JPG
(原文件名:未命名.JPG) 好大的项目啊,个人感觉有点复杂了,协议层太多
象楼主这样全能的人少啊,又做can,又做profibus,还有tcp/ip
profibus的程序是自己开发还是用开发包? 呵呵。
目前只做CANBUS协议。PROFIBUS和TCP/IP是现成的。
不过TCP/IP和PROFIBUS(从站)应该不太复杂。
尤其是TCP/IP网上到处都是源代码。
PROFIBUS(从站)也是,就是操作SCP3芯片。网上也到处是相关的文章。 恩,是啊,我用的是vpc3+,移植的他们的demo程序,还算好用 先记下,以后学习! mark 不错不错哦 有一点不理解。为什么ican要做成主从方式,CAN最大的特点就是能实现多主,以提高总线利用率,若用主从方式,比RS485除了能够实现故障封闭和错误恢复,就没什么明显优势了。不知我的理解对不对? 先做个记号 记号,过段时间正准备用profibus 嗯,学习了,谢谢。 我觉得如果CAN只是做数据转发之类,没有必要用iCAN,自定义协议更简单,效率也更高。像iCAN,CANOpen,DeviceNet这类协议有其应用背景,在有些数据传输中反而觉得臃肿,它们的优点就是规范,所以重在互操作性好 lz 这个貌似是ican从站的 关于主站的 你那里的资料或者源码有吗 可以给我发一份 pcrulovehust@foxmail.com 谢谢啦 下载链接,见1楼。 谢谢楼主共享。呵呵。等待有空时好好研究下。ZLG的ican貌似比can open精简很多,在工控上值得一用。 谢谢。 楼主门路广 这是他自己搞出来的,用的估计不多。
不过比canopen好理解多了。符合中国人的思维习惯呀。 mark MARK mark mark!~ 支持,MARK CAN数据帧最长只有8字节吧? mark 记号,收藏 好牛xx啊 不错,学习学习,周公还是提供了很多好东西的
页:
[1]