GD32F303RCT6(4):CAN总线教程详解

        CAN是控制器区域网络(Controller Area Network)的缩写,可以在无主机的情况下实现微处理器或者设备之间互相通信的总线标准。GD32F30x CL系列具有28个过滤器。其中显性电平是逻辑0,隐形电平是逻辑1。

1、CAN总线结构

        CAN总线网络的结构有闭环和开环两种形式,其中闭环形式是一种高度、短距离的CAN网络,最高通信速率一般为1Mbit/s,这时总线最长达40m,对应的开环形式是一种低速、远距离的CAN网络,通信速率最高可达128kbit/s,在40kbit/s时总线最长,可达1000m。

        CAN总线只有两根线,即CAN_H和CAN_L,由于没有时钟信号,因此CAN是一种异步通信方式,这两根线通常采用双绞线,传输差分信号,通过两根信号线的电压差来表示总电平,因为差分信号传输信息抗干扰能力强,这是CAN总线在工业上运用广泛的一个原因,RS485网络也是使用差分信号表示总线电平,也是一种常用的工业现场总线。

        在CAN总线网络中,CAN总线上的一个终端设备称为一个节点,其中无主机从机之分。一个CAN节点的硬件部分一般由CAN控制器和CAN收发器两个部分组成,CAN控制器用于CAN总线的逻辑控制,实现CAN传输协议;CAN收发器主要负责MCU逻辑电平和CAN总线电平之间的转换。


2、CAN总线传输协议

2.1 传输特点

        CAN总线上的节点既可以发送数据也可以接收数据,没有主从之分,但在同一时刻,只能有一个节点发送数据,其他数据只能接收数据。

        CAN总线上的节点没有地址的概念,数据以帧为单位传输。

        CAN总线具有“线与”的特性,也就是当两个节点同时向总线发送信号时,一个发送显性电平另一个发送隐形电平,则总线呈现显性电平,这个特性常用于总线仲裁,用于确定哪个节点优先占用总线执行发送操作。

        每一个帧都有一个标识符(Identifier,简称ID)。ID不是地址,它表示传输数据的类型,也可以用于总线仲裁时确定优先级,ID越小优先级越高。

        CAN总线发出的帧属于是一个广播,每个节点都可以接收,但是可以对接收的帧根据ID进行过滤,只有节点需要的数据才会被节点进一步处理,不需要的则丢弃。

        CAN总线通信时半双工的,总线不能同时接收和发送,在多个节点竞争总线进行发送时,通过ID的优先级进行仲裁,竞争胜出的节点继续发送,竞争失败的节点立刻转入接收状态。

2.2 位时序和波特率

        CAN网络是异步通信,需要规定一个通信的波特率,各个节点都以相同的波特率进行数据通信。位时序指的是一个节点采集CAN总线上的一个位数据的时序。通过位时序的控制,CAN总线可以进行位同步,以吸收节点时钟差异产生的波特率误差,保证接收数据的准确性。

       

        图中的位时间(标称位时间)指的是传输一个位数据的时间,用于确定CAN总线的波特率,这个时间被分为了四段。 

        (1)同步段(SYNC_SEG):这个是按段内,总线上应该至少发生了一次位信号的跳变。如果节点检测到了总线上的一个跳变沿,就表示节点和总线是同步的。同步段长度固定为1个t_{q}

        t_{q}被称为时间片,t_{q}由CAN控制器的时钟频率f_{CAN}决定,CAN控制线在APB1总线上,CAN总线在APB1总线上,CAN控制器有预分频器,APB1总线的时钟信号PCLK1经过分频之后得到f_{CAN}

        (2)传播段(PROT_SEG):这个时间段是用于补偿网络的物理延时时间,是总线上输入比较器延时和输出驱动器延时综合的两倍。

        (3)传播段和相位缓存段统称为位段1(BS1):定义了采样点的位置,在BS1结束的时间时间点对总线采样,得到的电平就是这个位的电平。长度设置为1-16个t_{q},但是它的长度可以在同步的时候被自动加长,以补偿各节点频率差异导致的正相位漂移。

        (4)位段2(BS2)定义了发送点的位置,初始长度是1-8个t_{q},在同步时可以被自动缩短,以补偿负相位漂移。

        CAN控制器可以自动对位时序进行再同步,再同步时调整BS1和BS2的长度,位段加上或缩短的上限称为再同步跳转宽度(SJW),SJW的取值一般为1-4个t_{q}

        CAN总线的波特率就由标称位时间长度NBT(同步段+位段1+位段2)决定,Baudrate=\frac{1}{NBT}

