Skip to content

GPT5扫出的MCAN的一些Bug, 仅供参考, 主要是 hpm_mcan_soc.h 和 hpm_mcan_drv.c, 请特别注意 message ram 的起始地址计算的问题 #93

@weifengdq

Description

@weifengdq

weifengdq: 详细解读hpm_mcan_soc.h

GitHub Copilot: 下面是对当前头文件的逐段解读与改进建议要点。

文件作用

  • 为 HPMicro HPM53xx 平台上的 MCAN 外设提供 SoC 相关适配:消息 RAM(AHB_RAM)地址/大小约束、外部时间基选择(TSU/TBSEL)、待机脚控制,以及基于寄存器基地址推导实例号等。
  • 提供一组内联函数,便于上层驱动快速访问和配置这些 SoC 相关特性。

关键类型与全局

  • mcan_msg_buf_attr_t:描述每个 MCAN 实例使用的消息缓冲(AHB_RAM)区间,包含 ram_base 与 ram_size,单位字节。
  • mcan_soc_msg_buf_attr[]:外部定义的全局数组,按实例号存放每个 MCAN 的消息 RAM 属性。

重要宏与常量

  • HPM_MCAN_TBSEL_BASE/HPM_MCAN_TBSEL:TBSEL 外部时间基选择寄存器的物理地址映射(0xF02FF000)。通过该 32 位寄存器为最多 4 路 ext_tbsel 配置时间源。
  • HPM_MCAN_TBSEL_BITWIDTH=6/HPM_MCAN_TBSEL_MASK/SHIFT:每路 TBSEL 选择域宽 6 bit,从 bit8 起,每路间隔 6 bit。
  • 外部时间基编号:0~3(HPM_MCAN_EXT_TBSEL_NUM=4)。
  • AHB_RAM 有效区间:
    • 起始:MCAN_MSG_BUF_BASE_VALID_START=0xF0400000
    • 最大:32KB
    • 结束:MCAN_MSG_BUF_BASE_VALID_END=起始+最大
    • 对齐:MCAN_MSG_BUF_ALIGNMENT_SIZE=4 字节
  • MCAN_IP_SLOT_SIZE=0x4000:MCAN 寄存器块槽位大小,用于根据基地址推导实例序号。
  • TSU 时间基选项宏:MCAN_TSU_TBSEL_PTPC0、MCAN_TSU_TBSEL_MCANx 等,表示把 TBSEL 对应的外部时间基接到 PTPC0 或某个 MCAN 的内部时间基输出。

内联函数解读

  • mcan_get_instance_from_base(MCAN_Type*):
    • 用基地址与 HPM_MCAN0_BASE 的差,除以槽位大小 0x4000 计算实例号。
    • 假设各 MCAN 外设地址连续且等距。
  • mcan_set_tsu_ext_timebase_src(MCAN_Type*, uint8_t src):
    • 设置 TSU 的“外部时间输入选择”字段(GLB_CTL.TSU_TBIN_SEL)。src 范围 0..3。
  • mcan_set_tsu_tbsel_option(MCAN_Type*, uint8_t ext_tbsel, uint8_t tbsel_option):
    • 选择某一路外部 TBSEL 的时间来源(写 HPM_MCAN_TBSEL 指定域)。
    • ext_tbsel 0..3,tbsel_option 6 bit 有效。写法为先算移位与掩码再合成。
  • mcan_enable_standby_pin/mcan_disable_standby_pin:
    • 置/清 GLB_CTL.M_CAN_STBY 控制待机脚。
  • mcan_set_msg_buf_attr(MCAN_Type*, const mcan_msg_buf_attr_t*):
    • 参数有效性检查:
      • 指针非空
      • ram_base 落在合法 AHB_RAM 区间内,且 ram_base+ram_size 不越界
      • ram_size 不大于 AHB_RAM 最大容量
      • ram_base 与 ram_size 均为 4 字节对齐
      • 实例号在范围内
    • 通过实例号把属性写入 mcan_soc_msg_buf_attr[]。
  • mcan_get_ram_base():
    • 固定返回 AHB_RAM 基地址(不是实例专属基址)。真正实例偏移由 mcan_get_ram_offset 提供。
  • mcan_get_ram_offset(MCAN_Type*):
    • 返回该实例相对 AHB_RAM 起点的偏移(由先前 set_msg_buf_attr 设置的 ram_base 决定)。
  • mcan_get_ram_size(MCAN_Type*):
    • 返回该实例配置的消息 RAM 大小。

