【GD32篇】CAN总线入门教程——实现数据收发

2023-10-27

本文主要介绍CAN总线的软件配置

1. 简介

        CAN 总线协议已经成为汽车计算机控制系统和嵌入式工业控制局域网的标准总线,并且拥有以 CAN 为底层协议专为大型货车和重工机械车辆设计的 J1939 协议。近年来,它具有的高可靠性和良好的错误检测能力受到重视,被广泛应用于汽车计算机控制系统和环境温度恶劣、电磁辐射强及振动大的工业环境。
        
        CAN 总线控制器作为 CAN 网络接口,遵循 CAN 总线协议 2.0A 2.0B CAN 总线控制器 可以处理总线上的数据收发,非 GD32130x CL 系列产品中,具有 14 个过滤器,在 GD32F10xCL 系列产品中, CAN 具有 28 个过滤器,过滤器用于筛选并接收用户需要的消息。用户可以通过 3 个发送邮箱将待发送数据传输至总线,邮箱发送的顺序由发送调度器决定。并通过 2 个深度为 3 的接收 FIFO 获取总线上的数据,接收 FIFO 的管理完全由硬件控制。同时 CAN 总线控制器硬件支持时间触发通信(Time-trigger communication )功能。

2.GD32的CAN外设介绍

MCU:GD32F103VET6

2.1 CAN初始化结构体

API:ErrStatus can_init(uint32_t can_periph, can_parameter_struct* can_parameter_init)

can_periph:CANx(x=0,1), CAN1 仅适用于 GD32F10x_CL

can_parameter_struct:初始化结构体

ErrStatus: SUCCESS or ERROR

/* CAN initiliaze parameters structure */
typedef struct
{
    uint8_t working_mode;                         /*!< 配置CAN的工作模式 */ 
    uint8_t resync_jump_width;                    /*!< 重新同步跳跃宽度 */
    uint8_t time_segment_1;                       /*!< 配置 BS1 段长度 */
    uint8_t time_segment_2;                       /*!< 配置 BS2 段长度 */
    ControlStatus time_triggered;                 /*!< 时间触发通信方式 */
    ControlStatus auto_bus_off_recovery;          /*!< 自动离线管理 */
    ControlStatus auto_wake_up;                   /*!< 自动唤醒功能 */
    ControlStatus auto_retrans;                   /*!< 自动重传模式 */
    ControlStatus rec_fifo_overwrite;             /*!< 配置接收 FIFO 锁定 */
    ControlStatus trans_fifo_order;               /*!< 配置 FIFO 优先级 */
    uint16_t prescaler;                           /*!< 配置CAN外设的时钟频率 */
}can_parameter_struct;

(1)working_mode

        设置CAN的工作模式,可设置为正常通信模式(CAN_NORMAL_MODE),回环通信模式(CAN_LOOPBACK_MODE),静默通信模式(CAN_SILENT_MODE)以及回环静默通信模式(CAN_SILENT_LOOPBACK_MODE)。

         CAN 总线控制器通常工作在正常通信模式下,可以从 CAN 总线接收数据,也可以向 CAN 总线发送数据。(详情请查看GD32F10x用户手册) 

(2)resync_jump_width

        设置CAN的再同步补偿宽度SJW,对CAN网络节点同步误差进行补偿占1~4个时间单元。(CAN_BT_SJW_1/2/3/4TQ)

(3)time_segment_1

        设置CAN位时序中BS1段长度,可以配置为1~16个时间单元。(CAN_BT_BS1_1~16TQ)

(4)time_segment_2

        设置CAN位时序中BS2段长度,可以配置为1~8个时间单元。(CAN_BT_BS2_1~8TQ)

(5)time_triggered

        用于配置是否使用时间触发功能(ENABLE / DISABLE)。在这种通信模式下,自动重发功能是禁止的。

(6)auto_bus_off_recovery

        用于配置是否使用自动离线管理功能(ENABLE / DISABLE),使用自动离线管理可以在节点出错离线后适应自动恢复,不需要软件干预。

(7)auto_wake_up

        用于配置是否使用自动唤醒功能(ENABLE / DISABLE),使用自动唤醒功能后会在检测到总线活动后自动唤醒。

(8)auto_retrans

        用于配置是否使用自动重传功能(ENABLE / DISABLE),使用自动重传功能时,会一直发送报文直到成功为止,否则只会发送一次报文。

(9)rec_fifo_overwrite

        用于配置是否使用接收FIFO锁定功能(ENABLE / DISABLE),锁定接收FIFO后,FIFO溢出时会丢弃新数据,否则在FIFO溢出时新数据覆盖旧数据。

(10)trans_fifo_order

        用于设置是否使用发送报文的优先级判定方法(ENABLE / DISABLE),使能后,以报文存入FIFO的先后顺序来发送,否则按照报文ID的优先级来发送。

(11)prescaler

        设置CAN外设的时钟分频,写入的值即为分频值。可控制时间片的时间长度。

波特率计算:

