Initial commit: CCU621_M firmware project with BLE debug link support.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-08 17:28:36 +08:00
commit 9ceb218f80
1597 changed files with 724159 additions and 0 deletions
+521
View File
@@ -0,0 +1,521 @@
/*!
\file can.c
\brief CAN统一接口实现(整合 CAN0/CAN1/CAN2
*/
#include "gd32h7xx.h"
#include "can.h"
#include <string.h>
/* 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;
}