易错点与建议

  • 字符串拼写错误:
    • 头尾 C++ 兼容宏写成了 __cpluspus,正确应为 __cplusplus。
  • 命名可能的笔误:
    • 宏 MCAN_MSG_BUG_SIZE_MAX 似为 MCAN_MSG_BUF_SIZE_MAX 的笔误。建议保留别名兼容,统一使用 BUF。
  • 边界/健壮性:
    • mcan_get_ram_offset/mcan_get_ram_size 未再次校验实例号范围;若未先调用 mcan_set_msg_buf_attr 进行初始化,可能访问未初始化内容。建议在这两个 getter 中补充实例范围检查。
    • mcan_set_msg_buf_attr 可考虑禁止 ram_size 为 0。
  • 并发/原子性:
    • 对 HPM_MCAN_TBSEL 的读改写在并发环境下可能被打断,建议在临界区或关中断上下文中执行,或以位带/掩码原子写支持为准。
  • 可移植性:
    • mcan_get_instance_from_base 中把指针强转为 uint32_t 依赖 32 位地址宽度;在该 MCU 平台上合理,但在不同编译环境可考虑使用 uintptr_t。

可选修改建议(补丁)

  • 修复 __cplusplus 宏拼写错误。
  • 引入 MCAN_MSG_BUF_SIZE_MAX 并保留旧名别名。
  • 在 getter 中补充实例号校验;在 setter 中禁止 size=0。
  • 在条件判断中统一使用新的 BUF 名称。
// ...existing code...
#ifdef __cpluspus
extern "C" {
#endif
// ...existing code...
#define MCAN_MSG_BUF_BASE_VALID_START (0xF0400000UL)    /* This is the start address of AHB_RAM */
#define MCAN_MSG_BUF_SIZE_MAX         (32UL * 1024UL)   /* This is the total size of AHB_RAM */
/* Backward-compat alias (typo kept for existing code) */
#define MCAN_MSG_BUG_SIZE_MAX         MCAN_MSG_BUF_SIZE_MAX
#define MCAN_MSG_BUF_BASE_VALID_END   (MCAN_MSG_BUF_BASE_VALID_START + MCAN_MSG_BUF_SIZE_MAX)
// ...existing code...
static inline hpm_stat_t mcan_set_msg_buf_attr(MCAN_Type *ptr, const mcan_msg_buf_attr_t *attr)
{
    if ((attr == NULL) || \
        (attr->ram_base < MCAN_MSG_BUF_BASE_VALID_START) || \
        (attr->ram_base >= MCAN_MSG_BUF_BASE_VALID_END) || \
        (attr->ram_size == 0U) || \
        (attr->ram_size > MCAN_MSG_BUF_SIZE_MAX) || \
        (attr->ram_base + attr->ram_size > MCAN_MSG_BUF_BASE_VALID_END) || \
        (attr->ram_base % MCAN_MSG_BUF_ALIGNMENT_SIZE != 0U) || \
        (attr->ram_size % MCAN_MSG_BUF_ALIGNMENT_SIZE != 0U)) {
        return status_invalid_argument;
    }
    uint32_t instance = mcan_get_instance_from_base(ptr);
    if (instance >= MCAN_SOC_MAX_COUNT) {
        return status_invalid_argument;
    }

    mcan_soc_msg_buf_attr[instance].ram_base = attr->ram_base;
    mcan_soc_msg_buf_attr[instance].ram_size = attr->ram_size;

    return status_success;
}
// ...existing code...
static inline uint32_t mcan_get_ram_offset(MCAN_Type *ptr)
{
    uint32_t instance = mcan_get_instance_from_base(ptr);
    if (instance >= MCAN_SOC_MAX_COUNT) {
        return 0U;
    }
    return (mcan_soc_msg_buf_attr[instance].ram_base - MCAN_MSG_BUF_BASE_VALID_START);
}
// ...existing code...
static inline uint32_t mcan_get_ram_size(MCAN_Type *ptr)
{
    uint32_t instance = mcan_get_instance_from_base(ptr);
    if (instance >= MCAN_SOC_MAX_COUNT) {
        return 0U;
    }
    return mcan_soc_msg_buf_attr[instance].ram_size;
}
// ...existing code...
#ifdef __cplusplus
}
#endif
// ...existing code...

