STM32H7双路CAN踩坑记录
目录
- STM32H7双路CAN踩坑记录
- 1 问题描述
- 2 原因分析
- 3 解决办法
- 4 CAN配置参考代码
1 问题描述
STM32的CAN1和CAN2无法同时使用。
注:MCU使用的是STM32H743,其他型号不确定是否一样,本文只以STM32H743举例说明。
2 原因分析
经过测试分析,双路CAN无法同时使用与CAN的消息RAM配置有关。
其中问题就出现在上图的RAM配置,STM32H7系列的两路CAN是共用同一块RAM的,并且把这块内存空间的分配交给了用户,也没有检查内存分配是否合理的操作,这点其实挺坑的,稍不注意就掉进去了。
我之前用过STM32其他的系列,它们芯片内部就定义好了哪部分RAM给哪个CAN,是使用不允许用户自由分配的,因此就吃了这个亏。我原本两路CAN的配置基本都是一致的,其中也包括消息RAM这一点,因此,在初始化之后,先初始化的那路CAN就会被覆盖导致不能使用。
3 解决办法
CAN1的消息RAM从地址偏移0开始,而CAN2则从CAN1消息RAN结束的位置开始,这样就先保证了CAN1有足够的RAM空间使用,也保证了CAN2的RAM不会跟CAN1冲突。具体代码配置参考如下:
FDCAN2_Handler.Init.MessageRAMOffset = FDCAN1_Handler.msgRam.EndAddress - SRAMCAN_BASE; // CAN1消息RAM的结束地址 - 消息RAM的起始地址 = CAN1实际使用的内存大小,把这个作为CAN2的内存偏移地址
注:当然,你也可以先计算一下或用串口打印一下CAN1具体使用了多少内存空间,然后在这个基础上做偏移,只要保证两路CAN有足够的内存并且内存不重叠就可以了。
4 CAN配置参考代码
/*** @brief FDCAN初始化* @param presc : 分频值,取值范围1~512;* @param tsjw : 重新同步跳跃时间单元.范围:1~128;* @param ntsg1 : 时间段1的时间单元.取值范围2~256;* @param ntsg2 : 时间段2的时间单元.取值范围2~128;* @param mode : FDCAN_MODE_NORMAL,普通模式; FDCAN_MODE_INTERNAL_LOOPBACK,内部回环模式;FDCAN_MODE_EXTERNAL_LOOPBACK,外部回环模式;FDCAN_MODE_RESTRICTED_OPERATION,限制操作模式FDCAN_MODE_BUS_MONITORING,总线监控模式* @note 以上5个参数, 除了模式选择其余的参数在函数内部会减1, 所以, 任何一个参数都不能等于0* FDCAN其输入时钟频率为 Fpclk1 = 20Mhz* 波特率 = Fpclk1 / ((ntsg1 + ntsg2 + 1) * presc);* 我们设置 fdcan_init(10, 8, 31, 8, 1), 则CAN波特率为:* 20M / ((31 + 8 + 1) * 10) = 500Kbps* @retval 0, 初始化成功; 其他, 初始化失败;*/
u8 FDCAN1_Mode_Init(uint16_t presc, uint8_t tsjw, uint16_t ntsg1, uint8_t ntsg2, uint32_t mode)
{FDCAN_FilterTypeDef fdcan_filterconfig;HAL_FDCAN_DeInit(&FDCAN1_Handler); /* 先清除以前的设置 */FDCAN1_Handler.Instance = FDCAN1; FDCAN1_Handler.Init.FrameFormat = FDCAN_FRAME_FD_BRS; /* 位速率变换FDCAN模式 */FDCAN1_Handler.Init.Mode = mode; /* 模式设置 */FDCAN1_Handler.Init.AutoRetransmission = ENABLE; /* 使能自动重传!传统模式下一定要关闭!!! */FDCAN1_Handler.Init.TransmitPause = ENABLE; /* 使能传输暂停 */FDCAN1_Handler.Init.ProtocolException = DISABLE; /* 关闭协议异常处理 *//* FDCAN中仲裁段位速率最高1Mbit/s, 数据段位速率最高8Mbit/s *//* 数据段通信速率(仅FDCAN模式需配置) = 20M / (1 + dseg1 + dseg2) = 20M / (2 + 1 + 1) = 5 Mbit/s */FDCAN1_Handler.Init.DataPrescaler = 10; /* 数据段分频系数范围:1~32 */FDCAN1_Handler.Init.DataSyncJumpWidth = 16; /* 数据段重新同步跳跃宽度1~16 */FDCAN1_Handler.Init.DataTimeSeg1 = 2; /* 数据段dsg1范围:1~32 5 */FDCAN1_Handler.Init.DataTimeSeg2 = 1; /* 数据段dsg2范围:1~16 1 *//* 仲裁段通信速率(FDCAN与传统CAN均需配置) = 20M / (1 + ntsg1 + ntsg2) = 20M / (31 + 8 + 1) = 500Kbit/s */FDCAN1_Handler.Init.NominalPrescaler = presc; /* 分频系数 */FDCAN1_Handler.Init.NominalSyncJumpWidth = tsjw; /* 重新同步跳跃宽度 */FDCAN1_Handler.Init.NominalTimeSeg1 = ntsg1; /* tsg1范围:2~256 */FDCAN1_Handler.Init.NominalTimeSeg2 = ntsg2; /* tsg2范围:2~128 */FDCAN1_Handler.Init.MessageRAMOffset = 0; /* 信息RAM偏移(使用2路CAN时非常重要) */FDCAN1_Handler.Init.StdFiltersNbr = 28; /* 标准信息ID滤波器编号 */FDCAN1_Handler.Init.ExtFiltersNbr = 8; /* 扩展信息ID滤波器编号 */FDCAN1_Handler.Init.RxFifo0ElmtsNbr = 1; /* 接收FIFO0元素编号 */FDCAN1_Handler.Init.RxFifo0ElmtSize = FDCAN_DATA_BYTES_64; /* 接收FIFO0元素大小:最大64字节 */FDCAN1_Handler.Init.RxBuffersNbr = 0; /* 接收FIFO0元素编号 */FDCAN1_Handler.Init.TxEventsNbr = 0; /* 发送事件编号 */FDCAN1_Handler.Init.TxBuffersNbr = 0; /* 发送缓冲编号 */FDCAN1_Handler.Init.TxFifoQueueElmtsNbr = 1; /* 发送FIFO序列元素编号 */FDCAN1_Handler.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; /* 发送FIFO序列模式 */FDCAN1_Handler.Init.TxElmtSize = FDCAN_DATA_BYTES_64; /* 发送大小:最大64字节 */if (HAL_FDCAN_Init(&FDCAN1_Handler) != HAL_OK) {return 1; /* 初始化 */}/* 配置CAN过滤器 */fdcan_filterconfig.IdType = FDCAN_STANDARD_ID; /* 标准ID */fdcan_filterconfig.FilterIndex = 0; /* 滤波器索引 */fdcan_filterconfig.FilterType = FDCAN_FILTER_MASK; /* 滤波器类型 */fdcan_filterconfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; /* 过滤器0关联到FIFO0 */fdcan_filterconfig.FilterID1 = 0x0000; /* 32位ID */fdcan_filterconfig.FilterID2 = 0x0000; /* 如果FDCAN配置为传统模式的话,这里是32位掩码 *//* 过滤器配置 */if (HAL_FDCAN_ConfigFilter(&FDCAN1_Handler, &fdcan_filterconfig) != HAL_OK) {return 2; /* 滤波器初始化 */}/* 配置全局过滤器,拒收所有不匹配的标准帧或扩展帧 */if (HAL_FDCAN_ConfigGlobalFilter(&FDCAN1_Handler, FDCAN_REJECT, FDCAN_REJECT, FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE) != HAL_OK){return 3;}/* 启动CAN外围设备 */if (HAL_FDCAN_Start(&FDCAN1_Handler) != HAL_OK){return 4;}HAL_FDCAN_ActivateNotification(&FDCAN1_Handler, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0);return 0;
}/*** @brief FDCAN初始化* @param presc : 分频值,取值范围1~512;* @param tsjw : 重新同步跳跃时间单元.范围:1~128;* @param ntsg1 : 时间段1的时间单元.取值范围2~256;* @param ntsg2 : 时间段2的时间单元.取值范围2~128;* @param mode : FDCAN_MODE_NORMAL,普通模式; FDCAN_MODE_INTERNAL_LOOPBACK,内部回环模式;FDCAN_MODE_EXTERNAL_LOOPBACK,外部回环模式;FDCAN_MODE_RESTRICTED_OPERATION,限制操作模式FDCAN_MODE_BUS_MONITORING,总线监控模式* @note 以上5个参数, 除了模式选择其余的参数在函数内部会减1, 所以, 任何一个参数都不能等于0* FDCAN其输入时钟频率为 Fpclk1 = 20Mhz* 波特率 = Fpclk1 / ((ntsg1 + ntsg2 + 1) * presc);* 我们设置 fdcan_init(10, 8, 31, 8, 1), 则CAN波特率为:* 20M / ((31 + 8 + 1) * 10) = 500Kbps* @retval 0, 初始化成功; 其他, 初始化失败;*/
u8 FDCAN2_Mode_Init(uint16_t presc, uint8_t tsjw, uint16_t ntsg1, uint8_t ntsg2, uint32_t mode)
{FDCAN_FilterTypeDef fdcan_filterconfig;HAL_FDCAN_DeInit(&FDCAN2_Handler); /* 先清除以前的设置 */FDCAN2_Handler.Instance = FDCAN2; FDCAN2_Handler.Init.FrameFormat = FDCAN_FRAME_FD_BRS; /* 位速率变换FDCAN模式 */FDCAN2_Handler.Init.Mode = mode; /* 模式设置 */FDCAN2_Handler.Init.AutoRetransmission = ENABLE; /* 使能自动重传!传统模式下一定要关闭!!! */FDCAN2_Handler.Init.TransmitPause = ENABLE; /* 使能传输暂停 */FDCAN2_Handler.Init.ProtocolException = DISABLE; /* 关闭协议异常处理 *//* FDCAN中仲裁段位速率最高1Mbit/s, 数据段位速率最高8Mbit/s *//* 数据段通信速率(仅FDCAN模式需配置) = 20M / (1 + dseg1 + dseg2) = 20M / (2 + 1 + 1) = 5 Mbit/s */FDCAN2_Handler.Init.DataPrescaler = 10; /* 数据段分频系数范围:1~32 */FDCAN2_Handler.Init.DataSyncJumpWidth = 16; /* 数据段重新同步跳跃宽度1~16 */FDCAN2_Handler.Init.DataTimeSeg1 = 2; /* 数据段dsg1范围:1~32 5 */FDCAN2_Handler.Init.DataTimeSeg2 = 1; /* 数据段dsg2范围:1~16 1 *//* 仲裁段通信速率(FDCAN与传统CAN均需配置) = 20M / (1 + ntsg1 + ntsg2) = 20M / (31 + 8 + 1) = 500Kbit/s */FDCAN2_Handler.Init.NominalPrescaler = presc; /* 分频系数 */FDCAN2_Handler.Init.NominalSyncJumpWidth = tsjw; /* 重新同步跳跃宽度 */FDCAN2_Handler.Init.NominalTimeSeg1 = ntsg1; /* tsg1范围:2~256 */FDCAN2_Handler.Init.NominalTimeSeg2 = ntsg2; /* tsg2范围:2~128 */FDCAN2_Handler.Init.MessageRAMOffset = FDCAN1_Handler.msgRam.EndAddress - SRAMCAN_BASE;/* 信息RAM偏移(使用2路CAN时非常重要) */FDCAN2_Handler.Init.StdFiltersNbr = 32; /* 标准信息ID滤波器编号 */FDCAN2_Handler.Init.ExtFiltersNbr = 1; /* 扩展信息ID滤波器编号 */FDCAN2_Handler.Init.RxFifo1ElmtsNbr = 1; /* 接收FIFO1元素编号 */FDCAN2_Handler.Init.RxFifo1ElmtSize = FDCAN_DATA_BYTES_64; /* 接收FIFO1元素大小:最大64字节 */FDCAN2_Handler.Init.RxBuffersNbr = 1; /* 接收FIFO1元素编号 */FDCAN2_Handler.Init.TxEventsNbr = 1; /* 发送事件编号 */FDCAN2_Handler.Init.TxBuffersNbr = 1; /* 发送缓冲编号 */FDCAN2_Handler.Init.TxFifoQueueElmtsNbr = 2; /* 发送FIFO序列元素编号 */FDCAN2_Handler.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; /* 发送FIFO序列模式 */FDCAN2_Handler.Init.TxElmtSize = FDCAN_DATA_BYTES_64; /* 发送大小:最大64字节 */if (HAL_FDCAN_Init(&FDCAN2_Handler) != HAL_OK) {return 1; /* 初始化 */}/* 配置CAN过滤器 */fdcan_filterconfig.IdType = FDCAN_STANDARD_ID; /* 标准ID */fdcan_filterconfig.FilterIndex = 1; /* 滤波器索引 */fdcan_filterconfig.FilterType = FDCAN_FILTER_MASK; /* 滤波器类型 */fdcan_filterconfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO1; /* 过滤器1关联到FIFO1 */fdcan_filterconfig.FilterID1 = 0x0000; /* 32位ID */fdcan_filterconfig.FilterID2 = 0x0000; /* 如果FDCAN配置为传统模式的话,这里是32位掩码 *//* 过滤器配置 */if (HAL_FDCAN_ConfigFilter(&FDCAN2_Handler, &fdcan_filterconfig) != HAL_OK) {return 2; /* 滤波器初始化 */}/* 配置全局过滤器,拒收所有不匹配的标准帧或扩展帧 */if (HAL_FDCAN_ConfigGlobalFilter(&FDCAN2_Handler, FDCAN_REJECT, FDCAN_REJECT, FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE) != HAL_OK){return 3;}/* 启动CAN外围设备 */if (HAL_FDCAN_Start(&FDCAN2_Handler) != HAL_OK){return 4;}HAL_FDCAN_ActivateNotification(&FDCAN2_Handler, FDCAN_IT_RX_FIFO1_NEW_MESSAGE, 0);return 0;
}static uint32_t HAL_RCC_FDCAN_CLK_ENABLED=0;//FDCAN底层驱动,引脚配置,时钟使能
//HAL_FDCAN_Init()调用
//hsdram:FDCAN1句柄
void HAL_FDCAN_MspInit(FDCAN_HandleTypeDef* hfdcan)
{GPIO_InitTypeDef GPIO_Initure;RCC_PeriphCLKInitTypeDef FDCAN_PeriphClk;if(hfdcan->Instance==FDCAN1){//FDCAN时钟源配置为PLL1QFDCAN_PeriphClk.PeriphClockSelection=RCC_PERIPHCLK_FDCAN;FDCAN_PeriphClk.FdcanClockSelection=RCC_FDCANCLKSOURCE_PLL;if (HAL_RCCEx_PeriphCLKConfig(&FDCAN_PeriphClk) != HAL_OK){printf("can1 init failed.\n");}/* Peripheral clock enable */HAL_RCC_FDCAN_CLK_ENABLED++;if(HAL_RCC_FDCAN_CLK_ENABLED==1){__HAL_RCC_FDCAN_CLK_ENABLE();}// FDCAN1__HAL_RCC_GPIOD_CLK_ENABLE(); //开启GPIOD时钟GPIO_Initure.Pin=GPIO_PIN_0|GPIO_PIN_1; GPIO_Initure.Mode=GPIO_MODE_AF_PP; //推挽复用GPIO_Initure.Pull=GPIO_PULLUP; //上拉GPIO_Initure.Speed=GPIO_SPEED_FREQ_MEDIUM; //超高速GPIO_Initure.Alternate=GPIO_AF9_FDCAN1; //复用为CAN1HAL_GPIO_Init(GPIOD,&GPIO_Initure); //初始化#if FDCAN1_RX0_INT_ENABLE HAL_NVIC_SetPriority(FDCAN1_IT0_IRQn,1,2);HAL_NVIC_EnableIRQ(FDCAN1_IT0_IRQn);#endif }else if(hfdcan->Instance==FDCAN2){FDCAN_PeriphClk.PeriphClockSelection = RCC_PERIPHCLK_FDCAN;FDCAN_PeriphClk.FdcanClockSelection = RCC_FDCANCLKSOURCE_PLL;if (HAL_RCCEx_PeriphCLKConfig(&FDCAN_PeriphClk) != HAL_OK){printf("can2 init failed.\n");}/* Peripheral clock enable */HAL_RCC_FDCAN_CLK_ENABLED++;if(HAL_RCC_FDCAN_CLK_ENABLED==1){__HAL_RCC_FDCAN_CLK_ENABLE();}// FDCAN2__HAL_RCC_GPIOB_CLK_ENABLE(); //开启GPIOB时钟GPIO_Initure.Pin=GPIO_PIN_5|GPIO_PIN_6;GPIO_Initure.Mode=GPIO_MODE_AF_PP; //推挽复用GPIO_Initure.Pull=GPIO_PULLUP; //上拉GPIO_Initure.Speed=GPIO_SPEED_FREQ_MEDIUM; //超高速GPIO_Initure.Alternate=GPIO_AF9_FDCAN2; //复用为CAN2HAL_GPIO_Init(GPIOB,&GPIO_Initure); //初始化#if FDCAN2_RX0_INT_ENABLE HAL_NVIC_SetPriority(FDCAN2_IT0_IRQn,1,2);HAL_NVIC_EnableIRQ(FDCAN2_IT0_IRQn);#endif }
}//此函数会被HAL_FDCAN_DeInit调用
//hfdcan:fdcan句柄
void HAL_FDCAN_MspDeInit(FDCAN_HandleTypeDef* hfdcan)
{/* Peripheral clock disable */if(hfdcan->Instance==FDCAN1){HAL_RCC_FDCAN_CLK_ENABLED--;if(HAL_RCC_FDCAN_CLK_ENABLED==0){__HAL_RCC_FDCAN_CLK_DISABLE();}HAL_GPIO_DeInit(GPIOD, GPIO_PIN_0|GPIO_PIN_1);#if FDCAN1_RX0_INT_ENABLE HAL_NVIC_DisableIRQ(FDCAN1_IT0_IRQn);#endif}else if(hfdcan->Instance==FDCAN2){/* Peripheral clock disable */HAL_RCC_FDCAN_CLK_ENABLED--;if(HAL_RCC_FDCAN_CLK_ENABLED==0){__HAL_RCC_FDCAN_CLK_DISABLE();}HAL_GPIO_DeInit(GPIOB, GPIO_PIN_5|GPIO_PIN_6);#if FDCAN2_RX0_INT_ENABLE HAL_NVIC_DisableIRQ(FDCAN2_IT0_IRQn);#endif}
}