STM32H743的FDCAN使用方法(2):STM32CubeMX初始化代码修改

0 工具准备

1.STM32CubeMX

1 前言

本文介绍基于STM32CubeMX,修改基于STM32CubeMX生成的FDCAN初始化代码,成为我们能够正常使用的状态。

2 初始化代码修改

2.1 FDCAN初始化代码修改

typedef enum
{
    FDCAN_100K = 0,
    FDCAN_250K,
    FDCAN_500K,
    FDCAN_1M,
    FDCAN_2_5M,
    FDCAN_5M,
} FDCAN_BAUD_CFG_Type;
typedef enum
{
    FDCAN_NORMAL = FDCAN_MODE_NORMAL,                            /* 正常模式 */
    FDCAN_RESTRICTED_OPERATIO = FDCAN_MODE_RESTRICTED_OPERATION, /* 受限模式 */
    FDCAN_BUS_MONITORING = FDCAN_MODE_BUS_MONITORING,            /* 监控模式 */
    FDCAN_INTERNAL_LOOPBACK = FDCAN_MODE_INTERNAL_LOOPBACK,      /* 内部环回模式 */
    FDCAN_EXTERNAL_LOOPBACK = FDCAN_MODE_EXTERNAL_LOOPBACK,      /* 外部环回模式 */
} FDCAN_MODE_Type;
const fdcan_cfg_t fdCANCfg[] =
    {
        /* 输入时钟100MHz,测试100K、250K、500K、1M、2M、5M收发CAN2.0正常 */
        {100000, 17, 2},  /* 采样点 :90% */
        {250000, 17, 2},  /* 采样点 :90% */
        {500000, 17, 2},  /* 采样点 :90% */
        {1000000, 17, 2}, /* 采样点 :90% */
        {2500000, 17, 2}, /* 采样点 :90% */
        {5000000, 17, 2}, /* 采样点 :90% */
};
/**
 * @brief 设置FDCAN标准ID过滤器
 *
 * @param id ID(0-0x7ff)
 * @param mask 掩码(0-0x7ff)
 */
void set_fdcan_std_filter(u32 id, u32 mask)
{

    hfdCan2StdFilter.IdType = FDCAN_STANDARD_ID; /* 设置标准 ID */
    /* 用于过滤索引,如果是标准 ID,范围 0 到 127。如果是扩展 ID,范围 0 到 64 */
    /* 过滤索引和前面配置的过滤器个数对应,如果个数为n,则索引为0 - n-1 */
    hfdCan2StdFilter.FilterIndex = 0;
    hfdCan2StdFilter.FilterType = FDCAN_FILTER_MASK;         /* 过滤器采样屏蔽位模式 */
    hfdCan2StdFilter.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; /* 如果过滤匹配,将数据保存到 Rx FIFO 0 */
    hfdCan2StdFilter.FilterID1 = id;                         /* 屏蔽位模式下,FilterID1 是消息 ID */
    hfdCan2StdFilter.FilterID2 = mask;                       /* 屏蔽位模式下,FilterID2 是消息屏蔽位 */
}

/**
 * @brief 设置FDCAN扩展ID过滤器
 *
 * @param id ID(0-0x1fffffff)
 * @param mask 掩码(0-0x1fffffff)
 */
void set_fdcan_ext_filter(u32 id, u32 mask)
{

    hfdCan2ExtFilter.IdType = FDCAN_EXTENDED_ID; /* 设置扩展 ID */
    /* 用于过滤索引,如果是标准 ID,范围 0 到 127。如果是扩展 ID,范围 0 到 64 */
    /* 过滤索引和前面配置的过滤器个数对应,如果个数为n,则索引为0 - n-1 */
    hfdCan2ExtFilter.FilterIndex = 0;
    hfdCan2ExtFilter.FilterType = FDCAN_FILTER_MASK;         /* 过滤器采样屏蔽位模式 */
    hfdCan2ExtFilter.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; /* 如果过滤匹配,将数据保存到 Rx FIFO 0 */
    hfdCan2ExtFilter.FilterID1 = id;                         /* 屏蔽位模式下,FilterID1 是消息 ID */
    hfdCan2ExtFilter.FilterID2 = mask;                       /* 屏蔽位模式下,FilterID2 是消息屏蔽位 */
}
/**
 * @brief FDCAN初始化
 *
 * @param mode 模式 环回、正常等
 * @param fdCANBaud FDCAN波特率
 * @return int 0:成功 -1:失败
 */