快速计算:BaudRate = APB1_LCK / ( 1 + time_segment_1 +time_segment_2 ) / prescaler

例:APB1总线时钟频率为54MHZ,BS1=5,BS2=3,prescaler=12,实际波特率为500Kbps。

2.2 CAN过滤器结构体

API:void can_filter_init(can_filter_parameter_struct* can_filter_parameter_init)

can_filter_parameter_struct:过滤器结构体

/* CAN filter parameters structure */
typedef struct
{
    uint16_t filter_list_high;                 /*!< 过滤器列表高位数 */
    uint16_t filter_list_low;                  /*!< 过滤器列表低位数 */
    uint16_t filter_mask_high;                 /*!< 滤波掩码数高位数 */
    uint16_t filter_mask_low;                  /*!< 滤波掩码数低位数 */
    uint16_t filter_fifo_number;               /*!< 接收与过滤器相关联的FIFO */
    uint16_t filter_number;                    /*!< 筛选器编号 */
    uint16_t filter_mode;                      /*!< 列表或掩码模式 */
    uint16_t filter_bits;                      /*!< 筛选器位宽 */
    ControlStatus filter_enable;               /*!< 是否使能改筛选器 */
}can_filter_parameter_struct;

 注:SFID为标准帧ID,长度为11bit;EFID为扩展帧ID,长度为18bit。

         FF为帧格式(0:标准帧 / 1:扩展帧),FT为帧类型(0:数据帧 / 1:遥控帧)

(1)filter_list_high

        用于存储要过滤的ID,若过滤器工作在32位模式,他存储的是所过滤ID的高16位;若过滤器工作在16位模式,它存储的就是一个完整的要过滤的ID。

(2)filter_list_low

        用于存储要过滤的ID,若过滤器工作在32位模式,他存储的是所过滤ID的低16位;若过滤器工作在16位模式,它存储的就是一个完整的要过滤的ID。

(3)filter_mask_high

        filter_mask_high的存储的内容分两种情况,当过滤器工作在列表模式时,他的功能与filter_list_high相同,都是存储要过滤的ID;当工作在掩码模式时,它存储的是与filter_list_high成员对应的掩码。

(4)filter_mask_low

        filter_mask_low的存储的内容分两种情况,当过滤器工作在列表模式时,他的功能与filter_list_low相同,都是存储要过滤的ID;当工作在掩码模式时,它存储的是与filter_list_low成员对应的掩码。

 在掩码模式下,filter_mask_high与filter_mask_low填入的是要筛选的掩码,掩码为1时,筛选的ID必须与ID相同。

列表模式下,筛选的ID需与ID相同。

(5)filter_fifo_number

        用于设置当报文通过过滤器匹配后,该报文会被存储到哪个接收FIFO中(CAN_FIFO0/1)。

(6)filter_number

        用于设计过滤器的编号。

(7)filter_mode

        设置过滤器的工作模式,可以设置为列表模式(CAN_FILTERMODE_LIST),也可以设置为掩码模式(CAN_FILTERMODE_MASK)。

(8)filter_bits

        设置过滤器位宽(32位或16位)。

(9)filter_enable

        用于设置是否使能这个过滤器(ENABLE/DISABLE)。

2.3 CAN发送及接收结构体

API:

uint8_t can_message_transmit(uint32_t can_periph, can_trasnmit_message_struct* transmit_message);//发送CAN报文

void can_message_receive(uint32_t can_periph, uint8_t fifo_number, can_receive_message_struct* receive_message);//接收CAN报文

/* CAN 发送报文结构 */
typedef struct
{
    uint32_t tx_sfid;                      /*!< 标准帧ID */
    uint32_t tx_efid;                      /*!< 扩展帧ID */
    uint8_t tx_ff;                         /*!< 帧格式,标准帧或数据帧 */
    uint8_t tx_ft;                         /*!< 帧类型,数据帧或遥控帧 */
    uint8_t tx_dlen;                       /*!< 数据长度 */
    uint8_t tx_data[8];                    /*!< 传输的数据 */
}can_trasnmit_message_struct;

/* CAN 接收报文结构 */
typedef struct
{
    uint32_t rx_sfid;                       /*!< 标准帧ID */
    uint32_t rx_efid;                       /*!< 扩展帧ID */
    uint8_t rx_ff;                          /*!< 帧格式,标准帧或数据帧 */
    uint8_t rx_ft;                          /*!< 帧类型,数据帧或遥控帧 */
    uint8_t rx_dlen;                        /*!< 数据长度 */
    uint8_t rx_data[8];                     /*!< 接收的数据 */
    uint8_t rx_fi;                          /*!< 过滤器编号 */
} can_receive_message_struct;

3. 硬件与引脚连接

MCU:GD32F103VET6;

CAN模块;//CAN收发器,GD32的CAN只是控制器

RS232_CAN模块;//用于单片机与电脑通信

 3.1 MCU引脚选择:

寄存器 CAN0
CAN0_REMAP[1:0]="00"

PA11(CAN0_RX)

PA12(CAN0_TX)

