/*! \file can.c \brief CAN统一接口实现(整合 CAN0/CAN1/CAN2) */ #include "gd32h7xx.h" #include "can.h" #include /* RX strategy: TX on MB0, RX on MB1 (validated on all 3 CAN ports). */ /* 支持的 CAN 端口数量(CAN0/CAN1/CAN2) */ #define CAN_PORT_COUNT 3U /* 每个端口的软件接收环形缓存深度(单位:帧) */ /* 单帧最大数据长度(Classic CAN,8 字节) */ #define CAN_DATA_MAX_LEN 8U /** * @brief 单帧接收缓存条目 */ typedef struct { uint32_t id; /* 帧 ID(标准/扩展均可) */ uint8_t data[CAN_DATA_MAX_LEN]; /* 帧数据 */ uint8_t dlc; /* 数据长度 */ uint8_t ide; /* 帧类型:0 标准帧,1 扩展帧 */ uint8_t rtr; /* 帧类型:0 数据帧,1 远程帧 */ uint8_t used; /* 条目是否有效:0 无效,1 有效 */ } can_rx_item_t; /** * @brief 单端口运行态(软件接收环形缓存) */ typedef struct { can_rx_item_t rx_buffer[CAN_RX_BUFFER_SIZE]; /* 缓存数组 */ volatile uint8_t head; /* 写指针(中断侧推进) */ volatile uint8_t tail; /* 读指针(任务侧推进) */ } can_port_state_t; /** * @brief 端口硬件静态配置表项 */ typedef struct { uint32_t canx; /* CAN 外设基址(CAN0/CAN1/CAN2) */ can_idx_enum idx; /* 时钟源索引(IDX_CANx) */ rcu_periph_enum can_clock; /* CAN 外设时钟 */ rcu_periph_enum gpio_clock; /* GPIO 端口时钟 */ uint32_t gpio_port; /* GPIO 端口 */ uint32_t gpio_af; /* 复用功能编号 */ uint32_t tx_pin; /* TX 引脚 */ uint32_t rx_pin; /* RX 引脚 */ IRQn_Type irqn; /* 对应中断号 */ } can_port_cfg_t; /* 各端口运行态(接收缓存和读写索引) */ static can_port_state_t g_can_state[CAN_PORT_COUNT]; static uint8_t g_can_initialized = 0U; /* 各端口硬件配置映射表 */ static const can_port_cfg_t g_can_cfg[CAN_PORT_COUNT] = { { CAN0, IDX_CAN0, RCU_CAN0, RCU_GPIOB, GPIOB, GPIO_AF_9, GPIO_PIN_9, GPIO_PIN_8, CAN0_Message_IRQn }, { CAN1, IDX_CAN1, RCU_CAN1, RCU_GPIOB, GPIOB, GPIO_AF_9, GPIO_PIN_6, GPIO_PIN_5, CAN1_Message_IRQn }, { CAN2, IDX_CAN2, RCU_CAN2, RCU_GPIOF, GPIOF, GPIO_AF_2, GPIO_PIN_7, GPIO_PIN_6, CAN2_Message_IRQn } }; /** * @brief 获取指定端口的配置项 * @param port 端口号(0/1/2) * @return 配置项指针,非法端口返回 NULL */ static const can_port_cfg_t *can_get_cfg(uint8_t port) { if (port >= CAN_PORT_COUNT) { return NULL; } return &g_can_cfg[port]; } /** * @brief 初始化指定端口的软件接收缓存 * @param port 端口号(0/1/2) */ static void can_buffer_init(uint8_t port) { memset(&g_can_state[port], 0, sizeof(g_can_state[port])); } /** * @brief 配置指定端口 CAN GPIO 和时钟 * @param port 端口号(0/1/2) */ static void can_gpio_config(uint8_t port) { const can_port_cfg_t *cfg = can_get_cfg(port); if (cfg == NULL) { return; } rcu_can_clock_config(cfg->idx, RCU_CANSRC_APB2); rcu_periph_clock_enable(cfg->can_clock); rcu_periph_clock_enable(cfg->gpio_clock); gpio_af_set(cfg->gpio_port, cfg->gpio_af, cfg->rx_pin); gpio_mode_set(cfg->gpio_port, GPIO_MODE_AF, GPIO_PUPD_NONE, cfg->rx_pin); gpio_output_options_set(cfg->gpio_port, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, cfg->rx_pin); gpio_af_set(cfg->gpio_port, cfg->gpio_af, cfg->tx_pin); gpio_mode_set(cfg->gpio_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, cfg->tx_pin); gpio_output_options_set(cfg->gpio_port, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, cfg->tx_pin); } /** * @brief 配置指定端口 CAN 协议参数和中断 * @param port 端口号(0/1/2) * @note CAN0/CAN1: 250kbps, CAN2: 125kbps,采样点约 87.5% */ static void can_parameter_config(uint8_t port) { const can_port_cfg_t *cfg = can_get_cfg(port); can_parameter_struct can_parameter; uint32_t prescaler; if (cfg == NULL) { return; } /* 根据端口选择预分频器:CAN2使用125kbps,其他使用250kbps */ if (port == 2) { /* CAN2 */ prescaler = 150U; /* 125 kbps */ } else { prescaler = 75U; /* 250 kbps */ } can_deinit(cfg->canx); can_struct_para_init(CAN_INIT_STRUCT, &can_parameter); can_parameter.internal_counter_source = CAN_TIMER_SOURCE_BIT_CLOCK; can_parameter.self_reception = DISABLE; can_parameter.mb_tx_order = CAN_TX_HIGH_PRIORITY_MB_FIRST; can_parameter.mb_tx_abort_enable = ENABLE; can_parameter.local_priority_enable = DISABLE; can_parameter.mb_rx_ide_rtr_type = CAN_IDE_RTR_FILTERED; can_parameter.mb_remote_frame = CAN_STORE_REMOTE_REQUEST_FRAME; can_parameter.rx_private_filter_queue_enable = DISABLE; can_parameter.edge_filter_enable = DISABLE; can_parameter.protocol_exception_enable = DISABLE; can_parameter.rx_filter_order = CAN_RX_FILTER_ORDER_MAILBOX_FIRST; can_parameter.memory_size = CAN_MEMSIZE_32_UNIT; /* Follow official examples mailbox filter setting. */ can_parameter.mb_public_filter = 0U; can_parameter.resync_jump_width = 1U; can_parameter.prop_time_segment = 2U; can_parameter.time_segment_1 = 11U; can_parameter.time_segment_2 = 2U; can_parameter.prescaler = prescaler; can_init(cfg->canx, &can_parameter); // nvic_irq_enable(cfg->irqn, 0U, 0U); /* 已迁移到 sys_drv_init.c:nvic_configuration() */ /* Mailbox path: TX on MB0, RX on MB1. */ can_interrupt_enable(cfg->canx, CAN_INT_MB0); /* TX complete/abort events */ can_interrupt_enable(cfg->canx, CAN_INT_MB1); /* Enable error-related interrupts to avoid "silent dead" and help recovery. */ can_interrupt_enable(cfg->canx, CAN_INT_ERR_SUMMARY); can_interrupt_enable(cfg->canx, CAN_INT_BUSOFF); can_interrupt_enable(cfg->canx, CAN_INT_BUSOFF_RECOVERY); can_interrupt_enable(cfg->canx, CAN_INT_RX_WARNING); can_interrupt_enable(cfg->canx, CAN_INT_TX_WARNING); /* Real bus communication mode. */ can_operation_mode_enter(cfg->canx, CAN_NORMAL_MODE); } /** * @brief 配置指定端口接收过滤器(当前放行标准帧和扩展帧) * @param port 端口号(0/1/2) */ static void can_filter_config(uint8_t port) { if (can_get_cfg(port) == NULL) { return; } /* Mailbox RX uses mb_public_filter configured in can_parameter_config(). */ } /** * @brief 指定端口发送一帧 CAN 数据 * @param port 端口号(0/1/2) * @param id 帧 ID * @param data 数据指针 * @param dlc 数据长度(1~8) * @param ide 帧类型:0 标准帧,1 扩展帧 * @param rtr 帧类型:0 数据帧,1 远程帧 * @return 0 成功,1 参数错误 */ static uint8_t can_port_send_data(uint8_t port, uint32_t id, const uint8_t *data, uint8_t dlc, uint8_t ide, uint8_t rtr) { can_mailbox_descriptor_struct tx_mailbox; /* 发送邮箱描述符 */ uint32_t tx_buffer[CAN_DATA_MAX_LEN] = {0}; /* 局部发送缓冲(驱动要求 32bit 对齐) */ const can_port_cfg_t *cfg = can_get_cfg(port); /* 端口配置 */ if ((cfg == NULL) || (data == NULL) || (dlc == 0U) || (dlc > CAN_DATA_MAX_LEN)) { return 1U; } can_struct_para_init(CAN_MDSC_STRUCT, &tx_mailbox); tx_mailbox.id = id; tx_mailbox.dlc = dlc; tx_mailbox.ide = ide; tx_mailbox.rtr = rtr; tx_mailbox.srr = (ide == 1U) ? 1U : 0U; tx_mailbox.code = CAN_MB_TX_STATUS_DATA; tx_mailbox.data_bytes = dlc; memcpy(tx_buffer, data, dlc); tx_mailbox.data = tx_buffer; can_mailbox_config(cfg->canx, 0U, &tx_mailbox); return 0U; } /** * @brief 从指定端口的软件接收缓存读取一帧数据 * @param port 端口号(0/1/2) * @param id 输出:帧 ID * @param data 输出:帧数据 * @param dlc 输出:数据长度 * @param ide 输出:帧类型(标准/扩展) * @param rtr 输出:帧类型(数据/远程) * @return 0 成功,1 无数据,2 参数错误 */ static uint8_t can_port_receive_data(uint8_t port, uint32_t *id, uint8_t *data, uint8_t *dlc, uint8_t *ide, uint8_t *rtr) { can_port_state_t *state; /* 指向端口运行态 */ if ((port >= CAN_PORT_COUNT) || (id == NULL) || (data == NULL) || (dlc == NULL) || (ide == NULL) || (rtr == NULL)) { return 2U; } state = &g_can_state[port]; if (state->rx_buffer[state->tail].used == 0U) { return 1U; } *id = state->rx_buffer[state->tail].id; *dlc = state->rx_buffer[state->tail].dlc; *ide = state->rx_buffer[state->tail].ide; *rtr = state->rx_buffer[state->tail].rtr; memcpy(data, state->rx_buffer[state->tail].data, *dlc); state->rx_buffer[state->tail].used = 0U; state->tail = (uint8_t)((state->tail + 1U) % CAN_RX_BUFFER_SIZE); return 0U; } /** * @brief 统一中断处理逻辑(按端口复用) * @param port 端口号(0/1/2) * @note 在中断上下文执行:从硬件邮箱读帧并入软件环形缓存 */ static void can_irq_handler_common(uint8_t port) { can_port_state_t *state; /* 端口运行态 */ const can_port_cfg_t *cfg = can_get_cfg(port); /* 端口配置 */ uint8_t next_head; /* 计算后的下一写指针 */ if (cfg == NULL) { return; } { can_mailbox_descriptor_struct rx_mb; uint32_t rx_data_u32[2] = {0U, 0U}; /* 8 bytes aligned buffer */ if (RESET == can_interrupt_flag_get(cfg->canx, CAN_INT_FLAG_MB1)) { return; } can_struct_para_init(CAN_MDSC_STRUCT, &rx_mb); rx_mb.data = rx_data_u32; rx_mb.data_bytes = 8U; if (SUCCESS != can_mailbox_receive_data_read(cfg->canx, 1U, &rx_mb)) { can_interrupt_flag_clear(cfg->canx, CAN_INT_FLAG_MB1); return; } /* Only accept: extended + data frame */ if ((rx_mb.ide == 0U) || (rx_mb.rtr != 0U)) { can_interrupt_flag_clear(cfg->canx, CAN_INT_FLAG_MB1); return; } state = &g_can_state[port]; next_head = (uint8_t)((state->head + 1U) % CAN_RX_BUFFER_SIZE); if ((next_head != state->tail) && (state->rx_buffer[state->head].used == 0U)) { uint8_t dlc = (uint8_t)rx_mb.dlc; if (dlc > CAN_DATA_MAX_LEN) { dlc = CAN_DATA_MAX_LEN; } state->rx_buffer[state->head].id = rx_mb.id; state->rx_buffer[state->head].dlc = dlc; state->rx_buffer[state->head].ide = (uint8_t)rx_mb.ide; state->rx_buffer[state->head].rtr = (uint8_t)rx_mb.rtr; memcpy(state->rx_buffer[state->head].data, (uint8_t *)rx_data_u32, dlc); state->rx_buffer[state->head].used = 1U; state->head = next_head; } can_interrupt_flag_clear(cfg->canx, CAN_INT_FLAG_MB1); } } /** * @brief CAN 中断统一入口(消息/错误/BusOff/告警等均可调用) * @param can_port 端口号(0/1/2) * * 说明: * - GD32H7 的 CAN 中断线有 Message/Busoff/Error/TEC/REC 多条, * 不同中断源触发哪一条与库/配置有关。这里统一收敛到一个入口,保证不中断“漏接”。 */ void v_can_irq_handler(uint8_t can_port) { const can_port_cfg_t *cfg = can_get_cfg(can_port); if (cfg == NULL) { return; } /* Message path dispatch (MB1 RX mailbox). */ if (SET == can_interrupt_flag_get(cfg->canx, CAN_INT_FLAG_MB1)) { can_irq_handler_common(can_port); } /* MB0 indicates TX mailbox event, clear it to avoid stale status. */ if (SET == can_interrupt_flag_get(cfg->canx, CAN_INT_FLAG_MB0)) { can_interrupt_flag_clear(cfg->canx, CAN_INT_FLAG_MB0); } /* 清理可能导致反复进入的错误/BusOff 标志(不依赖业务侧处理) */ if (SET == can_interrupt_flag_get(cfg->canx, CAN_INT_FLAG_BUSOFF)) { can_interrupt_flag_clear(cfg->canx, CAN_INT_FLAG_BUSOFF); } if (SET == can_interrupt_flag_get(cfg->canx, CAN_INT_FLAG_BUSOFF_RECOVERY)) { can_interrupt_flag_clear(cfg->canx, CAN_INT_FLAG_BUSOFF_RECOVERY); } if (SET == can_interrupt_flag_get(cfg->canx, CAN_INT_FLAG_ERR_SUMMARY)) { can_interrupt_flag_clear(cfg->canx, CAN_INT_FLAG_ERR_SUMMARY); } if (SET == can_interrupt_flag_get(cfg->canx, CAN_INT_FLAG_RX_WARNING)) { can_interrupt_flag_clear(cfg->canx, CAN_INT_FLAG_RX_WARNING); } if (SET == can_interrupt_flag_get(cfg->canx, CAN_INT_FLAG_TX_WARNING)) { can_interrupt_flag_clear(cfg->canx, CAN_INT_FLAG_TX_WARNING); } } /** * @brief 初始化全部 CAN 端口(GPIO/参数/过滤器/缓存) */ void v_can_interface_init(void) { uint8_t port; /* 端口遍历索引 */ for (port = 0U; port < CAN_PORT_COUNT; port++) { can_mailbox_descriptor_struct rx_mb; uint32_t dummy_data[2] = {0U, 0U}; can_gpio_config(port); can_parameter_config(port); can_filter_config(port); can_buffer_init(port); /* Configure MB1 as RX mailbox. */ can_struct_para_init(CAN_MDSC_STRUCT, &rx_mb); rx_mb.rtr = 0U; rx_mb.ide = 1U; /* expect extended */ rx_mb.code = CAN_MB_RX_STATUS_EMPTY; /* No ID filtering in mailbox setup; extension/data filtering is done in software path. */ rx_mb.id = 0U; rx_mb.data = dummy_data; rx_mb.data_bytes = 8U; can_mailbox_config(g_can_cfg[port].canx, 1U, &rx_mb); } g_can_initialized = 1U; } uint8_t u8_can_is_initialized(void) { return g_can_initialized; } /** * @brief 统一 CAN 发送接口(兼容旧接口) * @param can_port 端口号(0/1/2) * @param id 帧 ID * @param data 数据指针 * @param dlc 数据长度(1~8) * @param ide 0 标准帧,1 扩展帧 * @param rtr 0 数据帧,1 远程帧 * @return 0 成功,1 参数错误 */ uint8_t can_send_data(uint8_t can_port, uint32_t id, uint8_t *data, uint8_t dlc, uint8_t ide, uint8_t rtr) { return can_port_send_data(can_port, id, data, dlc, ide, rtr); } /** * @brief 统一 CAN 接收接口(兼容旧接口) * @param can_port 端口号(0/1/2) * @param id 输出:帧 ID * @param data 输出:帧数据 * @param dlc 输出:数据长度 * @param ide 输出:帧类型(标准/扩展) * @param rtr 输出:帧类型(数据/远程) * @return 0 成功,1 无数据,2 参数错误 */ uint8_t can_receive_data(uint8_t can_port, uint32_t *id, uint8_t *data, uint8_t *dlc, uint8_t *ide, uint8_t *rtr) { return can_port_receive_data(can_port, id, data, dlc, ide, rtr); } /** * @brief 对外统一发送接口(与参考工程接口风格一致) * @param can_id 端口枚举 * @param can_data 发送数据结构 */ void v_can_tx(E_CAN_ID can_id, const CAN_DATA *can_data) { if ((can_data == NULL) || (can_data->Len == 0U) || (can_data->Len > CAN_DATA_MAX_LEN)) { return; } (void)can_send_data((uint8_t)can_id, can_data->ID, (uint8_t *)can_data->Data, can_data->Len, 1U, 0U); } /** * @brief 对外统一接收接口(仿照参考工程:一次取出当前软件缓存内全部CAN报文) * @param can_id 端口枚举 * @param can_data 输出:接收数据结构体数组 * @return 返回本次拷出的报文数量;0 表示无数据或参数错误 */ uint8_t u8_can_rx(E_CAN_ID can_id, CAN_DATA *can_data) { can_port_state_t *state; const can_port_cfg_t *cfg; uint8_t count = 0U; if ((can_id >= E_CAN_0 + CAN_PORT_COUNT) || (can_data == NULL)) { return 0U; } cfg = can_get_cfg((uint8_t)can_id); if (cfg == NULL) { return 0U; } state = &g_can_state[(uint8_t)can_id]; NVIC_DisableIRQ(cfg->irqn); while ((count < CAN_RX_BUFFER_SIZE) && (state->rx_buffer[state->tail].used != 0U)) { can_data[count].ID = state->rx_buffer[state->tail].id; can_data[count].Len = state->rx_buffer[state->tail].dlc; memcpy(can_data[count].Data, state->rx_buffer[state->tail].data, can_data[count].Len); state->rx_buffer[state->tail].used = 0U; state->tail = (uint8_t)((state->tail + 1U) % CAN_RX_BUFFER_SIZE); count++; } if (state->head == state->tail) { state->head = 0U; state->tail = 0U; } NVIC_EnableIRQ(cfg->irqn); return count; } /** * @brief 打包扩展帧 ID(协议字段组装) * @param prior 优先级字段 * @param cmd 命令字段 * @param desAddr 目的地址字段 * @param oriAddr 源地址字段 * @return 29 位扩展帧 ID */ uint32_t u32_get_can_ID(uint8_t prior, uint8_t cmd, uint8_t desAddr, uint8_t oriAddr) { return ((uint32_t)prior << 26) | ((uint32_t)cmd << 16) | ((uint32_t)desAddr << 8) | (uint32_t)oriAddr; } /** * @brief 解析扩展帧 ID(协议字段拆解) * @param u32_id 待解析 ID * @param prior 输出:优先级(可为 NULL) * @param cmd 输出:命令(可为 NULL) * @param desAddr 输出:目的地址(可为 NULL) * @param oriAddr 输出:源地址(可为 NULL) * @return 1 成功,0 参数 ID 非法 */ uint8_t u8_can_ID_unpack(uint32_t u32_id, uint8_t *prior, uint8_t *cmd, uint8_t *desAddr, uint8_t *oriAddr) { if ((u32_id == 0U) || (u32_id > 0x1FFFFFFFU)) { return 0U; } if (prior != NULL) { *prior = (uint8_t)((u32_id >> 26) & 0xFFU); } if (cmd != NULL) { *cmd = (uint8_t)((u32_id >> 16) & 0xFFU); } if (desAddr != NULL) { *desAddr = (uint8_t)((u32_id >> 8) & 0xFFU); } if (oriAddr != NULL) { *oriAddr = (uint8_t)(u32_id & 0xFFU); } return 1U; }