2.3 帧的种类

        数据帧:节点发送包含ID和数据的帧;

        遥控帧:节点向网络上的其他节点发出的某个ID的数据请求,发送节点收到遥控帧后就可以发送相应ID的数据帧;

        错误帧:节点检测到错误时,向其他节点发送的通知错误的帧;

        过载帧:接受单元未做好接受数据的准备时发送的帧,发送节点收到过载帧可以暂缓发送数据帧;

        帧间空间:用于将数据帧、遥控帧与前后的帧分隔开的帧。

        其中,数据帧和遥控帧才有ID,并且有标准格式和拓展格式两种形式,标准格式的ID是11位,拓展格式的ID是29位。

2.4 标准格式数据帧和遥控帧

        数据帧和遥控帧都有11位的ID,其中数据帧传输带有ID的0-8字节的数据,而遥控帧有ID但没有数据,只用于请求数据。

        数据帧大概可以分为:

        (1)帧起始。帧起始只有一个位,为显性电平,表示一个帧的开始。、        

        (2)仲裁段。仲裁段包括11位的ID和RTR位,共12位。根据仲裁段的数据决定哪个节点优先占用总线,哪个ID先出现显性电平(逻辑0),对应的节点就占用总线,如果两个节点发送数据帧的ID相同,再根据最后的RTR位裁决。

        其中RTR是远程传输请求,用于区分数据帧还是遥控帧,数据帧是显性电平,遥控帧是隐形电平,因此相同ID的数据帧和遥控帧竞争总线时,数据帧优先级更高。

        (3)控制段。控制段包括IDE位、RB0位和四位的DLC,共6位。

        IDE位是标识符扩展位,用于表示帧是标准格式还是扩展格式,标准格式位显性电平。

        RB0是保留位,默认为显性电平。

        DLC是四个位的数据长度编码,编码数值为0到8,表示后面数据段的字节数。遥控帧的DLC编码数值总是0,因为遥控帧不传输数据。

        (4)数据段。数据段里是数据帧需要传输的数据,可以是0-8个字节,长度由DLC编码确定

        (5)CRC段。CRC段共16位,其中前五位是CRC校验码,最后一位总是隐性电平,为CRC段的界定符。

        (6)ACK段。ACK段包括一个ACK位和一个ACK界定符,发送节点发送的ACK位是隐性电平,接收节点接收的ACK位是显性电平。

        (7)帧结束。帧结束由七个隐性位表示EOF,在数据帧或遥控帧结束后,后面一般是帧间空间,用于分隔数据帧或遥控帧。

2.5 扩展格式数据帧和遥控帧

        扩展格式的ID是29位的,扩展格式帧和标准格式帧的差异在于仲裁段和遥控段。

        (1)仲裁段。扩展格式数据帧的仲裁段总共32,其中包括11位的标准ID、SSR位、IDE位、18位扩展ID和RTR位。

        其中SRR位仅存在于扩展格式帧中,用于替代标准格式帧中的RTR位,SRR位总是隐性电平,相当于一个占位符,并无什么实际意义。真正的RTR位还是位于仲裁段的最后一位,用于区分数据帧和遥控帧。

        IDE用于表示这是扩展格式的帧,总是隐性电平。

        (2)控制段。控制端由RB1位、RB0位和四位的DLC组成,RB1和RB0是保留位,总是显性电平;四位DLC位还是用来表示数据的长度,从0-8字节。

2.6 优先级仲裁原则

        (1)如果总线空闲时,先发送的节点获得发送权。

        (2)多个节点进行仲裁时,先输出显性电平的节点获得发送权,因此ID越小优先级越高。

        (3)相同ID的数据帧和遥控帧,数据帧拥有更高的优先级。

        (4)ID相同的标准帧和拓展帧,标准帧有更高的优先级。

3、CAN模块特性

