RTT STM32系列CAN发送卡死问题与根本解决

STM32 CAN总线异常中断处理RTT底层优化实践 本文深入探讨了STM32 CAN总线异常中断处理RTT底层优化实践,重点解决发送卡死问题。通过分析CAN总线异常中断的典型场景和RTT底层驱动的问题定位,提出了中断服务函数的优化方案和状态机管理的改进措施,有效提升了系统稳定性和可靠性。文章还提供了硬件异常机制的深入理解和错误恢复的实践建议,为开发者提供了根本解决方案。 阅读详情

本文由RT-Thread论坛用户@DIODEX原创发布:https://club.rt-thread.org/ask/article/3034.html

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行:

/**
 * @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

    else
    {
      rt_hw_can_isr(&drv_can2.device, RT_CAN_EVENT_TX_FAIL | 0 << 8);
    }

  接着,需要在_can_sendmsg中注释掉Change CAN state对应的程序,如下程序中的#if(0)—#endif

    /*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都会受到影响,修改时需要仔细检查,做好记录或备份。

STM32CAN---中断管理浅析 1 前言 bxCAN占用4个专用的中断向量。通过设置CAN中断允许寄存器(CAN_IER),每个中断源都可以单独允许和禁用。 图1 从图1可以看出,最右边共四个中断,中断是可以通过CAN_IER来屏蔽或允许的。 2 CAN中断允许寄存器 (CAN_IER) 地址偏移量: 0x14 复位值: 0x0000 0000 图2 位31:18 保留位,硬件强制为0 ... 阅读详情

相关推荐

RTT——stm32f103的can总线通信

注意:drv_can.c和drv_can.h在“D:\RT-ThreadStudio\repo\Extract\RT-Thread_Source_Code\RT-Thread\4.0.3\bsp\stm32\libraries\HAL_Drivers”目录下。将void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)函数复制至broad.c文件中。将drv_can.c和drv_can.h到工程的drives目录下。在broad.h中添加以下代码。

zxz_Fine的博客 633

STM32模块CAN接口工作异常排除故障记录

前几天硬小二的一个STM32的板卡的CAN接口出现异常,CAN调试器无法收到STM32CAN报文。同时这个板卡在异常出现前的工作状态是良好的。硬小二,排除其故障时用了三个多小时的时间,排除故障的流程还有很多待优化的地方。接下来且看硬小二为你详细分解。

fydar的博客 3044

RT-Thread 4.0.3下STM32 CAN发送线程卡死的排查修复手记(附drv_can.c补丁)

本文详细记录了在RT-Thread 4.0.3下STM32 CAN发送线程卡死问题的排查修复过程。通过分析硬件调试数据和源码,定位到驱动层中断处理不完整和状态机管理缺陷,提供了针对性的drv_can.c补丁方案,最终实现工业级稳定通信。文章还探讨了CAN总线异常处理的优化策略,包括发送超时机制和错误统计设计。

weixin_42647395的博客 374

STM32CubeMX | STM32 HALCAN总线收发、中断方式接收示例教程

CAN总线收发,中断方式接收 平台:战舰mini板,STM32F103RB STM32CUBEMX V5.3 TrueSTUDIO V9.3 配置CAN CAN波特率计算方法:时钟主频 / 分频 / (tq1 + tq2 + swj) stm32f103的CAN的时钟主频是36M,分9频就是4M,在除以(5 + 2 + 1)得到500K的波特率。 注意:stm32cubemx生成的CAN代码是不...

^_^ 3万+

HALSTM32F407----CAN通信----中断详解

HALSTM32F407----CAN通信----中断详解

MQ0522的博客 1万+

stm32多块开发板can总线互联卡死问题

单块板子在接入can总线时没有任何问题,但是多块板子同时接入can时,基本只有一块是可以用的,其他板子会卡死,起初认定是总线连接的问题,试过总线上接入120ohm电阻一只或两只,都没有效果,通过keil使用jlink进入调试模式发现程序卡死在startup_stm32f10x_md.s的下面位置,经老师指点此处应为stm32的中断服务程序的入口位置,推测是...

weixin_30827565的博客 1255

STM32 CAN总线异常中断处理RTT驱动稳定性优化实践

本文深入解析STM32 CAN总线异常中断现象,特别是RT-Thread(RTT)驱动中的'发送卡死'问题,并提供稳定性优化方案。通过改造中断服务程序、取消错误状态锁定及实现软件重发机制,显著提升系统稳定性。适用于工业控制、自动化产线等场景,帮助开发者解决CAN总线通信难题。

weixin_28717683的博客 132

STM32F103RCT6可用的DAPLink固件工程,内置J-Link RTT日志输出支持(Keil5环境)

基于STM32F103RCT6芯片的DAPLink调试器固件完整Keil MDK-ARM 5工程,开箱即用,无需额外烧录J-Link固件即可通过RTT Viewer实时查看串口级日志。工程已预配置启动文件、链接脚本和板级驱动,涵盖hic_hal硬件抽象层、board板级适配、usb CDC通信栈、daplink协议核心、cmsis-core底层支持,以及独立集成的J-Link-RTT模块和轻量RTOS基础框架。source目录按功能模块组织源码,便于理解CMSIS-DAP在Cortex-M3上的移植逻辑;u

qsc901234的博客 253

【技术文章】9月优秀文章收集汇总帖,快来充充电!

互相分享,共同进步!汇总了9月左右的优秀文章拿来大家分享。有时间可以来看看,给自己充充电 。作为一名合格的开发者,持续学习是技术提升的关键! 充电完毕后,也别忘记来分享噢,RT-Thread每月都有原创文章征集活动,鼓励大家多多分享技术文章! 9月优秀文章收集汇总 记一次解决MQTT软件包内存泄露的心路历程 本文为MQTT软件包内存泄露问题分析帖,从定位、分析到解决超详细,看完受益匪浅,别错过! J-Trace入门系列:1)感动人心的功能更感动人心的售价 本文简单介绍了 J-Trace ,并做了一些功能

rtthreadiotos的博客 439

STM32 F系列选型指南:从F103到F746,如何根据项目需求选择合适MCU

嵌入式系统开发中,微控制器(MCU)的选型是项目成功的关键基础。其核心原理在于根据应用场景,在性能、外设、成本功耗之间取得最佳平衡。从技术价值看,合理的选型能确保系统稳定运行,避免后期因资源不足导致的重大调整,从而节省开发时间和成本。典型的应用场景包括消费电子、工业控制、物联网网关以及带显示屏的人机交互界面等。本文聚焦于STM32 F系列这一主流ARM Cortex-M内核MCU家族,深入对比分析STM32F103、F407、F429和F746等热门型号,旨在帮助开发者规避常见陷阱,例如在涉及以太网通信

weixin_30731305的博客 329

嵌入式系统调试方法论:从现象定位到根因闭环

嵌入式系统调试是面向实时性、资源约束软硬协同的技术实践,其核心在于建立可证伪的故障假设,并通过硬件信号观测、固件行为追踪协议链路分析实现全栈验证。在STM32H7等高性能MCU平台中,时序违例、外设配置失配FreeRTOS任务通信异常成为高频故障源;借助示波器物理层诊断、J-Link RTT非侵入日志及CAN端到端追踪等手段,可系统性剥离抽象干扰,还原真实执行路径。该方法论广泛适用于机器人控制、电机驱动、传感器融合等强实时场景,尤其支撑RM机器人等复杂嵌入式系统的稳定交付。

weixin_33072399的博客 77

单片机全方位调试实战指南:串口/RTT/逻辑分析仪/示波器/内核排错分层避坑教程

单片机开发中80%以上的偶发死机、时序错乱、随机丢包、中断冲突、长期运行不稳定,并不是业务逻辑错误,而是调试方式误用、排查顺序错误,无法抓到真实硬件状态导致。很多开发者只会依靠串口打印调试,面对时序抖动、信号干扰、内存越界等隐性问题,只能盲目修改代码,调试效率低下。 本文系统讲解UART串口、J‑Link RTT、逻辑分析仪、示波器四大调试工具,结合断点仿真、内存监测、HardFault死机快照、看门狗、中断优先级调试等内核排错手段,搭配工程实战案例,搭建「软件日志→实时监测→数字时序→硬件波形→内核排错」

chujing124486的博客 550

STM32高效学习路径:从框架构建到项目实战的嵌入式开发指南

嵌入式系统开发的核心在于理解微控制器(MCU)的硬件架构软件驱动原理。以广泛应用的ARM Cortex-M内核MCU为例,其学习关键在于建立清晰的认知框架,而非孤立记忆外设功能。开发者需从硬件抽象层入手,理解核心、存储器及外设(如通信类的USART、I2C、SPI和控制类的GPIO、定时器)如何通过总线时钟树协同工作。软件层面,掌握HAL等标准驱动的初始化-使用-中断处理通用模式,能显著提升开发效率。这种框架感的价值在于,使工程师能高效组合模块,应对如数据采集、无线通信等实际应用场景的需求。基于此,

weixin_30530339的博客 404

【超详细】STM32F407ZGT6 + HAL 手把手移植RT-Thread(附源码)

嵌入式开发中,裸机开发仅能实现简单的前后台循环逻辑,面对多设备协同、多任务并行、定时处理等复杂场景,存在任务阻塞、实时性差、代码耦合度高、维护难度大等诸多问题。而RT-Thread作为国内开源、轻量化、高实时性的嵌入式RTOS,兼具小巧精简、稳定可靠、生态完善的优势,是单片机裸机开发进阶实时操作系统开发的最优选择之一。本文采用STM32CubeMX原生HAL裸机工程 + 纯手动移植RT-Thread。

桃蹊、的博客 613

RTT调试替代printf:零延迟日志输出实战指南

嵌入式调试中,printf重定向到串口常引发卡顿、乱码HardFault等典型问题,本质是UART外设通信的物理瓶颈。RTT(Real-Time Transfer)通过J-Link硬件协同管理RAM环形缓冲区,将日志输出降维为内存读写操作,实现零延迟、零资源占用和高鲁棒性。其核心价值在于绕过波特率、DMA和中断依赖,显著提升STM32H7等高性能MCU的调试效率实时性保障。本文深入解析RTT控制块结构、多IDE集成要点、中文/浮点/线程安全三大重定向痛点,并提供Keil/STM32CubeIDE/IAR

weixin_29171087的博客 204

STM32FreeRTOS从入门到实战:系统化学习指南智能台灯项目

嵌入式系统开发是连接物理世界数字世界的核心技术,其核心在于微控制器(MCU)对硬件外设的精准控制实时响应。以广泛应用的ARM Cortex-M内核MCU为例,开发者需掌握寄存器操作、中断处理等底层原理,以实现对GPIO、UART、ADC等外设的驱动。随着应用复杂度的提升,裸机编程的超级循环模式面临任务管理困难、响应不及时等挑战,实时操作系统(RTOS)的价值由此凸显。RTOS通过任务调度、优先级管理及进程间通信等机制,为资源受限的嵌入式设备提供了确定性的多任务并发执行能力,极大地提升了开发效率和系统可靠

weixin_34253126的博客 475

如何用JLink优化工业控制器启动流程:操作指南

利用JLink调试工具可显著提升工业控制器的启动效率,通过精准固件烧录启动流程监控,实现系统快速响应。掌握JLink在实际场景中的应用技巧,有助于提高开发部署效率。

weixin_36073714的博客 738

RTT替代串口printf:嵌入式实时调试内存直连方案

嵌入式调试中,传统串口printf受限于波特率、中断开销、中文乱码和硬件依赖,导致开发效率低下。SEGGER RTT通过J-Link调试通道实现RAM级内存共享,以零中断、微秒级响应、原生二进制透传等特性,构建稳定高效的调试数据通路。其本质是绕过UART外设的‘内存直连’机制,不依赖GPIO、时钟或中断配置,显著提升STM32、ESP32、RK3566等平台的log输出实时性可靠性,广泛应用于PID调参、HardFault定位、多核日志分流及结构化可观测性建设。

weixin_29168153的博客 259
上一篇: 【国产MCU移植】移植RT-Thread到国产芯片HC32F460PETB
下一篇: 【AI简报20210910期】联想发布LA2智能嵌入式控制器、单目摄像头实时感知车辆形状...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值