int MX_FDCAN_Init(FDCAN_MODE_Type mode, FDCAN_BAUD_CFG_Type fdCANBaud)
{
    HAL_StatusTypeDef ret;
    /* 选择FDCAN2 */
    hfdcan2.Instance = FDCAN2;

    /* 帧格式选择 */
    /* FDCAN_FRAME_CLASSIC:经典CAN格式(CAN2.0),最高支持1M
        仅支持收发CAN2.0报文
    */
    /* FDCAN_FRAME_FD_NO_BRS:不可变波特率FDCAN格式(仲裁段和数据段波特率一致),不兼容CAN2.0
        仲裁段和数据段的波特率一致
        支持收发FDCAN报文和CAN2.0报文
    */
    /* FDCAN_FRAME_FD_BRS:可变波特率FDCAN格式(仲裁段和数据段波特率可以一致),兼容CAN2.0
        在仲裁段使用较低波特率如500K,在数据段使用较高波特率如2M
        支持收发FDCAN报文和CAN2.0报文
    */

    /* 帧格式:不可变波特率 */
    hfdcan2.Init.FrameFormat = FDCAN_FRAME_FD_NO_BRS;
    /* 工作模式:自环、正常工作模式等 */
    /* 实测使用自环有bug,自己收不到自己发出的CAN报文 */
    hfdcan2.Init.Mode = mode;

    /* 关闭重传、发送暂停、协议错误处理 */
    hfdcan2.Init.AutoRetransmission = DISABLE;
    hfdcan2.Init.TransmitPause = DISABLE;
    hfdcan2.Init.ProtocolException = DISABLE;

    /* FDCAN分频系数 */
    /* CAN/FDCAN 1bit = 1Tq同步段 + 1-8Tq传播时间段 + 1-8相位缓冲段1 + 1-8相位缓冲段2 */
    /* CAN/FDCAN 实际使用中:1bit = 1Tq同步段 + TimeSeg1/BS1(传播时间段+相位缓冲段)+ TimeSeg2/BS2(相位缓冲段2) */
    /* 采样点推荐位置:85-90% 满足 (1Tq + BS1) / (1Tq + BS1 + BS2) , 建议87.5 */

    /* 经典CAN只需要关注仲裁阶段波特率设置,FDCAN需要设置仲裁段和数据段波特率且允许二者不一致(波特率可变)*/
    /*
        选择CAN2.0或FDCAN不可变波特率,仲裁段设置的波特率就是整个CAN报文波特率
        选择FDCAN可变波特率,仲裁段设置的波特率是仲裁段的CAN报文波特率
    */
    hfdcan2.Init.NominalPrescaler = FDCAN2_INPUT_CLOCK / (fdCANCfg[fdCANBaud].baud * (1 + fdCANCfg[fdCANBaud].bs1 + fdCANCfg[fdCANBaud].bs2));
    /* 用于动态调节Phase_Seg1和Phase_Seg2,所以不可以比Phase_Seg2和Phase_Seg2大,固定为1 */
    hfdcan2.Init.NominalSyncJumpWidth = 1;
    /* 设置位段1、位段2 */
    hfdcan2.Init.NominalTimeSeg1 = fdCANCfg[fdCANBaud].bs1;
    hfdcan2.Init.NominalTimeSeg2 = fdCANCfg[fdCANBaud].bs2;

    /* 只有选择FDCAN可变波特率帧格式这里的配置才作为数据段波特率 */
    hfdcan2.Init.DataPrescaler = FDCAN2_INPUT_CLOCK / (fdCANCfg[fdCANBaud].baud * (1 + fdCANCfg[fdCANBaud].bs1 + fdCANCfg[fdCANBaud].bs2));
    /* 用于动态调节Phase_Seg1和Phase_Seg2,所以不可以比Phase_Seg2和Phase_Seg2大,固定为1 */
    hfdcan2.Init.DataSyncJumpWidth = 1;
    /* 设置位段1、位段2 */
    hfdcan2.Init.DataTimeSeg1 = fdCANCfg[fdCANBaud].bs1;
    hfdcan2.Init.DataTimeSeg2 = fdCANCfg[fdCANBaud].bs2;

    /*
        FDCAN RAM偏移地址,只有一个FDCAN,偏移地址为0
        FDCAN1和FDCAN2共享2560个字(4Byte)
    */
    hfdcan2.Init.MessageRAMOffset = 0;
    /* 标准ID过滤器个数,范围0到128 */
    hfdcan2.Init.StdFiltersNbr = 1;
    /* 标准ID过滤器个数,范围0到64 */
    hfdcan2.Init.ExtFiltersNbr = 1;
    /* RXFIFO0元素个数,范围0到64 */
    hfdcan2.Init.RxFifo0ElmtsNbr = 64;
    /* RXFIFO0每个元素数据大小 */
    hfdcan2.Init.RxFifo0ElmtSize = FDCAN_DATA_BYTES_64;
    /* RXFIFO1元素个数,范围0到64 */
    hfdcan2.Init.RxFifo1ElmtsNbr = 0;
    /* RXFIFO1每个元素数据大小 */
    hfdcan2.Init.RxFifo1ElmtSize = FDCAN_DATA_BYTES_64;
    /* 设置Rx Buffer元素个数,范围0-64 */
    hfdcan2.Init.RxBuffersNbr = 0;
    /* 设置RxBuffer元素中每个数据大小,范围0-64 */
    hfdcan2.Init.RxBufferSize = FDCAN_DATA_BYTES_64;
    /* Tx Event FIFO元素个数,范围0到32 */
    hfdcan2.Init.TxEventsNbr = 0;
    /* 设置专用的 Tx Buffer 元素个数,范围 0 到 32*/
    hfdcan2.Init.TxBuffersNbr = 0;
    /* 设置用于Tx FIFO/Queue 的 Tx Buffers 个数。范围 0 到 32*/
    hfdcan2.Init.TxFifoQueueElmtsNbr = 32;
    /* 设置 FIFO 模式或者 QUEUE 队列模式 */
    hfdcan2.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;
    /* 设置 Tx Element 中的数据域大小 */
    hfdcan2.Init.TxElmtSize = FDCAN_DATA_BYTES_8;
    /* 初始化FDCAN2 */
    if (HAL_FDCAN_Init(&hfdcan2) != HAL_OK)
    {
        return -1;
    }

    /* 配置标准ID过滤器 */
    if (HAL_FDCAN_ConfigFilter(&hfdcan2, &hfdCan2StdFilter) != HAL_OK)
    {
        return -1;
    }

    /* 配置扩展ID过滤器 */
    if (HAL_FDCAN_ConfigFilter(&hfdcan2, &hfdCan2ExtFilter) != HAL_OK)
    {
        return -1;
    }

    /* 配置全局过滤器,配置后过滤器配置才会生效 */
    ret = HAL_FDCAN_ConfigGlobalFilter(&hfdcan2, FDCAN_REJECT, FDCAN_REJECT,
                                       ENABLE, ENABLE);
    if (ret != HAL_OK)
    {
        return -1;
    }

    /* 设置RxFIFO0的水印为1,接收到1个CAN报文就触发中断 */
    ret = HAL_FDCAN_ConfigFifoWatermark(&hfdcan2, FDCAN_CFG_RX_FIFO0, 1);
    if (ret != HAL_OK)
    {
        return -1;
    }

    /* 激活RXFIFO0的水印通知中断 */
    ret = HAL_FDCAN_ActivateNotification(&hfdcan2, FDCAN_IT_RX_FIFO0_WATERMARK, 0);
    if (ret != HAL_OK)
    {
        return -1;
    }

    /* 启动 FDCAN */
    HAL_FDCAN_Start(&hfdcan2);


    return 0;
}

