/** * @file CANFD.cpp * @brief CANFD 通信外设初始化、收发与诊断实现。 */ #include "main.h" #include "device/canfd.h" #include "sys/config.h" namespace { /** @brief 内部接收帧缓存结构。 */ struct CanfdRxFrame { uint32_t id = 0U; uint8_t data[64] = {0}; uint8_t length = 0U; uint8_t valid = 0U; }; /** @brief 内部发送帧缓存结构。 */ struct CanfdTxBuffer { can_trasnmit_message_struct tx_message{}; uint8_t payload[64] = {0}; uint8_t valid = 0U; }; canfd_diag_t g_can_diag{}; CanfdRxFrame g_rx_frame{}; CanfdTxBuffer g_tx_buffer{}; /** * @brief 初始化 CAN 外设所需 GPIO 引脚。 */ inline void can_gpio_init() { rcu_periph_clock_enable(CONFIG_CAN_INSTANCE); rcu_periph_clock_enable(RCU_GPIOB); rcu_periph_clock_enable(RCU_AF); gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_8); gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); gpio_pin_remap_config(GPIO_CAN0_PARTIAL_REMAP, ENABLE); } /** * @brief 配置 CAN 接收过滤器为接受所有报文。 */ inline void can_filter_accept_all() { can_filter_parameter_struct can_filter{}; can_filter.filter_number = 0U; can_filter.filter_mode = CAN_FILTERMODE_MASK; can_filter.filter_bits = CAN_FILTERBITS_32BIT; can_filter.filter_list_high = 0x0000U; can_filter.filter_list_low = 0x0000U; can_filter.filter_mask_high = 0x0000U; can_filter.filter_mask_low = 0x0000U; can_filter.filter_fifo_number = CAN_FIFO0; can_filter.filter_enable = ENABLE; can_filter_init(CONFIG_CAN_INSTANCE, &can_filter); } /** * @brief 配置 CAN 标称位定时参数。 */ inline void can_nominal_init() { can_parameter_struct can_parameter{}; can_struct_para_init(CAN_INIT_STRUCT, &can_parameter); can_parameter.time_triggered = DISABLE; can_parameter.auto_bus_off_recovery = ENABLE; can_parameter.auto_wake_up = DISABLE; can_parameter.auto_retrans = ENABLE; can_parameter.rec_fifo_overwrite = DISABLE; can_parameter.trans_fifo_order = DISABLE; can_parameter.working_mode = CAN_NORMAL_MODE; can_parameter.resync_jump_width = CAN_BT_SJW_1TQ; can_parameter.time_segment_1 = CAN_BT_BS1_8TQ; can_parameter.time_segment_2 = CAN_BT_BS2_3TQ; can_parameter.prescaler = CONFIG_CAN_NOMINAL_BAUD_PRESCALER; can_init(CONFIG_CAN_INSTANCE, &can_parameter); } /** * @brief 配置 CANFD 可变速率模式参数。 * @note 在 GD32E508 上启用 ISO 模式;非 GD32E508 平台仅启用 FD 功能。 */ inline void can_fd_mode_init() { #ifdef GD32E508 can_fdframe_struct can_fd_parameter{}; can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter); can_fd_parameter.fd_frame = ENABLE; can_fd_parameter.excp_event_detect = ENABLE; can_fd_parameter.delay_compensation = DISABLE; can_fd_parameter.iso_bosch = CAN_FDMOD_ISO; can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE; can_fd_parameter.data_prescaler = 1U; can_fd_parameter.data_resync_jump_width = 0U; can_fd_parameter.data_time_segment_1 = 2U; can_fd_parameter.data_time_segment_2 = 1U; can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter); #else can_fd_function_enable(CONFIG_CAN_INSTANCE); #endif } /** * @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。 * @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。 */ inline void canfd_build_status_frame(const canfd_status_payload_t *status) { const int16_t temp_x100 = static_cast(status->temperature * 100.0f); const uint16_t vdda_x1000 = static_cast(status->vdda * 1000.0f); can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message); g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID; g_tx_buffer.tx_message.tx_efid = 0U; g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD; g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA; g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN; #ifdef GD32E508 g_tx_buffer.tx_message.fd_flag = ENABLE; g_tx_buffer.tx_message.fd_brs = CAN_BRS_DISABLE; g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT; #endif auto &p = g_tx_buffer.payload; p[0] = static_cast(status->temp_raw & 0xFFU); p[1] = static_cast((status->temp_raw >> 8) & 0xFFU); p[2] = static_cast(status->vref_raw & 0xFFU); p[3] = static_cast((status->vref_raw >> 8) & 0xFFU); p[4] = static_cast(temp_x100 & 0xFF); p[5] = static_cast((temp_x100 >> 8) & 0xFF); p[6] = static_cast(vdda_x1000 & 0xFFU); p[7] = static_cast((vdda_x1000 >> 8) & 0xFFU); p[8] = CONFIG_CAN_STATUS_TAG0; p[9] = CONFIG_CAN_STATUS_TAG1; p[10] = CONFIG_CAN_STATUS_TAG2; p[11] = CONFIG_CAN_STATUS_TAG3; for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) { g_tx_buffer.tx_message.tx_data[i] = p[i]; } g_tx_buffer.valid = 1U; } } // namespace /** * @brief 初始化 CANFD 外设、GPIO、波特率参数与接收过滤器。 */ void canfd_init(void) { can_gpio_init(); can_deinit(CONFIG_CAN_INSTANCE); can_nominal_init(); can_fd_mode_init(); can_filter_accept_all(); g_rx_frame.valid = 0U; g_tx_buffer.valid = 0U; } /** * @brief 轮询 CANFD 接收 FIFO,并缓存最近收到的一帧数据。 * @note 若当前没有待接收报文,直接返回。 */ void canfd_poll(void) { can_receive_message_struct rx_message{}; if(can_receive_message_length_get(CONFIG_CAN_INSTANCE, CAN_FIFO0) == 0U) { return; } can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message); can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message); g_can_diag.rx_count++; g_can_diag.last_payload_length = rx_message.rx_dlen; g_can_diag.last_rx_fd_flag = rx_message.fd_flag; g_can_diag.last_rx_brs = rx_message.fd_brs; g_can_diag.last_rx_esi = rx_message.fd_esi; g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid; g_rx_frame.id = g_can_diag.last_rx_id; g_rx_frame.length = rx_message.rx_dlen; for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) { g_rx_frame.data[i] = rx_message.rx_data[i]; } g_rx_frame.valid = 1U; } /** * @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。 * @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。 */ void canfd_prepare_status(const canfd_status_payload_t *status) { if(status == nullptr) { g_tx_buffer.valid = 0U; return; } canfd_build_status_frame(status); } /** * @brief 发送已准备好的 CANFD 状态报文。 * @note 若当前没有有效待发送报文,直接返回。 */ void canfd_send_prepared(void) { if(g_tx_buffer.valid == 0U) { return; } const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message); if(mailbox != CAN_NOMAILBOX) { g_can_diag.tx_count++; g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen; g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid; } else { g_can_diag.error_count++; } } /** * @brief 查询是否存在尚未消费的接收报文。 * @retval 非 0 存在待消费报文,0 无报文。 */ uint8_t canfd_has_rx_frame(void) { return g_rx_frame.valid; } /** * @brief 读取并消费最近一次缓存的接收报文。 * @param[out] id 输出报文标识符的指针;允许为空。 * @param[out] data 输出报文数据缓存区指针;允许为空。 * @param[out] length 输出报文数据长度的指针;允许为空。 * @retval 1 成功消费一帧报文,0 当前无有效缓存报文。 */ uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length) { if(g_rx_frame.valid == 0U) { return 0U; } if(id != nullptr) { *id = g_rx_frame.id; } if(length != nullptr) { *length = g_rx_frame.length; } if(data != nullptr) { for(uint8_t i = 0; i < g_rx_frame.length; ++i) { data[i] = g_rx_frame.data[i]; } } g_rx_frame.valid = 0U; return 1U; } /** * @brief 获取 CANFD 诊断统计信息只读指针。 * @retval 指向内部诊断统计结构体的只读指针。 */ const canfd_diag_t *canfd_get_diag(void) { return &g_can_diag; }