- 新增完整的CMSIS解决方案配置,包含AC6/GCC/IAR/CLANG编译器支持 - 添加项目配置文件,定义了设备层、系统层、应用层的模块结构 - 配置ARM::CMSIS和GigaDevice::GD32E50x_DFP软件包依赖 - 创建多层代码架构:应用入口(main)、系统层(sys)、设备层(device) - 实现任务管理系统,包含任务调度器和任务执行器分离设计 - 集成ADC、CANFD、定时器、串口、温度采样等核心功能模块 - 添加详细的README文档说明项目结构和代码组织方式 - 配置AC6链接脚本和运行时环境文件 - 设置Git忽略规则以排除编译产物和临时文件
326 lines
10 KiB
Plaintext
326 lines
10 KiB
Plaintext
/**
|
||
* @file CANFD.cpp
|
||
* @brief CANFD 通信外设初始化、收发与诊断实现。
|
||
*/
|
||
|
||
#include "main.h"
|
||
#include "device/canfd.h"
|
||
#include "sys/config.h"
|
||
|
||
namespace {
|
||
constexpr uint32_t kCanSlpGpio = GPIOA;
|
||
constexpr uint32_t kCanSlpPin = GPIO_PIN_8;
|
||
|
||
/** @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 初始化 CAN0 外设所需 GPIO 引脚。
|
||
* @note 按原理图连接关系配置:
|
||
* - PA12 -> CAN0_TX
|
||
* - PA11 -> CAN0_RX
|
||
* - PA8 -> CAN0_SLP(连接 TCAN1057A #0 模式控制脚)
|
||
*/
|
||
inline void can_gpio_init()
|
||
{
|
||
rcu_periph_clock_enable(CONFIG_CAN_RCU);
|
||
rcu_periph_clock_enable(RCU_GPIOA);
|
||
rcu_periph_clock_enable(RCU_AF);
|
||
|
||
gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_11);
|
||
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);
|
||
gpio_init(kCanSlpGpio, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, kCanSlpPin);
|
||
}
|
||
|
||
/**
|
||
* @brief 将 TCAN1057A #0 置为正常工作模式。
|
||
* @note 模式控制脚按 STB 语义处理:低电平为 Normal,高电平为 Silent。
|
||
*/
|
||
inline void can_transceiver_normal_mode()
|
||
{
|
||
gpio_bit_reset(kCanSlpGpio, kCanSlpPin);
|
||
}
|
||
|
||
/**
|
||
* @brief 将 TCAN1057A #0 置为静默模式。
|
||
* @note 静默模式下收发器不驱动总线显性位,可用于总线监听或受控隔离场景。
|
||
*/
|
||
inline void can_transceiver_silent_mode()
|
||
{
|
||
gpio_bit_set(kCanSlpGpio, kCanSlpPin);
|
||
}
|
||
|
||
/**
|
||
* @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 标称位定时参数。
|
||
* @note 参考 APP 项目配置:仲裁段波特率为 1 Mbps,采样点约 80%。
|
||
*/
|
||
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 = ENABLE;
|
||
can_parameter.trans_fifo_order = ENABLE;
|
||
can_parameter.working_mode = CAN_NORMAL_MODE;
|
||
can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
|
||
can_parameter.time_segment_1 = CAN_BT_BS1_7TQ;
|
||
can_parameter.time_segment_2 = CAN_BT_BS2_2TQ;
|
||
can_parameter.prescaler = 6U;
|
||
can_init(CONFIG_CAN_INSTANCE, &can_parameter);
|
||
}
|
||
|
||
/**
|
||
* @brief 配置 CANFD 可变速率模式参数。
|
||
* @note 参考 APP 项目配置:
|
||
* - 仲裁段保持 1 Mbps;
|
||
* - 数据段配置为 2 Mbps,采样点约 80%;
|
||
* - 发送时启用 BRS 后才会切到数据段速率。
|
||
*/
|
||
inline void can_fd_mode_init()
|
||
{
|
||
#ifdef GD32E508
|
||
can_fdframe_struct can_fd_parameter{};
|
||
can_fd_tdc_struct can_fd_tdc_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 = ENABLE;
|
||
can_fd_tdc_parameter.tdc_filter = 0x04U;
|
||
can_fd_tdc_parameter.tdc_mode = CAN_TDCMOD_CALC_AND_OFFSET;
|
||
can_fd_tdc_parameter.tdc_offset = 0x04U;
|
||
can_fd_parameter.p_delay_compensation = &can_fd_tdc_parameter;
|
||
can_fd_parameter.iso_bosch = CAN_FDMOD_ISO;
|
||
can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE;
|
||
can_fd_parameter.data_prescaler = 6U;
|
||
can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ;
|
||
can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ;
|
||
can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ;
|
||
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<int16_t>(status->temperature * 100.0f);
|
||
const uint16_t vdda_x1000 = static_cast<uint16_t>(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 = ENABLE;
|
||
g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT;
|
||
#endif
|
||
|
||
auto &p = g_tx_buffer.payload;
|
||
p[0] = static_cast<uint8_t>(status->temp_raw & 0xFFU);
|
||
p[1] = static_cast<uint8_t>((status->temp_raw >> 8) & 0xFFU);
|
||
p[2] = static_cast<uint8_t>(status->vref_raw & 0xFFU);
|
||
p[3] = static_cast<uint8_t>((status->vref_raw >> 8) & 0xFFU);
|
||
p[4] = static_cast<uint8_t>(temp_x100 & 0xFF);
|
||
p[5] = static_cast<uint8_t>((temp_x100 >> 8) & 0xFF);
|
||
p[6] = static_cast<uint8_t>(vdda_x1000 & 0xFFU);
|
||
p[7] = static_cast<uint8_t>((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 CAN0 RX0 中断服务例程。
|
||
* @note 在 CANFD 帧到达 FIFO0 时自动执行,将报文缓存到内部接收帧。
|
||
*/
|
||
extern "C" void CAN0_RX0_IRQHandler(void)
|
||
{
|
||
if(can_interrupt_flag_get(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0) == RESET) {
|
||
return;
|
||
}
|
||
|
||
can_receive_message_struct rx_message{};
|
||
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;
|
||
|
||
can_interrupt_flag_clear(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0);
|
||
}
|
||
|
||
/**
|
||
* @brief 初始化 CANFD 外设、GPIO、波特率参数、接收过滤器与中断。
|
||
* @note 启用 FIFO0 RX 中断替代轮询,TX 保持 mailbox 异步发送;GPIO 与原理图一致使用 PA11/PA12/PA8。
|
||
* 其中 PA8 已封装为正常/静默两个模式控制函数,初始化时默认进入 Normal 模式。
|
||
*/
|
||
void canfd_init(void)
|
||
{
|
||
can_gpio_init();
|
||
can_transceiver_normal_mode();
|
||
can_deinit(CONFIG_CAN_INSTANCE);
|
||
can_nominal_init();
|
||
can_fd_mode_init();
|
||
can_filter_accept_all();
|
||
|
||
/* 使能 FIFO0 接收中断,替代轮询 */
|
||
can_interrupt_enable(CONFIG_CAN_INSTANCE, CAN_INT_RFNE0);
|
||
nvic_irq_enable(CAN0_RX0_IRQn, 1U, 1U);
|
||
|
||
g_rx_frame.valid = 0U;
|
||
g_tx_buffer.valid = 0U;
|
||
}
|
||
|
||
/**
|
||
* @brief 轮询 CANFD 接收 FIFO(中断模式下退化为兼容空壳)。
|
||
* @note 实际接收已由 CAN0_RX0_IRQHandler 以中断方式完成,此函数保留接口不破坏现有调用链。
|
||
*/
|
||
void canfd_poll(void)
|
||
{
|
||
/* 接收已由 CAN0_RX0_IRQHandler 中断驱动 */
|
||
}
|
||
|
||
/**
|
||
* @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;
|
||
} |