CAN0_REMAP[1:0]="10"

PB8(CAN0_RX)

PB9(CAN0_TX)

CAN0_REMAP[1:0]="11"

PD0(CAN0_RX)

PD1(CAN0_TX)

        在这3组引脚中可任意选择一组作为CAN0的通信引脚。使用PB8、PB9或PD0、PD1时,需分别打开GPIO的部分重映射和完全重映射功能,本文中使用PB8、PB9两个引脚。

API:void gpio_pin_remap_config(uint32_t remap, ControlStatus newvalue)

GPIO_CAN_PARTIAL_REMAP:CAN部分重映射

GPIO_CAN_FULL_REMAP:CAN全重映射

3.2 MCU与CAN模块连接

GD32F103 CAN收发器
3.3V或5V VCC
GND GND
PB8 RX
PB9 TX

        在数据通信引脚连接时,不需要反接,MCU的RX接CAN收发器RX即可。

3.3 CAN模块与RS232_CAN模块连接

没有RS232_CAN模块也可使用双机通信,RS232_CAN模块实际也是一个单片机与CAN收发器连接与电脑进行通信。

CAN模块 RS232_CAN
CAN_H CAN_H
CAN_L CAN_L
GND GND
5V(MCU) 5-24V

4. 驱动代码

4.1 CAN初始化配置

/*
	\brief:	CAN0初始化配置 波特率500Kbps
	引脚连接:	CAN_TX   PB9
				CAN_RX   PB8
	\param:	none
	\retval:	none
*/

void CAN_Init(void)
{
	can_parameter_struct can0_param_struct;
	can_filter_parameter_struct can0_filter_param_struct;	//筛选器结构体
	//1.引脚初始化配置
	gpio_pin_remap_config(GPIO_CAN_PARTIAL_REMAP,ENABLE);	//开启CAN部分重映射
	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);
	
	//2.初始化CNA0
	can_deinit(CAN0);										//复位CAN0
	can0_param_struct.working_mode = CAN_NORMAL_MODE;		//正常通信模式
	can0_param_struct.time_triggered = DISABLE;				//静止时间触发通信
	can0_param_struct.auto_bus_off_recovery = DISABLE;		//静止离线恢复
	can0_param_struct.auto_wake_up = DISABLE;				//禁止自动唤醒
	can0_param_struct.auto_retrans = ENABLE;				//使能自动重传
	can0_param_struct.rec_fifo_overwrite = DISABLE;			//禁用接收FIFO锁定
	can0_param_struct.trans_fifo_order = DISABLE;			//禁用FIFO优先级
	//波特率 54M/(1+3+5)/12
	can0_param_struct.resync_jump_width = CAN_BT_SJW_1TQ;	//再同步宽度补偿 1TQ
	can0_param_struct.time_segment_1 = CAN_BT_BS1_3TQ;		//位段1长度
	can0_param_struct.time_segment_2 = CAN_BT_BS2_5TQ;		//位段2长度
	can0_param_struct.prescaler = 12;						//预分频系数
	if(can_init(CAN0,&can0_param_struct)==ERROR)
	{
		return ;
	}
	//3.配置CAN过滤器
	can0_filter_param_struct.filter_list_high = (((uint32_t)0x000<<21|CAN_FT_DATA|CAN_FF_STANDARD)&0xFFFF0000)>>16;	//过滤器高字节
	can0_filter_param_struct.filter_list_low =  (((uint32_t)0x000<<21|CAN_FT_DATA|CAN_FF_STANDARD) & 0x0000FFFF);	//过滤器低字节
	can0_filter_param_struct.filter_mask_high = 0x0000;			//过滤器掩码数高位
	can0_filter_param_struct.filter_mask_low = 	0x0000;			//过滤器掩码数低位
	can0_filter_param_struct.filter_fifo_number = CAN_FIFO0;	//滤过器关联FIFO0
	can0_filter_param_struct.filter_number = 0;
	can0_filter_param_struct.filter_mode = CAN_FILTERMODE_MASK;	//掩码模式
	can0_filter_param_struct.filter_bits = CAN_FILTERBITS_32BIT;//32位
	can0_filter_param_struct.filter_enable = ENABLE;
	can_filter_init(&can0_filter_param_struct);
	//4.配置中断
	can_interrupt_enable(CAN0,CAN_INT_RFNE0);
}

(1)配置GPIO工作模式。配置CAN部分重映射,将PB8配置为上拉输入模式,PB9配置为复用推挽输出模式。

(2)初始化CAN0。该结构体重要的是将CAN配置为正常通信模式,并配置波特率,其他功能配置可根据实际需求配置。值得注意的是,在配置波特率时需要配置准确,否则将无法进行通信。

(3)配置过滤器。在这里选择了32位掩码模式,关联FIFO0。过滤器掩码高低位都设置为0x0000000,接收所有ID的数据。这里先确保可以接收到数据。

 filter_list_high和filter_list_low中需要填入需要筛选的ID,由上图可知,32位过滤器中,