3.1 三种工作模式

        (1)睡眠工作模式,芯片复位后,CAN总线控制器处于睡眠工作模式,该模式下CAN总线控制器的时钟停止工作并处于一种低功耗状态。将CAN_CTL寄存器的AWU位置1,并当CAN检测到总线活动时,CAN总线控制器将自动退出睡眠工作模式。

        将CAN_CTL寄存器的SLPWMOD位置1,可以使CAN总线控制器进入睡眠工作模式。当进入睡眠工作模式后,CAN_STAT寄存器的SLPWS位将被硬件置1。将CAN_CTL寄存器的AWU位置1,并当CAN检测到总线活动时,CAN总线控制器将自动退出睡眠工作模式。将CAN_CTL寄存器的SLPWMOD位清0,也可以退出睡眠工作模式。

        由睡眠模式进入初始化工作模式:将CAN_CTL寄存器的IWMOD位置1,SLPWMOD位清0。

        由睡眠模式进入正常工作模式:将CAN_CTL寄存器的IWMOD位和SLPWMOD位清0。

        (2)初始化工作模式
        如果需要配置 CAN 总线通信参数,CAN 总线控制器必须进入初始化工作模式。将 CAN_CTL 寄存器的 IWMOD 位置 1,使 CAN 总线控制器进入初始化工作模式,将其清 0 则离开初始化工作模式。在进入初始化工作模式后,CAN_STAT 寄存器的IWS 位将被硬件置 1。

        由初始化模式进入睡眠模式:CAN_CTL 寄存器的 SLPWMOD 位置 1,IWMOD 位清 0。

        由初始化模式进入正常工作模式:CAN_CTL 寄存器的 SLPWMOD 位和 IWMOD 位清0。

        (3)正常工作模式

        在初始化工作模式中配置完CAN 总线通信参数后,将 CAN_CTL 寄存器的IWMOD位清0可以进入正常工作模式并与 CAN 总线网络中的节点进行正常通信。

        由正常工作模式进入睡眠工作模式:CAN_CTL 寄存器的 SLPWMOD 位置 1,并等待当前数 据收发过程结束。

        由正常工作模式初始化工作模式:CAN_CTL 寄存器的 IWMOD 位置 1,并等待当前数据收发 过程结束。

        三种工作模式具体寄存器配置

3.2 CAN模块的测试模式

        要进入测试模式必须要在CAN模块初始化时进行设置,在测试模式下,CAN模块可以自收自发,测试CAN模块的功能是否正常。

        (1)静默模式。可以接收到有效的数据帧和遥控帧,但不能向总线发送任何有效数据(只能向总线发送隐性位,发送的显性位被自己接收),这个模式常用于检测总线流量。

        (2)回环模式。可以正常地向总线发送数据,但不能接受来自总线上的数据,只能接收izji发送的数据,这个模式常用于自检。在此模式下,CAN内核不会对数据帧或遥控帧的ACK段采样。

        (3)回环静默模式。只能接收自己发送的数据,只能向总线上发送隐性位,不会影响到CAN总线,这个模式常用于通信自检。

3.3 消息发送功能

        每个CAN模块都有三个发送邮箱,发送数据时,用户需要选择一个空闲的发送邮箱,将标识符ID、数据长度和数据(最多8字节)写入邮箱,然后CAN模块会自动将邮箱内的数据发送出去(和DMA类似,有数据在发送缓冲区且接收缓冲区为空时自动发送数据)。

        发送数据可以设置自动重发,即在出现错误后会自动重发,直至发送成功。禁止自动重发后,则发送失败后不会重发,但会通过发送状态寄存器相应的位指出错误原因。

        可以设置位时间触发通信模式,在此模式下,会使能CAN模块内部的一个计数器,CAN模块每收到一个位数据,计数器都会递增,在发送或接收时,在帧的起始位时刻捕获计数值,作为发送或接收数据帧的时间戳数据。

        可以选择终止邮箱数据的发送,终止后邮箱会变为空闲状态。

3.4 消息接收功能

        每个CAN模块都有两个接收FIFO,每个FIFO有三个邮箱,FIFO完全由硬件管理。从邮箱中读取消息后,邮箱就自动释放,如果一个FIFO的三个邮箱都接受到消息而没有及时读出,再有消息进入时就会产生上溢。可以根据是否设置FIFO锁定,有两种处理情况:

        (1)使能FIFO锁定,则新传入的消息会被舍弃。

        (2)禁止FIFO锁定,则新传入的消息会覆盖FIFO中存储的最后一条消息。

        可以通过轮询方式和中断方式读取接收邮箱中的消息。