为了便于使用,这里为FDCAN初始化函数增加了模式、波特率2个形参。所有的配置全部有详细注释,可以参考注释进行配置。

2.2 增加FDCAN的RXFIFO0接收回调函数

/**
 * @brief FDCAN的RXFIFO0接收回调
 *
 * @param hfdcan FDCAN句柄
 * @param RxFifo0ITs RXFIFO0状态
 */
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
    FDCAN_RxHeaderTypeDef rxHeader;
    uint8_t rxData[64];
    if (hfdcan == &hfdcan2)
    {
        if ((RxFifo0ITs & FDCAN_IT_RX_FIFO0_WATERMARK) != RESET)
        {
            /* 轮询RX FIFO,直到无数据可读 */
            for (;;)
            {
                if (HAL_FDCAN_GetRxFifoFillLevel(hfdcan, FDCAN_RX_FIFO0) > 0)
                {
                    /* 从 RX FIFO0 读取数据 */
                    HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &rxHeader, rxData);
                    add_fdcan_recv_msg(&rxHeader, rxData);
                }
                else
                {
                    break;
                }
            }
        }
    }
}

2.3 增加FDCAN的发送函数

/**
 * @brief 发送CAN/FDCAN报文
 *
 * @param buff 数据
 * @param len 长度
 * @return int 0-成功 -1-失败
 */
