CANOpen协议详解(二):协议具体内容

本文详细介绍了CANOpen协议中的CAN消息格式,NMT协议,包括NMT控制消息、节点监控消息、节点心跳消息和节点启动消息。此外,还深入解析了SDO协议,包括快速读写协议和中止协议,用于读取或写入节点对象字典。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.CAN消息

一帧CAN消息的通信协议如下:
在这里插入图片描述
其中,
id: CAN消息的标识符,通常是11bits
rtr: 0–消息帧,普通消息;1–远程帧,远程传输请求消息,这种类型的消息不能包含数据帧;
dn: 数据帧,一帧普通CAN消息包括0~8bytes数据
在CANOpen中需要自己实现CAN发送消息接口,类似如下代码:

/********CAN接口函数 ********/
/*****************************
函数名称 : canSend
功    能 : CAN发送一帧消息
参    数 : notused --- 未使用
            m ----------- 消息内容
返 回 值 : 0:失败  1:成功
*******************************/
unsigned char canSend(CAN_PORT notused, Message *m)
{
  uint8_t i;
  static CanTxMsg TxMsg;

  TxMsg.StdId = m->cob_id;

  if(m->rtr)
      TxMsg.RTR = CAN_RTR_REMOTE;//远程帧
  else
      TxMsg.RTR = CAN_RTR_DATA;//消息帧

  TxMsg.IDE = CAN_ID_STD;//标准消息类型
  TxMsg.DLC = m->len;//数据帧长度,byte
  for(i=0; i<m->len; i++)
      TxMsg.Data[i] = m->data[i];//数据帧

  if(xQueueSend(xCANSendQueue, &TxMsg, 100) != pdPASS)
  {
      return 1;
  }
  return 0;
}

2.CANOpen中的CAN消息

在CANOpen中的CAN消息形式如下:
在这里插入图片描述
在CAN帧中数据都是以低位优先(小端模式)的方式存放。例如,0x01020304应该按如下方式存放在CAN帧中:
在这里插入图片描述
其中,cobId用于表示CANOpen中消息类型和nodeID,4bits表示消息类型,7bits表示nodeID,如下:
在这里插入图片描述
在这里插入图片描述

3.NMT协议

NMT用于管理和监控网络中的各个节点,包括Boot-up消息、Heartbeat消息、节点保护消息、NMT控制消息。

3.1NMT控制消息

将节点置于operational模式:
在这里插入图片描述
将节点置于stop模式:
在这里插入图片描述
将节点置于pre-operational模式:
在这里插入图片描述
将节点置于reset-application模式:
在这里插入图片描述
将节点置于reset-communication模式:
在这里插入图片描述
如果要控制所有节点,则nodeId=0x00。
例如:

  1. 将节点0x6置于operational模式,000 01 06;
  2. 将所有节点置于pre-operational模式,000 80 00;
    在CanFestival中的代码如下:
/*!
**
**
** @param d
** @param nodeId
** @param cs
**
** @return
**/
UNS8 masterSendNMTstateChange(CO_Data* d, UNS8 nodeId, UNS8 cs)
{
  Message m;

  MSG_WAR(0x3501, "Send_NMT cs : ", cs);
  MSG_WAR(0x3502, "    to node : ", nodeId);
  /* message configuration */
  m.cob_id = 0x0000; /*(NMT) << 7*/
  m.rtr = NOT_A_REQUEST;
  m.len = 2;
  m.data[0] = cs;
  m.data[1] = nodeId;

  return canSend(d->canHandle,&m);
}

其中,cs的定义如下:

/* NMT Command Specifier, sent by master to change a slave state */
/* ------------------------------------------------------------- */
/* Should not be modified */
#define NMT_Start_Node              0x01
#define NMT_Stop_Node               0x02
#define NMT_Enter_PreOperational    0x80
#define NMT_Reset_Node              0x81
#define NMT_Reset_Comunication      0x82

3.2 节点监控消息

主节点通过发送以下消息询问从节点的状态:
在这里插入图片描述
从节点收到后回复:

例如:1.为了询问节点5的状态,主节点需要发送一条CAN远程帧(rtr=1):705;
2.节点5处于停止状态,所以回复:705 04
3.主节点通过NMT控制消息使它进入pre-operational模式:000 80 05
4.主节点发送新的request请求:705
5.节点5现在处于pre-operational模式,所以它回复:705 FF(注意:state的最高位t翻转到了1,因为7F的二进制是:01111111,最高位翻转到1就成了FF)

在CanFestival中的节点监控代码如下:

/*!
**
**
** @param d
** @param nodeId
**
** @return
**/
UNS8 masterSendNMTnodeguard(CO_Data* d, UNS8 nodeId)
{
  Message m;

  /* message configuration */
  UNS16 tmp = nodeId | (NODE_GUARD << 7); 
  m.cob_id = UNS16_LE(tmp);
  m.rtr = REQUEST;
  m.len = 0;

  MSG_WAR(0x3503, "Send_NODE_GUARD to node : ", nodeId);

  return canSend(d->canHandle,&m);
}