3.5 标识符(ID)筛选功能

        在CAN网络中,发送节点是以广播方式发送消息的,所有CAN节点都可以收到消息,但一个CAN节点只对特定ID的消息感兴趣,因此需要通过ID过滤接收指定的消息。每个节点都需要对收到的消息进行过滤,且ID最少都有11位,因此如果用软件方式进行过滤,会占用CPU大量资源和时间。我们选择硬件方式对接收的帧进行筛选,只允许符合条件的帧进入接收邮箱。

        在非 GD32F30x CL 系列产品中,具有14 个过滤器,在 GD32F30x CL 系列产品中,具有 28 个过滤器。此次使用的产品有28个过滤器。

        每一个过滤器有2个32位寄存器CAN_FxDATA0和CAN_ FxDATA1,它们可以配置为两个32位长度寄存器或者4个16位长度筛选器,可以是掩码模式或列表模式,因此一个筛选器有四种配置模式,如图所示(转载自:STM32 CAN过滤器细节-CSDN博客):

        (1)1个32位筛选器——标识符掩码模式,在此模式下,寄存器CAN_FxR1(在gd32中对应CAN_FxDATA0)存储一个32位ID,这个ID与11位标准ID(STID[10:0])、18位拓展ID(EXID[17:0])、IDE位、RTR位的对应关系如图模式1所示。当IDE位为0是表示标准格式帧、否则表示拓展格式帧。

        在寄存器CAN——FxR2(在gd32中对应CAN_FDATA1)存储一个32位掩码,如果掩码为1,则表示该位与ID中的位一致,为0则表示不一致。

        (2)2个32位筛选器——标识符列表模式,在此模式下,这两个存储器分别存储一个32位ID,只有匹配这两个ID的帧才能通过筛选。

        (3)2个16位筛选器——标识符掩码模式,在此模式下,两个寄存器都是高16位组成一个ID,低16位组成一个掩码。

        (4)4个16位筛选器——标识符列表模式,在此模式下,两个寄存器都分别表示个16位ID。

        用户可以为一个FIFO设置多个筛选器,但一个筛选器只能配置给一个FIFO,只有通过了筛选器,帧才会被存入接受邮箱。

3.6 中断

        CAN总线控制器占用4个中断向量,通过寄存器CAN_INTEN进行控制,这四个中断向量对应4类中断源:发送中断、FIFO0中断、FIFO1中断、错误和状态改变中断.

        发送中断只有一个中断事件源,在3个发送邮箱中任意一个发送完成时都产生该事件的中断。

        FIFO0中断是在FIFO0接收消息、满或上溢时触发的中断,CAN模块接收消息一般是使用中断方式。FIFO1与FIFO0同理。

        状态改变或错误中断在CAN模块发送状态改变或错误时触发。

4、 代码示例中断方式CAN通信

can,c

#include "systick.h"
#include "gd32f30x.h"
#include "can.h"
#include "uart.h"
#include "string.h"

can_receive_message_struct receive_message;

//can所用GPIO口初始化,PA11和PA12
void can_gpio_init(void)
{
	rcu_periph_clock_enable(RCU_CAN0);
	rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_AF);

	gpio_pin_remap_config(GPIO_CAN_PARTIAL_REMAP,ENABLE);	//开启CAN部分重映射
	gpio_init(GPIOA,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_11);   //CAN0_RX
	gpio_init(GPIOA,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_12);		//CAN0_TX
	
	
}