简要用法示例

  • 为各实例分配 AHB_RAM 子区并设置属性:
#include "hpm_mcan_drv.h"
#include "hpm_mcan_soc.h"

void mcan_init_ram(void)
{
    const uint32_t ahb_base = MCAN_MSG_BUF_BASE_VALID_START;
    const uint32_t ahb_size = MCAN_MSG_BUF_SIZE_MAX; // 32KB
    const uint32_t inst_cnt = MCAN_SOC_MAX_COUNT;
    uint32_t chunk = (ahb_size / inst_cnt) & ~((uint32_t)MCAN_MSG_BUF_ALIGNMENT_SIZE - 1U);

    for (uint32_t i = 0; i < inst_cnt; i++) {
        MCAN_Type *base = (MCAN_Type *)(HPM_MCAN0_BASE + i * MCAN_IP_SLOT_SIZE);
        mcan_msg_buf_attr_t attr = {
            .ram_base = ahb_base + i * chunk,
            .ram_size = chunk
        };
        (void)mcan_set_msg_buf_attr(base, &attr);
    }
}
  • 配置 TSU 外部时间基 0 来自 PTPC0,选择 TSU 输入为 TBSEL_0:
void mcan_config_timebase(MCAN_Type *base)
{
    // 选择外部时间输入源索引 0
    mcan_set_tsu_ext_timebase_src(base, MCAN_TSU_EXT_TIMEBASE_SRC_TBSEL_0);
    // 设置 TBSEL_0 的来源为 PTPC0
    mcan_set_tsu_tbsel_option(base, 0, MCAN_TSU_TBSEL_PTPC0);
}
  • 控制待机脚:
void mcan_standby(MCAN_Type *base, bool enable)
{
    if (enable) {
        mcan_enable_standby_pin(base);
    } else {
        mcan_disable_standby_pin(base);
    }
}

测试与注意事项

  • 在单元测试中应覆盖:
    • 非法地址/大小/对齐时返回 status_invalid_argument。
    • 设置后 get_ram_offset/get_ram_size 返回期望值。
    • 越界实例(伪造 base)时 getter 返回 0。
  • 若在中断或多核环境中更新 HPM_MCAN_TBSEL,建议加临界区,避免并发读改写。

weifengdq: 详细解读hpm_mcan_drv.h

GitHub Copilot: 下面是对 hpm_mcan_drv.h 的结构化解读、用法提示与可改进点。

文件职责概览

  • 定义 HPM M_CAN 驱动的对外数据结构、宏与 API 原型,以及少量性能关键的内联寄存器访问函数。
  • 覆盖功能:初始化/反初始化、RAM 布局配置(简化/灵活两种方案)、滤波器配置、收发帧格式、事件/状态/错误查询、TSU 时间戳访问、中断管理、超时计数、TDC 配置等。
  • 与 soc 适配层 hpm_mcan_soc.h 配合使用(实例号、AHB RAM 边界、TSU 外部时间源 TBSEL 选择等)。

关键数据结构与用途

  • 帧与事件
    • mcan_tx_frame_t/mcan_rx_message_t:发送/接收帧结构,前两字包含标识符+控制域,后续为最大 64 字节数据。通过位段描述 std_id/ext_id、RTR、ESI、BRS、DLC 等。
    • mcan_tx_event_fifo_elem_t:TX 事件元素,含标识符、DLC、BRS、CANFD、事件类型、时间戳/时间戳指针等。
  • RAM 布局
    • mcan_ram_flexible_config_t:最细粒度配置,需手动指定每一块(滤波、RXFIFO0/1、RXBUF、TXBUF、TXEVT)起始地址/元素数/水位等,适合熟悉 IP 的用户。
    • mcan_ram_config_t:简化配置,仅提供各模块是否启用、元素数、数据域大小等,驱动自动计算布局与起始地址,适合大多数用户。
  • 滤波相关
    • mcan_filter_elem_t/mcan_filter_elem_list_t、mcan_all_filters_config_t:标准/扩展滤波元素列表与全局滤波配置(非匹配帧去向、远程帧拒绝、扩展 ID 掩码)。
  • 时序与时间戳
    • mcan_bit_timing_param_t + mcan_bit_timing_option_t:提供“自动算参数”与“直接给定分频/段/同步跳宽”的两种风格。
    • mcan_tdc_config_t:发射延迟补偿。
    • mcan_internal_timestamp_config_t:内部时间戳分频与选择。
    • mcan_tsu_config_t:外部时间戳单元(TSU)配置,含是否用外部时间基、来源/tbsel 选项、64 位时间戳等;与 soc 层 TSU 相关宏配合。
    • mcan_timestamp_value_t:统一封装 16/32/64 位时间戳读取结果。
  • 错误与状态
    • mcan_last_err_code_t、mcan_activity_state_t、mcan_error_count_t、mcan_protocol_status_t:错误码/活动状态/错误计数/协议状态抽象。
  • 中断
    • 一组 MCAN_INT_* 掩码与事件集合(接收/发送/错误)。
    • 便捷内联:启停/使能/选择中断线、查询与清除中断标志、TXBUF 专属中断掩码等。

