
各系列 CAN 功能差异
GD32系列MCU有关CAN外设各系列功能差异如下表GD32各系列MCU CAN外设功能差异表所示。

15.3.硬件连接说明
CAN 外设硬件连接图

如图CAN外设硬件连接图所示,为典型的CAN外设硬件连接图:SN65HVD230是收发器,其作用就是把CAN控制器的TTL电平转换成差分信号。发送数据时,控制器把要发送的二进制编码通过CAN_TX线发送到收发器,然后由收发器把这个逻辑电平转换成差分信号,通过差分线CANH、CANL线输出到总线网络。当接收数据时,收发器把总线上收到的CANH、CANL信号转换成逻辑电平,通过CAN_RX输入到控制器。
读者可以根据典型硬件连接图和相应系列的Datasheet设计出自己的硬件连接方式。
15.4.软件配置说明
本小节讲解CAN_Example历程中CAN模块的配置说明,主要包括CAN外设配置、GPIO引脚配置、主函数介绍以及运行结果。本例程主要介绍GD32 MCU各系列CAN模块的数据发送、接收,有关CAN其他功能例程可参考各系列固件库历程。
CAN 外设配置
外设配置如代码清单CAN外设配置所示,在GD32全系列MCU中CAN外设的配置基本相同。GD32标准库提供了CAN初始化结构体及初始化函数来配置CAN外设,其初始化结构体说明如下表CAN参数初始化结构体说明列表和CAN过滤器初始化结构体说明列表所示。需要注意的是本例程需要用到两个开发板,一个发送,一个接收,这可以通过打开宏定义CAN_RECEIVE或CAN_TRANSMIT来决定当前是接收还是发送。
代码清单 CAN 外设配置
void can_config(void) { #if defined (GD32F30X_CL) ||(GD32F4XX) ||(GD32F20X_CL)||(GD32F10X_HD) can_parameter_struct can_parameter; can_filter_parameter_struct can_filter_parameter; can_struct_para_init(CAN_INIT_STRUCT, &can_parameter); can_struct_para_init(CAN_FILTER_STRUCT, &can_filter_parameter); /* initialize CAN register */ can_deinit(CAN0); /* initialize CAN parameters */ can_parameter.time_triggered = DISABLE; can_parameter.auto_bus_off_recovery = DISABLE; can_parameter.auto_wake_up = DISABLE; can_parameter.no_auto_retrans = DISABLE; 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_5TQ; can_parameter.time_segment_2 = CAN_BT_BS2_4TQ; can_parameter.prescaler = 12; /* initialize CAN */ can_init(CAN0, &can_parameter); /* initialize filter */ can_filter_parameter.filter_number = 0; can_filter_parameter.filter_mode = CAN_FILTERMODE_MASK; can_filter_parameter.filter_bits = CAN_FILTERBITS_32BIT; can_filter_parameter.filter_list_high = 0x0000; can_filter_parameter.filter_list_low = 0x0000; can_filter_parameter.filter_mask_high = 0x0000; can_filter_parameter.filter_mask_low = 0x0000; can_filter_parameter.filter_fifo_number = CAN_FIFO0; can_filter_parameter.filter_enable = ENABLE; can_filter_init(&can_filter_parameter); #if defined (CAN_RECEIVE) #if defined (GD32F10X_HD) nvic_irq_enable(USBD_LP_CAN0_RX0_IRQn,0,0); #else nvic_irq_enable(CAN0_RX0_IRQn,0,0); #endif /* enable can receive FIFO0 not empty interrupt */ can_interrupt_enable(CAN0, CAN_INT_RFNE0); #endif #elif defined (GD32E10X) can_parameter_struct can_parameter; can_struct_para_init(CAN_INIT_STRUCT, &can_parameter); /* initialize CAN register */ can_deinit(CAN0); /* initialize CAN parameters */ can_parameter.time_triggered = DISABLE; can_parameter.auto_bus_off_recovery = DISABLE; can_parameter.auto_wake_up = DISABLE; can_parameter.auto_retrans = DISABLE; can_parameter.rec_fifo_overwrite = DISABLE; can_parameter.trans_fifo_order = DISABLE; can_parameter.working_mode = CAN_NORMAL_MODE; /* initialize CAN */ can_init(CAN0, &can_parameter); /* config CAN0 baud rate */ can_frequency_set(CAN0, DEV_CAN_BAUD_RATE); /* initialize filter */ can_filter_mask_mode_init(DEV_CAN_ID, DEV_CAN_MASK, CAN_EXTENDED_FIFO0, 0); #if defined (CAN_RECEIVE) /* configure CAN0 NVIC */ nvic_irq_enable(CAN0_RX0_IRQn, 0, 0); /* enable can receive FIFO0 not empty interrupt */ can_interrupt_enable(CAN0, CAN_INTEN_RFNEIE0); #endif #elif defined (GD32F1X0) can_parameter_struct can_parameter; can_filter_parameter_struct can_filter_parameter; /* initialize CAN register */ can_deinit(CAN1); /* initialize CAN parameters */ can_parameter.time_triggered = DISABLE; can_parameter.auto_bus_off_recovery = DISABLE; can_parameter.auto_wake_up = DISABLE; can_parameter.no_auto_retrans = DISABLE; 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_4TQ; can_parameter.time_segment_2 = CAN_BT_BS2_3TQ; can_parameter.prescaler = 18; /* initialize CAN */ can_init(CAN1, &can_parameter); /* initialize filter */ can_filter_parameter.filter_number = 15; can_filter_parameter.filter_mode = CAN_FILTERMODE_MASK; can_filter_parameter.filter_bits = CAN_FILTERBITS_32BIT; can_filter_parameter.filter_list_high = 0x0000; can_filter_parameter.filter_list_low = 0x0000; can_filter_parameter.filter_mask_high = 0x0000; can_filter_parameter.filter_mask_low = 0x0000; can_filter_parameter.filter_fifo_number = CAN_FIFO0; can_filter_parameter.filter_enable = ENABLE; can_filter_init(&can_filter_parameter); #if defined (CAN_RECEIVE) /* configure CAN1 NVIC */ nvic_irq_enable(CAN1_RX0_IRQn,0,0); can_interrupt_enable(CAN1, CAN_INTEN_RFNEIE0); #endif #endif }
CAN 参数初始化结构体说明列表

CAN 过滤器初始化结构体说明列表

GPIO 引脚配置
GPIO引脚配置如代码清单CAN例程GPIO引脚配置所示。GD32F10X、GD32F30X、GD32F20X、GD32E10X系列用到remap功能,因此需要打开AF时钟。使用GD32F1X0时,例程中是使用CAN1来进行收发。需要注意的是,发送时,不需要打开CAN0的时钟,但是接收时需要打开CAN0的时钟。
代码清单 CAN 例程 GPIO 引脚配置
void can_gpio_config(void) { #if defined (GD32F4XX) /* enable CAN0 clock */ rcu_periph_clock_enable(RCU_CAN0); rcu_periph_clock_enable(RCU_GPIOB); /* configure CAN0 GPIO */ gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8); gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8); gpio_af_set(GPIOB, GPIO_AF_9, GPIO_PIN_8); gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9); gpio_af_set(GPIOB, GPIO_AF_9, GPIO_PIN_9); #elif defined (GD32E10X) || (GD32F30X_CL) ||(GD32F10X_HD) /* enable CAN0 clock */ rcu_periph_clock_enable(RCU_CAN0); rcu_periph_clock_enable(RCU_GPIOD); rcu_periph_clock_enable(RCU_AF); /* configure CAN0 GPIO */ gpio_init(GPIOD,GPIO_MODE_IPU,GPIO_OSPEED_50MHZ,GPIO_PIN_0); gpio_init(GPIOD,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_1); #if defined (GD32F10X_HD) gpio_pin_remap_config(GPIO_CAN_FULL_REMAP,ENABLE); #else gpio_pin_remap_config(GPIO_CAN0_FULL_REMAP,ENABLE); #endif #elif defined (GD32F20X_CL) /* enable CAN0 clock */ rcu_periph_clock_enable(RCU_CAN0); rcu_periph_clock_enable(RCU_GPIOB); rcu_periph_clock_enable(RCU_AF); /* configure CAN0 GPIO */ 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); #elif defined (GD32F1X0) //only GD32F170 and GD32F190 have CAN, GD32F130 and GD32F150 don't have /* enable CAN clock */ #if defined (CAN_RECEIVE) rcu_periph_clock_enable(RCU_CAN0); #endif rcu_periph_clock_enable(RCU_CAN1); rcu_periph_clock_enable(RCU_GPIOB); /* configure CAN1 GPIO */ gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_12); gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12); gpio_af_set(GPIOB, GPIO_AF_9, GPIO_PIN_12); gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_13); gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13); gpio_af_set(GPIOB, GPIO_AF_9, GPIO_PIN_13); #endif }
主函数配置
主函数配置如代码清单主函数配置所示。本例程需要两个开发板,一个作为发送,一个作为接收,这可以通过使用宏定义CAN_RECEIVE和CAN_TRANSMIT来实现。如果作为接收,那么主函数的开始会把can_receive_flag设置为RESET,并初始化LED2。LED2作为通讯的指示灯,如果通讯成功,就点亮LED2,否则就不点亮。can_receive_flag状态的改变在中断里实现,如果成功接收到数据,就会进入中断,在中断里把can_receive_flag设置为SET。主函数接下来对CAN的GPIO以及CAN模块进行初始化。完成后,如果是作为发送方,程序需要设置需要发送的数据。有关发送结构体参数的说明,请见下表CAN发送消息初始化结构体说明列表。
代码清单主函数配置
int main(void) { #if defined (CAN_RECEIVE) can_receive_flag = RESET; LED2_init(); #endif /* GPIO config */ can_gpio_config(); /* CAN config */ can_config(); #if defined (CAN_TRANSMIT) /* initialize transmit message */ #if defined (GD32E10X) || (GD32F30X_CL) || (GD32F10X_HD) || (GD32F4XX) || (GD32F20X_CL) can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_transmit_message); #endif g_transmit_message.tx_sfid = 0x00; g_transmit_message.tx_efid = DEV_CAN_ID; g_transmit_message.tx_ft = CAN_FT_DATA; g_transmit_message.tx_ff = CAN_FF_EXTENDED; g_transmit_message.tx_dlen = 4; g_transmit_message.tx_data[0] = 0xaa; g_transmit_message.tx_data[1] = 0xbb; g_transmit_message.tx_data[2] = 0xcc; g_transmit_message.tx_data[3] = 0xdd; #if defined (GD32E10X) || (GD32F30X_CL) || (GD32F10X_HD) || (GD32F4XX) || (GD32F20X_CL) can_message_transmit(CAN0, &g_transmit_message); #elif defined (GD32F1X0) can_message_transmit(CAN1, &g_transmit_message); #endif #endif #if defined (CAN_RECEIVE) while(can_receive_flag == RESET); if((g_receive_message.rx_data[0] == 0xaa) && (g_receive_message.rx_data[1] == 0xbb)&& (g_receive_message.rx_data[2] == 0xcc) && (g_receive_message.rx_data[3] == 0xdd)) { LED2_ON(); } else { LED2_OFF(); } #endif while(1); }
CAN 发送消息初始化结构体说明列表

运行结果
本例程需要用到两个开发板,用杜邦线把两个开发板的CAN_H、CAN_L分别连接起来。打开CAN_Example例程,选择好对应开发板的芯片工程后,先屏蔽掉CAN_RECEIVE,不屏蔽CAN_TRANSMIT ,编译后把程序下载到一号开发板上。然后换一块开发板,屏蔽掉CAN_TRANSMIT,不屏蔽CAN_RECEIVE,编译后把程序下载到二号开发板上。接下来按下二号开发板的复位键,然后再按下一号开发板的复位键,此时会看到二号开发板的LED2被点亮,这表明通讯成功。
15.5.CAN 使用注意事项
(1) 使用F10X、F20X、F30X、F1X0、E10X、F403接收数据时如果出现接收两帧数据会丢失一包的情况,这是由于手动多调用一次清缓存的操作导致的。因此,软件中无需调用can_fifo_release函数;
(2) 使用F10X、F20X、F1X0时,会出现CAN离线后无法自动恢复,这是由于CAN模块的离线自动恢复功能与CAN协议定义的离线恢复序列存在一定理解误差造成的。该状况可以通过使能离线中断,在离线中断内重新初始化CAN模块来规避;
(3) GD32F170和GD32F190的CAN0内置PHY,其CANH和CANL口的耐压范围为VSS-0.3到VSS+7.5,内置的PHY不支持12V和24V系统。当使用CAN0时,建议硬件上按照下图CAN0引脚连接图连接。
CAN0 引脚连接图

上一篇:【GD32 MCU 入门教程】GD32 MCU 常见外设介绍(1)RCU 时钟介绍
下一篇:【GD32 MCU 入门教程】GD32 MCU 常见外设介绍(3)NVIC 介绍
推荐阅读最新更新时间:2026-02-26 10:42



使用PIC单片机控制心电前端ADS1192的实现单导心电测量
Follow me第三季第4期任务
非常经典的关于LLC的杨波博士论文
NJU7096M(T2)
XC6406PP60DL






京公网安备 11010802033920号