相关文章

CAN总线简易入门教程
CAN总线显性电平和隐性电平详解
STM32的CAN总线调试经验分享

文章目录

  • 相关文章
  • 背景
  • CAN总线
    • CAN控制器
    • CAN收发器
  • 调试过程
    • 硬件排查
    • CAN分析仪
    • 芯片CAN控制器调试
  • 总结

背景

最近负责的一个项目用的主控芯片是STM32F407IGT6,需要和几个电机控制器进行通讯,有很多参数需要进行监控。负责固件开发的同事一直搞不定一个问题。就是开启CAN的接收中断,接收不到数据,问题卡了很久,一直无法闭环。

CAN总线

CAN总线是一种串行通信协议,用于在微控制器和其他设备之间传输数据。CAN总线通常用于汽车、工业自动化和机器人等领域。

CAN总线的硬件通常由以下几个部分组成:

  • 控制器区域:包括CAN控制器和CAN收发器;
  • 总线电缆:用于连接CAN总线上的所有设备;
  • 终端电阻:用于终止总线,以减少反射和信号干扰;
  • 外部电源:用于为CAN总线提供电源;

CAN总线的控制器区域通常包括CAN控制器CAN收发器

  • CAN控制器负责处理CAN总线上的数据传输,包括数据发送和接收、错误检测和纠正等;
  • CAN收发器则负责将CAN控制器的信号转换为总线上的电信号,并将总线上的电信号转换为CAN控制器可以理解的信号。

CAN控制器

主板上的芯片STM32F407IGT6中带有两路的CAN控制器,分别为CAN1CAN2,具体如下图所示;

CAN收发器

主板上使用的是芯片SN65HVD230,这是TI公司的一款性能强大且具体低功耗功能的CAN收发器,具体的典型应用电路如下所示;

调试过程

硬件排查

设备的调试过程中,首先要确保硬件链路上是否正常。最常见的方法就是直接用示波器进行检查。
具体如下所示;

  1. 检查CAN控制器和CAN收发器之间是否正常;
  2. 检查CAN收发器的差分信号是否正常,这里可能要了解一下CAN总线电平的显性电平和隐性电平的特点,以及CAN底层协议的细节,会比较复杂;

个人比较推荐使用上述步骤检查硬件链路是否存在问题,那如何对数据进行分析呢?当然可以对着示波器的波形一点一点进行分析,但是这样是很低效的,这里我建议使用CAN分析仪进行数据抓包,下面我们继续进行介绍。

CAN分析仪

至于数据传输是否正确,可以使用CAN盒进行数据监听,下面是我使用的一款CAN分析仪,如图;


将CAN分析仪的CAN_HCAN_L分别并联到CAN收发器的CAN_HCAN_L上,然后打开CAN分析仪厂家提供的PC软件,就可以对CAN总线的数据进行监听;

  1. 将CAN分析仪接入到CAN总线;
  2. 将CAN分析仪连接到电脑(这里是USB接口),需要配置相同的波特率;
  3. 打开CAN分析仪配套的PC软件,进行数据的收发;

    进行到这里,我在项目中遇到的问题是,发送正常,但是STM32F407无法接收到连续的数据,可以接收到一次数据,后面便无法再进入中断。
    这时候,只能再芯片端进行Debug了。

芯片CAN控制器调试

这里的代码用的HAL库,库版本相对来说比较老,是V1.7.10版本的,如下图所示;

当时我把项目升级到最新的HAL库,发现CAN部分的驱动改动比较大,另外,下文都是基于V1.7.10版本的HAL库。

CAN控制器的初始化代码如下所示;