核心内联 API 速览

  • 模式/控制:mcan_enter_init_mode/mcan_enter_normal_mode、mcan_enable_disable_*(TXP/EFBI/PXHD/WMM/UTSU/BRSE/DAR/ASM/MON/CSR)。
  • 时钟与计数:mcan_enable_clock/mcan_stop_clock/mcan_is_clock_stopped、mcan_get_timestamp_counter_value、mcan_get_timeout_counter_value/mcan_reset_timeout_counter_value。
  • 状态与错误:mcan_get_error_counter、mcan_get_last_error_code/mcan_get_last_data_error_code、mcan_get_activity、mcan_is_in_* 状态、mcan_get_transmitter_delay_compensation_value。
  • 中断:mcan_enable/disable_interrupts、mcan_interrupt_line_select、mcan_get/clear_interrupt_flags;TXBUF 传输/取消完成中断的独立开关。
  • 发送队列:mcan_is_txfifo_full、mcan_get_txfifo_put_index、mcan_send_add_request/mcan_send_add_multiple_requests、mcan_cancel_tx_buf_send_request、mcan_is_tx_buf_cancellation_finished、mcan_is_transmit_request_pending/mcan_is_transmit_occurred。
  • 接收队列:mcan_is_rxbuf_data_available/mcan_clear_rxbuf_data_available_flag。
  • TSU:mcan_enable_tsu/mcan_is_tsu_used、mcan_is_64bit_tsu_timestamp_used、mcan_is_tsu_timestamp_available;配套的读取函数原型 mcan_read_32bit_tsu_timestamp/mcan_read_64bit_tsu_timestamp。

非内联的主要函数原型

  • 缺省配置:mcan_get_default_config、mcan_get_default_ram_flexible_config、mcan_get_default_ram_config。
  • 初始化:mcan_init/mcan_deinit;其中 init 通常会处理位时序、模式、RAM 布局、滤波、TDC、时间戳、超时、中断等。
  • RAM 配置:mcan_config_ram_with_flexible_config、mcan_config_ram。
  • 滤波:mcan_config_all_filters、mcan_set_filter_element。
  • 收发:mcan_write_txbuf、mcan_write_txfifo、mcan_request_and_fill_txfifo、mcan_transmit_*、mcan_read_rxbuf、mcan_read_rxfifo、mcan_read_tx_evt_fifo。
  • 时间戳抽取:mcan_get_timestamp_from_tx_event、mcan_get_timestamp_from_received_message。
  • 协议状态:mcan_parse_protocol_status、mcan_get_protocol_status。
  • 工具:mcan_get_message_size_from_dlc、mcan_get_data_field_size。

与 soc 层的衔接

  • RAM 空间与对齐限制由 soc 层定义(mcan_set_msg_buf_attr/mcan_get_ram_*)。
  • TSU 外部时间源与 TBSEL 硬件寄存器地址在 soc 层(mcan_set_tsu_ext_timebase_src、mcan_set_tsu_tbsel_option)。

典型使用流程示例(简化)

#include "hpm_mcan_drv.h"
#include "hpm_clock.h"

