你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】基于 Cube 库无法检测 CAN2 的接收中断

[复制链接]
STMCU小助手 发布时间:2022-2-14 21:54
1 前言
本文将针对客户在使用 Cube 库时 CAN2 不能产生接收中断进行分析。
2 问题描述
客户使用的是 STM32F105,同时用到了 CAN1 与 CAN2,使用 cube 库,但有个奇怪的现象,CAN1能正常工作,CAN2 却无法正常产生接收中断,CAN1 与 CAN2 的代码几乎没有什么差别。

3 重现问题
实验使用 STM3210C-EVAL 评估板,这款板子有同时将 CAN1 和 CAN2 引出,此板上的 MCU 为STM32F107VC,与客户所用到的 STM32F105 只多了个 Ethernet 外设,其他无差别,正适合用来验证此问题。

3.1 工程制作
使用 cubemx 生成工程,cubemx 得配置如下图:

XA(C606K`S}{0B0TSE6`~JJ.png

W)`X4(QJNRY(6PSZB`BW]UJ.png

V2DE`@XDL0Y[K6C`5K$E2.png

配置波特率为 500K,自动离线使能。
点击生产代码,在 main 函数内的/* USER CODE BEGIN 2 */与/* USER CODE END 2 */添加:
  1. /* USER CODE BEGIN 2 */
  2. sFilterConfig.FilterNumber = 0;
  3. sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
  4. sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
  5. sFilterConfig.FilterIdHigh = 0x0000;
  6. sFilterConfig.FilterIdLow = 0x0000;
  7. sFilterConfig.FilterMaskIdHigh = 0x0000;
  8. sFilterConfig.FilterMaskIdLow = 0x0000;
  9. sFilterConfig.FilterFIFOAssignment = 0;
  10. sFilterConfig.FilterActivation = ENABLE;
  11. sFilterConfig.BankNumber = 14;

  12. if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK) //为 CAN1 配置过滤器
  13. {
  14. /* Filter configuration Error */
  15. Error_Handler();
  16. }
  17. sFilterConfig.FilterNumber = 14;
  18. sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
  19. sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
  20. sFilterConfig.FilterIdHigh = 0x0000;
  21. sFilterConfig.FilterIdLow = 0x0000;
  22. sFilterConfig.FilterMaskIdHigh = 0x0000;
  23. sFilterConfig.FilterMaskIdLow = 0x0000;
  24. sFilterConfig.FilterFIFOAssignment = 0;
  25. sFilterConfig.FilterActivation = ENABLE;
  26. sFilterConfig.BankNumber = 14;
  27. if (HAL_CAN_ConfigFilter(&hcan2, &sFilterConfig) != HAL_OK) //为 CAN2 配置过滤器,请留意这
  28. 行代码
  29. {
  30. /* Filter configuration Error */
  31. Error_Handler();
  32. }
  33. hcan1.pTxMsg = &TxMessage1;
  34. hcan1.pRxMsg = &RxMessage1;
  35. hcan1.pTxMsg->StdId = 0x321;
  36. hcan1.pTxMsg->ExtId = 0x01;
  37. hcan1.pTxMsg->RTR = CAN_RTR_DATA;
  38. hcan1.pTxMsg->IDE = CAN_ID_STD;
  39. hcan1.pTxMsg->DLC = 8;
  40. hcan1.pTxMsg->Data[0] =0x01;
  41. hcan1.pTxMsg->Data[1] =0x02;
  42. hcan1.pTxMsg->Data[2] =0x03;
  43. hcan1.pTxMsg->Data[3] =0x04;
  44. hcan1.pTxMsg->Data[4] =0x05;
  45. hcan1.pTxMsg->Data[5] =0x06;
  46. hcan1.pTxMsg->Data[6] =0x07;
  47. hcan1.pTxMsg->Data[7] =0x08;
  48. hcan2.pTxMsg = &TxMessage2;
  49. hcan2.pRxMsg = &RxMessage2;
  50. hcan2.pTxMsg->StdId = 0x322;
  51. hcan2.pTxMsg->ExtId = 0x01;
  52. hcan2.pTxMsg->RTR = CAN_RTR_DATA;
  53. hcan2.pTxMsg->IDE = CAN_ID_STD;
  54. hcan2.pTxMsg->DLC = 6;
  55. hcan2.pTxMsg->Data[0] =0x01;
  56. hcan2.pTxMsg->Data[1] =0x02;
  57. hcan2.pTxMsg->Data[2] =0x03;
  58. hcan2.pTxMsg->Data[3] =0x04;
  59. hcan2.pTxMsg->Data[4] =0x05;
  60. hcan2.pTxMsg->Data[5] =0x06;
  61. hcan2.pTxMsg->Data[6] =0x07;
  62. hcan2.pTxMsg->Data[7] =0x08;
  63. if (HAL_CAN_Receive_IT(&hcan1, CAN_FIFO0) != HAL_OK) //CAN1 开始接收
  64. {
  65. /* Reception Error */
  66. Error_Handler();
  67. }
  68. if (HAL_CAN_Receive_IT(&hcan2, CAN_FIFO0) != HAL_OK)//CAN2 开始接收
  69. {
  70. /* Reception Error */
  71. Error_Handler();
  72. }
  73. /* USER CODE END 2 */