void MX_CAN_Init(void)
{CAN_FilterConfTypeDef  sFilterConfig;/*CAN单元初始化*/hCAN.Instance = CANx;             /* CAN外设 */hCAN.pTxMsg = &TxMessage;hCAN.pRxMsg = &RxMessage;hCAN.Init.Prescaler = 6;          /* BTR-BRP 波特率分频器  定义了时间单元的时间长度 42/(1+6+7)/6 = 500Kbps */hCAN.Init.Mode = CAN_MODE_NORMAL; /* 正常工作模式 */hCAN.Init.SJW = CAN_SJW_1TQ;      /* BTR-SJW 重新同步跳跃宽度 1个时间单元 */hCAN.Init.BS1 = CAN_BS1_6TQ;      /* BTR-TS1 时间段1 占用了6个时间单元 */hCAN.Init.BS2 = CAN_BS2_7TQ;      /* BTR-TS1 时间段2 占用了7个时间单元 */hCAN.Init.TTCM = DISABLE;         /* MCR-TTCM  关闭时间触发通信模式使能 */hCAN.Init.ABOM = ENABLE;          /* MCR-ABOM  自动离线管理 */hCAN.Init.AWUM = ENABLE;          /* MCR-AWUM  使用自动唤醒模式 */hCAN.Init.NART = DISABLE;         /* MCR-NART  禁止报文自动重传   DISABLE-自动重传 */hCAN.Init.RFLM = DISABLE;         /* MCR-RFLM  接收FIFO 锁定模式  DISABLE-溢出时新报文会覆盖原有报文 */hCAN.Init.TXFP = DISABLE;         /* MCR-TXFP  发送FIFO优先级 DISABLE-优先级取决于报文标示符 */HAL_CAN_Init(&hCAN);/*CAN过滤器初始化*/sFilterConfig.FilterNumber = 0;                    /* 过滤器组0 */sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;  /* 工作在标识符屏蔽位模式 */sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; /* 过滤器位宽为单个32位。*//* 使能报文标示符过滤器按照标示符的内容进行比对过滤,扩展ID不是如下的就抛弃掉,是的话,会存入FIFO0。 */sFilterConfig.FilterIdHigh         = 0x0000;    //(((uint32_t)0x1314<<3)&0xFFFF0000)>>16;               /* 要过滤的ID高位 */sFilterConfig.FilterIdLow          = 0x0000; //(((uint32_t)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF; /* 要过滤的ID低位 */sFilterConfig.FilterMaskIdHigh     = 0x0000;         /* 过滤器高16位每位必须匹配 */sFilterConfig.FilterMaskIdLow      = 0x0000;            /* 过滤器低16位每位必须匹配 */sFilterConfig.FilterFIFOAssignment = 0;           /* 过滤器被关联到FIFO 0 */sFilterConfig.FilterActivation = ENABLE;          /* 使能过滤器 */ sFilterConfig.BankNumber = 14;HAL_CAN_ConfigFilter(&hCAN, &sFilterConfig);}

根据注释,可以大概看懂,另外再简单分析一下关键的几点;

  • 波特率设置为 500Kbps;
  • 对报文不进行过滤,可以接收任何扩展ID的数据;

虽然不进行任何过滤,但是还是无法接收到CAN回传的数据,无法进入的接收中断;

从STM32F407的编程手册里了解到,

不难发现,CAN1FIFO0产生接收中断需要满足三个条件中的任意一个;

  • FMPIE01FMP01FIFO不为空会产生中断
  • FFIE01FULL1FIFO满,会产生中断
  • FOVIE01FOVR01FIFO溢出,会产生中断

手册里是这样描述的,如下图所示;

使用仿真器对芯片进行调试,设置断点,发现FMPIE0被清空了,具体如下图所示;

FMPIE0这一位是FIFO0中有挂起的消息会产生中断的中断使能标志位;


所以到这里,问题有点明朗了,为什么无法进入中断?是中断使能位被清空了。

那么下面就是检查代码,看看是哪里把中断给disable了。

继续调试,发现在ESR寄存器中,TEC的值一直增加,然后EWGF被值1了;具体如下所示;

TECREC分别是发送错误计数器和接收错误计数器;

如 CAN 协议所述,错误管理完全由硬件通过发送错误计数器( CAN_ESR 寄存器中的 TEC
值)和接收错误计数器( CAN_ESR 寄存器中的 REC 值)来处理,这两个计数器根据错误
状况进行递增或递减。有关 TEC 和 REC 管理的详细信息,请参见 CAN 标准。
两者均可由软件读取,用以确定网络的稳定性。此外, CAN 硬件还将在 CAN_ESR 寄存器中
提供当前错误状态的详细信息。通过 CAN_IER 寄存器( ERRIE 位等),软件可以非常灵活
地配置在检测到错误时生成的中断。

TEC大于96的时候,硬件会将EWGF1(错误警告标志位);在代码中找到了相应的宏定义;这下问题越来越清晰了。


全文搜索这个宏定义,在HAL_CAN_IRQHandler中找到了__HAL_CAN_DISABLE_IT(CAN_IT_FMP0),关闭了FIFO0的消息挂起中断,
整体代码如下;

/*** @brief  Handles CAN interrupt request  * @param  hcan: pointer to a CAN_HandleTypeDef structure that contains*         the configuration information for the specified CAN.* @retval None*/
void HAL_CAN_IRQHandler(CAN_HandleTypeDef* hcan)
{uint32_t tmp1 = 0U, tmp2 = 0U, tmp3 = 0U;uint32_t errorcode = HAL_CAN_ERROR_NONE;/* Check Overrun flag for FIFO0 */tmp1 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_FOV0);tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_FOV0);if(tmp1 && tmp2){/* Set CAN error code to FOV0 error */errorcode |= HAL_CAN_ERROR_FOV0;/* Clear FIFO0 Overrun Flag */__HAL_CAN_CLEAR_FLAG(hcan, CAN_FLAG_FOV0);}/* Check Overrun flag for FIFO1 */tmp1 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_FOV1);tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_FOV1);if(tmp1 && tmp2){/* Set CAN error code to FOV1 error */errorcode |= HAL_CAN_ERROR_FOV1;/* Clear FIFO1 Overrun Flag */__HAL_CAN_CLEAR_FLAG(hcan, CAN_FLAG_FOV1);}/* Check End of transmission flag */if(__HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_TME)){tmp1 = __HAL_CAN_TRANSMIT_STATUS(hcan, CAN_TXMAILBOX_0);tmp2 = __HAL_CAN_TRANSMIT_STATUS(hcan, CAN_TXMAILBOX_1);tmp3 = __HAL_CAN_TRANSMIT_STATUS(hcan, CAN_TXMAILBOX_2);if(tmp1 || tmp2 || tmp3)  {tmp1 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_TXOK0);tmp2 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_TXOK1);tmp3 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_TXOK2);/* Check Transmit success */if(tmp1 || tmp2 || tmp3){/* Call transmit function */CAN_Transmit_IT(hcan);}else /* Transmit failure */{/* Set CAN error code to TXFAIL error */errorcode |= HAL_CAN_ERROR_TXFAIL;}/* Clear transmission status flags (RQCPx and TXOKx) */SET_BIT(hcan->Instance->TSR, CAN_TSR_RQCP0  | CAN_TSR_RQCP1  | CAN_TSR_RQCP2 | \CAN_FLAG_TXOK0 | CAN_FLAG_TXOK1 | CAN_FLAG_TXOK2);}}tmp1 = __HAL_CAN_MSG_PENDING(hcan, CAN_FIFO0);tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_FMP0);/* Check End of reception flag for FIFO0 */if((tmp1 != 0U) && tmp2){/* Call receive function */CAN_Receive_IT(hcan, CAN_FIFO0);}tmp1 = __HAL_CAN_MSG_PENDING(hcan, CAN_FIFO1);tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_FMP1);/* Check End of reception flag for FIFO1 */if((tmp1 != 0U) && tmp2){/* Call receive function */CAN_Receive_IT(hcan, CAN_FIFO1);}/* Set error code in handle */hcan->ErrorCode |= errorcode;tmp1 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_EWG);tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_EWG);tmp3 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_ERR);/* Check Error Warning Flag */if(tmp1 && tmp2 && tmp3){/* Set CAN error code to EWG error */hcan->ErrorCode |= HAL_CAN_ERROR_EWG;}tmp1 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_EPV);tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_EPV);tmp3 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_ERR); /* Check Error Passive Flag */if(tmp1 && tmp2 && tmp3){/* Set CAN error code to EPV error */hcan->ErrorCode |= HAL_CAN_ERROR_EPV;}tmp1 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_BOF);tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_BOF);tmp3 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_ERR);  /* Check Bus-Off Flag */if(tmp1 && tmp2 && tmp3){/* Set CAN error code to BOF error */hcan->ErrorCode |= HAL_CAN_ERROR_BOF;}tmp1 = HAL_IS_BIT_CLR(hcan->Instance->ESR, CAN_ESR_LEC);tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_LEC);tmp3 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_ERR);/* Check Last error code Flag */if((!tmp1) && tmp2 && tmp3){tmp1 = (hcan->Instance->ESR) & CAN_ESR_LEC;switch(tmp1){case(CAN_ESR_LEC_0):/* Set CAN error code to STF error */hcan->ErrorCode |= HAL_CAN_ERROR_STF;break;case(CAN_ESR_LEC_1):/* Set CAN error code to FOR error */hcan->ErrorCode |= HAL_CAN_ERROR_FOR;break;case(CAN_ESR_LEC_1 | CAN_ESR_LEC_0):/* Set CAN error code to ACK error */hcan->ErrorCode |= HAL_CAN_ERROR_ACK;break;case(CAN_ESR_LEC_2):/* Set CAN error code to BR error */hcan->ErrorCode |= HAL_CAN_ERROR_BR;break;case(CAN_ESR_LEC_2 | CAN_ESR_LEC_0):/* Set CAN error code to BD error */hcan->ErrorCode |= HAL_CAN_ERROR_BD;break;case(CAN_ESR_LEC_2 | CAN_ESR_LEC_1):/* Set CAN error code to CRC error */hcan->ErrorCode |= HAL_CAN_ERROR_CRC;break;default:break;}/* Clear Last error code Flag */ hcan->Instance->ESR &= ~(CAN_ESR_LEC);}/* Call the Error call Back in case of Errors */if(hcan->ErrorCode != HAL_CAN_ERROR_NONE){/* Clear ERRI Flag */ hcan->Instance->MSR = CAN_MSR_ERRI; /* Set the CAN state ready to be able to start again the process */hcan->State = HAL_CAN_STATE_READY;/* Disable interrupts: *//*  - Disable Error warning Interrupt *//*  - Disable Error passive Interrupt *//*  - Disable Bus-off Interrupt *//*  - Disable Last error code Interrupt *//*  - Disable Error Interrupt *//*  - Disable FIFO 0 message pending Interrupt *//*  - Disable FIFO 0 Overrun Interrupt *//*  - Disable FIFO 1 message pending Interrupt *//*  - Disable FIFO 1 Overrun Interrupt *//*  - Disable Transmit mailbox empty Interrupt */__HAL_CAN_DISABLE_IT(hcan, CAN_IT_EWG |CAN_IT_EPV |CAN_IT_BOF |CAN_IT_LEC |CAN_IT_ERR |CAN_IT_FMP0|CAN_IT_FOV0|CAN_IT_FMP1|CAN_IT_FOV1|CAN_IT_TME);/* Call Error callback function */HAL_CAN_ErrorCallback(hcan);}
}

