news 2026/5/5 6:42:29

GD32F105双CAN实战:从零配置250K波特率到收发数据(附完整代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
GD32F105双CAN实战:从零配置250K波特率到收发数据(附完整代码)

GD32F105双CAN实战:从零配置250K波特率到收发数据(附完整代码)

当你第一次拿到GD32F105开发板时,看到双CAN接口可能会既兴奋又困惑。作为工业控制、汽车电子等领域广泛使用的通讯协议,CAN总线以其高可靠性和实时性著称。本文将带你从零开始,一步步完成双CAN接口的配置、波特率计算、数据收发全流程,并附上可直接运行的完整代码。

1. 硬件准备与基础概念

在开始编码前,我们需要了解几个关键点。GD32F105系列微控制器内置了两个独立的CAN控制器,共享相同的API接口但使用不同的外设资源。CAN总线通信需要终端电阻匹配,通常在总线的两端各接一个120Ω电阻。

CAN通信三要素

  • 波特率:决定通信速度,常见的有125Kbps、250Kbps、500Kbps等
  • 标识符:用于区分不同消息的优先级
  • 数据帧:包含实际传输的数据内容

开发板连接示意图:

CAN0_TX --| |-- CAN_H CAN0_RX --| GD32 |-- CAN_L CAN1_TX --| F105 |-- (第二路CAN) CAN1_RX --|________|

2. 时钟配置与波特率计算

GD32F105的CAN控制器时钟来自APB1总线,默认频率为16MHz。要得到250Kbps的波特率,我们需要正确设置预分频器(prescaler)和时间段参数。

波特率计算公式

波特率 = APB1时钟 / (Prescaler × (TimeSegment1 + TimeSegment2 + 1))

对于250Kbps的目标,我们选择:

  • Prescaler = 8
  • TimeSegment1 = 6Tq
  • TimeSegment2 = 1Tq
  • SJW = 1Tq

计算验证:

16,000,000 / (8 × (6 + 1 + 1)) = 250,000 (即250Kbps)

对应的初始化代码片段:

can_parameter_struct can_parameter; can_struct_para_init(CAN_INIT_STRUCT, &can_parameter); can_parameter.time_segment_1 = CAN_BT_BS1_6TQ; // 时间段1为6Tq can_parameter.time_segment_2 = CAN_BT_BS2_1TQ; // 时间段2为1Tq can_parameter.prescaler = 8; // 预分频系数 can_parameter.resync_jump_width = CAN_BT_SJW_1TQ; // 同步跳转宽度

3. 双CAN接口完整初始化

下面给出完整的双CAN初始化函数,包含GPIO配置、CAN参数设置和过滤器初始化:

void CAN_Init_Config(void) { /* 使能时钟 */ rcu_periph_clock_enable(RCU_CAN0); rcu_periph_clock_enable(RCU_CAN1); /* GPIO配置 */ gpio_init(CAN0_RX_PORT, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, CAN0_RX_PIN); gpio_init(CAN0_TX_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CAN0_TX_PIN); gpio_init(CAN1_RX_PORT, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, CAN1_RX_PIN); gpio_init(CAN1_TX_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CAN1_TX_PIN); /* 复位CAN控制器 */ can_deinit(CAN0); can_deinit(CAN1); /* CAN参数配置 */ can_parameter_struct can_param; can_struct_para_init(CAN_INIT_STRUCT, &can_param); can_param.working_mode = CAN_NORMAL_MODE; can_param.resync_jump_width = CAN_BT_SJW_1TQ; can_param.time_segment_1 = CAN_BT_BS1_6TQ; can_param.time_segment_2 = CAN_BT_BS2_1TQ; can_param.prescaler = 8; /* 初始化双CAN */ can_init(CAN0, &can_param); can_init(CAN1, &can_param); /* 过滤器配置 */ can_filter_parameter_struct can_filter; can_struct_para_init(CAN_FILTER_STRUCT, &can_filter); can_filter.filter_number = 0; can_filter.filter_mode = CAN_FILTERMODE_MASK; can_filter.filter_bits = CAN_FILTERBITS_32BIT; can_filter.filter_list_high = 0x0000; can_filter.filter_list_low = 0x0000; can_filter.filter_mask_high = 0x0000; can_filter.filter_mask_low = 0x0000; can_filter.filter_fifo_number = CAN_FIFO0; can_filter.filter_enable = ENABLE; can_filter_init(&can_filter); /* 使能中断 */ can_interrupt_enable(CAN0, CAN_INT_RFNE0); can_interrupt_enable(CAN1, CAN_INT_RFNE0); nvic_irq_enable(CAN0_RX0_IRQn, 0, 0); nvic_irq_enable(CAN1_RX0_IRQn, 0, 0); }

4. CAN数据收发实战

4.1 发送数据帧

发送CAN消息需要填充传输数据结构体,以下是一个标准的发送函数实现:

void CAN_Send_Message(uint32_t can_periph, uint32_t id, uint8_t *data, uint8_t len) { can_trasnmit_message_struct tx_msg; tx_msg.tx_ff = CAN_FF_STANDARD; // 标准帧 tx_msg.tx_ft = CAN_FT_DATA; // 数据帧 tx_msg.tx_sfid = id; // 标准ID tx_msg.tx_dlen = len; // 数据长度 for(uint8_t i=0; i<len; i++) { tx_msg.tx_data[i] = data[i]; // 填充数据 } can_message_transmit(can_periph, &tx_msg); }

使用示例:

uint8_t test_data[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; CAN_Send_Message(CAN0, 0x123, test_data, 8);

4.2 接收数据与中断处理

CAN接收通常采用中断方式,下面是完整的中断服务例程实现:

/* CAN0接收中断 */ void CAN0_RX0_IRQHandler(void) { can_receive_message_struct rx_msg; if(can_interrupt_flag_get(CAN0, CAN_INT_FLAG_RFF0)) { can_message_receive(CAN0, CAN_FIFO0, &rx_msg); // 处理接收到的数据 if(rx_msg.rx_ff == CAN_FF_STANDARD) { printf("收到标准帧, ID:0x%03X\n", rx_msg.rx_sfid); } else { printf("收到扩展帧, ID:0x%08lX\n", rx_msg.rx_efid); } // 清除中断标志 can_interrupt_flag_clear(CAN0, CAN_INT_FLAG_RFF0); } } /* CAN1接收中断 */ void CAN1_RX0_IRQHandler(void) { // 类似CAN0的处理逻辑 }

5. 常见问题排查

在实际开发中,你可能会遇到以下问题:

问题1:CAN通信无反应

  • 检查终端电阻是否正确连接(120Ω)
  • 确认波特率设置与对端设备一致
  • 使用逻辑分析仪抓取CAN总线波形

问题2:只能发送不能接收

  • 检查过滤器配置是否过于严格
  • 确认中断优先级和使能状态
  • 验证GPIO模式设置(RX应为上拉输入)

问题3:通信不稳定

  • 检查总线长度和布线质量
  • 适当调整时间段参数
  • 考虑增加CAN收发器的驱动能力

6. 进阶技巧与性能优化

当系统需要处理大量CAN消息时,可以考虑以下优化策略:

双缓冲接收机制

typedef struct { can_receive_message_struct buffer[2]; uint8_t active_buf; } CAN_Double_Buffer; CAN_Double_Buffer can0_rx_buf; void CAN0_RX0_IRQHandler(void) { uint8_t process_buf = 1 - can0_rx_buf.active_buf; can_message_receive(CAN0, CAN_FIFO0, &can0_rx_buf.buffer[process_buf]); can0_rx_buf.active_buf = process_buf; // 处理非活跃缓冲区的数据 }

波特率自动检测

uint32_t CAN_Auto_Baudrate_Detect(uint32_t can_periph) { uint32_t test_rates[] = {1000000, 800000, 500000, 250000, 125000}; for(uint8_t i=0; i<sizeof(test_rates)/sizeof(test_rates[0]); i++) { if(CAN_Test_Baudrate(can_periph, test_rates[i])) { return test_rates[i]; } } return 0; }

7. 完整项目示例

以下是一个简单的双CAN通信完整示例,实现两个CAN接口之间的数据回环:

#include "gd32f10x.h" #include <stdio.h> #define CAN0_RX_PORT GPIOA #define CAN0_RX_PIN GPIO_PIN_11 #define CAN0_TX_PORT GPIOA #define CAN0_TX_PIN GPIO_PIN_12 #define CAN1_RX_PORT GPIOB #define CAN1_RX_PIN GPIO_PIN_12 #define CAN1_TX_PORT GPIOB #define CAN1_TX_PIN GPIO_PIN_13 void CAN_Init_Config(void) { // 初始化代码如前文所示 } void CAN_Send_Message(uint32_t can_periph, uint32_t id, uint8_t *data, uint8_t len) { // 发送函数如前文所示 } void CAN0_RX0_IRQHandler(void) { can_receive_message_struct rx_msg; if(can_interrupt_flag_get(CAN0, CAN_INT_FLAG_RFF0)) { can_message_receive(CAN0, CAN_FIFO0, &rx_msg); // 将接收到的数据通过CAN1发送回去 CAN_Send_Message(CAN1, rx_msg.rx_sfid, rx_msg.rx_data, rx_msg.rx_dlen); can_interrupt_flag_clear(CAN0, CAN_INT_FLAG_RFF0); } } int main(void) { // 系统时钟初始化 rcu_clock_freq_set(RCU_CKSYSSRC_PLL, RCU_PLL_MUL9); SystemCoreClockUpdate(); // CAN初始化 CAN_Init_Config(); // 测试数据 uint8_t test_data[8] = {1,2,3,4,5,6,7,8}; while(1) { // 每隔1秒通过CAN0发送数据 CAN_Send_Message(CAN0, 0x123, test_data, 8); delay_ms(1000); // 测试数据递增 for(int i=0; i<8; i++) { test_data[i]++; } } }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/5 6:41:28

微信网页版访问革命:wechat-need-web 扩展的技术突破与实践指南

微信网页版访问革命&#xff1a;wechat-need-web 扩展的技术突破与实践指南 【免费下载链接】wechat-need-web 让微信网页版可用 / Allow the use of WeChat via webpage access 项目地址: https://gitcode.com/gh_mirrors/we/wechat-need-web 在当今数字化办公时代&…

作者头像 李华
网站建设 2026/5/5 6:38:40

ROS2 Humble + Mid360实战:从3D点云到2D导航地图的完整转换保姆级教程

ROS2 Humble Mid360实战&#xff1a;3D点云到2D导航地图的高效转换策略 在自主移动机器人开发中&#xff0c;将3D点云数据转换为实用的2D导航地图是一个关键环节。Mid360激光雷达因其高性价比和稳定性能&#xff0c;已成为许多机器人项目的首选传感器。本文将深入探讨如何在RO…

作者头像 李华
网站建设 2026/5/5 6:38:38

从“可能对”到“证明对”:我是如何用Dafny给祖传算法代码上保险的

从“可能对”到“证明对”&#xff1a;我是如何用Dafny给祖传算法代码上保险的 接手一个遗留系统时&#xff0c;最令人头疼的莫过于那些核心算法模块——它们往往由早已离职的同事编写&#xff0c;文档寥寥无几&#xff0c;但任何改动都可能引发连锁反应。我曾面对一个计算税金…

作者头像 李华
网站建设 2026/5/5 6:37:42

订单管理系统的战略价值与技术实践

1. 订单管理的战略价值与行业挑战在当今的商业环境中&#xff0c;订单管理已从简单的交易处理演变为企业核心竞争力的关键组成部分。根据行业调研数据&#xff0c;一个典型的订单流程平均需要跨越10个部门、涉及12个信息系统交互&#xff0c;而每个手动操作环节都可能成为潜在的…

作者头像 李华