static void can_init(MCAN_Type *base, uint32_t ahb_clk_hz)
{
    mcan_config_t cfg;
    mcan_get_default_config(base, &cfg);

    cfg.mode = mcan_mode_normal;
    cfg.enable_canfd = true;
    cfg.enable_tdc = true;
    cfg.use_lowlevel_timing_setting = false;
    cfg.baudrate = 500000;     // nominal
    cfg.baudrate_fd = 2000000; // data

    // RAM 简化配置(默认会在 mcan_get_default_ram_config 中给出合理值)
    mcan_get_default_ram_config(base, &cfg.ram_config, cfg.enable_canfd);

    // 中断:接收、新消息、传输完成等
    cfg.interrupt_mask = MCAN_EVENT_RECEIVE | MCAN_EVENT_TRANSMIT;

    if (mcan_init(base, &cfg, ahb_clk_hz) != status_success) {
        // 处理错误
    }
}

static void can_tx_example(MCAN_Type *base)
{
    mcan_tx_frame_t tx = {0};
    tx.use_ext_id = 0;
    tx.std_id = 0x123;
    tx.dlc = 8;
    tx.canfd_frame = 1;
    tx.bitrate_switch = 1;
    for (int i = 0; i < 8; i++) tx.data_8[i] = (uint8_t)i;

    (void)mcan_transmit_blocking(base, &tx);
}

static void can_rx_example(MCAN_Type *base)
{
    mcan_rx_message_t rx;
    (void)mcan_receive_from_fifo_blocking(base, 0, &rx); // FIFO0
}

易错点与改进建议

  • 中断线使能可能覆盖已有位
    • mcan_interrupt_line_select 最后写 ptr->ILE = (1UL << line_index) 会清掉已使能的其它中断线位,建议改为 OR。
  • 多处“超时配置”定义重复
    • 已有 mcan_timeout_sel_t/mcan_timeout_config_t,同时又定义了 MCAN_TIMEOUT_SEL_TYPE_* 宏与 mcan_timeout_counter_config_t,语义上重复,建议仅保留一套 API,以免混淆。
  • 滤波类型宏存在无值定义
    • MCAN_FILTER_TYPE_DUAL_ID_EXT_FILTER_IGNORE_MASK 宏没有赋值,且 M_CAN 标准 SFT 仅有 0..3 四种类型,建议删除该多余宏。
  • 文字/注释细节
    • “SIze”“QQueue”等注释小拼写问题可修正,减少歧义。
  • 索引范围校验
    • 若对外暴露的非内联函数内部已做边界检查则无问题;内联如 mcan_is_transmit_request_pending/mcan_is_rxbuf_data_available 等,调用侧需确保 index 在硬件支持范围,否则读写无效位。
  • 位段与可移植性
    • 帧结构使用位段描述寄存器格式,编译器/ABI 对位段布局要求一致;作为寄存器访问用途问题不大,但不建议跨编译器/端序序列化为线上的报文。
  • mcan_reset_timeout_counter_value
    • 当前通过 32 位写指针清零,直接对 TOCV 赋 0 更直观,volatile 已保证写访问。

建议补丁(不影响对外 API)

// ...existing code...
#define MCAN_FILTER_TYPE_RANGE_FILTER (0U)              /*!< CAN Identifier Range filter */
#define MCAN_FILTER_TYPE_SPECIFIED_ID_FILTER (1U)       /*!< CAN Identifier ID filter */
#define MCAN_FILTER_TYPE_CLASSIC_FILTER (2U)            /*!< CAN classic filter */
#define MCAN_FILTER_TYPE_FILTER_DISABLED (3U)           /*!< CAN filter disabled */
// 删除多余且无取值的宏,避免误用
/* #define MCAN_FILTER_TYPE_DUAL_ID_EXT_FILTER_IGNORE_MASK */ 
// ...existing code...
static inline void mcan_interrupt_line_select(MCAN_Type *ptr, uint32_t mask, uint8_t line_index)
{
    if (line_index == 0) {
        ptr->ILS &= ~mask;
    } else {
        ptr->ILS |= mask;
    }
    // 仅置位目标线,不覆盖其他线的使能
    ptr->ILE |= (1UL << line_index);
}
// ...existing code...
static inline void mcan_reset_timeout_counter_value(MCAN_Type *ptr)
{
    // 直接对寄存器清零,volatile 可避免被优化掉
    ptr->TOCV = 0U;
}
// ...existing code...
/* 修正文档小拼写 */
// #define MCAN_FILTER_ELEM_EXT_ID_SIZE (8U)                           /*!< Extended Filter Element Size in Bytes */
// “TXFIFO/QQueue” -> “TXFIFO/Queue”
// ...existing code...