最后,找到无法进入接收中断的原因,是CAN总线出现发送错误的情况,从而触发了错误警告标志位EWGF,进而将关闭了消息挂起中断。

总结

本文简单介绍了在STM32F407上的CAN总线调试过程,解决了一个数据接收的问题,简单整理了一下调试的过程和思路。不过本人能力有限,难免存在错误和纰漏,请不吝赐教,如果文章帮到了你,点赞支持一下

STM32的CAN总线调试经验分享相关推荐

  1. CMOS Sensor的调试经验分享(转)

    源:CMOS Sensor的调试经验分享 转载于:https://www.cnblogs.com/LittleTiger/p/8251670.html

  2. [单片机芯片]CH32V203的USB1 HID库调试经验分享

    背景: 今天要水的帖子是关于使用CH32V203替代旧STM32F103实现USB霍尔摇杆的项目.关于调试经验啥的,主要分享CH32V203的USB使用.这个项目最开始是使用迈来芯的方案,但是价格30 ...

  3. RGB屏调试经验分享——颜色不对

    本篇博客分享我之前调试RGB888 LCD屏遇到的两个比较麻烦的问题及解决方法. 我这里不说怎么调试屏的周期,时间宽度等调试方法,寄存器怎么填,根据LCD数据手册和主控手册挨个对比设置,花点时间应该很 ...

  4. Android 5.0 + IDA 6.8 调试经验分享

    现在升级快,网上的资料只能做参考.  学到了NDK逆向这一块,昨天为了能让IDA 能动态调试SO,瞎折腾了很久,这里分享一下我的经验. 工具:  IDA pro 6.8  Android 5.x ID ...

  5. 【腾讯WeTest干货分享】高并发性能调试经验分享

    本文由腾讯WeTest授权发布 作者:helloworlds 链接:https://zhuanlan.zhihu.com/p/21348220 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权 ...

  6. 高并发内存占用持续下降_高并发性能调试经验分享

    引文 4月份的时候看到一道面试题,据说是腾讯校招面试官提的:在多线程和高并发环境下,如果有一个平均运行一百万次才出现一次的bug,你如何调试这个bug?知乎原贴地址如下:腾讯实习生面试,这两道题目该怎 ...

  7. POWERLINK协议在stm32单片机+w5500移植成功经验分享

    连续折腾了多个晚上,又趁周末又花了一天时间,终于把powerlink协议移植成功到单片机上啦.本想放弃,但想了下不管我能不能用上,结个尾吧,分享给有需要的人.放弃并不难,但坚持一定很酷.为了移植测试这 ...

  8. vivado生成mig_ddr3调试经验分享(一)——modelsim实现对vivado中的MIG ddr3的仿真

    Vivado中的MIG已经集成了modelsim仿真环境,是不是所有IP 都有这个福利呢,不知道哦,没空去验证. 第一步:使用vivado中的MIG IP生成一堆东西 ,这个过程自己百度.或者是ug5 ...

  9. camera调试基础经验分享

    http://www.ccm99.com/thread-1953-1-2.html CMOS Sensor的调试经验分享/ e# \. A3 Z5 P 我这里要介绍的就是CMOS摄像头的一些调试经验. ...

