rtthread CAN驱动解读

一、CAN中断接收(线程只能非阻塞读)

  线程 Open can设备时,驱动默认初始化并激活 Filter bank0:绑定到FIFO0,single 32-bit scale,mask mode,不过滤报文的任何 bit。

若使能了宏 “RT_CAN_USING_HDR”:

1、CAN2的起始Filter bank 强制设为 14
  PS:应用层在配置 filter时,若要指定使用 filter n,注意使用CAN1时,n不能 >= 14,使用CAN2时,n 不能 < 14)

2、各Filter bank 强制配置为 single 32-bit scale

3、各Filter bank 强制绑定到 FIFO0

4、各Filter bank 设为 mask mode时,IDE位、RTR位 强制需要匹配

5、线程A 传入的ID、mask,都不包含IDE位、RTR位

6、线程A 设置 Filter bank_X 为 List mode时, Filter bank_X被分成 filter 0 和 filter 1,线程A 传入的 ID 写入 filter 0, 传入的 mask 写入 filter 1(即线程A 一次设置了 两个要过滤的报文)。

  存在的问题:线程A 配置Filter bank 或 读CAN报文时都要设置 参数“hdr”,参数“hdr”表示 Filter bank 的编号,共有28个 Filter bank,因此定义数组can->hdr[ 28] ,并使用 filter match index (filter num ) 作为数组的索引。问题是:Filter bank的编号 是定死的,而 filter num 是FIFO0 根据绑定的各个Filter bank的配置自动设置的,二者可能不一样。只要任何一个Filter bank 被线程A设置为 List mode,则 FIFO0中的 Filter Match Index 和 Filter bank编号 不相等,线程A 读指定hdr的报文时就有问题,接收的报文保存到can->hdr[ filter_num ]时也会有问题

  解决方法:为了让 Filter bank编号 和 filter num 保持一一对应关系,将各Filter bank 强制配置为 single 32-bit scale 、mask mode,即一个Filter bank,只有一个 filter num。不管线程A配置Filter bank 是 List mode 还是 Mask mode,驱动都认为是 Mask mode(在Mask mode时,只要mask值设对,也能实现 List mode)。

​ 线程A 在配置Filter bank时,若模式为 List mode,则 参数mask可忽略(线程A 一次只能设置 一个要过滤的报文)。驱动发现模式为 List mode时,除了把 ID、IDE位、RTR位等过滤值写入Filter bank寄存器外,也会根据 ID、IDE位、RTR位 计算出对应的 mask值,并将mask值写入Filter bank寄存器(驱动通过 Mask mode实现 List mode)。

7、缓存 msg_list_X.data 被APP读取后,将同时脱离链表 can->can_rx->uselist (rt_list_remove(&msg_list_X.list)) 和链表
can->hdr[ msg_list_X.data.hdr ].list (rt_list_remove(&msg_list_X.hdrlist)),
并插入链表 can->can_rx->freelist
(rt_list_insert_before(&can->can_rx->freelist, &msg_list_X.list)))。

8、应用层如果有多份CAN协议,每份协议都可以定义一个线程,然后每个线程设置自己的过滤器,读的话只读自己设置的过滤器过滤的报文,这样多份协议就相互独立,互不影响。

9、can->hdr[ filter_num ]: 编号为 filter_num 的过滤器 成功过滤一帧报文并保存到FIFO0时,软件在中断函数中从FIFO0读出此报文,并保存到 空闲节点msg_list_X中(链表freelist有节点则直接取出,否则把链表uselist的最老的那个节点取出来),然后把 节点msg_list_X 插入链表 can->hdr[ filter_num ].list、链表uselist。当线程A读报文时,把链表 can->hdr[ filter_num ].list(指定hdr)的首节点 or 链表uselist(不指定hdr)的首节点X 取出并将其内容复制到线程A的buff中,然后将节点X从 链表can->hdr[ filter_num ].list 和 链表uselist 中一起移除,最后把节点X插入链表freelist。

在这里插入图片描述

二、CAN中断发送(线程只能阻塞发送)

线程 Open can设备时,驱动默认初始化CAN:

① 禁能自动唤醒,使能bus-off自动管理,禁能自动重发,发送邮箱优先级 by the request order。
② 使能 EWGF、EPVF和BOFF中断(使能后,若 EWGF、EPVF、BOFF三个标志中任何一个置位,则ERRI标志置位);
使能ERRI中断(使能后,若ERRI标志置位则向NVIC发送CAN1_SCE中断请求);
在NVIC中使能了 CAN1_SCE中断。