3.3 节点心跳消息

在不需要主节点request的情况下,节点周期性的发送它的状态:

注意:这里没有需要翻转的位了。
例如:节点5处于pre-operational模式,那么它就会定时发送:705 7F

在CanFestival中的节点心跳代码如下:

/*! The Producer Timer Callback
**
**
** @param d
** @param id
 * @ingroup heartbeato
**/
void ProducerHeartbeatAlarm(CO_Data* d, UNS32 id)
{
  (void)id;
  if(*d->ProducerHeartBeatTime)
    {
      Message msg;
      /* Time expired, the heartbeat must be sent immediately
      ** generate the correct node-id: this is done by the offset 1792
      ** (decimal) and additionaly
      ** the node-id of this device.
      */
      UNS16 tmp = *d->bDeviceNodeId + 0x700;
      msg.cob_id = UNS16_LE(tmp);
      msg.len = (UNS8)0x01;
      msg.rtr = 0;
      msg.data[0] = d->nodeState; /* No toggle for heartbeat !*/
      /* send the heartbeat */
      MSG_WAR(0x3130, "Producing heartbeat: ", d->nodeState);
      canSend(d->canHandle,&msg );

    }else{
      d->ProducerHeartBeatTimer = DelAlarm(d->ProducerHeartBeatTimer);
    }
}

这个函数在定时器中断中调用。

3.4 节点启动消息

当节点经过initializingm模式进入pre-operational模式时,会发送节点启动消息:
在这里插入图片描述

/*!
**
**
** @param d
**
** @return
**/
UNS8 slaveSendBootUp(CO_Data* d)
{
  Message m;

#ifdef CO_ENABLE_LSS
  if(*d->bDeviceNodeId==0xFF)return 0;
#endif

  MSG_WAR(0x3407, "Send a Boot-Up msg ", 0);

  /* message configuration */
  {
	  UNS16 tmp = NODE_GUARD << 7 | *d->bDeviceNodeId; 
	  m.cob_id = UNS16_LE(tmp);
  }
  m.rtr = NOT_A_REQUEST;
  m.len = 1;
  m.data[0] = 0x00;

  return canSend(d->canHandle,&m);
}

4.SDO协议

SDO协议用于读取或写入节点的对象字典。读或写的请求(request)由客户端节点发起,数据被读取或被写入的节点是服务器节点。如果是读取或写入小于4bytes的数据,那么最简单的方式是使用SDO快速读写协议。所有的SDO协议都与一帧CAN消息长度相同,包括8bytes数据和rtr=0。

4.1 SDO快速写协议

如果要将数据0xd0写入服务器节点的对象字典中,那么客户端需要发送如下请求:
在这里插入图片描述
如果要将数据0xd0d1写入服务器节点的对象字典中,那么客户端需要发送如下请求:
在这里插入图片描述
如果要将数据0xd0d1d2写入服务器节点的对象字典中,那么客户端需要发送如下请求:
在这里插入图片描述
如果要将数据0xd0d1d2d3写入服务器节点的对象字典中,那么客户端需要发送如下请求:
在这里插入图片描述
如果服务器端成功接收到数据,则服务器发送回复(response):
在这里插入图片描述
如果服务器端接收失败,则服务器发送响应:
在这里插入图片描述
例如:
1.将1byte的数据0xFD写入到服务器节点5的对象字典,索引0x1400,子索引2,那么客户端节点发送:605 2F 00 14 02 FD 00 00 00
如果接收成功,节点5回复:585 60 00 14 02 00 00 00 00
2.将4byte数据0x60120208写入到服务器节点5的对象字典,索引0x1603,子索引1,那么客户端节点发送:605 23 03 16 01 08 02 12 60
如果接收成功,节点5回复:585 60 03 16 01 00 00 00 00

4.2 SDO快速读协议

如果要从服务器节点的对象字典中读取数据0xd0d1…,那么客户端需要发送如下请求:
在这里插入图片描述
如果读取的数据是1byte,那么如果读取成功的话服务器返回:
在这里插入图片描述
如果读取的数据是2byte,那么如果读取成功的话服务器返回:
在这里插入图片描述
如果读取的数据是3byte,那么如果读取成功的话服务器返回:
在这里插入图片描述
如果读取的数据是4byte,那么如果读取成功的话服务器返回:
在这里插入图片描述
如果读取失败,则服务器返回:
在这里插入图片描述
例如:
1.从节点5的对象字典中读取1byte数据0xFD,索引0x1400,子索引2,需要发送:605 40 00 14 02 00 00 00 00
如果读取成功,节点5返回:585 4F00 14 02 FD 00 00 00
2.从节点5的对象字典中读取4byte数据0x60120208,索引0x1603,子索引1,需要发送:605 40 03 16 01 00 00 00 00
如果读取成功,节点5返回:585 43 03 16 01 08 02 12 60

4.3 SDO中止协议

如果SDO读写错误,会发送SDO中止错误码,错误码的含义如下:
在这里插入图片描述

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巴普蒂斯塔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值