结语

  • 该头文件提供了完整的 M_CAN 驱动“接口层”,与 soc 适配层共同覆盖从外设初始化、RAM 布局、滤波、收发到时间戳与中断管理的常见需求。上层应用可优先采用默认配置与简化 RAM 配置,快速跑通,再基于灵活配置做精细优化。

找到具有 1 个许可证类型的类似代码


weifengdq: 详细解读hpm_mcan_drv.c

GitHub Copilot: 下面是对当前 hpm_mcan_drv.c 的结构化解读,并给出若干明显问题与可选修复补丁。你当前选中的函数位于文件末段,已单独说明。

文件职责与结构

  • 常量/限制定义:包含各类元素上限(标准/扩展滤波条目、RXFIFO/RXBUF/TXBUF/TXEVT 元素数)、位时序范围(CAN2.0、CANFD Nominal、CANFD Data)、采样点范围等。
  • 位时序计算:
    • mcan_calc_bit_timing_from_baudrate:根据时钟/波特率/采样点推导分频、Seg1、Seg2、SJW。
    • mcan_set_bit_timing_from_baudrate:封装上面的计算并写入 NBTP/DBTP。
    • is_mcan_bit_timing_param_valid:对用户自定义时序参数做合法性校验。
  • RAM 默认布局:
    • mcan_get_default_ram_config:为简化配置结构 mcan_ram_config_t 预分配标准/扩展滤波、TXBUF、RXFIFO0/1、RXBUF、TXEVT 的大小与顺序,依据是否 CANFD 选不同尺寸。
    • mcan_get_default_ram_flexible_config:为灵活配置结构 mcan_ram_flexible_config_t 计算每段起始地址/大小。
  • RAM 寄存器配置:
    • mcan_config_ram:使用简化配置计算每段大小与元素数,写入 SIDFC/XIDFC、RXF0C/RXF1C、RXBC/RXESC、TXBC/TXESC、TXEFC,并检测超出 RAM。
    • mcan_config_ram_with_flexible_config:直接写入灵活配置提供的寄存器值,同时做元素数上限检查。
  • 过滤器配置:
    • k_default_std_id_filter/k_default_ext_id_filter:经典过滤默认接受全部,放入 RXFIFO0。
    • mcan_config_all_filters:写 GFC/XIDAM,并将标准/扩展列表逐条写入消息 RAM。
    • mcan_set_filter_element:按标准/扩展编码写入对应 FLSSA/FLESA 列表项。
  • 初始化/反初始化:
    • mcan_get_default_config:生成一套默认的波特率、过滤、RAM、时间戳/TSU、超时等配置。
    • mcan_init:开时钟、进 INIT/CCE、设置位时序(自动/手动)、模式、CANFD、NISO、TXP/EFBI/PXHD/WMM/DAR/ASM、TDC、TSU、内部时间戳、清空并配置 RAM、配置过滤器、中断、超时、退出 INIT。
    • mcan_deinit:进入 INIT,恢复关键寄存器默认值,关闭中断并清标志。
  • 发送/接收路径:
    • 地址与尺寸辅助:mcan_get_rxfifo0_base/1_base、mcan_get_rxbuf_elem_addr、mcan_get_txbuf_elem_addr、mcan_get_message_size_from_dlc、mcan_get_data_field_size。
    • 写入发送:mcan_write_txbuf、mcan_write_txfifo,非阻塞提交:mcan_transmit_via_txbuf_nonblocking/mcan_transmit_via_txfifo_nonblocking,请求并回填 FIFO:mcan_request_and_fill_txfifo,阻塞发送:mcan_transmit_blocking。
    • 接收读取:mcan_read_rxbuf、mcan_read_rxfifo,阻塞接收:mcan_receive_from_buf_blocking/mcan_receive_from_fifo_blocking。
    • 事件:mcan_read_tx_evt_fifo。
  • 时间戳/TSU:
    • mcan_set_tsu/mcan_set_internal_timestamp:配置 TSU 与内部时间戳。
    • mcan_read_32bit_tsu_timestamp/mcan_read_64bit_tsu_timestamp:读取 TSU 时间戳(64 位存在索引对齐和清位 workaround)。
    • mcan_get_timestamp_from_tx_event/mcan_get_timestamp_from_received_message:根据是否使用 TSU、是否捕获,返回 16/32/64 位时间戳。
  • 协议状态:
    • mcan_parse_protocol_status:解析 PSR,填充 protocol_status(异常、CANFD、BRS/ESI、BusOff/Warning/ErrorPassive、ACT 活动状态、TDCV、以及 LEC/DLEC)。
    • mcan_get_protocol_status:读取 ptr->PSR 并调用解析。