PS:
存在的问题:EWGF、EPVF、BOFF等标志是只读标志,当发送、接收失败次数达到指定阈值时硬件自动置位这些标志,且必须总线恢复正常后标志才能由硬件清零。这样可能出现的问题是,当总线出现了异常,导致can1发送和接收一直失败,则EWGF、EPVF、BOFF等标志最终会置位,并且因为总线一直处于异常状态,这些标志不会由硬件清零,因此它们会一直触发 CAN1_SCE中断,导致CPU卡死在中断中,线程无法被运行。
-----总线上没有接收节点时,节点A发送报文时会卡死在CAN1_SCE中断----
-----CAN总线异常时,节点A会卡死在CAN1_SCE中断-------

解决方法:在CAN1_SCE中断函数中,EWGF、EPVF、BOFF等标志置位时,禁能其对应的中断。


1、线程配置 can->config.privmode 为 RT_CAN_MODE_NOPRIV时:驱动使用链表freelist 来指定 TX Mailbox_X 发送。

  线程A 获取 tx_fifo互斥量(永久等待),从 链表freelist 取出首节点 sndbxinx_list_X 并找到与其绑定的 TX Mailbox_X,接着把报文写到 TX Mailbox_X 并请求发送此邮箱(若无法发送则把节点sndbxinx_list_X 重新插入 链表freelist,并释放 tx_fifo互斥量 ,然后返回线程A),然后永久等待完成量“sndbxinx_list_X.completion”… … 线程A被唤醒后,表明 TX Mailbox_X 发送结束,把 节点sndbxinx_list_X 重新插入 链表freelist,并释放 tx_fifo互斥量,若发送失败(sndbxinx_list_X.result = RT_CAN_SND_RESULT_ERR)则直接返回线程A,否则继续发送线程A 的下一帧报文。

  在CAN1_TX中断函数中,释放完成量“sndbxinx_list_X.completion”,并通过变量“sndbxinx_list_X.result”告诉线程A 发送 TX Mailbox_X是成功还是失败 。

PS:
存在的问题:线程A 使用 TX Mailbox_X 发送时,线程A被挂起永久等待完成量“sndbxinx_list_X.completion”,这个完成量是在 标志RQCP_X置位 触发的 CAN1_TX中断里被释放的。这样可能出现的问题是,在使用自动重传功能时,如果总线异常了,导致 TX Mailbox_X 发送失败了,则 TX Mailbox_X 会重发,RQCP_X保持清零状态,不触发CAN1_TX中断,那么完成量“sndbxinx_list_X.completion”就不会被释放,线程A将一直被挂起,其他所有等待获取tx_fifo互斥量的线程也将一直被挂起。

解决方法:使用自动重传功能后,TX Mailbox_X 发送错误时,RQCP_X标志为0,TERR_X标志为1,且CAN1_SCE中断会触发,在CAN1_SCE中断函数中,遍历 TX Mailbox_0、 TX Mailbox_1、 TX Mailbox_2 三个邮箱,发现 TX Mailbox_X 的 TERR_X 标志为1 且 RQCP_X 标志为0,则说明 TX Mailbox_X 发送失败且使能了重发功能,此时软件手动取消 TX Mailbox_X 的发送(置位ABRQ_X标志),取消后,RQCP_X置位触发CAN1_TX中断,这样在 CAN1_TX中断函数中就可以释放完成量“sndbxinx_list_X.completion”。


2、线程配置 can->config.privmode 为 RT_CAN_MODE_PRIV时:驱动使用线程A指定的 TX Mailbox_X 发送。

  线程A 通过设置 msg.priv 告诉驱动用哪个发送邮箱发送。驱动根据 msg.priv 找到邮箱 TX Mailbox_X(msg.priv表示邮箱编号,取值 0 ~ 2 ),接着把报文写到 TX Mailbox_X 并请求发送此邮箱(若无法发送则返回线程A),然后永久等待完成量“sndbxinx_list_X.completion”… … 线程A被唤醒后,表明 TX Mailbox_X 发送结束,若发送失败(sndbxinx_list_X.result = RT_CAN_SND_RESULT_ERR)则直接返回线程A,否则继续发送线程A 的下一帧报文。

  在CAN1_TX中断函数中,释放完成量“sndbxinx_list_X.completion”,并通过变量“sndbxinx_list_X.result”告诉线程A发送 TX Mailbox_X 是成功还是失败 。

PS:线程A在 用TX Mailbox_X 发送时,若其他线程也调用 device_write() 并使用 TX Mailbox_X 发送,则在rt_completion_wait中 RT_ASSERT失败。

在这里插入图片描述


参考资料

[1] https://gitee.com/rtthread/rt-thread/blob/master/components/drivers/can/dev_can.c

[2] https://gitee.com/rtthread/rt-thread/blob/master/bsp/stm32/libraries/HAL_Drivers/drivers/drv_can.c

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值