[2:0]位保存的是数据帧格式和帧类型。此处设置的帧格式为标准帧CAN_FF_STANDARD(也可设置为扩展帧CAN_FF_EXTENDED)。帧类型设置为数据帧CAN_FT_DATA(也可设置为数据帧)。

[20:3]保存扩展帧ID。

[31:21]保存标准帧ID。此处将标准帧ID(0x000)左移21位。

filter_mask_high和filter_mask_low保存过滤器掩码数,用于筛选需要的ID。在此设置为0,表示接收所有ID的数据。

(4)使能FIFO不为空中断。

4.2 发送CAN报文

/*
	\brief:	发送CAN报文
	\param:	id:标准帧ID
				data:要发送的数据
				len:数据长度 0~8byte
	\retval:	none
*/
uint8_t CAN_Send_Message(uint32_t id,uint8_t *data,uint8_t len)
{
	uint8_t i,mbox;
	can0_Tx_Data.tx_efid = 0x00;
	can0_Tx_Data.tx_sfid = id;
	can0_Tx_Data.tx_ff = CAN_FF_STANDARD;//标准帧
	can0_Tx_Data.tx_ft = CAN_FT_DATA;//数据帧
	can0_Tx_Data.tx_dlen = len;
	for(i=0;i<len;i++)
	{
		can0_Tx_Data.tx_data[i] = data[i];
	}
	mbox = can_message_transmit(CAN0,&can0_Tx_Data);				//发送CAN报文
	while(can_transmit_states(CAN0,mbox) == CAN_TRANSMIT_PENDING);	//等待发送完成
	if(can_transmit_states(CAN0,mbox) == CAN_TRANSMIT_OK)
	{
		return 0;
	}
	return 1;
}

发送CAN报文主要是填充can_trasnmit_message_struct 结构体,调用can_message_transmit函数发送报文即可。在等待发送完成中可加入超时重发或者强制退出,避免函数卡死。

4.3 接收CAN报文

//中断服务函数,接收CAN报文
void USBD_LP_CAN0_RX0_IRQHandler(void)
{
	if(can_interrupt_flag_get(CAN0,CAN_INT_FLAG_RFL0))
	{
		memset(&can0_Rx_Data,0,sizeof(can0_Rx_Data));//清空接收结构体
        can_message_receive(CAN0,CAN_FIFO0,&can0_Rx_Data);//获取信息
	}
}

        FIFO非空是会触发CAN非空中断,首先初始化接收结构体,然后读取FIFO中的数据。在GD32F103的CAN中断中没有清除非空标志位这个函数,can_message_receive接收CAN报文函数中不仅将数据保存至结构体中,还释放了一次数据。

 观用户手册可知,FIFO中可保存3帧报文。FIFO0帧的数量保存在CAN_RFIFO0寄存器的RFL0[1:0]中,置位RFD0将释放一次FIFO中帧的数据即RFL0[1:0] - 1。

 4.4 main函数

#include "main.h"


void rcu_config(void);
void nvic_config(void);


/*!
    \brief      main function
    \param[in]  none
    \param[out] none
    \retval     none
*/

int main(void)
{
    //gpio_afio_deinit();
	/* system clocks configuration */
    rcu_config();
	nvic_config();
    /* systick configuration */
    systick_config();
	UART_init();
    /* Initialize the peripheral module */
	CAN_Init();
	uint8_t t_data[8]={0,99,3,4,5,55,7,110};
    while(1){
		CAN_Send_Message(0x0000,t_data,8);
		delay_1ms(1000);
	}
}

/*!
    \brief      configure the different system clocks
    \param[in]  none
    \param[out] none
    \retval     none
*/
void rcu_config(void)
{
    /* enable GPIO clock */
	rcu_periph_clock_enable(RCU_AF);
	rcu_periph_clock_enable(RCU_GPIOA);
	rcu_periph_clock_enable(RCU_GPIOB);
	rcu_periph_clock_enable(RCU_GPIOC);
    rcu_periph_clock_enable(RCU_GPIOD);
	rcu_periph_clock_enable(RCU_GPIOE);
    /* enable periph clock */
    rcu_periph_clock_enable(RCU_CAN0);
	rcu_periph_clock_enable(RCU_DMA0);
	rcu_periph_clock_enable(RCU_USART2);
	rcu_periph_clock_enable(RCU_UART3);
}

/*!
    \brief      enable the different NVIC request
    \param[in]  none
    \param[out] none
    \retval     none
*/
void nvic_config(void)
{
	nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
	nvic_irq_enable(USBD_LP_CAN0_RX0_IRQn,1,0);
}

main.c中还是一如既往的配置外设时钟,配置中断优先级和初始化外设。

(1)时钟配置:在使用过程中只需使能AFIO、GPIOB和CAN0时钟即可。

(2)配置中断优先级分组和中断优先级。

(3)初始化外设。

(4)每秒钟向外发送一次报文。

4.5 实验结果