最新文章

  1. H5 客户端设置title 滑动验证码
  2. AWS — AWS VPC 虚拟专用云
  3. js 识别汉字和全角字符
  4. 现代化自定制 - 页面上的自定制
  5. xlwt什么版本支持xlsx的版本_Spring Framework Versions版本支持说明
  6. 为什么我要重新开始数据科学
  7. HDU 4403 A very hard Aoshu problem DFS
  8. 【CodeForces - 144D】Missile Silos(单源最短路,枚举中间边,枚举情况可能性)
  9. redis批量删除指定的key
  10. The “data“ option should be a function that returns a per-instance value in component definitions.
  11. 教你如何理解Vue中的nextTick
  12. arping命令解析
  13. 数据仓库(8)数仓事实表和维度表技术
  14. 面试:MRO问题 (理解笔记)
  15. A survey of network-based intrusion detection data sets翻译二(5-8)
  16. PageHelper处理分页问题,total总数不对
  17. win11桌面改成win7桌面的设置方法
  18. Python获取文件的行数和某一行的内容
  19. valgrind小结
  20. 一种保护眼睛的好方法:

热门文章

  1. 95 hbase命令
  2. 下载 Python中文文档,方便学习查找
  3. SpringSecurity OAuth2 (7) 自定义 AccessToken 和 RefreshToken (JWT with RSA 签名)
  4. 程序员面试题精选100题:求从1到n的正数中1出现的次数
  5. 一篇文章搞懂CSS3网格布局(Grid)
  6. 基于ERP集成的流程制造管理系统
  7. ubuntu16.04 不能在本地显示图片的问题
  8. vue node_modules复制到内网环境
  9. 三用表校验仪TD1855多用表校准装置
  10. 优化:YALMIP一般使用方法及例程