使用can驱动会卡死在接收中断,一直产生接收中断
芯片: stm32f429/bsp: stm32/工具链: rt-thread studio,RT-Thread版本-4.03 HAL_Drivers里的can驱动有点问题。
1 插拔线、振动时线松动
会导致发送出错,hcan->State = HAL_CAN_STATE_ERROR;
static int _can_sendmsg(struct rt_can_device *can, const void *buf, rt_uint32_t box_num)
{
CAN_HandleTypeDef *hcan;
hcan = &((struct stm32_can *) can->parent.user_data)->CanHandle;
struct rt_can_msg *pmsg = (struct rt_can_msg *) buf;
CAN_TxHeaderTypeDef txheader = {0};
HAL_CAN_StateTypeDef state = hcan->State;
/* Check the parameters */
RT_ASSERT(IS_CAN_DLC(pmsg->len));
if ((state == HAL_CAN_STATE_READY) ||
(state == HAL_CAN_STATE_LISTENING))
{
/*check select mailbox is empty */
switch (1 << box_num)
{
case CAN_TX_MAILBOX0:
if (HAL_IS_BIT_SET(hcan->Instance->TSR, CAN_TSR_TME0) != SET)
{
/* Change CAN state */
hcan->State = HAL_CAN_STATE_ERROR;
/* Return function status */
return -RT_ERROR;
}
break;
2 此时因插拔线导致接收了些乱的帧或者过载
进接收中断,但进HAL_CAN_GetRxMessage因hcan->State为 HAL_CAN_STATE_ERROR退出了,邮箱有数据但就是读不出来,于是不断疯狂进中断,系统就像死机一样。
建议
上面贴的那段代码是不是不应该操作hcan->State,插拔线这种操作,CAN又不是出什么错,一旦变HAL_CAN_STATE_ERROR,所有正常库函数操作不了
还有个前提,我把CAN.c的发送改了,一直死等发送成功有点坑,加了resend_cnt
rt_inline int _can_int_tx(struct rt_can_device *can, const struct rt_can_msg *data, int msgs)
{
int size;
struct rt_can_tx_fifo *tx_fifo;
uint8_t resend_cnt = 0;
RT_ASSERT(can != RT_NULL);
size = msgs;
tx_fifo = (struct rt_can_tx_fifo *) can->can_tx;
RT_ASSERT(tx_fifo != RT_NULL);
while (msgs)
{
rt_base_t level;
rt_uint32_t no;
rt_uint32_t result;
struct rt_can_sndbxinx_list *tx_tosnd = RT_NULL;
rt_sem_take(&(tx_fifo->sem), RT_WAITING_FOREVER);
level = rt_hw_interrupt_disable();
tx_tosnd = rt_list_entry(tx_fifo->freelist.next, struct rt_can_sndbxinx_list, list);
RT_ASSERT(tx_tosnd != RT_NULL);
rt_list_remove(&tx_tosnd->list);
rt_hw_interrupt_enable(level);
no = ((rt_uint32_t)tx_tosnd - (rt_uint32_t)tx_fifo->buffer) / sizeof(struct rt_can_sndbxinx_list);
tx_tosnd->result = RT_CAN_SND_RESULT_WAIT;
if (can->ops->sendmsg(can, data, no) != RT_EOK)
{
/* send failed. */
level = rt_hw_interrupt_disable();
rt_list_insert_after(&tx_fifo->freelist, &tx_tosnd->list);
rt_hw_interrupt_enable(level);
rt_sem_release(&(tx_fifo->sem));
resend_cnt++;
if(resend_cnt < 0x7f)
{
continue;
}
else
{
tx_tosnd->result = RT_CAN_SND_RESULT_ERR;
}
}
can->status.sndchange = 1;
if(tx_tosnd->result == RT_CAN_SND_RESULT_WAIT)
rt_completion_wait(&(tx_tosnd->completion), 10);
level = rt_hw_interrupt_disable();
result = tx_tosnd->result;
if (!rt_list_isempty(&tx_tosnd->list))
{
rt_list_remove(&tx_tosnd->list);
}
rt_list_insert_before(&tx_fifo->freelist, &tx_tosnd->list);
rt_hw_interrupt_enable(level);
rt_sem_release(&(tx_fifo->sem));
if (result == RT_CAN_SND_RESULT_OK)
{
level = rt_hw_interrupt_disable();
can->status.sndpkg++;
rt_hw_interrupt_enable(level);
data ++;
msgs -= sizeof(struct rt_can_msg);
if (!msgs) break;
}
else
{
level = rt_hw_interrupt_disable();
can->status.dropedsndpkg++;
rt_hw_interrupt_enable(level);
break;
}
}
return (size - msgs);
}
CAN必须至少有两个设备在总线上
还有个前提,我把CAN.c的发送改了,一直死等发送成功有点坑,加了resend_cnt
rt_inline int _can_int_tx(struct rt_can_device *can, const struct rt_can_msg *data, int msgs) { int size; struct rt_can_tx_fifo *tx_fifo; uint8_t resend_cnt = 0; RT_ASSERT(can != RT_NULL); size = msgs; tx_fifo = (struct rt_can_tx_fifo *) can->can_tx; RT_ASSERT(tx_fifo != RT_NULL); while (msgs) { rt_base_t level; rt_uint32_t no; rt_uint32_t result; struct rt_can_sndbxinx_list *tx_tosnd = RT_NULL; rt_sem_take(&(tx_fifo->sem), RT_WAITING_FOREVER); level = rt_hw_interrupt_disable(); tx_tosnd = rt_list_entry(tx_fifo->freelist.next, struct rt_can_sndbxinx_list, list); RT_ASSERT(tx_tosnd != RT_NULL); rt_list_remove(&tx_tosnd->list); rt_hw_interrupt_enable(level); no = ((rt_uint32_t)tx_tosnd - (rt_uint32_t)tx_fifo->buffer) / sizeof(struct rt_can_sndbxinx_list); tx_tosnd->result = RT_CAN_SND_RESULT_WAIT; if (can->ops->sendmsg(can, data, no) != RT_EOK) { /* send failed. */ level = rt_hw_interrupt_disable(); rt_list_insert_after(&tx_fifo->freelist, &tx_tosnd->list); rt_hw_interrupt_enable(level); rt_sem_release(&(tx_fifo->sem)); resend_cnt++; if(resend_cnt < 0x7f) { continue; } else { tx_tosnd->result = RT_CAN_SND_RESULT_ERR; } } can->status.sndchange = 1; if(tx_tosnd->result == RT_CAN_SND_RESULT_WAIT) rt_completion_wait(&(tx_tosnd->completion), 10); level = rt_hw_interrupt_disable(); result = tx_tosnd->result; if (!rt_list_isempty(&tx_tosnd->list)) { rt_list_remove(&tx_tosnd->list); } rt_list_insert_before(&tx_fifo->freelist, &tx_tosnd->list); rt_hw_interrupt_enable(level); rt_sem_release(&(tx_fifo->sem)); if (result == RT_CAN_SND_RESULT_OK) { level = rt_hw_interrupt_disable(); can->status.sndpkg++; rt_hw_interrupt_enable(level); data ++; msgs -= sizeof(struct rt_can_msg); if (!msgs) break; } else { level = rt_hw_interrupt_disable(); can->status.dropedsndpkg++; rt_hw_interrupt_enable(level); break; } } return (size - msgs); }
亲测可用,大神受我一拜。
CAN必须至少有两个设备在总线上
有多少个设备不重要,关键接口松动会死机,你总不能要求两根线焊死到电路板上吧
RTT的CAN驱动用下来觉得问题还是不少的,需要遇到具体的问题再自己手动打补丁。后来又发现,我这边会卡在 rt_sem_take(&(tx_fifo->sem), RT_WAITING_FOREVER),秉持着只要不死等那就等不死的想法,绕过去了
https://github.com/RT-Thread/rt-thread/issues/4981#issue-981722139 can发送错误后要取消相应的邮箱发送。(要不然,下次再发送时,没有空邮箱就会一直死等)。然后,在上层自己做重发机制。