电脑接收报文:软件配置波特率为500Kbps

         在RS232_CAN软件上,发送和读取的数据为16进制。MCU发送的数据与电脑上接收的数据一致。

MCU接收报文:

         使用KEIL软件上的调试功能,发送数据后,代码运行至打断点位置,单步运行至函数最下方,读取到的数据保存至接收CAN报文结构体中。数据长度为8个字节,数据与发送的数据一致。RS232_CAN软件上的标准帧ID设置为0x000,所以MCU中读取到的ID也为0x000。使用双机进行通信的代码配置和操作基本相同。

5. 使用筛选功能

        上述测试中将过滤器掩码设置为0,即接收所有ID报文,无法体现CAN的筛选器功能。以下将修改ID和掩码,测试是否能筛选特定ID。

5.1 筛选固定ID数据

本次测试只接收一个ID的数据,其他ID数据不接收。

RS232_CAN帧模式

 标准帧ID范围为0x000~0x7FF,共11为数据。此次测试只接收ID为0x7FF的标准数据帧,其他数据帧一律不接收。

过滤器中写入的ID:(32位宽)

过滤器  1111 1110 0000 0000 0000 0000 0000 0000    //[31:21]位为标准帧ID

掩码      1111 1110 0000 0000 0000 0000 0000 0110     //[2:1]筛选标准数据帧,[31:21]筛选ID

掩码中填入的数据即:0xFE00 0006;      //扩展ID位也可全填入1

void CAN_Init(void)
{
	can_parameter_struct can0_param_struct;
	can_filter_parameter_struct can0_filter_param_struct;	//筛选器结构体
	//1.引脚初始化配置
	gpio_pin_remap_config(GPIO_CAN_PARTIAL_REMAP,ENABLE);	//开启CAN部分重映射
	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);
	
	//2.初始化CNA0
	can_deinit(CAN0);										//复位CAN0
	can0_param_struct.working_mode = CAN_NORMAL_MODE;		//正常通信模式
	can0_param_struct.time_triggered = DISABLE;				//静止时间触发通信
	can0_param_struct.auto_bus_off_recovery = DISABLE;		//静止离线恢复
	can0_param_struct.auto_wake_up = DISABLE;				//禁止自动唤醒
	can0_param_struct.auto_retrans = ENABLE;				//使能自动重传
	can0_param_struct.rec_fifo_overwrite = DISABLE;			//禁用接收FIFO锁定
	can0_param_struct.trans_fifo_order = DISABLE;			//禁用FIFO优先级
	//波特率 54M/(1+3+5)/12
	can0_param_struct.resync_jump_width = CAN_BT_SJW_1TQ;	//再同步宽度补偿 1TQ
	can0_param_struct.time_segment_1 = CAN_BT_BS1_3TQ;		//位段1长度
	can0_param_struct.time_segment_2 = CAN_BT_BS2_5TQ;		//位段2长度
	can0_param_struct.prescaler = 12;						//预分频系数
	if(can_init(CAN0,&can0_param_struct)==ERROR)
	{
		return ;
	}
	//3.配置CAN过滤器
	can0_filter_param_struct.filter_list_high = (((uint32_t)0x7FF<<21|CAN_FT_DATA|CAN_FF_STANDARD)&0xFFFF0000)>>16;	//过滤器高字节
	can0_filter_param_struct.filter_list_low =  (((uint32_t)0x7FF<<21|CAN_FT_DATA|CAN_FF_STANDARD) & 0x0000FFFF);	//过滤器低字节
	can0_filter_param_struct.filter_mask_high = 0xFFE0;			//过滤器掩码数高位
	can0_filter_param_struct.filter_mask_low = 	0x0006;			//过滤器掩码数低位
	can0_filter_param_struct.filter_fifo_number = CAN_FIFO0;	//滤过器关联FIFO0
	can0_filter_param_struct.filter_number = 0;
	can0_filter_param_struct.filter_mode = CAN_FILTERMODE_MASK;	//掩码模式
	can0_filter_param_struct.filter_bits = CAN_FILTERBITS_32BIT;//32位
	can0_filter_param_struct.filter_enable = ENABLE;
	can_filter_init(&can0_filter_param_struct);
	//4.配置中断
	can_interrupt_enable(CAN0,CAN_INT_RFNE0);
}

代码只修改了以下几处。

测试1:ID号与帧格式相同

发送:00 00 00 07 FF 08 11 22 33 44 55 66 77 88

接收:ID:0x7FF(标准数据帧)

数据长度:8

数据: 11 22 33 44 55 66 77 88

 测试2:帧格式相同,ID不同

发送:00 00 00 06 FF 08 11 22 33 44 55 66 77 88

接收:无接收,程序未进入中断。

 测试3:ID相同,数据帧不同

发送:01 00 00 07 FF 08 11 22 33 44 55 66 77 88

接收:无接收,未程序未进入中断。

测试成功!

5.2 接收多个ID数据

在具体使用过程中,会存在接收多个ID数据的情况,本次测试只接收多个固定ID的数据。

例如:标准帧ID:0x000~0x7FF.