//can初始化
void can_network_init(void)
{
	can_parameter_struct can_parameter;  //can初始化结构体
	can_filter_parameter_struct can_filter;		//can滤波器初始化结构体
	
	can_struct_para_init(CAN_INIT_STRUCT,&can_parameter);  //使用默认值初始化CAN参数结构
	can_struct_para_init(CAN_FILTER_STRUCT,&can_filter);
	
	can_deinit(CAN0); //取消之前的can初始化
	
	can_parameter.time_triggered  =  DISABLE;   / *关闭时间触发通信模式,若启用会在发送或接收消息时,加上一个内部计数器的计数值 */
	can_parameter.auto_bus_off_recovery =  ENABLE;	//启用自动的总线关闭管理
	can_parameter.auto_retrans  =  ENABLE;   /* 启用自动重传模式,CAN模块将自动重发消息,直至发送成功为止 */
	can_parameter.auto_wake_up  =  DISABLE;		//  关闭自动唤醒
	can_parameter.prescaler  = 12 ;				//预分频系数
	can_parameter.rec_fifo_overwrite =  DISABLE;	/* 禁用FIFO锁定模式,表明FIFO满时,下一条新消息覆盖前一条消息 */
	can_parameter.resync_jump_width  = CAN_BT_SJW_1TQ ;  //再同步跃变宽度1TQ
	can_parameter.time_segment_1  =CAN_BT_BS1_3TQ;		//时序中的位段1,BS1
	can_parameter.time_segment_2  =  CAN_BT_BS2_5TQ;	//时序的位段2,BS2
    /* 𝐵𝑎𝑢𝑑𝑅𝑎𝑡𝑒 = (𝑡𝑆𝑌𝑁𝐶_𝑆𝐸𝐺 + 𝑡𝐵𝑆1 + 𝑡𝐵𝑆2)/1 */
    /* 一个时间片长度由预分频系数和所用时钟有关,𝑡𝑞 = (1 + 𝐵𝑇.𝐵𝐴𝑈𝐷𝑃𝑆𝐶)× 𝑡𝑃𝐶𝐿𝐾1 */
    /* 由此可以算出波特率 */

	can_parameter.trans_fifo_order  = DISABLE;		//关闭FIFO顺序发送
	can_parameter.working_mode  =  CAN_SILENT_LOOPBACK_MODE;  /* 环回和静默通信模式,自收自发测试 */
	can_init(CAN0,&can_parameter);
	
	
	//滤波器配置
	can_filter.filter_mode  =  CAN_FILTERMODE_MASK;	//ID掩码模式
	can_filter.filter_bits  =  CAN_FILTERBITS_32BIT;//32位长度
	can_filter.filter_number  =  0;//筛选器组编号

	can_filter.filter_list_high  =  (((uint32_t)0x200<<21|CAN_FT_DATA|CAN_FF_STANDARD)&0xFFFF0000)>>16;
	can_filter.filter_list_low  =  (((uint32_t)0x200<<21|CAN_FT_DATA|CAN_FF_STANDARD) & 0x0000FFFF);
	can_filter.filter_mask_high = 0xE000;        //掩码高16位
	can_filter.filter_mask_low  =  0x0006;        //掩码低16位
/* filter_mask_low的存储的内容分两种情况,当过滤器工作在列表模式时,他的功能与filter_list_low相同,都是存储要过滤的ID;当工作在掩码模式时,它存储的是与filter_list_low成员对应的掩码 */
/* 在掩码模式下,filter_mask_high与filter_mask_low填入的是要筛选的掩码,掩码为1时,筛选的ID必须与ID相同 */
	can_filter.filter_fifo_number  =  CAN_FIFO0; //应用于FIFO0
	can_filter.filter_enable  =  ENABLE;    //使能
	can_filter_init(&can_filter);
	
	nvic_irq_enable(CAN0_RX1_IRQn,0,0);//使能接收中断
	can_interrupt_enable(CAN0,CAN_INT_RFNE0);
}


uint8_t can_send_msg(uint8_t* buffer,uint32_t id,uint8_t len)
{
	int t = len;
	int i =0;
	uint32_t timeout = 0xFFFF;
	uint8_t transmit_mailbox = 0;
	
	can_trasnmit_message_struct transmit_message;
	transmit_message.tx_dlen =  len;
	transmit_message.tx_efid  = id;
	transmit_message.tx_ff  =  CAN_FF_STANDARD;
	transmit_message.tx_ft	=  CAN_FT_DATA;
	transmit_message.tx_sfid =	id;
	
while(t--)
	{
		transmit_message.tx_data[i] =  buffer[i];		
		i++;
	}
	
		transmit_mailbox   =  can_message_transmit(CAN0,&transmit_message);
	while(can_transmit_states(CAN0,transmit_mailbox) == CAN_TRANSMIT_PENDING);	//等待发送完成
	//等待发送完成、判断发送状态
	while((CAN_TRANSMIT_OK  !=	can_transmit_states(CAN0,transmit_mailbox))||( timeout<10))timeout--;
	
	if(timeout<10)return 1;//发送超时
	return 0; //发送成功
	
}


void CAN0_RX0_IRQHandler(void)
{
	if(can_interrupt_flag_get(CAN0,CAN_INT_FLAG_RFL0))
	{
    /* check the receive message */
    can_message_receive(CAN0, CAN_FIFO0, &receive_message);
    //if((0x7ab == receive_message.rx_sfid)&&(CAN_FF_STANDARD == receive_message.rx_ff) && (8 == receive_message.rx_dlen))
    //{
    //    u1_printf("接受错误");
    //}
    memset(&receive_message,0,sizeof(receive_message));//清空接收结构体
    }
}

main.c

#include "string.h"
#include "uart.h"  
#include "gd32f30x.h"
#include "systick.h"
#include <stdio.h>
#include"stdarg.h"
#include "can.h"
#include "main.h"

int main(void)
{
	uint8_t t_data[8]={0,99,3,4,5,55,7,110};
	systick_config();
	can_gpio_init();

	can_network_init();
    while(1){
		can_send_msg(t_data,0x0000,8);
		delay_1ms(1000);
	}
}
 

作者:塔克拉玛干沙漠到底有多干

物联沃分享整理
物联沃-IOTWORD物联网 » GD32F303RCT6(4):CAN总线教程详解

发表回复