复制代码

在 main 函数内的/* USER CODE BEGIN 3 */与/* USER CODE END 3 */添加:
  1. while (1)
  2. {
  3. /* USER CODE END WHILE */
  4. /* USER CODE BEGIN 3 */
  5. if (HAL_CAN_Transmit(&hcan1, 100) != HAL_OK) //CAN1 发送数据
  6. {
  7. /* Transmition Error */
  8. //Error_Handler();
  9. }
  10. HAL_Delay(10);
  11. if (HAL_CAN_Transmit(&hcan2, 100) != HAL_OK) //CAN2 发送数据
  12. {
  13. /* Transmition Error */
  14. //Error_Handler();
  15. }
  16. HAL_Delay(1000);
  17. }
  18. /* USER CODE END 3 */
复制代码

3.2 测试重现问题现象
使用 ZLG 的 USBCAN-2E-U 盒子与 STM3210C-EVAL 评估板以及 PC 连接进行测试:

LQAZTB4Z@52I~WRCHSP[CVQ.png

在调试下,在 PC 端软件 CANTest 下都能看到 CAN1 与 CAN2 发送的数据,这说明连接上是完全没有问题的,但是,在调试 CAN 接收时,在 CAN2 的接收中断处设置断点,使用 PC 端软件 CAN_Test 向CAN2 发送数据发现并不能产生中断,而对比 CAN1 时,发现 CAN1 却可以产生接收中断。

4 问题分析
仔细对比 CAN1 与 CAN2 的代码,发现并没有什么不同之处。仔细思考一下,由于 CAN 接收中断是与CAN 过滤器有关,因此,着重对比 CAN1 与 CAN2 设置过滤器时的代码差异。
CAN1 设置过滤器代码如下:
  1. if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
  2. {
  3. /* Filter configuration Error */
  4. Error_Handler();
  5. }
复制代码

而 CAN1 设置过滤器代码如下:
  1. if (HAL_CAN_ConfigFilter(&hcan2, &sFilterConfig) != HAL_OK)
  2. {
  3. /* Filter configuration Error */
  4. Error_Handler();
  5. }
复制代码

看来好像没有什么不妥的地方,基本差不多,只不过过调用 HAL_CAN_ConfigFilter 函数配置过滤器传入的第一个参数一个是&hcan1,一个是&hcan2,
查看参考手册,对比 CAN1 与 CAN2 的差别:
在参考手册的 24.4 节中对 CAN 的功能介绍时有下面一段话:

5VG](@WM5EZV002R%9~{)VW.png

上面一段话的意思是说,CAN1 是作为主,而 CAN2 做为从,做为从的 CAN2 不能直接访问这个专用的 512bytes 的 SRAM,而只能通过作为主的 CAN1 来间接访问,且 CAN1 与 CAN2 是共享这个专用的 512bytes 的 SRAM。打开图 222,如下所示:

A_N4{(~WCJC4RX)C8IMK~}5.png

如上图可知,CAN1 与 CAN2 共用过滤器(Acceptance Filters),CAN2 是通过 CAN1 的 Memory Access Controler 来访问过滤器的。过滤器共有 28 个,占用 512 专用字节其中一部分。
接着在数据手册中查下内存映射,如下图所示:

XZ]QKL6KUZ)27@35L2JDIQO.png

可知,CAN1 的地址为:

YS)V@HBBVU_%{3FK(3F7{KS.png

然后查看 bxCAN 的寄存器,在参考手册的 24.9.5 节中,发现有这么一段话:

]IXZUCWY9I){Q25{A`PS(WB.png

也就是说,从偏移地址 0x200 开始到 0x31C 之间的寄存器只存在于 CAN1 中,在 CAN2 中并不存在,那么这些都是些什么寄存器呢?
查看参考手册的 CAN 寄存器映射表,如下内容所示:

~(R%G0YAMA5ZV][WV{WLP5D.png

TKMH2{ZY7EKL6~A{J{J2R.png

可知这些都是 CAN 过滤器相关的寄存器,可知离 CAN1 起始地址 0x4000 6400 偏移 0x200~0x320 之间是 CAN 过滤器寄存器的地址,且这些过滤器由 CAN1 和 CAN2 共享,且最重要的一点信息是,离CAN2 起始地址 0x4000 6800 偏移 0x200~0x320 之间是没有寄存器的,这个非常重要!
再回过头来看 CAN2 过滤器的设置代码:
  1. if (HAL_CAN_ConfigFilter(&hcan2, &sFilterConfig) != HAL_OK)
  2. {
  3. /* Filter configuration Error */
  4. Error_Handler();
  5. }
复制代码

这几行设置过滤器的代码到底是将参数设置到 CAN1 偏移 0x200~0x320 之间还是 CAN2 偏移的0x200~0x320 之间?这个问题要弄清楚,非常重要的!继续看 HAL_CAN_ConfigFilter 函数内:
  1. if (sFilterConfig->FilterScale == CAN_FILTERSCALE_32BIT)
  2. {
  3. /* 32-bit scale for the filter */
  4. SET_BIT(hcan->Instance->FS1R, filternbrbitpos);
  5. /* 32-bit identifier or First 32-bit identifier */
  6. hcan->Instance->sFilterRegister[sFilterConfig->FilterNumber].FR1 =
  7. ((0x0000FFFF & (uint32_t)sFilterConfig->FilterIdHigh) FilterIdLow);
  8. /* 32-bit mask or Second 32-bit identifier */
  9. hcan->Instance->sFilterRegister[sFilterConfig->FilterNumber].FR2 =
  10. ((0x0000FFFF & (uint32_t)sFilterConfig->FilterMaskIdHigh) FilterMaskIdLow);
  11. }
复制代码

由上代码可知,设置的参数实际上是设置到传入的句柄 hcan 的示例 Instance 下的成员 FS1R,sFilterRegister[]中去了,查看这些成员的定义:
  1. typedef struct
  2. {
  3. __IO uint32_t MCR;
  4. __IO uint32_t MSR;
  5. __IO uint32_t TSR;
  6. __IO uint32_t RF0R;
  7. __IO uint32_t RF1R;
  8. __IO uint32_t IER;
  9. __IO uint32_t ESR;
  10. __IO uint32_t BTR;
  11. uint32_t RESERVED0[88];
  12. CAN_TxMailBox_TypeDef sTxMailBox[3];
  13. CAN_FIFOMailBox_TypeDef sFIFOMailBox[2];
  14. uint32_t RESERVED1[12];
  15. __IO uint32_t FMR; //过滤器主寄存器
  16. __IO uint32_t FM1R; //过滤器模式寄存器
  17. uint32_t RESERVED2;
  18. __IO uint32_t FS1R; //过滤器位宽寄存器
  19. uint32_t RESERVED3;
  20. __IO uint32_t FFA1R; //过滤器 FIFO 分配寄存器
  21. uint32_t RESERVED4;
  22. __IO uint32_t FA1R; //过滤器使能寄存器
  23. uint32_t RESERVED5[8];
  24. CAN_FilterRegister_TypeDef sFilterRegister[28]; //过滤器
  25. } CAN_TypeDef;
复制代码

由以上代码可知,HAL_CAN_ConfigFilter 函数实际上就是对传入的 CAN 起始地址偏移 0x200~0x320之间的过滤器相关寄存器进行参数设置,但是,对于 CAN2 来说,实际并不存在这个这些寄存器,因此不会生效。这个可以在调试过程中查看 CAN2 寄存器值来验证,如下图:

OTFV~}307SU6RTO_P4~D12S.png

在运行过 HAL_CAN_ConfigFilter(&hcan2, &sFilterConfig)之后,CAN2 相关的过滤器还是没有变化,进一步说明此函数对于 CAN2 来说是无效的。
那么要解决这个问题,该如何修改呢?很明显,通过 CAN1 来设置即可。CAN1 和 CAN2 共享过滤器,但设置过滤器时可以通过 CAN1 的偏移来访问过滤器,这么说来,也可以将这个理解为 CAN1 作为“主”的确切含义吧。
修改后如下:
  1. if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK) //使用 CAN1 作为参数
  2. {
  3. /* Filter configuration Error */
  4. Error_Handler();
  5. }
  6. sFilterConfig.FilterNumber = 14;
  7. sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
  8. sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
  9. sFilterConfig.FilterIdHigh = 0x0000;
  10. sFilterConfig.FilterIdLow = 0x0000;
  11. sFilterConfig.FilterMaskIdHigh = 0x0000;
  12. sFilterConfig.FilterMaskIdLow = 0x0000;
  13. sFilterConfig.FilterFIFOAssignment = 0;
  14. sFilterConfig.FilterActivation = ENABLE;
  15. sFilterConfig.BankNumber = 14;
  16. if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK) //还是使用 CAN1 来作为参数
  17. {
  18. /* Filter configuration Error */
  19. Error_Handler();
  20. }
复制代码

在 CAN2 中断设置断点,使用 PC 端软件 CANTest 向 STM3210C-EVAL 评估板的 CAN2 通道发送数
据进行测试验证:

B0OH_3X0B2_L%CUTIZL{{5R.png

如上图可见,CAN2 的接收中断已经可以正常捕获到了。

5 横向扩展
此问题是在使用 Cube 库下时发现的,那么标准库是否也会存在同样的问题?
其实,在使用标准库下,在设置过滤器时,并不需要传入 CAN1 或 CAN2 的句柄,因此也就不会这这种问题,如下基于标准库的示例代码:
  1. void CANFilter_Config(void)
  2. {
  3. #ifdef __CAN1_USED__
  4. /* CAN filter init */
  5. CAN_FilterInitStructure.CAN_FilterNumber = 0;
  6. CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
  7. CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
  8. CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
  9. CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
  10. CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
  11. CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
  12. CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
  13. CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
  14. CAN_FilterInit(&CAN_FilterInitStructure);
  15. #endif
  16. #ifdef __CAN2_USED__
  17. CAN_SlaveStartBank(14);
  18. /* CAN filter init */
  19. CAN_FilterInitStructure.CAN_FilterNumber = 14;
  20. CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
  21. CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
  22. CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
  23. CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
  24. CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
  25. CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
  26. CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
  27. CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
  28. CAN_FilterInit(&CAN_FilterInitStructure);
  29. #endif
  30. }
复制代码

在函数 CAN_FilterInit 中,内部自始至终操作的都是 CAN1,如下所示:
  1. //…
  2. if (CAN_FilterInitStruct->CAN_FilterScale == CAN_FilterScale_32bit)
  3. {
  4. /* 32-bit scale for the filter */
  5. CAN1->FS1R |= filter_number_bit_pos; //操作的是 CAN1 的偏移地址
  6. /* 32-bit identifier or First 32-bit identifier */
  7. CAN1->sFilterRegister[CAN_FilterInitStruct->CAN_FilterNumber].FR1 =
  8. ((0x0000FFFF & (uint32_t)CAN_FilterInitStruct->CAN_FilterIdHigh) CAN_FilterIdLow);
  9. /* 32-bit mask or Second 32-bit identifier */
  10. CAN1->sFilterRegister[CAN_FilterInitStruct->CAN_FilterNumber].FR2 =
  11. ((0x0000FFFF & (uint32_t)CAN_FilterInitStruct->CAN_FilterMaskIdHigh) CAN_FilterMaskIdLow);
  12. }
  13. //…
复制代码

因此,不会出现在 Cube 库下出现的问题。

6 总结
此文所描述的问题之会出现在使用 Cube 库的情况下,且基本覆盖包含双 CAN 的所有系列,在使用HAL 函数 HAL_CAN_ConfigFilter 配置过滤器时,将第一个参数固定指向 CAN1 的句柄就可以了。

收藏 评论0 发布时间:2022-2-14 21:54

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版