只接收标准帧ID为:0x200~0x2FF的数据。

过滤器    0010 0000 0000 0000 0000 0000 0000 0000

掩码        1110 0000 0000 0000 0000 0000 0000 0110

掩码中填入0xE000 0006。

由于只接收ID为0x200~0x2FF的数据,只需将掩码的[31:29]位都置一。

测试1:写入范围内的标准数据帧ID

写入:00 00 00 02 00 08 11 22 33 44 55 66 77 88

接收:ID:0x200(标准数据帧)

数据长度:8

数据:11 22 33 44 55 66 77 88 

 写入:00 00 00 02 19 08 11 22 33 44 55 66 77 99

接收:ID:0x219(标准数据帧)

数据长度:8

数据:11 22 33 44 55 66 77 99

测试2:写入范围外的ID

 发送:00 00 00 03 19 08 11 22 33 44 55 66 77 99

接收:无接收(程序未进入中断)

测试成功!

6. 参考程序

 can.c


#include "can.h"
#include <string.h>
can_receive_message_struct can0_Rx_Data;
can_trasnmit_message_struct can0_Tx_Data;
/*
	\brief:	CAN0初始化配置 波特率500Kbps
	引脚连接:	CAN_TX   PB9
				CAN_RX   PB8
	\param:	none
	\retval:	none
*/

void CAN_Init(void)
{
	can_parameter_struct can0_param_struct;
	can_filter_parameter_struct can0_filter_param_struct;	//筛选器结构体
	//1.引脚初始化配置
	gpio_pin_remap_config(GPIO_CAN_PARTIAL_REMAP,ENABLE);	//开启CAN部分重映射
	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);
	
	//2.初始化CNA0
	can_deinit(CAN0);										//复位CAN0
	can0_param_struct.working_mode = CAN_NORMAL_MODE;		//正常通信模式
	can0_param_struct.time_triggered = DISABLE;				//静止时间触发通信
	can0_param_struct.auto_bus_off_recovery = DISABLE;		//静止离线恢复
	can0_param_struct.auto_wake_up = DISABLE;				//禁止自动唤醒
	can0_param_struct.auto_retrans = ENABLE;				//使能自动重传
	can0_param_struct.rec_fifo_overwrite = DISABLE;			//禁用接收FIFO锁定
	can0_param_struct.trans_fifo_order = DISABLE;			//禁用FIFO优先级
	//波特率 54M/(1+3+5)/12
	can0_param_struct.resync_jump_width = CAN_BT_SJW_1TQ;	//再同步宽度补偿 1TQ
	can0_param_struct.time_segment_1 = CAN_BT_BS1_3TQ;		//位段1长度
	can0_param_struct.time_segment_2 = CAN_BT_BS2_5TQ;		//位段2长度
	can0_param_struct.prescaler = 12;						//预分频系数
	if(can_init(CAN0,&can0_param_struct)==ERROR)
	{
		return ;
	}
	//3.配置CAN过滤器
	can0_filter_param_struct.filter_list_high = (((uint32_t)0x200<<21|CAN_FT_DATA|CAN_FF_STANDARD)&0xFFFF0000)>>16;	//过滤器高字节
	can0_filter_param_struct.filter_list_low =  (((uint32_t)0x200<<21|CAN_FT_DATA|CAN_FF_STANDARD) & 0x0000FFFF);	//过滤器低字节
	can0_filter_param_struct.filter_mask_high = 0xE000;			//过滤器掩码数高位
	can0_filter_param_struct.filter_mask_low = 	0x0006;			//过滤器掩码数低位
	can0_filter_param_struct.filter_fifo_number = CAN_FIFO0;	//滤过器关联FIFO0
	can0_filter_param_struct.filter_number = 0;
	can0_filter_param_struct.filter_mode = CAN_FILTERMODE_MASK;	//掩码模式
	can0_filter_param_struct.filter_bits = CAN_FILTERBITS_32BIT;//32位
	can0_filter_param_struct.filter_enable = ENABLE;
	can_filter_init(&can0_filter_param_struct);
	//4.配置中断
	can_interrupt_enable(CAN0,CAN_INT_RFNE0);
}
/*
	\brief:	发送CAN报文
	\param:	id:标准帧ID
				data:要发送的数据
				len:数据长度 0~8byte
	\retval:	none
*/
uint8_t CAN_Send_Message(uint32_t id,uint8_t *data,uint8_t len)
{
	uint8_t i,mbox;
	can0_Tx_Data.tx_efid = 0x00;
	can0_Tx_Data.tx_sfid = id;
	can0_Tx_Data.tx_ff = CAN_FF_STANDARD;//标准帧
	can0_Tx_Data.tx_ft = CAN_FT_DATA;//数据帧
	can0_Tx_Data.tx_dlen = len;
	for(i=0;i<len;i++)
	{
		can0_Tx_Data.tx_data[i] = data[i];
	}
	mbox = can_message_transmit(CAN0,&can0_Tx_Data);				//发送CAN报文
	while(can_transmit_states(CAN0,mbox) == CAN_TRANSMIT_PENDING);	//等待发送完成
	if(can_transmit_states(CAN0,mbox) == CAN_TRANSMIT_OK)
	{
		return 0;
	}
	return 1;
}
//中断服务函数,接收CAN报文
void USBD_LP_CAN0_RX0_IRQHandler(void)
{
	if(can_interrupt_flag_get(CAN0,CAN_INT_FLAG_RFL0))
	{
		memset(&can0_Rx_Data,0,sizeof(can0_Rx_Data));//清空接收结构体
		can_message_receive(CAN0,CAN_FIFO0,&can0_Rx_Data);//获取信息
	}
}