你选中的函数详解

  • 函数:mcan_get_protocol_status(MCAN_Type *ptr, mcan_protocol_status_t *protocol_status)
  • 行为:
    • 判空;若合法则读取 PSR 并调用 mcan_parse_protocol_status 填充 protocol_status。
    • 解析规则在 mcan_parse_protocol_status 中:
      • 若上一帧接收为 CAN FD 且 BRS 置位,使用 DLEC 作为 last_error_code;否则使用 LEC。
      • activity 取自 PSR.ACT,tdc_val 取自 PSR.TDCV,其他布尔状态按各位判断。
  • 返回:status_success 或参数非法。

潜在问题与建议

  1. memset 使用了错误的结构尺寸
  • mcan_get_default_ram_flexible_config 中清零用了 sizeof(mcan_ram_config_t),应为 sizeof(mcan_ram_flexible_config_t)。
  1. CANFD 灵活配置的 TXBUF 空间计算错误
  • 在 enable_canfd == true 分支,TXBUF 大小增量使用了 CAN 的默认尺寸与最大元素字节数,应改为 CANFD 对应值。
  1. 扩展滤波表空间增量错误
  • mcan_config_ram 中 XIDFC 后的 start_addr 增量错误地使用了 std_filter_elem_count,应该用 ext_filter_elem_count。
  1. RXBUF 元素数上限检查与常量不匹配
  • mcan_config_ram 对 RXBUF 用了 MCAN_RXFIFO_ELEM_CNT_MAX 检查,应改用 MCAN_RXBUF_ELEM_CNT_MAX。
  1. TXBUF 元素数上限检查常量不匹配
  • mcan_config_ram 中把 TXBUF 元素数与 MCAN_TXEVT_FIFO_ELEM_CNT_MAX 比较,应使用 MCAN_TXBUF_ELEM_CNT_MAX。
  1. RXFIFO 水位未使用配置值
  • mcan_config_ram 中 rxfifo_config.watermark 固定为 1U,应使用 config->rxfifos[i].watermark。
  1. deinit 中重复设置 XIDAM
  • 先写 0 再写 0x1FFFFFFF,多余;保留后者即可。

可选修复补丁

  • 修复以上问题,尽量小改动、向后兼容。
// ...existing code...
void mcan_get_default_ram_flexible_config(MCAN_Type *ptr, mcan_ram_flexible_config_t *ram_config, bool enable_canfd)
{
-    (void) memset(ram_config, 0, sizeof(mcan_ram_config_t));
+    (void) memset(ram_config, 0, sizeof(mcan_ram_flexible_config_t));
    uint32_t start_addr = mcan_get_ram_offset(ptr);
// ...existing code...
    } else {
        ram_config->enable_std_filter = true;
        // ...existing code...
        ram_config->txbuf_elem_config.data_field_size = MCAN_DATA_FIELD_SIZE_64BYTES;
-        start_addr += MCAN_TXBUF_SIZE_CAN_DEFAULT * MCAN_TXRX_ELEM_SIZE_CAN_MAX;
+        start_addr += MCAN_TXBUF_SIZE_CANFD_DEFAULT * MCAN_TXRX_ELEM_SIZE_CANFD_MAX;
        // ...existing code...
    }
// ...existing code...
}
// ...existing code...

