什么是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 紧急报文会执行这个回调函数
}
紧急报文概念不抽象,源码实现也较少,理解起来还算轻松,紧急报文就介绍到这里。