can.h

#ifndef _CAN_H_
#define _CAN_H_

#include "gd32f10x.h"

#define CAN0_ID 	0x200	//ID号

extern can_receive_message_struct can0_Rx_Data;//读取信息结构体
extern can_trasnmit_message_struct can0_Tx_Data;//发送信息结构体

void CAN_Init(void);
uint8_t CAN_Send_Message(uint32_t id,uint8_t *data,uint8_t len);

#endif

mian.c


#include "main.h"


void rcu_config(void);
void nvic_config(void);


/*!
    \brief      main function
    \param[in]  none
    \param[out] none
    \retval     none
*/

int main(void)
{
    //gpio_afio_deinit();
	/* system clocks configuration */
    rcu_config();
	nvic_config();
    /* systick configuration */
    systick_config();
	//UART_init();
    /* Initialize the peripheral module */
	CAN_Init();
	uint8_t t_data[8]={0,99,3,4,5,55,7,110};
    while(1){
		CAN_Send_Message(0x0000,t_data,8);
		delay_1ms(1000);
	}
}

/*!
    \brief      configure the different system clocks
    \param[in]  none
    \param[out] none
    \retval     none
*/
void rcu_config(void)
{
    /* enable GPIO clock */
	rcu_periph_clock_enable(RCU_AF);
	rcu_periph_clock_enable(RCU_GPIOB);
    /* enable periph clock */
    rcu_periph_clock_enable(RCU_CAN0);
}

/*!
    \brief      enable the different NVIC request
    \param[in]  none
    \param[out] none
    \retval     none
*/
void nvic_config(void)
{
	nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
	nvic_irq_enable(USBD_LP_CAN0_RX0_IRQn,1,0);
}

最后

文章写的不好,希望能对初学CAN总线通信的友友们有所帮助!文章有错误的或者有更容易理解的方法,希望能写到评论区供大家学习。

非常感谢!

2023/06/28

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【GD32篇】CAN总线入门教程——实现数据收发 的相关文章

  • Ubuntu环境下配置Tomcat开机自动启动

    背景 项目部署好后 每次重启虚拟机后 需手动启动Tomcat 为了减少重复操作 特研究怎么设置Tomcat开机自动启动 实践了几种方法 下边的方法靠谱有效 以供参考 检查安装环境配置路径 JDK根目录 usr lib jvm java 1
  • Pandas提速与时间序列处理

    Pandas提速与时间序列处理 在数据分析和处理的领域中 Pandas是一个强大而受欢迎的Python库 它为我们提供了高效的数据结构和数据分析工具 可以轻松处理大规模数据集 本文将重点介绍如何通过优化Pandas代码来提高运算效率 并展示
  • Nginx的配置大全(经测试可用)

    文章目录 一 Nginx下载安装 二 Nginx启动 三 Nginx配置 四 Nginx日志管理 五 Nginx实现视频点播 六 Nginx实现直播 七 Nginx实现视频回放 八 Nginx实现负载均衡 九 Nginx实现IP黑名单 十
  • 软件工程概述-架构师(三)

    软件工程概述 老版 软件开发生命周期 软件定义时期 包括 可行性研究和详细需求分析过程 任务是软件工程必需完成的目标 具有可行问题分析 可行性研究 需求分析等 软件开发时期 软件的 设计与实现 可分为概要设计 详细设计 编码 测试等 软件运

