feat(can): 实现CANFD协议栈及DMA优化
- 添加CANFD DMA接收功能,使用32位宽数据传输提升效率 - 实现CANFD收发队列管理,支持异步消息处理防止丢帧 - 重构CAN中断处理机制,采用FIFO0中断配合DMA完成数据接收 - 添加CANFD DLC长度转换表,规范化数据长度处理 - 实现临界区保护机制,确保多线程环境下的数据安全 - 优化发送队列机制,使用邮箱空闲中断进行消息调度 - 添加DMA错误处理,增强系统稳定性 - 移除原有8字节限制,支持CANFD最大64字节数据传输
This commit is contained in:
+313
-163
@@ -37,26 +37,38 @@ extern u32 motor_runtime_50us; // 电机驱动计算轮次(用于静默模式
|
|||||||
|
|
||||||
// 添加 DMA 相关变量
|
// 添加 DMA 相关变量
|
||||||
dma_parameter_struct dma_rx_parameter;
|
dma_parameter_struct dma_rx_parameter;
|
||||||
dma_parameter_struct dma_tx_parameter;
|
|
||||||
uint8_t can_rx_buffer[64]; // CAN 接收缓冲区(支持CAN FD最大64字节)
|
uint8_t can_rx_buffer[64]; // CAN 接收缓冲区(支持CAN FD最大64字节)
|
||||||
uint8_t can_tx_buffer[64]; // CAN 发送缓冲区(支持CAN FD最大64字节)
|
static uint32_t can_rx_dma_words[16]; // CAN FD接收DMA按32位搬运,最多64字节
|
||||||
|
static volatile bool can_rx_dma_busy = false;
|
||||||
|
static can_receive_message_struct can_rx_dma_message;
|
||||||
|
|
||||||
|
// CAN接收/发送队列结构
|
||||||
|
#define CAN_RX_QUEUE_SIZE 32
|
||||||
|
#define CAN_TX_QUEUE_SIZE 32
|
||||||
|
|
||||||
// CAN发送队列结构
|
|
||||||
#define CAN_TX_QUEUE_SIZE 8
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u32 id;
|
u32 id;
|
||||||
u8 length;
|
u8 length;
|
||||||
u8 data[64];
|
u8 data[64];
|
||||||
|
} can_rx_queue_item_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
can_trasnmit_message_struct message;
|
||||||
u8 valid;
|
u8 valid;
|
||||||
} can_tx_queue_item_t;
|
} can_tx_queue_item_t;
|
||||||
|
|
||||||
|
static can_rx_queue_item_t rx_queue[CAN_RX_QUEUE_SIZE];
|
||||||
|
static volatile u8 rx_queue_head = 0;
|
||||||
|
static volatile u8 rx_queue_tail = 0;
|
||||||
|
static volatile u8 rx_queue_count = 0;
|
||||||
|
|
||||||
static can_tx_queue_item_t tx_queue[CAN_TX_QUEUE_SIZE];
|
static can_tx_queue_item_t tx_queue[CAN_TX_QUEUE_SIZE];
|
||||||
static volatile u8 tx_queue_head = 0;
|
static volatile u8 tx_queue_head = 0;
|
||||||
static volatile u8 tx_queue_tail = 0;
|
static volatile u8 tx_queue_tail = 0;
|
||||||
static volatile u8 tx_queue_count = 0;
|
static volatile u8 tx_queue_count = 0;
|
||||||
|
|
||||||
// 发送队列使用的独立消息结构体
|
// CAN FD DLC 9~15对应的非线性payload长度表
|
||||||
static can_trasnmit_message_struct queue_transmit_message;
|
static constexpr u8 CANFD_DLC_LENGTH_TABLE[7] = {12, 16, 20, 24, 32, 48, 64};
|
||||||
|
|
||||||
CAN_transmiter CAN0_transmiter; // CAN 初始化
|
CAN_transmiter CAN0_transmiter; // CAN 初始化
|
||||||
|
|
||||||
@@ -288,36 +300,21 @@ void can_dma_init(void) {
|
|||||||
dma_struct_para_init(&dma_rx_parameter);
|
dma_struct_para_init(&dma_rx_parameter);
|
||||||
dma_rx_parameter.periph_addr =
|
dma_rx_parameter.periph_addr =
|
||||||
(uint32_t)(&CAN_RFIFOMDATA0(CAN0, CAN_FIFO0)); // CAN 接收数据寄存器地址
|
(uint32_t)(&CAN_RFIFOMDATA0(CAN0, CAN_FIFO0)); // CAN 接收数据寄存器地址
|
||||||
dma_rx_parameter.memory_addr = (uint32_t)can_rx_buffer; // 接收缓冲区地址
|
dma_rx_parameter.memory_addr = (uint32_t)can_rx_dma_words; // 接收 DMA 缓冲区地址
|
||||||
dma_rx_parameter.direction = DMA_PERIPHERAL_TO_MEMORY; // 外设到内存
|
dma_rx_parameter.direction = DMA_PERIPHERAL_TO_MEMORY; // 外设到内存
|
||||||
dma_rx_parameter.memory_width = DMA_MEMORY_WIDTH_8BIT; // 内存宽度 8 位
|
dma_rx_parameter.memory_width = DMA_MEMORY_WIDTH_32BIT; // 内存宽度 32 位
|
||||||
dma_rx_parameter.periph_width = DMA_PERIPHERAL_WIDTH_8BIT; // 外设宽度 8 位
|
dma_rx_parameter.periph_width = DMA_PERIPHERAL_WIDTH_32BIT; // 外设宽度 32 位
|
||||||
dma_rx_parameter.priority = DMA_PRIORITY_HIGH; // 高优先级
|
dma_rx_parameter.priority = DMA_PRIORITY_HIGH; // 高优先级
|
||||||
dma_rx_parameter.number = 8; // 传输字节数
|
dma_rx_parameter.number = 16; // 最多 16 个 word = 64 字节
|
||||||
dma_rx_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE; // 外设地址不递增
|
dma_rx_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE; // CAN FIFO 数据端口不递增
|
||||||
dma_rx_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE; // 内存地址递增
|
dma_rx_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE; // 内存地址递增
|
||||||
dma_init(DMA0, DMA_CH4, &dma_rx_parameter); // 使用 DMA 通道 4
|
dma_init(DMA0, DMA_CH4, &dma_rx_parameter); // 使用 DMA 通道 4
|
||||||
|
dma_memory_to_memory_enable(DMA0, DMA_CH4); // CAN 无独立 DMA request 时,按帧启动 M2M 读取 FIFO 数据端口
|
||||||
|
|
||||||
// 配置发送 DMA
|
// RX DMA 按帧启动,初始化时不使能 CH4,避免无有效帧时搬运垃圾数据
|
||||||
dma_struct_para_init(&dma_tx_parameter);
|
dma_channel_disable(DMA0, DMA_CH4);
|
||||||
dma_tx_parameter.periph_addr =
|
can_rx_dma_busy = false;
|
||||||
(uint32_t)(&CAN_TMI0(CAN0)); // CAN 发送数据寄存器地址
|
memset(can_rx_dma_words, 0, sizeof(can_rx_dma_words));
|
||||||
dma_tx_parameter.memory_addr = (uint32_t)can_tx_buffer; // 发送缓冲区地址
|
|
||||||
dma_tx_parameter.direction = DMA_MEMORY_TO_PERIPHERAL; // 内存到外设
|
|
||||||
dma_tx_parameter.memory_width = DMA_MEMORY_WIDTH_8BIT; // 内存宽度 8 位
|
|
||||||
dma_tx_parameter.periph_width = DMA_PERIPHERAL_WIDTH_8BIT; // 外设宽度 8 位
|
|
||||||
dma_tx_parameter.priority = DMA_PRIORITY_HIGH; // 高优先级
|
|
||||||
dma_tx_parameter.number = 8; // 传输字节数
|
|
||||||
dma_tx_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE; // 外设地址不递增
|
|
||||||
dma_tx_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE; // 内存地址递增
|
|
||||||
dma_init(DMA0, DMA_CH5, &dma_tx_parameter); // 使用 DMA 通道 5
|
|
||||||
|
|
||||||
// 使能 DMA 通道
|
|
||||||
dma_channel_enable(DMA0, DMA_CH4);
|
|
||||||
dma_channel_enable(DMA0, DMA_CH5);
|
|
||||||
|
|
||||||
// 使能 CAN 接收 DMA
|
|
||||||
// can_dma_enable(CAN0, CAN_DMA_RECEIVE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -390,7 +387,7 @@ void can_config(void) {
|
|||||||
*/
|
*/
|
||||||
void canfd_config(void) {
|
void canfd_config(void) {
|
||||||
can_gpio_init();
|
can_gpio_init();
|
||||||
can_dma_init(); // 初始化 DMA
|
can_dma_init();
|
||||||
can_parameter_struct can_parameter;
|
can_parameter_struct can_parameter;
|
||||||
can_fdframe_struct can_fd_parameter;
|
can_fdframe_struct can_fd_parameter;
|
||||||
can_fd_tdc_struct can_fd_tdc_parameter;
|
can_fd_tdc_struct can_fd_tdc_parameter;
|
||||||
@@ -451,11 +448,12 @@ void canfd_config(void) {
|
|||||||
|
|
||||||
/* configure CAN0 NVIC */
|
/* configure CAN0 NVIC */
|
||||||
nvic_irq_enable(CAN0_RX0_IRQn, 1, 0);
|
nvic_irq_enable(CAN0_RX0_IRQn, 1, 0);
|
||||||
|
nvic_irq_enable(CAN0_TX_IRQn, 1, 1);
|
||||||
/* enable can receive FIFO0 not empty interrupt */
|
/* enable can receive FIFO0 not empty interrupt */
|
||||||
can_interrupt_enable(CAN0, CAN_INTEN_RFNEIE0);
|
can_interrupt_enable(CAN0, CAN_INTEN_RFNEIE0);
|
||||||
// 使能 DMA 中断
|
// 使能 DMA 中断
|
||||||
nvic_irq_enable(DMA0_Channel4_IRQn, 1, 1);
|
nvic_irq_enable(DMA0_Channel4_IRQn, 1, 1);
|
||||||
dma_interrupt_enable(DMA0, DMA_CH4, DMA_INT_FTF);
|
dma_interrupt_enable(DMA0, DMA_CH4, DMA_INT_FTF | DMA_INT_ERR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -651,9 +649,19 @@ u8 CAN_transmiter::init() {
|
|||||||
/* initialize receive message */
|
/* initialize receive message */
|
||||||
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &receive_message);
|
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &receive_message);
|
||||||
|
|
||||||
|
// 初始化收发队列和标志位
|
||||||
|
rx_queue_head = 0;
|
||||||
|
rx_queue_tail = 0;
|
||||||
|
rx_queue_count = 0;
|
||||||
|
tx_queue_head = 0;
|
||||||
|
tx_queue_tail = 0;
|
||||||
|
tx_queue_count = 0;
|
||||||
|
can0_send_flag = RESET;
|
||||||
|
|
||||||
// 初始化 DMA 缓冲区
|
// 初始化 DMA 缓冲区
|
||||||
memset(can_rx_buffer, 0, sizeof(can_rx_buffer));
|
memset(can_rx_buffer, 0, sizeof(can_rx_buffer));
|
||||||
memset(can_tx_buffer, 0, sizeof(can_tx_buffer));
|
memset(can_rx_dma_words, 0, sizeof(can_rx_dma_words));
|
||||||
|
can_rx_dma_busy = false;
|
||||||
|
|
||||||
// 初始化节点 ID 为默认值
|
// 初始化节点 ID 为默认值
|
||||||
node_id = DEFAULT_NODE_ID;
|
node_id = DEFAULT_NODE_ID;
|
||||||
@@ -663,6 +671,216 @@ u8 CAN_transmiter::init() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32_t CAN_EnterCritical(void) {
|
||||||
|
uint32_t primask = __get_PRIMASK();
|
||||||
|
__disable_irq();
|
||||||
|
return primask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CAN_ExitCritical(uint32_t primask) { __set_PRIMASK(primask); }
|
||||||
|
|
||||||
|
static u8 CANFD_DlcToLength(u8 dlc) {
|
||||||
|
return (dlc <= 8U) ? dlc
|
||||||
|
: ((dlc <= 15U) ? CANFD_DLC_LENGTH_TABLE[dlc - 9U] : 0U);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 CANFD_NormalizeLength(u8 length) {
|
||||||
|
if (length <= 8U)
|
||||||
|
return length;
|
||||||
|
for (u8 i = 0; i < sizeof(CANFD_DLC_LENGTH_TABLE); i++) {
|
||||||
|
if (length <= CANFD_DLC_LENGTH_TABLE[i]) {
|
||||||
|
return CANFD_DLC_LENGTH_TABLE[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 CANFD_WordCount(u8 length) { return (length + 3U) / 4U; }
|
||||||
|
|
||||||
|
static void CAN_RX_QueuePushFromIsr(const can_receive_message_struct &message) {
|
||||||
|
if (rx_queue_count < CAN_RX_QUEUE_SIZE) {
|
||||||
|
rx_queue[rx_queue_tail].id = message.rx_efid;
|
||||||
|
rx_queue[rx_queue_tail].length = message.rx_dlen;
|
||||||
|
if (rx_queue[rx_queue_tail].length > 64) {
|
||||||
|
rx_queue[rx_queue_tail].length = 64;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < rx_queue[rx_queue_tail].length; i++) {
|
||||||
|
rx_queue[rx_queue_tail].data[i] = message.rx_data[i];
|
||||||
|
}
|
||||||
|
rx_queue_tail = (rx_queue_tail + 1) % CAN_RX_QUEUE_SIZE;
|
||||||
|
rx_queue_count++;
|
||||||
|
} else {
|
||||||
|
CAN_DEBUG_PRINT("RX queue full, message dropped\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CAN_RX_ReleaseFifo0(void) { CAN_RFIFO0(CAN0) |= CAN_RFIFO0_RFD0; }
|
||||||
|
|
||||||
|
static u8 CAN_RX_Fifo0PendingCount(void) {
|
||||||
|
return can_receive_message_length_get(CAN0, CAN_FIFO0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CAN_TX_HasEmptyMailbox(void) {
|
||||||
|
return (can_flag_get(CAN0, CAN_FLAG_TME0) != RESET) ||
|
||||||
|
(can_flag_get(CAN0, CAN_FLAG_TME1) != RESET) ||
|
||||||
|
(can_flag_get(CAN0, CAN_FLAG_TME2) != RESET);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CAN_RX_ReadMetadata(can_receive_message_struct *message) {
|
||||||
|
uint8_t dlc;
|
||||||
|
uint32_t rfifomi = CAN_RFIFOMI(CAN0, CAN_FIFO0);
|
||||||
|
uint32_t rfifomp = CAN_RFIFOMP(CAN0, CAN_FIFO0);
|
||||||
|
uint32_t canfd_en = CAN_FDCTL(CAN0) & CAN_FDCTL_FDEN;
|
||||||
|
|
||||||
|
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, message);
|
||||||
|
message->rx_ff = (uint8_t)(CAN_RFIFOMI_FF & rfifomi);
|
||||||
|
if (CAN_FF_STANDARD == message->rx_ff) {
|
||||||
|
message->rx_sfid = (uint32_t)(GET_RFIFOMI_SFID(rfifomi));
|
||||||
|
} else {
|
||||||
|
message->rx_efid = (uint32_t)(GET_RFIFOMI_EFID(rfifomi));
|
||||||
|
}
|
||||||
|
message->rx_ft = (uint8_t)(CAN_RFIFOMI_FT & rfifomi);
|
||||||
|
message->rx_fi = (uint8_t)(GET_RFIFOMP_FI(rfifomp));
|
||||||
|
message->fd_flag = (uint8_t)((CAN_RFIFOMP_FDF & rfifomp) >> 7);
|
||||||
|
dlc = (uint8_t)(GET_RFIFOMP_DLENC(rfifomp));
|
||||||
|
if ((canfd_en != 0U) && (message->fd_flag != CAN_FDF_CLASSIC)) {
|
||||||
|
message->rx_dlen = CANFD_DlcToLength(dlc);
|
||||||
|
message->fd_brs = (uint8_t)((rfifomp & CAN_RFIFOMP_BRS) >> 5);
|
||||||
|
message->fd_esi = (uint8_t)((rfifomp & CAN_RFIFOMP_ESI) >> 4);
|
||||||
|
} else {
|
||||||
|
message->rx_dlen = (dlc <= 8U) ? dlc : 8U;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CAN_RX_StartPayloadDma(const can_receive_message_struct &metadata) {
|
||||||
|
can_rx_dma_message = metadata;
|
||||||
|
if (can_rx_dma_message.rx_dlen > 64) {
|
||||||
|
can_rx_dma_message.rx_dlen = 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 word_count = CANFD_WordCount(can_rx_dma_message.rx_dlen);
|
||||||
|
if (word_count == 0) {
|
||||||
|
CAN_RX_QueuePushFromIsr(can_rx_dma_message);
|
||||||
|
CAN_RX_ReleaseFifo0();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
can_rx_dma_busy = true;
|
||||||
|
dma_channel_disable(DMA0, DMA_CH4);
|
||||||
|
dma_interrupt_flag_clear(DMA0, DMA_CH4, DMA_INT_FLAG_G);
|
||||||
|
dma_periph_address_config(DMA0, DMA_CH4,
|
||||||
|
(uint32_t)(&CAN_RFIFOMDATA0(CAN0, CAN_FIFO0)));
|
||||||
|
dma_memory_address_config(DMA0, DMA_CH4, (uint32_t)can_rx_dma_words);
|
||||||
|
dma_transfer_number_config(DMA0, DMA_CH4, word_count);
|
||||||
|
dma_channel_enable(DMA0, DMA_CH4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CAN_RX_PumpFifo0(void) {
|
||||||
|
while (!can_rx_dma_busy) {
|
||||||
|
if (CAN_RX_Fifo0PendingCount() == 0U) {
|
||||||
|
can_interrupt_enable(CAN0, CAN_INTEN_RFNEIE0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
can_receive_message_struct temp_message;
|
||||||
|
CAN_RX_ReadMetadata(&temp_message);
|
||||||
|
|
||||||
|
if (temp_message.rx_ff == CAN_FF_EXTENDED) {
|
||||||
|
CAN_RX_StartPayloadDma(temp_message);
|
||||||
|
} else {
|
||||||
|
CAN_RX_ReleaseFifo0();
|
||||||
|
CAN_DEBUG_PRINT("Drop standard CAN frame in CANFD path\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 CAN_RX_QueuePop(can_rx_queue_item_t *item) {
|
||||||
|
u8 popped = 0U;
|
||||||
|
uint32_t primask = CAN_EnterCritical();
|
||||||
|
if (rx_queue_count == 0) {
|
||||||
|
CAN_ExitCritical(primask);
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
*item = rx_queue[rx_queue_head];
|
||||||
|
rx_queue_head = (rx_queue_head + 1) % CAN_RX_QUEUE_SIZE;
|
||||||
|
rx_queue_count--;
|
||||||
|
popped = 1U;
|
||||||
|
CAN_ExitCritical(primask);
|
||||||
|
|
||||||
|
return popped;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 CAN_TX_QueuePush(const can_trasnmit_message_struct &message) {
|
||||||
|
u8 queued = 0U;
|
||||||
|
uint32_t primask = CAN_EnterCritical();
|
||||||
|
if (tx_queue_count < CAN_TX_QUEUE_SIZE) {
|
||||||
|
tx_queue[tx_queue_tail].message = message;
|
||||||
|
tx_queue[tx_queue_tail].valid = 1U;
|
||||||
|
tx_queue_tail = (tx_queue_tail + 1U) % CAN_TX_QUEUE_SIZE;
|
||||||
|
tx_queue_count++;
|
||||||
|
queued = 1U;
|
||||||
|
}
|
||||||
|
CAN_ExitCritical(primask);
|
||||||
|
|
||||||
|
if (queued != 0U) {
|
||||||
|
can_interrupt_enable(CAN0, CAN_INTEN_TMEIE);
|
||||||
|
return 1U;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAN_DEBUG_PRINT("TX queue full, message dropped\r\n");
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 CAN_TX_QueuePeek(can_trasnmit_message_struct *message) {
|
||||||
|
u8 has_message = 0U;
|
||||||
|
uint32_t primask = CAN_EnterCritical();
|
||||||
|
if (tx_queue_count != 0U) {
|
||||||
|
*message = tx_queue[tx_queue_head].message;
|
||||||
|
has_message = 1U;
|
||||||
|
}
|
||||||
|
CAN_ExitCritical(primask);
|
||||||
|
return has_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 CAN_TX_QueuePop(can_trasnmit_message_struct *message) {
|
||||||
|
u8 popped = 0U;
|
||||||
|
uint32_t primask = CAN_EnterCritical();
|
||||||
|
if (tx_queue_count != 0U) {
|
||||||
|
*message = tx_queue[tx_queue_head].message;
|
||||||
|
tx_queue[tx_queue_head].valid = 0U;
|
||||||
|
tx_queue_head = (tx_queue_head + 1U) % CAN_TX_QUEUE_SIZE;
|
||||||
|
tx_queue_count--;
|
||||||
|
popped = 1U;
|
||||||
|
}
|
||||||
|
CAN_ExitCritical(primask);
|
||||||
|
return popped;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CAN_TX_DisableTmeIfQueueEmpty(void) {
|
||||||
|
uint32_t primask = CAN_EnterCritical();
|
||||||
|
if (tx_queue_count == 0U) {
|
||||||
|
can_interrupt_disable(CAN0, CAN_INTEN_TMEIE);
|
||||||
|
}
|
||||||
|
CAN_ExitCritical(primask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CAN_TX_ServiceQueue(void) {
|
||||||
|
can_trasnmit_message_struct queued_message;
|
||||||
|
while (CAN_TX_HasEmptyMailbox()) {
|
||||||
|
if (CAN_TX_QueuePeek(&queued_message) == 0U) {
|
||||||
|
CAN_TX_DisableTmeIfQueueEmpty();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (can_message_transmit(CAN0, &queued_message) == CAN_NOMAILBOX) {
|
||||||
|
CAN_DEBUG_PRINT(
|
||||||
|
"Mailbox became unavailable while draining TX queue\r\n");
|
||||||
|
can_interrupt_enable(CAN0, CAN_INTEN_TMEIE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CAN_TX_QueuePop(&queued_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief CAN 数据更新
|
\brief CAN 数据更新
|
||||||
*/
|
*/
|
||||||
@@ -757,9 +975,15 @@ void CAN_transmiter::communication_check() {
|
|||||||
can0_send_flag = RESET;
|
can0_send_flag = RESET;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理所有接收到的 CAN 消息(从环形缓冲区中)
|
// 处理所有接收到的 CANFD 消息(从底层中断队列中取出)
|
||||||
|
can_rx_queue_item_t rx_item;
|
||||||
|
while (CAN_RX_QueuePop(&rx_item)) {
|
||||||
can_receive_message_struct current_message;
|
can_receive_message_struct current_message;
|
||||||
while (dequeue_rx_message(current_message)) {
|
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, ¤t_message);
|
||||||
|
current_message.rx_efid = rx_item.id;
|
||||||
|
current_message.rx_dlen = rx_item.length;
|
||||||
|
memcpy(current_message.rx_data, rx_item.data, rx_item.length);
|
||||||
|
|
||||||
// 提取基础 ID (低 16 位,去除高 16 位节点 ID 部分)
|
// 提取基础 ID (低 16 位,去除高 16 位节点 ID 部分)
|
||||||
u32 base_id = current_message.rx_efid & 0xFFFF;
|
u32 base_id = current_message.rx_efid & 0xFFFF;
|
||||||
|
|
||||||
@@ -771,11 +995,14 @@ void CAN_transmiter::communication_check() {
|
|||||||
// 判断是广播消息还是其他节点的命令
|
// 判断是广播消息还是其他节点的命令
|
||||||
if (sender_node_id == BROADCAST_NODE_ID || sender_node_id != node_id) {
|
if (sender_node_id == BROADCAST_NODE_ID || sender_node_id != node_id) {
|
||||||
// 广播消息或其他节点的命令 → 进入静默模式(停发 10 秒)
|
// 广播消息或其他节点的命令 → 进入静默模式(停发 10 秒)
|
||||||
CAN_DEBUG_PRINT("Received base_id=0x0000 from node %d, entering silent mode for 10s\r\n", sender_node_id);
|
CAN_DEBUG_PRINT(
|
||||||
|
"Received base_id=0x0000 from node %d, entering silent mode for 10s\r\n",
|
||||||
|
sender_node_id);
|
||||||
enter_silent_mode();
|
enter_silent_mode();
|
||||||
} else {
|
} else {
|
||||||
// 自己节点的命令 → 执行 BOOT 功能
|
// 自己节点的命令 → 执行 BOOT 功能
|
||||||
CAN_DEBUG_PRINT("Received base_id=0x0000 from self, jumping to BOOT\r\n");
|
CAN_DEBUG_PRINT(
|
||||||
|
"Received base_id=0x0000 from self, jumping to BOOT\r\n");
|
||||||
motor_sleep();
|
motor_sleep();
|
||||||
flashEEP.save_system_config(false);
|
flashEEP.save_system_config(false);
|
||||||
fmc_unlock();
|
fmc_unlock();
|
||||||
@@ -800,8 +1027,7 @@ void CAN_transmiter::communication_check() {
|
|||||||
if (current_message.rx_dlen >= 2) {
|
if (current_message.rx_dlen >= 2) {
|
||||||
new_node_id = unpack_bytes_to_u16(current_message.rx_data, 0);
|
new_node_id = unpack_bytes_to_u16(current_message.rx_data, 0);
|
||||||
} else {
|
} else {
|
||||||
new_node_id =
|
new_node_id = current_message.rx_data[0]; // 只发送 1 字节时直接使用
|
||||||
current_message.rx_data[0]; // 只发送 1 字节时直接使用
|
|
||||||
}
|
}
|
||||||
u16 old_node_id = node_id; // 保存旧节点 ID 用于构建响应 ID
|
u16 old_node_id = node_id; // 保存旧节点 ID 用于构建响应 ID
|
||||||
CAN_DEBUG_PRINT(
|
CAN_DEBUG_PRINT(
|
||||||
@@ -824,14 +1050,17 @@ void CAN_transmiter::communication_check() {
|
|||||||
// 然后保存新节点 ID 到 Flash
|
// 然后保存新节点 ID 到 Flash
|
||||||
u8 save_status = save_node_id_to_flash(new_node_id);
|
u8 save_status = save_node_id_to_flash(new_node_id);
|
||||||
|
|
||||||
CAN_DEBUG_PRINT("SET_NODE_ID: save_status=%d, send_result=%d\r\n",
|
CAN_DEBUG_PRINT(
|
||||||
|
"SET_NODE_ID: save_status=%d, send_result=%d\r\n",
|
||||||
save_status, send_result);
|
save_status, send_result);
|
||||||
|
|
||||||
if (save_status == FLASH_EEPROM_OK) {
|
if (save_status == FLASH_EEPROM_OK) {
|
||||||
CAN_DEBUG_PRINT("SET_NODE_ID: SUCCESS, node_id updated from %d to %d\r\n",
|
CAN_DEBUG_PRINT(
|
||||||
|
"SET_NODE_ID: SUCCESS, node_id updated from %d to %d\r\n",
|
||||||
old_node_id, new_node_id);
|
old_node_id, new_node_id);
|
||||||
} else {
|
} else {
|
||||||
CAN_DEBUG_PRINT("SET_NODE_ID: Flash write FAILED, status=%d\r\n",
|
CAN_DEBUG_PRINT(
|
||||||
|
"SET_NODE_ID: Flash write FAILED, status=%d\r\n",
|
||||||
save_status);
|
save_status);
|
||||||
// 发送错误响应
|
// 发送错误响应
|
||||||
transmit_data[0] = 0x00; // 失败
|
transmit_data[0] = 0x00; // 失败
|
||||||
@@ -982,17 +1211,10 @@ u8 CAN_transmiter::CAN_Wakeup() {
|
|||||||
* @retval 无
|
* @retval 无
|
||||||
*/
|
*/
|
||||||
void CAN0_RX0_IRQHandler(void) {
|
void CAN0_RX0_IRQHandler(void) {
|
||||||
// 检查是否是 FIFO0 消息挂起中断
|
// FIFO0 非空时,关中断并持续抽空 FIFO;DMA 完成后由 DMA ISR 继续续传。
|
||||||
if (can_interrupt_flag_get(CAN0, CAN_INT_FLAG_RFL0) != RESET) {
|
if (CAN_RX_Fifo0PendingCount() != 0U) {
|
||||||
// 清除中断标志
|
can_interrupt_disable(CAN0, CAN_INTEN_RFNEIE0);
|
||||||
can_interrupt_flag_clear(CAN0, CAN_INT_FLAG_RFL0);
|
CAN_RX_PumpFifo0();
|
||||||
|
|
||||||
// 直接读取消息(包括数据),不依赖DMA
|
|
||||||
can_receive_message_struct temp_message;
|
|
||||||
can_message_receive(CAN0, CAN_FIFO0, &temp_message);
|
|
||||||
|
|
||||||
// 将接收到的消息加入环形缓冲区
|
|
||||||
CAN0_transmiter.enqueue_rx_message_from_isr(temp_message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查其他可能的中断源
|
// 检查其他可能的中断源
|
||||||
@@ -1014,14 +1236,32 @@ void CAN0_RX0_IRQHandler(void) {
|
|||||||
* @retval 无
|
* @retval 无
|
||||||
*/
|
*/
|
||||||
void DMA0_Channel4_IRQHandler(void) {
|
void DMA0_Channel4_IRQHandler(void) {
|
||||||
|
if (dma_interrupt_flag_get(DMA0, DMA_CH4, DMA_INT_FLAG_ERR)) {
|
||||||
|
dma_interrupt_flag_clear(DMA0, DMA_CH4, DMA_INT_FLAG_G);
|
||||||
|
dma_channel_disable(DMA0, DMA_CH4);
|
||||||
|
if (can_rx_dma_busy) {
|
||||||
|
CAN_RX_ReleaseFifo0();
|
||||||
|
can_rx_dma_busy = false;
|
||||||
|
}
|
||||||
|
CAN_DEBUG_PRINT("CAN RX DMA error, frame dropped\r\n");
|
||||||
|
CAN_RX_PumpFifo0();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (dma_interrupt_flag_get(DMA0, DMA_CH4, DMA_INT_FLAG_FTF)) {
|
if (dma_interrupt_flag_get(DMA0, DMA_CH4, DMA_INT_FLAG_FTF)) {
|
||||||
// 清除 DMA 传输完成中断标志
|
// 清除 DMA 传输完成中断标志
|
||||||
dma_interrupt_flag_clear(DMA0, DMA_CH4, DMA_INT_FLAG_G);
|
dma_interrupt_flag_clear(DMA0, DMA_CH4, DMA_INT_FLAG_G);
|
||||||
// 将 DMA 缓冲区的数据复制到接收消息结构体
|
dma_channel_disable(DMA0, DMA_CH4);
|
||||||
for (int i = 0; i < CAN0_transmiter.receive_message.rx_dlen && i < 8;
|
if (!can_rx_dma_busy) {
|
||||||
i++) {
|
CAN_RX_PumpFifo0();
|
||||||
CAN0_transmiter.receive_message.rx_data[i] = can_rx_buffer[i];
|
return;
|
||||||
}
|
}
|
||||||
|
memcpy(can_rx_dma_message.rx_data, can_rx_dma_words,
|
||||||
|
can_rx_dma_message.rx_dlen);
|
||||||
|
CAN_RX_QueuePushFromIsr(can_rx_dma_message);
|
||||||
|
CAN_RX_ReleaseFifo0();
|
||||||
|
can_rx_dma_busy = false;
|
||||||
|
CAN_RX_PumpFifo0();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1030,12 +1270,7 @@ void DMA0_Channel4_IRQHandler(void) {
|
|||||||
* @retval 无
|
* @retval 无
|
||||||
*/
|
*/
|
||||||
void DMA0_Channel5_IRQHandler(void) {
|
void DMA0_Channel5_IRQHandler(void) {
|
||||||
if (dma_interrupt_flag_get(DMA0, DMA_CH5, DMA_INT_FLAG_FTF)) {
|
// 保留空实现:发送链路改用 CAN mailbox + TME 中断队列。
|
||||||
// DMA 传输完成,可以在此处执行后续处理
|
|
||||||
// 清除 DMA 传输完成中断标志
|
|
||||||
dma_interrupt_flag_clear(DMA0, DMA_CH5, DMA_INT_FLAG_FTF);
|
|
||||||
// 可以在此添加后续处理逻辑,如发送下一个数据包等
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1087,50 +1322,6 @@ u8 CAN_transmiter::CAN_AsyncSend(u32 id, u8 length, u8 *data) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 向发送队列添加消息
|
|
||||||
* @param id CAN消息ID
|
|
||||||
* @param length CAN消息长度
|
|
||||||
* @param data CAN消息数据
|
|
||||||
* @retval 1=成功, 0=队列满
|
|
||||||
*/
|
|
||||||
static u8 CAN_TX_QueuePush(u32 id, u8 length, u8 *data) {
|
|
||||||
if (tx_queue_count >= CAN_TX_QUEUE_SIZE) {
|
|
||||||
CAN_DEBUG_PRINT("TX queue full, message dropped\r\n");
|
|
||||||
return 0; // 队列满,丢弃消息
|
|
||||||
}
|
|
||||||
|
|
||||||
tx_queue[tx_queue_tail].id = id;
|
|
||||||
tx_queue[tx_queue_tail].length = length;
|
|
||||||
for (int i = 0; i < length && i < 64; i++) {
|
|
||||||
tx_queue[tx_queue_tail].data[i] = data[i];
|
|
||||||
}
|
|
||||||
tx_queue[tx_queue_tail].valid = 1;
|
|
||||||
|
|
||||||
tx_queue_tail = (tx_queue_tail + 1) % CAN_TX_QUEUE_SIZE;
|
|
||||||
tx_queue_count++;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 从发送队列取出消息
|
|
||||||
* @param item 指向队列项的指针
|
|
||||||
* @retval 1=成功, 0=队列空
|
|
||||||
*/
|
|
||||||
static u8 CAN_TX_QueuePop(can_tx_queue_item_t *item) {
|
|
||||||
if (tx_queue_count == 0) {
|
|
||||||
return 0; // 队列空
|
|
||||||
}
|
|
||||||
|
|
||||||
*item = tx_queue[tx_queue_head];
|
|
||||||
tx_queue[tx_queue_head].valid = 0;
|
|
||||||
tx_queue_head = (tx_queue_head + 1) % CAN_TX_QUEUE_SIZE;
|
|
||||||
tx_queue_count--;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 异步 CANFD 数据发送 (使用队列机制防止丢帧)
|
* @brief 异步 CANFD 数据发送 (使用队列机制防止丢帧)
|
||||||
* @param id CAN 消息 ID
|
* @param id CAN 消息 ID
|
||||||
@@ -1138,42 +1329,32 @@ static u8 CAN_TX_QueuePop(can_tx_queue_item_t *item) {
|
|||||||
* @param data CAN 消息数据
|
* @param data CAN 消息数据
|
||||||
*/
|
*/
|
||||||
u8 CAN_transmiter::CANFD_AsyncSend(u32 id, u8 length, u8 *data) {
|
u8 CAN_transmiter::CANFD_AsyncSend(u32 id, u8 length, u8 *data) {
|
||||||
u8 valid_length = length;
|
u8 valid_length = 0U;
|
||||||
// 检查 CANFD 模式是否启用
|
// 检查 CANFD 模式是否启用
|
||||||
if (!(CAN_CTL(CAN0) & CAN_CTL_DFZ)) {
|
if ((CAN_FDCTL(CAN0) & CAN_FDCTL_FDEN) == 0U) {
|
||||||
CAN_DEBUG_PRINT("CANFD mode not enabled\r\n");
|
CAN_DEBUG_PRINT("CANFD mode not enabled\r\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查数据指针是否有效
|
// 检查数据指针是否有效
|
||||||
if (data == nullptr) {
|
if (data == nullptr && length != 0U) {
|
||||||
CAN_DEBUG_PRINT("Invalid data pointer\r\n");
|
CAN_DEBUG_PRINT("Invalid data pointer\r\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查数据长度是否符合 CANFD 规范
|
// 检查数据长度是否符合 CANFD 规范,并映射到合法 DLC 长度
|
||||||
if (length > 8) {
|
if (length == 0U) {
|
||||||
if (length > 8 and length < 13) {
|
valid_length = 0U;
|
||||||
valid_length = 12;
|
|
||||||
} else if (length > 12 and length < 17) {
|
|
||||||
valid_length = 16;
|
|
||||||
} else if (length > 16 and length < 21) {
|
|
||||||
valid_length = 20;
|
|
||||||
} else if (length > 20 and length < 25) {
|
|
||||||
valid_length = 24;
|
|
||||||
} else if (length > 24 and length < 33) {
|
|
||||||
valid_length = 32;
|
|
||||||
} else if (length > 32 and length < 49) {
|
|
||||||
valid_length = 48;
|
|
||||||
} else if (length > 48 and length < 65) {
|
|
||||||
valid_length = 64;
|
|
||||||
} else {
|
} else {
|
||||||
|
valid_length = CANFD_NormalizeLength(length);
|
||||||
|
}
|
||||||
|
if ((length != 0U) && (valid_length == 0U)) {
|
||||||
CAN_DEBUG_PRINT("CANFD data length error\r\n");
|
CAN_DEBUG_PRINT("CANFD data length error\r\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* 配置发送消息参数 */
|
/* 配置发送消息参数 */
|
||||||
|
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &transmit_message);
|
||||||
transmit_message.tx_ft = CAN_FT_DATA; // 数据帧
|
transmit_message.tx_ft = CAN_FT_DATA; // 数据帧
|
||||||
transmit_message.tx_ff = CAN_FF_EXTENDED; // 扩展帧格式
|
transmit_message.tx_ff = CAN_FF_EXTENDED; // 扩展帧格式
|
||||||
transmit_message.tx_efid = id; // 扩展帧 ID
|
transmit_message.tx_efid = id; // 扩展帧 ID
|
||||||
@@ -1183,12 +1364,10 @@ u8 CAN_transmiter::CANFD_AsyncSend(u32 id, u8 length, u8 *data) {
|
|||||||
transmit_message.fd_flag = CAN_FDF_FDFRAME; // CANFD 帧格式
|
transmit_message.fd_flag = CAN_FDF_FDFRAME; // CANFD 帧格式
|
||||||
transmit_message.fd_brs = ENABLE; // 使能比特率切换
|
transmit_message.fd_brs = ENABLE; // 使能比特率切换
|
||||||
transmit_message.fd_esi = DISABLE; // 错误状态指示
|
transmit_message.fd_esi = DISABLE; // 错误状态指示
|
||||||
// 复制数据到发送缓冲区
|
memset(transmit_message.tx_data, 0, sizeof(transmit_message.tx_data));
|
||||||
for (int i = 0; i < valid_length; i++) {
|
for (int i = 0; i < valid_length; i++) {
|
||||||
if (i < length) {
|
if (i < length) {
|
||||||
transmit_message.tx_data[i] = *(data + i);
|
transmit_message.tx_data[i] = *(data + i);
|
||||||
} else {
|
|
||||||
transmit_message.tx_data[i] = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1199,7 +1378,7 @@ u8 CAN_transmiter::CANFD_AsyncSend(u32 id, u8 length, u8 *data) {
|
|||||||
if (transmit_mailbox == CAN_NOMAILBOX) {
|
if (transmit_mailbox == CAN_NOMAILBOX) {
|
||||||
// 没有可用的 mailbox,将消息加入发送队列
|
// 没有可用的 mailbox,将消息加入发送队列
|
||||||
CAN_DEBUG_PRINT("No available mailbox, adding to TX queue\r\n");
|
CAN_DEBUG_PRINT("No available mailbox, adding to TX queue\r\n");
|
||||||
return CAN_TX_QueuePush(id, valid_length, data);
|
return CAN_TX_QueuePush(transmit_message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 启用 CAN 发送中断以检测传输完成(用于队列发送)
|
// 启用 CAN 发送中断以检测传输完成(用于队列发送)
|
||||||
@@ -1211,36 +1390,7 @@ u8 CAN_transmiter::CANFD_AsyncSend(u32 id, u8 length, u8 *data) {
|
|||||||
* @brief CAN 传输中断处理函数(支持队列发送)
|
* @brief CAN 传输中断处理函数(支持队列发送)
|
||||||
*/
|
*/
|
||||||
void CAN0_TX_IRQHandler(void) {
|
void CAN0_TX_IRQHandler(void) {
|
||||||
// 检查是否有发送邮箱空闲
|
if (CAN_TX_HasEmptyMailbox()) {
|
||||||
if (can_flag_get(CAN0, CAN_FLAG_TME0) != RESET ||
|
CAN_TX_ServiceQueue();
|
||||||
can_flag_get(CAN0, CAN_FLAG_TME1) != RESET ||
|
|
||||||
can_flag_get(CAN0, CAN_FLAG_TME2) != RESET) {
|
|
||||||
|
|
||||||
can_tx_queue_item_t next_msg;
|
|
||||||
// 尝试从队列中取出下一条消息发送
|
|
||||||
if (CAN_TX_QueuePop(&next_msg)) {
|
|
||||||
// 配置并发送队列中的消息(使用独立的队列消息结构体)
|
|
||||||
queue_transmit_message.tx_ft = CAN_FT_DATA;
|
|
||||||
queue_transmit_message.tx_ff = CAN_FF_EXTENDED;
|
|
||||||
queue_transmit_message.tx_efid = next_msg.id;
|
|
||||||
queue_transmit_message.tx_dlen = next_msg.length;
|
|
||||||
queue_transmit_message.fd_flag = CAN_FDF_FDFRAME;
|
|
||||||
queue_transmit_message.fd_brs = ENABLE;
|
|
||||||
queue_transmit_message.fd_esi = DISABLE;
|
|
||||||
|
|
||||||
for (int i = 0; i < next_msg.length; i++) {
|
|
||||||
queue_transmit_message.tx_data[i] = next_msg.data[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
can_message_transmit(CAN0, &queue_transmit_message);
|
|
||||||
} else {
|
|
||||||
// 队列为空,禁用发送中断
|
|
||||||
can_interrupt_disable(CAN0, CAN_INTEN_TMEIE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清除发送中断标志
|
|
||||||
can_flag_clear(CAN0, CAN_FLAG_TME0);
|
|
||||||
can_flag_clear(CAN0, CAN_FLAG_TME1);
|
|
||||||
can_flag_clear(CAN0, CAN_FLAG_TME2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ u8 can_gpio_init(void); // CAN 引脚初始化
|
|||||||
void can_config(void); // CAN 初始化
|
void can_config(void); // CAN 初始化
|
||||||
void canfd_config(void); // CANFD 初始化
|
void canfd_config(void); // CANFD 初始化
|
||||||
void can_dma_init(void); // CAN DMA 初始化
|
void can_dma_init(void); // CAN DMA 初始化
|
||||||
|
void CAN0_TX_IRQHandler(void); // CAN 发送中断处理函数
|
||||||
void CAN0_RX0_IRQHandler(void); // CAN 接收中断处理函数
|
void CAN0_RX0_IRQHandler(void); // CAN 接收中断处理函数
|
||||||
void DMA0_Channel4_IRQHandler(void); // DMA 通道 4 中断处理函数 (CAN 接收)
|
void DMA0_Channel4_IRQHandler(void); // DMA 通道 4 中断处理函数 (CAN 接收)
|
||||||
void DMA0_Channel5_IRQHandler(void); // DMA 通道 5 中断处理函数 (CAN 发送)
|
void DMA0_Channel5_IRQHandler(void); // DMA 通道 5 中断处理函数 (CAN 发送)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,169 @@
|
|||||||
|
#ifndef _CAN_H
|
||||||
|
#define _CAN_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include "gd32c10x_can.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
// 定义一个联合体类型用于 float 和 byte 数组之间的转换
|
||||||
|
typedef union {
|
||||||
|
float f;
|
||||||
|
u8 b[4];
|
||||||
|
} float_union_t;
|
||||||
|
|
||||||
|
// 添加辅助函数用于将 u16 类型数据打包到 u8 数组中
|
||||||
|
static inline void pack_u16_to_bytes(u8 *buffer, u16 offset, u16 value) {
|
||||||
|
buffer[offset] = (u8)(value & 0xFF);
|
||||||
|
buffer[offset + 1] = (u8)((value >> 8) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加辅助函数用于将 float 类型数据打包到 u8 数组中
|
||||||
|
static inline void pack_float_to_bytes(u8 *buffer, u16 offset,
|
||||||
|
float_union_t &float_union) {
|
||||||
|
buffer[offset] = float_union.b[0];
|
||||||
|
buffer[offset + 1] = float_union.b[1];
|
||||||
|
buffer[offset + 2] = float_union.b[2];
|
||||||
|
buffer[offset + 3] = float_union.b[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加辅助函数用于从 u8 数组中解包 short 类型数据
|
||||||
|
static inline short unpack_bytes_to_short(u8 *buffer, u16 offset) {
|
||||||
|
short value = (short)(buffer[offset] | (buffer[offset + 1] << 8));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加辅助函数用于从 u8 数组中解包 u16 类型数据
|
||||||
|
static inline u16 unpack_bytes_to_u16(u8 *buffer, u16 offset) {
|
||||||
|
u16 value = (u16)(buffer[offset] | (buffer[offset + 1] << 8));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加辅助函数用于从 u8 数组中解包 float 类型数据
|
||||||
|
static inline float_union_t unpack_bytes_to_float(u8 *buffer, u16 offset) {
|
||||||
|
float_union_t float_union;
|
||||||
|
float_union.b[0] = buffer[offset];
|
||||||
|
float_union.b[1] = buffer[offset + 1];
|
||||||
|
float_union.b[2] = buffer[offset + 2];
|
||||||
|
float_union.b[3] = buffer[offset + 3];
|
||||||
|
return float_union;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 can_gpio_init(void); // CAN 引脚初始化
|
||||||
|
void can_config(void); // CAN 初始化
|
||||||
|
void canfd_config(void); // CANFD 初始化
|
||||||
|
void can_dma_init(void); // CAN DMA 初始化
|
||||||
|
void CAN0_RX0_IRQHandler(void); // CAN 接收中断处理函数
|
||||||
|
void DMA0_Channel4_IRQHandler(void); // DMA 通道 4 中断处理函数 (CAN 接收)
|
||||||
|
void DMA0_Channel5_IRQHandler(void); // DMA 通道 5 中断处理函数 (CAN 发送)
|
||||||
|
void jumpToBOOT();
|
||||||
|
|
||||||
|
void can_loopback_init(void);
|
||||||
|
void can_loopback_test(void);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief CAN 通信类
|
||||||
|
\note 封装 CAN 发送和接收功能,支持多电机节点 ID 隔离
|
||||||
|
*/
|
||||||
|
class CAN_transmiter {
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const uint8_t CAN_RX_BUFFER_SIZE = 8; // CAN 接收缓冲区大小
|
||||||
|
static const u32 CAN_SILENT_DURATION_MS = 10000; // CAN 静默时长 10 秒
|
||||||
|
|
||||||
|
bool is_sleep = false; // 是否休眠
|
||||||
|
bool is_silent = false; // 是否静默(收到 base_id=0x0000 后进入静默)
|
||||||
|
u32 silent_start_time = 0; // 静默开始时间(ms)
|
||||||
|
|
||||||
|
u16 node_id; // 本节点 ID(0-254),使用 CAN ID 高 8 位存储 (在 init() 中初始化为 DEFAULT_NODE_ID)
|
||||||
|
u8 Target_Control_Mode = 0; // 目标控制模式
|
||||||
|
u8 controller_Mode = 0; // 当前模式
|
||||||
|
short mcu_temp = 0; // MCU 温度
|
||||||
|
short motor_temp = 0; // 电机温度
|
||||||
|
u16 Power_Voltage = 0; // 电源电压
|
||||||
|
|
||||||
|
u16 limit_voltage = 0; // 限制电压
|
||||||
|
|
||||||
|
float_union_t rt_angle_union = {0}; // 实时角度
|
||||||
|
float_union_t rt_velocity_union = {0}; // 实时速度
|
||||||
|
float_union_t rt_current_union = {0}; // 实时电流
|
||||||
|
|
||||||
|
float_union_t target_angle_union = {0}; // 目标角度
|
||||||
|
float_union_t target_velocity_union = {0}; // 目标速度
|
||||||
|
float_union_t target_current_union = {0}; // 目标电流
|
||||||
|
|
||||||
|
float_union_t u_current_union = {0}; // U 相电流
|
||||||
|
float_union_t w_current_union = {0}; // W 相电流
|
||||||
|
float_union_t v_current_union = {0}; // V 相电流
|
||||||
|
|
||||||
|
bool MotorStatus = 0; // 电机状态码
|
||||||
|
u8 EEPROMStatus = 0; // EEPROM 状态码
|
||||||
|
|
||||||
|
can_trasnmit_message_struct transmit_message; // 发送消息结构体
|
||||||
|
u8 transmit_mailbox = CAN_NOMAILBOX; // 初始化为无 mailbox 状态
|
||||||
|
u8 transmit_data[64]; // CANFD 最大 64 字节
|
||||||
|
|
||||||
|
// CAN 接收消息队列
|
||||||
|
can_receive_message_struct rx_message_buffer[CAN_RX_BUFFER_SIZE];
|
||||||
|
volatile uint8_t rx_buffer_head = 0;
|
||||||
|
volatile uint8_t rx_buffer_tail = 0;
|
||||||
|
|
||||||
|
void update(); // 更新数据
|
||||||
|
u8 CAN_Receive(); // CAN 接收
|
||||||
|
u8 CANFD_Receive(); // CANFD 接收
|
||||||
|
bool is_rx_buffer_full();
|
||||||
|
bool is_rx_buffer_empty();
|
||||||
|
bool dequeue_rx_message(can_receive_message_struct& message);
|
||||||
|
void check_silent_mode(); // 检查静默模式是否结束
|
||||||
|
|
||||||
|
public:
|
||||||
|
// 节点 ID 相关辅助函数 (使用高 16 位存储节点 ID)
|
||||||
|
u32 build_can_id(u32 base_id); // 根据节点 ID 构建 CAN ID,格式:[节点 ID(16 位)][基础 ID(16 位)]
|
||||||
|
bool is_message_for_me(u32 received_id); // 判断消息是否发给本节点
|
||||||
|
|
||||||
|
public:
|
||||||
|
// 添加构造函数声明
|
||||||
|
u8 init(void);
|
||||||
|
|
||||||
|
can_receive_message_struct receive_message; // 接收消息结构体
|
||||||
|
FlagStatus can0_send_flag; // CAN0 发送标志
|
||||||
|
float_union_t testfloat_union = {0}; // 测试用
|
||||||
|
u16 test_u16 = 0;
|
||||||
|
|
||||||
|
bool MagStatus = 0; // 磁状态码
|
||||||
|
bool ThermalStatus = 0; // 热能状态码
|
||||||
|
void communication_check(); // 通信检查
|
||||||
|
u8 CAN_Sleep(); // CAN 进入睡眠
|
||||||
|
u8 CAN_Wakeup(); // CAN 唤醒
|
||||||
|
u8 CAN_AsyncSend(u32 ID, u8 length, u8 *data); // CAN 异步发送 (使用 DMA)
|
||||||
|
u8 CANFD_AsyncSend(u32 ID, u8 length, u8 *data); // CANFD 异步发送 (使用 DMA)
|
||||||
|
u8 CAN_all_Send(); // CAN 发送
|
||||||
|
u8 CANFD_all_Send(); // CANFD 发送
|
||||||
|
void enter_silent_mode(); // 进入静默模式(停发 10 秒)
|
||||||
|
bool is_in_silent_mode(); // 检查是否在静默模式中
|
||||||
|
|
||||||
|
// 节点 ID 管理接口
|
||||||
|
u16 get_node_id(); // 获取当前节点 ID
|
||||||
|
void set_node_id(u16 new_id); // 设置节点 ID
|
||||||
|
u8 load_node_id_from_flash(); // 从 Flash 加载节点 ID
|
||||||
|
u8 save_node_id_to_flash(u16 new_id); // 保存节点 ID 到 Flash
|
||||||
|
|
||||||
|
// 中断处理函数需要访问的公共接口
|
||||||
|
void enqueue_rx_message_from_isr(const can_receive_message_struct& message) {
|
||||||
|
if (!is_rx_buffer_full()) {
|
||||||
|
rx_message_buffer[rx_buffer_head] = message;
|
||||||
|
rx_buffer_head = (rx_buffer_head + 1) % CAN_RX_BUFFER_SIZE;
|
||||||
|
}
|
||||||
|
// 如果缓冲区满,丢弃新消息
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user