hpm_stat_t mcan_config_ram(MCAN_Type *ptr, mcan_ram_config_t *config)
{
    hpm_stat_t status = status_invalid_argument;
    do {
        // ...existing code...
        if (config->enable_ext_filter) {
            if (config->ext_filter_elem_count > MCAN_EXT_FILTER_ELEM_CNT_MAX) {
                break;
            }
            mcan_filter_config_t filter_config = { .reg_val = 0 };
            filter_config.list_size = config->ext_filter_elem_count;
            filter_config.list_start_addr = start_addr;
            ptr->XIDFC = filter_config.reg_val;
-            start_addr += MCAN_FILTER_ELEM_EXT_ID_SIZE * (uint32_t) config->std_filter_elem_count;
+            start_addr += MCAN_FILTER_ELEM_EXT_ID_SIZE * (uint32_t) config->ext_filter_elem_count;
        } else {
            ptr->XIDFC = MCAN_RAM_ADDR_INVALID;
        }
        // ...existing code...
                mcan_rxfifo_config_t rxfifo_config = { .reg_val = 0 };
                rxfifo_config.start_addr = start_addr;
-                rxfifo_config.watermark = 1U;
+                rxfifo_config.watermark = config->rxfifos[i].watermark;
                rxfifo_config.operation_mode = config->rxfifos[i].operation_mode;
                rxfifo_config.fifo_size = elem_count;
                mcan_config_rxfifo(ptr, i, rxfifo_config.reg_val);
        // ...existing code...
        if (config->enable_rxbuf) {
            elem_bytes = mcan_get_data_field_size(config->rxbuf_data_field_size) + MCAN_MESSAGE_HEADER_SIZE_IN_BYTES;
            elem_count = config->rxbuf_elem_count;
-            if (elem_count > MCAN_RXFIFO_ELEM_CNT_MAX) {
+            if (elem_count > MCAN_RXBUF_ELEM_CNT_MAX) {
                break;
            }
            ptr->RXBC = start_addr;
            rx_fifo_buf_elem_config.buf_data_field_size = config->rxbuf_data_field_size;
            // ...existing code...
        }
        // ...existing code...
        if (config->enable_txbuf) {
            // ...existing code...
            elem_count = config->txbuf_fifo_or_queue_elem_count + config->txbuf_dedicated_txbuf_elem_count;
-            if (elem_count > MCAN_TXEVT_FIFO_ELEM_CNT_MAX) {
+            if (elem_count > MCAN_TXBUF_ELEM_CNT_MAX) {
                break;
            }
            // ...existing code...
            ptr->TXESC = (uint32_t) config->txbuf_data_field_size;
        } else {
-            ptr->TXESC = MCAN_RAM_ADDR_INVALID;
+            ptr->TXESC = 0U;
        }
        // ...existing code...
    } while (false);

    return status;
}
// ...existing code...

hpm_stat_t mcan_config_ram_with_flexible_config(MCAN_Type *ptr, mcan_ram_flexible_config_t *config)
{
    hpm_stat_t status = status_invalid_argument;
    do {
        // ...existing code...
        if (config->enable_ext_filter) {
-            if (config->std_filter_config.list_size > MCAN_EXT_FILTER_ELEM_CNT_MAX) {
+            if (config->ext_filter_config.list_size > MCAN_EXT_FILTER_ELEM_CNT_MAX) {
                break;
            }
            ptr->XIDFC = config->ext_filter_config.reg_val;
        } else {
            ptr->XIDFC = MCAN_RAM_ADDR_INVALID;
        }
        // ...existing code...
    } while (false);

    return status;
}
// ...existing code...

void mcan_deinit(MCAN_Type *ptr)
{
    if (ptr != NULL) {
        // ...existing code...
        ptr->GFC = 0UL;
        ptr->SIDFC = 0UL;
-        ptr->XIDAM = 0UL;
-        ptr->XIDAM = 0x1FFFFFFFUL;
+        ptr->XIDAM = 0x1FFFFFFFUL;
        ptr->RXBC = 0UL;
        // ...existing code...
    }
}
// ...existing code...

使用提示

  • 初始化时优先调用 mcan_get_default_config 并按需改动,再调用 mcan_init。
  • 如需自定义 RAM 布局,建议先用 mcan_get_default_ram_config/…flexible_config 获得基线配置,修改数量/尺寸后调用 mcan_config_ram/…with_flexible_config。
  • 解析协议状态推荐调用 mcan_get_protocol_status,避免直接读 PSR 并手动解码。

并发与健壮性注意

  • RAM 与 TBSEL 等寄存器的读改写建议在临界区,避免中断竞争。
  • 写 TX/RX 元素数据时确保 DLC 与 data_field_size 匹配,避免越界。
  • 默认超时上限较大,阻塞 API 在总线异常时可能等待较久,可按场景调小重试计数。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions