CANOpen EMCY紧急报文介绍

什么是CANOpen紧急报文

CANOpen中的Emcy紧急报文用于当设备出现故障或警告时,向其它节点报告故障或警告使用的。如设备某个设备出现过压或过流时,就可以发送紧急报文。

紧急报文的格式

在这里插入图片描述
错误代码:是0x1003索引预定义错误字段的内容,是16bit详细的错误代码
错误寄存器:是0x1001索引当前错误状态的内容,是8bit,每个bit代表一个含义,粗略的表示设备故障方向。

CANFestival 如何发送紧急报文,

当节点出现故障时,调用UNS8 EMCY_setError(CO_Data* d, UNS16 errCode, UNS8 errRegMask, UNS16 addInfo)函数发送紧急报文。

/*! Sets a new error with code errCode. Also sets corresponding bits in Error register (1001h)
 **                                                                                                 
 **  
 ** @param d
 ** @param errCode Code of the error                                                                                        
 ** @param errRegister Bits of Error register (1001h) to be set.
 ** @return 1 if error, 0 if successful
 */
UNS8 EMCY_setError(CO_Data* d, UNS16 errCode, UNS8 errRegMask, UNS16 addInfo)
{
	UNS8 index;
	UNS8 errRegister_tmp;
	
	for (index = 0; index < EMCY_MAX_ERRORS; ++index)
	{
		if (d->error_data[index].errCode == errCode)		/* error already registered */
		{
			if (d->error_data[index].active)
			{
				MSG_WAR(0x3052, "EMCY message already sent", 0);
				return 0;//错误已注册已发送直接返回
			} else d->error_data[index].active = 1;		/*已注册但还未发送 将active置1*/ /* set as active error */
			break;
		}
	}
	
	if (index == EMCY_MAX_ERRORS)	/*进了这个if表示错误代码还没注册*/	/* if errCode not already registered */
		for (index = 0; index < EMCY_MAX_ERRORS; ++index) if (d->error_data[index].active == 0) break;	/* find first inactive error */
	
	if (index == EMCY_MAX_ERRORS)		/* error_data full */
	{
		MSG_ERR(0x3053, "error_data full", 0);
		return 1;
	}
	
	d->error_data[index].errCode = errCode;
	d->error_data[index].errRegMask = errRegMask;
	d->error_data[index].active = 1;
	
	/* set the new state in the error state machine */
	d->error_state = Error_occurred;//将对象字典中的错误状态标记为错误发生

	/* set Error Register (1001h) */
	for (index = 0, errRegister_tmp = 0; index < EMCY_MAX_ERRORS; ++index)
		if (d->error_data[index].active == 1) errRegister_tmp |= d->error_data[index].errRegMask;
	*d->error_register = errRegister_tmp;//修改了索引0x1001的内容
	
	/* set Pre-defined Error Field (1003h) */
	for (index = d->error_history_size - 1; index > 0; --index)//for循环的将0x1003索引的错误代码向后滑动一个
		*(d->error_first_element + index) = *(d->error_first_element + index - 1);
	*(d->error_first_element) = errCode | ((UNS32)addInfo << 16);//将新的错误代码设置到最前边的索引
	if(*d->error_number < d->error_history_size) ++(*d->error_number);//错误个数自增
	
	/* send EMCY message */
	if (d->CurrentCommunicationState.csEmergency)//如果当前通信状态下emcy紧急报文服务是支持的则发送
		return sendEMCY(d, errCode, *d->error_register, NULL, 0);
	else return 1;
}

使用该函数时一定要注意,同一个错误代码,只发送一次。因为源码中判断错误代码已经注册到0x1003索引的,并且已经发送的则不再发送。到了这里可能有人有疑问了,假设这样一种情况:设备在运行过程中,某一时候设备过压,发送了紧急报文,然后故障解除,在之后的一段时间再次出现设备过压,这岂不是就无法再次发送紧急报文了吗?别急,这就说到了下边要介绍的内容,如何解除故障。

CANFestival 如何解除故障

当设备某个故障解除的时候,用户调用void EMCY_errorRecovered(CO_Data* d, UNS16 errCode)函数解除这个故障状态。

/*! Deletes error errCode. Also clears corresponding bits in Error register (1001h)
 **                                                                                                 
 **  
 ** @param d
 ** @param errCode Code of the error                                                                                        
 ** @param errRegister Bits of Error register (1001h) to be set.
 ** @return 1 if error, 0 if successful
 */
void EMCY_errorRecovered(CO_Data* d, UNS16 errCode)
{
	UNS8 index;
	UNS8 errRegister_tmp;
	UNS8 anyActiveError = 0;
	
	for (index = 0; index < EMCY_MAX_ERRORS; ++index)
		if (d->error_data[index].errCode == errCode) break;		/* find the position of the error */

	
	if ((index != EMCY_MAX_ERRORS) && (d->error_data[index].active == 1))
	{
		d->error_data[index].active = 0;//上边的for循环找到故障代码的位置 这里将其active标记为0表示该故障已解除
		
		/* set Error Register (1001h) and check error state machine */
		for (index = 0, errRegister_tmp = 0; index < EMCY_MAX_ERRORS; ++index)
			if (d->error_data[index].active == 1)
			{
				anyActiveError = 1;//标记还有其它故障
				errRegister_tmp |= d->error_data[index].errRegMask;
			}
		if(anyActiveError == 0)//判断有没有其他故障
		{
			d->error_state = Error_free;
			/* send a EMCY message with code "Error Reset or No Error" */
			if (d->CurrentCommunicationState.csEmergency)
				sendEMCY(d, 0x0000, 0x00, NULL, 0);//如果所有的故障都解除了就发送一个8字节全0的紧急报文
		}
		*d->error_register = errRegister_tmp;
	}
	else
		MSG_WAR(0x3054, "recovered error was not active", 0);
}

解除的核心就是将active 字段设置为0。解除以后如果再次发生同样的故障就可以调用EMCY_errorRecovered函数继续发送紧急报文了

CANFestival处理接收到的紧急报文

当CANFestival协议栈收到紧急报文就会调用void proceedEMCY(CO_Data* d, Message* m)函数去处理接收到的紧急报文。

/*! This function is responsible to process an EMCY canopen-message.
 **
 **
 ** @param d
 ** @param m The CAN-message which has to be analysed.
 **
 **/
void proceedEMCY(CO_Data* d, Message* m)
{
	UNS8 nodeID;
	UNS16 errCode;
	UNS8 errReg;
	
	MSG_WAR(0x3055, "EMCY received. Proceed. ", 0);
  
	/* Test if the size of the EMCY is ok */
	if ( m->len != 8) {
		MSG_ERR(0x1056, "Error size EMCY. CobId  : ", m->cob_id);
		return;
	}
	
	/* post the received EMCY */
	nodeID = m->cob_id & 0x7F;
	errCode = m->Data[0] | ((UNS16)m->Data[1] << 8);
	errReg = m->Data[2];
	(*d->post_emcy)(d, nodeID, errCode, errReg);//调用回调函数,具体的动作留给用户实现
}

当处理完紧急报文会执行回调函数,具体要做什么处理由用户决定。

CANFestival紧急报文回调函数实现

void _post_emcy(CO_Data* d, UNS8 nodeID, UNS16 errCode, UNS8 errReg)
{
	printf("%s\r\n",__FUNCTION__);//收到emcy 紧急报文会执行这个回调函数
}

紧急报文概念不抽象,源码实现也较少,理解起来还算轻松,紧急报文就介绍到这里。

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值