int fdcan_send(u8 *buff, u8 len)
{
    int timeout = 0;
    HAL_StatusTypeDef ret;
    /* 初始化FDCAN发送参数 */
    fdCANSendCfg.Identifier = 0x147;                      /* 设置消息的ID */
    fdCANSendCfg.IdType = FDCAN_STANDARD_ID;              /* 标准ID */
    fdCANSendCfg.TxFrameType = FDCAN_DATA_FRAME;          /* 数据帧 */
    fdCANSendCfg.ErrorStateIndicator = FDCAN_ESI_ACTIVE;  /* 设置错误状态指示 */
    fdCANSendCfg.BitRateSwitch = FDCAN_BRS_OFF;           /* 关闭可变波特率 */
    fdCANSendCfg.FDFormat = FDCAN_CLASSIC_CAN;            /* FDCAN格式 */
    fdCANSendCfg.TxEventFifoControl = FDCAN_NO_TX_EVENTS; /* 用于发送事件 FIFO 控制, 不存储 */
    fdCANSendCfg.MessageMarker = 0;                       /* 用于复制到 TX EVENT FIFO 的消息 Maker 来识别消息状态(检查消息是否发送成功等),范围0到0xFF */
    fdCANSendCfg.DataLength = (uint32_t)len << 16;        /* 发送数据长度 */
    /* 等待TX FIFO可用 */
    while (HAL_FDCAN_GetTxFifoFreeLevel(&hfdcan2) == 0)
    {
        HAL_Delay(1);
        timeout++;
        if (timeout > 100)
        {
            return -1;
        }
    }
    /* 添加数据到 TX FIFO */
    ret = HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan2, &fdCANSendCfg, buff);
    if (ret != HAL_OK)
    {
        return -1;
    }
    add_fdcan_send_msg(&fdCANSendCfg, buff);
    return 0;
}

以上代码和CAN调试器在100K、250K、500K、1M下测试100万次收发正常。在内部回环、外部回环下无法收到FDCAN自己发出的报文,怀疑是HAL库bug,本文使用的HAL库版本如下:

STM32Cube FW_H7 V1.11.0

此外,发现在高速接收CAN报文时,如果stm32H743主频被设置为480MHz则会丢掉几乎所有包,而主频设置为400MHz则能够正常接收。
如果你也遇到了和我一样的问题,欢迎留言交流!
问题确认:
stm32H743主频设置为480MHz时FDCAN外设在回环模式工作不正常。需要修改2个地方:
(1)将主频修改为400MHz。
(2)LDO稳压器输出的电压选择VOS1(480MHz时是VOS0),语句如下:
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
stm32H743即使是v版本,最好也不要将主频设置到480MHz,否则会有各种头疼和意想不到(在实际使用过程中,将主频提高到480MHz,温度上升将近10℃,一些外设也出现了令人头脑眩晕的问题)的问题,为了稳定和省事起见,还是老老实实将主频设置为400MHz。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NW嵌入式开发

感谢您的支持,让我们一起进步!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值