Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
STM32
CAN总线
RTT STM32系列 CAN发送卡死 RTT底层驱动BUG
18.00
发布于 2021-09-09 17:17:21 浏览:8407
订阅该版
[tocm] # STM32 CAN发送卡死问题与根本解决(RTT底层自身问题) ## 1 Bug导致的现象 * 问题1 RTT 4.0.2 CAN2**没有连接CAN设备**(或连接的设备未上电)时,一旦CAN2启动发送,RTT即卡死(此Bug官方在4.0.3修复了) * 问题2 RTT 4.0.2、4.0.3中,CAN正在发送的过程中,如果CAN线硬件出现松动,或CANH CANL出现临时短路,则**线程会卡死在CAN发送的 `rt_device_write` 中** ## 2 分析与解决 ### 2.1 CAN2没连接设备时CAN发送会卡死 此问题出现在**RTT4.0.3**之前的版本中。 drv_can.c中的 `CAN2_SCE_IRQHandler()` 中,`case RT_CAN_BUS_ACK_ERR` 中的`if`中将drv_can2写成了drv_can1,改正之后即可解决CAN2在发送遇到设备无应答时出现程序卡死的问题。 **原因分析**:这里的CAN2_SCE_IRQHandler函数为发送出错的处理中断函数,当CAN2没有连接CAN设备(或连接的设备未上电)时,如果CAN2发送了报文,则一段时间后就会进入这个错误处理函数,而这里的if写成了can1,导致错误没有正常处理,导致程序卡死。 ### 2.2 CAN发送过程硬件不稳或CANH-CANL短路后程序卡死 此问题出现在**RTT4.0.3**以及之前的版本中。笔者在4.0.2和4.0.3中均进行过测试。 #### 2.2.1 修改方法 类似的卡死问题,有些资料给出的解决方案是打开CAN初始化时的`AutoRetransmission`功能,这个方法看似也能解决问题,但其实是治标不治本。在2.2.2 问题分析中进行详细原因说明。 在drv_can.c中找到`CAN1_TX_IRQHandler`,该函数主要部分为if-elseif-elseif,我们需要在最后一个elseif结束后添加else,如下代码中的倒数第4行: ```C /** * @brief This function handles CAN1 TX interrupts. transmit fifo0/1/2 is empty can trigger this interrupt */ void CAN1_TX_IRQHandler(void) { rt_interrupt_enter(); CAN_HandleTypeDef *hcan; hcan = &drv_can1.CanHandle; if (__HAL_CAN_GET_FLAG(hcan, CAN_FLAG_RQCP0)) { if (__HAL_CAN_GET_FLAG(hcan, CAN_FLAG_TXOK0)) { rt_hw_can_isr(&drv_can1.device, RT_CAN_EVENT_TX_DONE | 0 << 8); } else { rt_hw_can_isr(&drv_can1.device, RT_CAN_EVENT_TX_FAIL | 0 << 8); } /* Write 0 to Clear transmission status flag RQCPx */ SET_BIT(hcan->Instance->TSR, CAN_TSR_RQCP0); } else if (__HAL_CAN_GET_FLAG(hcan, CAN_FLAG_RQCP1)) { if (__HAL_CAN_GET_FLAG(hcan, CAN_FLAG_TXOK1)) { rt_hw_can_isr(&drv_can1.device, RT_CAN_EVENT_TX_DONE | 1 << 8); } else { rt_hw_can_isr(&drv_can1.device, RT_CAN_EVENT_TX_FAIL | 1 << 8); } /* Write 0 to Clear transmission status flag RQCPx */ SET_BIT(hcan->Instance->TSR, CAN_TSR_RQCP1); } else if (__HAL_CAN_GET_FLAG(hcan, CAN_FLAG_RQCP2)) { if (__HAL_CAN_GET_FLAG(hcan, CAN_FLAG_TXOK2)) { rt_hw_can_isr(&drv_can1.device, RT_CAN_EVENT_TX_DONE | 2 << 8); } else { rt_hw_can_isr(&drv_can1.device, RT_CAN_EVENT_TX_FAIL | 2 << 8); } /* Write 0 to Clear transmission status flag RQCPx */ SET_BIT(hcan->Instance->TSR, CAN_TSR_RQCP2); } else { rt_hw_can_isr(&drv_can1.device, RT_CAN_EVENT_TX_FAIL | 0 << 8); } rt_interrupt_leave(); } ``` 同理,需要在`CAN2_TX_IRQHandler`中的最后添加else,注意是`drv_can2`: ```C else { rt_hw_can_isr(&drv_can2.device, RT_CAN_EVENT_TX_FAIL | 0 << 8); } ``` 接着,需要在`_can_sendmsg`中注释掉`Change CAN state`对应的程序,如下程序中的#if(0)---#endif ```C /*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) { #if (0) /* Change CAN state */ hcan->State = HAL_CAN_STATE_ERROR; #endif /* Return function status */ return -RT_ERROR; } break; case CAN_TX_MAILBOX1: if (HAL_IS_BIT_SET(hcan->Instance->TSR, CAN_TSR_TME1) != SET) { // HERO_EDIT_BEGIN #if (0) /* Change CAN state */ hcan->State = HAL_CAN_STATE_ERROR; #endif // HERO_EDIT_END /* Return function status */ return -RT_ERROR; } break; case CAN_TX_MAILBOX2: if (HAL_IS_BIT_SET(hcan->Instance->TSR, CAN_TSR_TME2) != SET) { #if (0) /* Change CAN state */ hcan->State = HAL_CAN_STATE_ERROR; #endif /* Return function status */ return -RT_ERROR; } break; default: RT_ASSERT(0); break; } ``` 完成以上修改之后就已经解决CAN卡死的问题了。 #### 2.2.2 程序分析 当CAN发送时遇到CAN线被短接的情况或者接触不良的情况,则stm32会产生发送完成中断,但相关标志位均为0,这个情况在STM32的手册里也没有描述,但是测试发现确实存在,下文称这种情况为**没有标志位的中断**。这个现象是导致CAN短接或接触不良时程序卡死的根本原因。 如果初始化CAN时没有打开CAN外设的发送失败自动重发功能(RTT初始化CAN时默认不会打开自动重发),则CAN线的硬件连接恢复后STM32也不会再次产生发送完成中断,即这次发送已经失败了,并且出现了一次没有标志位的发送完成中断,此后也不会再有这次发送的发送完成中断了。 RTT的STM32CAN发送函数`rt_device_write(...)`被调用时,启动发送之后,线程会挂起在一个completion信号量上,这个信号量将在CAN发送完成中断服务函数里释放,但中断服务函数中写的释放信号量是有条件的,条件是查到对应的标志位为1时释放对应的信号量。则如果出现了**没有标志位的中断**,该次中断中程序是不会释放completion信号量的,而由于CAN发送线程现在已经挂起,也不会发起下一轮发送,不再次启动发送则STM32不会再次产生发送完成中断,只有中断中释放信号量才能让CAN发送线程继续发送,而只有CAN发送线程再次正常启动发送时才能进入中断并释放信号量。这就陷入了死循环,导致发送CAN的线程被永远挂在了这个信号量上。 尝试解决这个问题的第一步就是在发送完成中断中,程序进行标志位判断的地方加个else,遇到没有标志位的中断时也得发信号量,并且返回`RT_CAN_EVENT_TX_FAIL`,后续的`0<<8`表示的是失败事件是由第一个CAN发送邮箱产生的。此时虽然无法通过标志位来判断产生发送完成中断的邮箱,但是由于RTT的CAN驱动实际只使用了第一个发送邮箱,所以只要中断就一定是第一个发送邮箱产生的。 这样可以解决发CAN线程被永远挂起在completion的问题,但是改了之后发现CAN还是会卡死。再次debug发现,当出现**没有标志位的中断**时,由于我们刚才改的程序会发送一个`RT_CAN_EVENT_TX_FAIL`标志,而原有的CAN驱动会在接收到这个标志的时候将HAL的CANstate置位为ERROR,这会导致以后都是ERROR了,后续发送之前CAN驱动会检查CANstate是否为ERROR,如果是ERROR就不会发送。所以把这个置位ERROR的代码注释掉就好了,大量实测证明这样做不会导致其它问题。 完成以上修改后,CAN驱动效果非常好,接触不良和can线临时短路等情况都不会造成程序卡住。 ### 2.3 其它方法与分析 #### 2.3.1 自动重发相关 一些文档中通过修改CAN初始化代码以开启`AutoRetransmission`功能,也可以从表面上解决这个问题,因为如果失败之后自动重发,则重新启动发送且发送成功后,总会产生正常的中断,此时就能释放信号量,发送线程就不再被挂起在completion信号量中了。但是这样的修改方式有缺点,即当CAN线硬件异常导致有一段时间不能正常发送时,rt_device_write函数将一直处于挂起在信号量上的状态,直到上次要发送的数据被成功发送,且由于实际发送次数>调用发送函数的次数,发出的CAN报文没有ACK时容易出现信号量溢出的问题。 采用自动重发的解决方法,则只要调用rt_device_write函数,函数返回时这帧数据就一定已经正常发送了,如果发送不正常的话CAN外设就会自动重发。这种情况会导致一帧发送失败后会一直等待CAN线恢复后再发,会导致CAN线硬件异常时调用rt_device_write函数的用户线程被挂起较长时间,且这段时间内有可能出现信号量溢出的问题,导致程序卡死。 如果按照本文介绍的方法修改底层,想实现类似发送失败重发的功能,可以自己通过软件实现,每次rt_device_write之后检查返回值,如果发送不成功,则重新发送当前数据。 ### 2.4 修改底层注意事项 本文修改的drv_can.c文件为rtt底层\library中的通用文件,如果多个bsp共用该library文件夹,则修改后多个bsp都会受到影响,修改时需要仔细检查,做好记录或备份。
24
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
DIODEX
这家伙很懒,什么也没写!
文章
1
回答
0
被采纳
0
关注TA
发私信
相关文章
1
我也来传一个CANOpen移植,RTT+STM32F107+CanOpenNode
2
谁有STM32裸跑的CANopen程序啊???
3
CAN驱动程序框架
4
CAN驱动接口如何规范一下
5
RTT无法进入线程.Cannot access Memory
6
编译提示arm-none-eabi/bin/ld: cannot find crt0.o: No such file o
7
rtt 2.1.0 正式版 mdk4 bsp/stm32 编译canapp.c错误
8
STM32F10XCAN驱动使用的问题
9
2.1版本stm32f10x分支bxcan驱动波特率设置的bug
10
rtthread2.1.0下,找不到can1设备
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
keil_MDK
rt_mq_消息队列_msg_queue
ulog
C++_cpp
at_device
本月问答贡献
踩姑娘的小蘑菇
7
个答案
3
次被采纳
a1012112796
13
个答案
2
次被采纳
张世争
9
个答案
2
次被采纳
rv666
5
个答案
2
次被采纳
用户名由3_15位
11
个答案
1
次被采纳
本月文章贡献
程序员阿伟
8
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
5
次点赞
ThinkCode
1
篇文章
1
次点赞
Betrayer
1
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部