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 得配置如下图:
配置波特率为 500K,自动离线使能。
点击生产代码,在 main 函数内的/* USER CODE BEGIN 2 */与/* USER CODE END 2 */添加:
- /* USER CODE BEGIN 2 */
- sFilterConfig.FilterNumber = 0;
- sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
- sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
- sFilterConfig.FilterIdHigh = 0x0000;
- sFilterConfig.FilterIdLow = 0x0000;
- sFilterConfig.FilterMaskIdHigh = 0x0000;
- sFilterConfig.FilterMaskIdLow = 0x0000;
- sFilterConfig.FilterFIFOAssignment = 0;
- sFilterConfig.FilterActivation = ENABLE;
- sFilterConfig.BankNumber = 14;
- if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK) //为 CAN1 配置过滤器
- {
- /* Filter configuration Error */
- Error_Handler();
- }
- sFilterConfig.FilterNumber = 14;
- sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
- sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
- sFilterConfig.FilterIdHigh = 0x0000;
- sFilterConfig.FilterIdLow = 0x0000;
- sFilterConfig.FilterMaskIdHigh = 0x0000;
- sFilterConfig.FilterMaskIdLow = 0x0000;
- sFilterConfig.FilterFIFOAssignment = 0;
- sFilterConfig.FilterActivation = ENABLE;
- sFilterConfig.BankNumber = 14;
- if (HAL_CAN_ConfigFilter(&hcan2, &sFilterConfig) != HAL_OK) //为 CAN2 配置过滤器,请留意这
- 行代码
- {
- /* Filter configuration Error */
- Error_Handler();
- }
- hcan1.pTxMsg = &TxMessage1;
- hcan1.pRxMsg = &RxMessage1;
- hcan1.pTxMsg->StdId = 0x321;
- hcan1.pTxMsg->ExtId = 0x01;
- hcan1.pTxMsg->RTR = CAN_RTR_DATA;
- hcan1.pTxMsg->IDE = CAN_ID_STD;
- hcan1.pTxMsg->DLC = 8;
- hcan1.pTxMsg->Data[0] =0x01;
- hcan1.pTxMsg->Data[1] =0x02;
- hcan1.pTxMsg->Data[2] =0x03;
- hcan1.pTxMsg->Data[3] =0x04;
- hcan1.pTxMsg->Data[4] =0x05;
- hcan1.pTxMsg->Data[5] =0x06;
- hcan1.pTxMsg->Data[6] =0x07;
- hcan1.pTxMsg->Data[7] =0x08;
- hcan2.pTxMsg = &TxMessage2;
- hcan2.pRxMsg = &RxMessage2;
- hcan2.pTxMsg->StdId = 0x322;
- hcan2.pTxMsg->ExtId = 0x01;
- hcan2.pTxMsg->RTR = CAN_RTR_DATA;
- hcan2.pTxMsg->IDE = CAN_ID_STD;
- hcan2.pTxMsg->DLC = 6;
- hcan2.pTxMsg->Data[0] =0x01;
- hcan2.pTxMsg->Data[1] =0x02;
- hcan2.pTxMsg->Data[2] =0x03;
- hcan2.pTxMsg->Data[3] =0x04;
- hcan2.pTxMsg->Data[4] =0x05;
- hcan2.pTxMsg->Data[5] =0x06;
- hcan2.pTxMsg->Data[6] =0x07;
- hcan2.pTxMsg->Data[7] =0x08;
- if (HAL_CAN_Receive_IT(&hcan1, CAN_FIFO0) != HAL_OK) //CAN1 开始接收
- {
- /* Reception Error */
- Error_Handler();
- }
- if (HAL_CAN_Receive_IT(&hcan2, CAN_FIFO0) != HAL_OK)//CAN2 开始接收
- {
- /* Reception Error */
- Error_Handler();
- }
- /* USER CODE END 2 */
复制代码
在 main 函数内的/* USER CODE BEGIN 3 */与/* USER CODE END 3 */添加:
- while (1)
- {
- /* USER CODE END WHILE */
- /* USER CODE BEGIN 3 */
- if (HAL_CAN_Transmit(&hcan1, 100) != HAL_OK) //CAN1 发送数据
- {
- /* Transmition Error */
- //Error_Handler();
- }
- HAL_Delay(10);
- if (HAL_CAN_Transmit(&hcan2, 100) != HAL_OK) //CAN2 发送数据
- {
- /* Transmition Error */
- //Error_Handler();
- }
- HAL_Delay(1000);
- }
- /* USER CODE END 3 */
复制代码
3.2 测试重现问题现象
使用 ZLG 的 USBCAN-2E-U 盒子与 STM3210C-EVAL 评估板以及 PC 连接进行测试:
在调试下,在 PC 端软件 CANTest 下都能看到 CAN1 与 CAN2 发送的数据,这说明连接上是完全没有问题的,但是,在调试 CAN 接收时,在 CAN2 的接收中断处设置断点,使用 PC 端软件 CAN_Test 向CAN2 发送数据发现并不能产生中断,而对比 CAN1 时,发现 CAN1 却可以产生接收中断。
4 问题分析
仔细对比 CAN1 与 CAN2 的代码,发现并没有什么不同之处。仔细思考一下,由于 CAN 接收中断是与CAN 过滤器有关,因此,着重对比 CAN1 与 CAN2 设置过滤器时的代码差异。
CAN1 设置过滤器代码如下:
- if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
- {
- /* Filter configuration Error */
- Error_Handler();
- }
复制代码
而 CAN1 设置过滤器代码如下:
- if (HAL_CAN_ConfigFilter(&hcan2, &sFilterConfig) != HAL_OK)
- {
- /* Filter configuration Error */
- Error_Handler();
- }
复制代码
看来好像没有什么不妥的地方,基本差不多,只不过过调用 HAL_CAN_ConfigFilter 函数配置过滤器传入的第一个参数一个是&hcan1,一个是&hcan2,
查看参考手册,对比 CAN1 与 CAN2 的差别:
在参考手册的 24.4 节中对 CAN 的功能介绍时有下面一段话:
上面一段话的意思是说,CAN1 是作为主,而 CAN2 做为从,做为从的 CAN2 不能直接访问这个专用的 512bytes 的 SRAM,而只能通过作为主的 CAN1 来间接访问,且 CAN1 与 CAN2 是共享这个专用的 512bytes 的 SRAM。打开图 222,如下所示:
如上图可知,CAN1 与 CAN2 共用过滤器(Acceptance Filters),CAN2 是通过 CAN1 的 Memory Access Controler 来访问过滤器的。过滤器共有 28 个,占用 512 专用字节其中一部分。
接着在数据手册中查下内存映射,如下图所示:
可知,CAN1 的地址为:
然后查看 bxCAN 的寄存器,在参考手册的 24.9.5 节中,发现有这么一段话:
也就是说,从偏移地址 0x200 开始到 0x31C 之间的寄存器只存在于 CAN1 中,在 CAN2 中并不存在,那么这些都是些什么寄存器呢?
查看参考手册的 CAN 寄存器映射表,如下内容所示:
可知这些都是 CAN 过滤器相关的寄存器,可知离 CAN1 起始地址 0x4000 6400 偏移 0x200~0x320 之间是 CAN 过滤器寄存器的地址,且这些过滤器由 CAN1 和 CAN2 共享,且最重要的一点信息是,离CAN2 起始地址 0x4000 6800 偏移 0x200~0x320 之间是没有寄存器的,这个非常重要!
再回过头来看 CAN2 过滤器的设置代码:
- if (HAL_CAN_ConfigFilter(&hcan2, &sFilterConfig) != HAL_OK)
- {
- /* Filter configuration Error */
- Error_Handler();
- }
复制代码
这几行设置过滤器的代码到底是将参数设置到 CAN1 偏移 0x200~0x320 之间还是 CAN2 偏移的0x200~0x320 之间?这个问题要弄清楚,非常重要的!继续看 HAL_CAN_ConfigFilter 函数内:
- if (sFilterConfig->FilterScale == CAN_FILTERSCALE_32BIT)
- {
- /* 32-bit scale for the filter */
- SET_BIT(hcan->Instance->FS1R, filternbrbitpos);
- /* 32-bit identifier or First 32-bit identifier */
- hcan->Instance->sFilterRegister[sFilterConfig->FilterNumber].FR1 =
- ((0x0000FFFF & (uint32_t)sFilterConfig->FilterIdHigh) FilterIdLow);
- /* 32-bit mask or Second 32-bit identifier */
- hcan->Instance->sFilterRegister[sFilterConfig->FilterNumber].FR2 =
- ((0x0000FFFF & (uint32_t)sFilterConfig->FilterMaskIdHigh) FilterMaskIdLow);
- }
复制代码
由上代码可知,设置的参数实际上是设置到传入的句柄 hcan 的示例 Instance 下的成员 FS1R,sFilterRegister[]中去了,查看这些成员的定义:
- typedef struct
- {
- __IO uint32_t MCR;
- __IO uint32_t MSR;
- __IO uint32_t TSR;
- __IO uint32_t RF0R;
- __IO uint32_t RF1R;
- __IO uint32_t IER;
- __IO uint32_t ESR;
- __IO uint32_t BTR;
- uint32_t RESERVED0[88];
- CAN_TxMailBox_TypeDef sTxMailBox[3];
- CAN_FIFOMailBox_TypeDef sFIFOMailBox[2];
- uint32_t RESERVED1[12];
- __IO uint32_t FMR; //过滤器主寄存器
- __IO uint32_t FM1R; //过滤器模式寄存器
- uint32_t RESERVED2;
- __IO uint32_t FS1R; //过滤器位宽寄存器
- uint32_t RESERVED3;
- __IO uint32_t FFA1R; //过滤器 FIFO 分配寄存器
- uint32_t RESERVED4;
- __IO uint32_t FA1R; //过滤器使能寄存器
- uint32_t RESERVED5[8];
- CAN_FilterRegister_TypeDef sFilterRegister[28]; //过滤器
- } CAN_TypeDef;
复制代码
由以上代码可知,HAL_CAN_ConfigFilter 函数实际上就是对传入的 CAN 起始地址偏移 0x200~0x320之间的过滤器相关寄存器进行参数设置,但是,对于 CAN2 来说,实际并不存在这个这些寄存器,因此不会生效。这个可以在调试过程中查看 CAN2 寄存器值来验证,如下图:
在运行过 HAL_CAN_ConfigFilter(&hcan2, &sFilterConfig)之后,CAN2 相关的过滤器还是没有变化,进一步说明此函数对于 CAN2 来说是无效的。
那么要解决这个问题,该如何修改呢?很明显,通过 CAN1 来设置即可。CAN1 和 CAN2 共享过滤器,但设置过滤器时可以通过 CAN1 的偏移来访问过滤器,这么说来,也可以将这个理解为 CAN1 作为“主”的确切含义吧。
修改后如下:
- if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK) //使用 CAN1 作为参数
- {
- /* Filter configuration Error */
- Error_Handler();
- }
- sFilterConfig.FilterNumber = 14;
- sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
- sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
- sFilterConfig.FilterIdHigh = 0x0000;
- sFilterConfig.FilterIdLow = 0x0000;
- sFilterConfig.FilterMaskIdHigh = 0x0000;
- sFilterConfig.FilterMaskIdLow = 0x0000;
- sFilterConfig.FilterFIFOAssignment = 0;
- sFilterConfig.FilterActivation = ENABLE;
- sFilterConfig.BankNumber = 14;
- if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK) //还是使用 CAN1 来作为参数
- {
- /* Filter configuration Error */
- Error_Handler();
- }
复制代码
在 CAN2 中断设置断点,使用 PC 端软件 CANTest 向 STM3210C-EVAL 评估板的 CAN2 通道发送数
据进行测试验证:
如上图可见,CAN2 的接收中断已经可以正常捕获到了。
5 横向扩展
此问题是在使用 Cube 库下时发现的,那么标准库是否也会存在同样的问题?
其实,在使用标准库下,在设置过滤器时,并不需要传入 CAN1 或 CAN2 的句柄,因此也就不会这这种问题,如下基于标准库的示例代码:
- void CANFilter_Config(void)
- {
- #ifdef __CAN1_USED__
- /* CAN filter init */
- CAN_FilterInitStructure.CAN_FilterNumber = 0;
- CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
- CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
- CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
- CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
- CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
- CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
- CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
- CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
- CAN_FilterInit(&CAN_FilterInitStructure);
- #endif
- #ifdef __CAN2_USED__
- CAN_SlaveStartBank(14);
- /* CAN filter init */
- CAN_FilterInitStructure.CAN_FilterNumber = 14;
- CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
- CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
- CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
- CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
- CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
- CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
- CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
- CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
- CAN_FilterInit(&CAN_FilterInitStructure);
- #endif
- }
复制代码
在函数 CAN_FilterInit 中,内部自始至终操作的都是 CAN1,如下所示:
- //…
- if (CAN_FilterInitStruct->CAN_FilterScale == CAN_FilterScale_32bit)
- {
- /* 32-bit scale for the filter */
- CAN1->FS1R |= filter_number_bit_pos; //操作的是 CAN1 的偏移地址
- /* 32-bit identifier or First 32-bit identifier */
- CAN1->sFilterRegister[CAN_FilterInitStruct->CAN_FilterNumber].FR1 =
- ((0x0000FFFF & (uint32_t)CAN_FilterInitStruct->CAN_FilterIdHigh) CAN_FilterIdLow);
- /* 32-bit mask or Second 32-bit identifier */
- CAN1->sFilterRegister[CAN_FilterInitStruct->CAN_FilterNumber].FR2 =
- ((0x0000FFFF & (uint32_t)CAN_FilterInitStruct->CAN_FilterMaskIdHigh) CAN_FilterMaskIdLow);
- }
- //…
复制代码
因此,不会出现在 Cube 库下出现的问题。
6 总结
此文所描述的问题之会出现在使用 Cube 库的情况下,且基本覆盖包含双 CAN 的所有系列,在使用HAL 函数 HAL_CAN_ConfigFilter 配置过滤器时,将第一个参数固定指向 CAN1 的句柄就可以了。
|