问题描述: 如果CAN因为外部干扰导致通讯的数据域期间出现位填充错误,此时会按照期望停止接收当前帧数据并回馈错误到总线上,但是下一帧通讯报文会出现数据错序,且随后的报文又自动恢复正常的现象。 解决方法:
方法1:开启CAN的错误类型记录中断号对应的错误中断(中断优先级需设定为最高),在CAN错误类型记录中断的中断函数内检测到出现位填充错误时,复位CAN(可只复位CAN寄存器,其相关的GPIO等、NVIC不需复位),并在CAN错误中断函数内完成CAN的重新初始化。
此方法适用于期望快速完成CAN的初始化,以保障CAN及时参与通讯,避免过多CAN数据丢失的场景。
以CAN1为例,其典型示例代码如下:
/* 开启CAN的上次错误中断号对应的错误中断并设定中断最高优先级 */
nvic_irq_enable(CAN1_SE_IRQn, 0x00, 0x00);
can_interrupt_enable(CAN1, CAN_ETRIEN_INT, TRUE);
can_interrupt_enable(CAN1, CAN_EOIEN_INT, TRUE);
/* 中断服务函数 */
void CAN1_SE_IRQHandler(void)
{
__IO uint32_t err_index = 0;
if(can_flag_get(CAN1,CAN_ETR_FLAG) != RESET)
{
err_index = CAN1->ests & 0x70;
can_flag_clear(CAN1, CAN_ETR_FLAG);
if(err_index == 0x00000010)
{
can_reset(CAN1);
/* 调用CAN初始化函数 */
}
}
}
注意事项
a) CAN错误类型记录中断的优先级需设定为最高;
b) 由于CAN初始化存在耗时,出现问题后CAN不能及时恢复参与通讯,因此存在丢数据的现象。 方法2:开启CAN的错误类型记录中断号对应的错误中断(中断优先级需设定为最高),在CAN错误类型记录中断的中断函数内检测到出现位填充错误时,复位CAN(可只复位CAN寄存器,其相关的GPIO等、NVIC不需复位),及记录复位事件,并通过在其他低优先级中断或main函数内重新进行CAN初始化。 此方法适用于可接受CAN不能及时参与通讯,需严格保障CAN的重新初始化不影响其他应用逻辑的实现场景。 以CAN1为例,其典型示例代码如下: /* 开启CAN的上次错误中断号对应的错误中断并设定中断最高优先级 */
nvic_irq_enable(CAN1_SE_IRQn, 0x00, 0x00);
can_interrupt_enable(CAN1, CAN_ETRIEN_INT, TRUE);
can_interrupt_enable(CAN1, CAN_EOIEN_INT, TRUE);
/* 中断服务函数 */
__IO uint32_t can_reset_index = 0;
void CAN1_SE_IRQHandler(void)
{
__IO uint32_t err_index = 0;
if(can_flag_get(CAN1,CAN_ETR_FLAG) != RESET)
{
err_index = CAN1->ests & 0x70;
can_flag_clear(CAN1, CAN_ETR_FLAG);
if(err_index == 0x00000010)
{
can_reset(CAN1);
can_reset_index = 1;
}
}
应用再在期望的地方(例如main函数内)查询can_reset_index是否置位,置位后调用CAN初始化函数。 注意事项 a) CAN错误类型记录中断的优先级需设定为最高; b) 由于CAN初始化及其他应用中断存在耗时,出现问题后CAN不能及时恢复参与通讯,因此存在丢数据的现象。 方法3:开启CAN的错误类型记录中断号对应的错误中断(中断优先级需设定为最高),在CAN错误类型记录中断的中断函数内检测到出现位填充错误时,强制发送一帧标识符优先级最高的无效报文。 此方法适用于不期望消耗时间去复位CAN,CAN总线上的所有报文标识符均为已知,且CAN各个节点有严格按照标识符过滤条件来接收报文的实现场景。 以CAN1为例,其典型示例代码如下: /* 强制发送一帧标识符优先级最高的无效报文 */
static void can_transmit_data(void)
{
uint8_t transmit_mailbox;
can_tx_message_type tx_message_struct;
tx_message_struct.standard_id = 0x0;
tx_message_struct.extended_id = 0x0;
tx_message_struct.id_type = CAN_ID_STANDARD;
tx_message_struct.frame_type = CAN_TFT_DATA;
tx_message_struct.dlc = 8;
tx_message_struct.data[0] = 0x00;
tx_message_struct.data[1] = 0x00;
tx_message_struct.data[2] = 0x00;
tx_message_struct.data[3] = 0x00;
tx_message_struct.data[4] = 0x00;
tx_message_struct.data[5] = 0x00;
tx_message_struct.data[6] = 0x00;
tx_message_struct.data[7] = 0x00;
can_message_transmit(CAN1, &tx_message_struct);
}
/* 开启CAN的上次错误中断号对应的错误中断并设定中断最高优先级 */
nvic_irq_enable(CAN1_SE_IRQn, 0x00, 0x00);
can_interrupt_enable(CAN1, CAN_ETRIEN_INT, TRUE);
can_interrupt_enable(CAN1, CAN_EOIEN_INT, TRUE);
/* 中断服务函数 */
void CAN1_SE_IRQHandler(void)
{
__IO uint32_t err_index = 0;
if(can_flag_get(CAN1,CAN_ETR_FLAG) != RESET)
{
err_index = CAN1->ests & 0x70;
can_flag_clear(CAN1, CAN_ETR_FLAG);
if(err_index == 0x00000010)
{
can_transmit_data;
}
}
}
注意事项 a) CAN错误类型记录中断的优先级需设定为最高; b) 此方法仅适用于发送FIFO优先级由报文标识符决定的场景; c) 此方法无效报文的标识符可修改,但一定要确保其标识符的优先级是CAN总线上最高的,且不会被其他节点当做正常报文接收。
|