STM32H7双路CAN踩坑记录

STM32H7双路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时钟源配置为PLL1Q
        FDCAN_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;         //复用为CAN1
        HAL_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;         //复用为CAN2
        HAL_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
    }
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值