随机推荐

  • Ler(一) stdlib.h,stdio.h,string.h头文件

    一 定位不同 1 stdlib h是standard library标准库头文件 定位在通用工具函数 2 stdio h是standard input output标准输入输出头文件 定位在标准的输入输出工具函数 二 封装函数不同 1 st
  • kitti中stereo的disparity的gt数据的读取和使用

    首先 kitti中disparity的gt的值是uint16的 https blog csdn net chenxicx1992 article details 44784559 这个博客是讲解如何读取存储的是uint16型数据的图片 我也
  • ETCD 详解

    一 ETCD简介 etcd是一个Go言编写的分布式 高可用的一致性键值存储系统 用于提供可靠的分布式键值存储 配置共享和服务发现等功能 具有以下特点 简单 易使用 基于HTTP JSON的API让你用curl就可以轻松使用 易部署 使用Go
  • JSON详解

    JSON的全称是 JavaScript Object Notation 意思是JavaScript对象表示法 它是一种基于文本 独立于语言的轻量级数据交换格式 XML也是一种数据交换格式 为什么没有选择XML呢 因为XML虽然可以作为跨平台
  • PC++:矩阵乘法操作

    实验要求 编写矩阵乘法代码实现 并编译执行 对代码进行执行时间分析 比较不同实现的效率差异 实验步骤 1 完成GEMM示例 并修改输入数据大小 首先 建立一个test cpp文件 利用以下代码 来源 https github com pen
  • canvas 画布 arcTo 方法的理解与使用

    参考文档 https codeplayer vip p j7scu arcTo x1 y1 x2 y2 radius x1 y1 理解为端点1 x2 y2 理解为端点2 radius 半径是基于断点2画的一个弧
  • 【uniapp之h5 微信小程序 app 开发项目结构图 思维导图及注意事项】

    uniapp之h5 微信小程序 app 开发项目结构图 思维导图及注意事项
  • python-网络安全编程第三天(正则表达式)

    python 正则表达式 正则表达式本身是一种小型的 高度专业化的编程语言 而在python中 通过内嵌集成re模块 程序媛们可以直接调用来实现正则匹配 正则表达式模式被编译成一系列的字节码 然后由用C编写的匹配引擎执行 用法 match
  • go.DB 富集分析子集 go子集 offspring children多层次结构go

    安装archr包 别处复制 libPaths c home data t040413 R x86 64 pc linux gnu library 4 2 home data t040413 R yll usr local lib R sit
  • Interceptor的基本介绍和使用preHandle、postHandle与afterCompletion

    目录 preHandle postHandle afterCompletion 项目测试代码 项目测试 preHandle 调用时间 Controller方法处理之前 执行顺序 链式Intercepter情况下 Intercepter按照声
  • 通用单目标跟踪综述《Handcrafted and Deep Trackers: A Review of Recent Object Tracking Approaches》

    近年来 视觉目标跟踪成为一个非常活跃的研究领域 每年都会提出越来越多的跟踪算法 跟踪在人机交互 自动驾驶汽车 机器人 监控和安全等各种现实问题中有着广泛的应用 本文将回顾跟踪领域的最新的趋势和进展 并基于特征提取方法评估了不同跟踪算法的鲁棒
  • pickle读文件解码问题

    运行 Revisiting Semi Supervised Learning with Graph Embeddings 的代码 kimiyoung planetoid 其中用 pickle 读数据文件出现问题 它本身是用 python 2
  • Nacos修改配置文件如何使其立即生效

    目录 前言 实现方案 方案一 方案二 注意事项 前言 当配置信息发生变动时 修改实时生效 无需要重新重启服务 就能够自动感知相应的变化 并将新的变化统一发送到相应程序上 快速响应变化 要实现这一目的 需要通过下面两种方案来实现 实现方案 方
  • 渗透测试工具ZAP入门教程(3)-渗透测试扫描流程

    使用ZAP对网站进行渗透测试流程如下 ZAP启动浏览器 输入URL 点击启动浏览器 在打开的浏览器登录要扫描的网站 在打开的浏览器上进行需要测试的流程操作 ZAP会记录操作过程中的HTTP请求 spider爬虫 点击Spider Start
  • 永恒话题,编程语言的选择

    当编程初学者要选择编程语言时 可以像选择一家餐厅一样 下面是一些建议 像选择美食一样选择编程语言 想象你是个美食家 编程语言就是菜单上的各种美食 先思考你的目标和口味偏好 是想开发网页 那就选择前端语言如HTML CSS和JavaScrip
  • 哈希(Hash)和哈希树(Merkle tree)

    哈希函数 英语 Hash function 又称散列函数 是一种从任何一种数据中创建小的数字 指纹 的方法 散列函数把消息或数据压缩成摘要 使得数据量变小 将数据的格式固定下来 该函数将数据打乱混合 重新创建一个叫做散列值 哈希值 hash
  • 白菜板裸机程序转换成智龙板PMON引导程序

    裸机程序转换成PMON引导程序 根据勤为本 gitee 上的龙芯1C 裸机程序 应用于白菜板 修改 Makefile 和 ld script 和 start s 使之应用于智龙开发板 1 替换修改文件 1 1 make与ld script
  • python 编码规范 Style Guide for Python Code

    目录 python 编码规范简述 编码规范的好处 PEP Python Enhancement Proposals 简介 规范基本内容 代码的整体布局 缩进与空格 制表符 隐式换行 悬挂缩进 行最大长度 运算符与换行 代码之间的空行 导入的
  • vue、uniapp调试工具【vueDevTools】

    点击下载
  • 【GD32篇】CAN总线入门教程——实现数据收发

    本文主要介绍CAN总线的软件配置 1 简介 CAN 总线协议已经成为汽车计算机控制系统和嵌入式工业控制局域网的标准总线 并且拥有以 CAN 为底层协议专为大型货车和重工机械车辆设计的 J1939 协议 近年来 它具有的高可靠性和良好的错误检