欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

stm32-CAN

程序员文章站 2022-04-01 18:41:17
...

1、概念

CAN是一种异步通信,使用CAN_HIGH,CAN_LOW两根信号线实现差分通信,通信网络有两种:闭环总线网络、开环总线网络

1.1闭环总线网络

特点:

  • 遵循 ISO11898 标准
  • 高速、短距离
  • 总线最大长度为 40m,通信速度最高为 1Mbps
  • 两端各要求有一个“120 欧”的电阻。

stm32-CAN

1.2开环总线网络

特点:

  • 遵循 ISO11519-2 标准
  • 低速远距离
  • 最大传输距离为 1km,最高通讯速率为 125kbps
  • 每根总线上各串联有一个“2.2 千欧”的电阻

stm32-CAN

1.3 通讯节点

CAN通讯节点由一个CAN控制器和一个CAN收发器构成;

  • 控制器与收发器之间:CAN_Tx CAN_Rx 信号线(TTL逻辑
  • 收发器与 CAN 总线之间: CAN_High CAN_Low信号线

1.4 差分信号电平

stm32-CAN

注:假如有两个 CAN 通讯节点,在同一时间,一个输出隐性电平,另一个输出显性电平,类似 I2C 总线的“线与”特性将使它处于显性电平状态,显性电平的名字就是这样来的, 即可以认为显性具有优先的意味。

        由于 CAN 总线协议的物理层只有 1 对差分线,在一个时刻只能表示一个信号,所以对通讯节点来说, CAN 通讯是半双工的,收发数据需要分时进行。在 CAN 的通讯网络中,因为共用总线,在整个网络中同一时刻只能有一个通讯节点发送信号,其余的节点在该时刻都只能接收。

2、协议层

2.1位时序分析

        为了实现位同步, CAN 协议把每一个数据位的时序分解成四段:SS 段、PTS 段、 PBS1 段、 PBS2 段,这四段的长度加起来即为一个 CAN 数据位的长度。(即每一个数据位都会经历同步等操作)

  • SS 段(SYNC SEG):SS 译为同步段, 若通讯节点检测到总线上信号的跳变沿被包含在 SS 段的范围之内,则表示节点与总线的时序是同步的,当节点与总线同步时,采样点采集到的总线电平即可被确定为该位的电平。 SS 段的大小固定为 1Tq。
  • PTS (PROP SEG) : PTS 译为传播时间段,这个时间段是用于补偿网络的物理延时时间。 是总线上输入比较器延时和输出驱动器延时总和的两倍。 PTS 段的大小可以为 1~8Tq
  • PBS1 段(PHASE SEG1): PBS1 译为相位缓冲段,主要用来补偿边沿阶段的误差,它的时间长度在重新同步的时候可以加长。 PBS1 段的初始大小可以为 1~8Tq。
  • PBS2 段(PHASE SEG2): PBS2 这是另一个相位缓冲段,也是用来补偿边沿阶段误差的,它的时间长度在重新同步时可以缩短。 PBS2 段的初始大小可以为 2~8Tq

stm32-CAN

        每一个数据位总长位19Tq,数据采样发生在5Tq和7Tq之间。

2.2通讯的波特率

    总线上的各个通讯节点只要约定好 1 个 Tq 的时间长度以及每一个数据位占据多少个Tq,就可以确定 CAN 通讯的波特率。

    例如,假设上图中的 1Tq=1us,而每个数据位由 19 个 Tq 组成,则传输一位数据需要时间 T1bit =19us,从而每秒可以传输的数据位个数为:

1x106/19 = 52631.6 (bps)

这个每秒可传输的数据位的个数即为通讯中的波特率。

2.3 同步过程

同步过程分为:硬同步和重新同步

  • 硬同步在起始帧的时候起作用
  • 重新同步在每一位的SS段进行检测,是否相位超前 或 相位滞后

2.4报文

几种通讯协议所需的信号线:

  • SPI:CS,SDI,SDO,SCLK
  • I2C:SCL,SDA
  • UART:RXD,TXD,GND
  • CAN:CAN_H,CAN_L

        CAN 使用的是两条差分信号线,只能表达一个信号,简洁的物理层决定了 CAN 必然要配上一套更复杂的协议, CAN协议给出的解决方案是对数据、操作命令(如读/写)以及同步信号进行打包,打包后的这些内容称为报文。

五种帧

  •     数据帧        用于节点向外传送数据
  •     遥控帧        用于向远端节点请求数据
  •     错误帧        用于向远端节点通知校验错误,请求重新发送上一个数据
  •     过载帧        用于通知远端节点:本节点尚未做好接收准备
  •     帧间隔        用于将数据帧及遥控帧与前面的帧分离开来

2.4.1数据帧

数据帧是在 CAN 通讯中最主要、最复杂的报文

图为数据帧结构:

stm32-CAN

数据帧可分为 帧起始(逻辑0)仲裁段控制段数据段CRC段ACK段 ,以及帧结束(7个连续隐性电平)

帧起始

通知其他节点有数据要传输,其他节点根据帧起始信号进行硬同步

仲裁段

仲裁段的内容包括ID信息,RTR,IDE,SRR位

     1.ID信息:其主要内容为本数据帧的 ID 信息 。仲裁段长度决定了数据帧是标准数据帧(ID 为 11 位)还是扩展数据帧(ID 为 29 位)。报文的优先级也通过对ID的仲裁来决定(物理层决定了如果总线上同时出现显性电平和隐性电平,总线的状态会被置为显性电平)

stm32-CAN

       2.RTR(Remote Transmission Request Bit)

        用于区分数据帧和遥控帧的,当它为显性电平时表示数据帧,隐性电平时表示遥控帧

        3.IDE位(Identifier Extension Bit)

        区分标准格式与扩展格式,当它为显性电平时表示标准格式,隐性电平时表示扩展格式

        4.SRR (Substitute Remote Request Bit)
        只存在于扩展格式,它用于替代标准格式中的RTR 位。由于扩展帧中的 SRR 位为隐性位, RTR 在数据帧为显性位,所以在两个 ID相同的标准格式报文与扩展格式报文中,标准格式的优先级较高

控制段:

     内容为:r1、r2位、DLC段         

     在控制段中的 r1 和 r0 为保留位,默认设置为显性位。它最主要的是 DLC 段(DataLength Code),译为数据长度码,它由 4 个数据位组成,用于表示本报文中的数据段含有多少个字节, DLC 段表示的数字为 0~8。例如DLC=4,数据段包含4个字节

数据段:

     数据段为数据帧的核心内容,它是节点要发送的原始信息,由 0~8 个字节组成, MSB先行。

CRC 段:

    CAN 的报文包含了一段 15 位的 CRC 校验码,一旦接收节点算出的 CRC 码跟接收到的 CRC 码不同, 则它会向发送节点反馈出错信息,利用错误帧请求它重新发送。 CRC的检验由硬件完成,出错后的处理由软件完成(控制最大重发数)
    CRC 校验码之后,有一个 CRC 界定符,它为隐性位,主要作用是把 CRC 校验码与后面的 ACK 段间隔起来。

ACK段:

    ACK 段包括一个 ACK 槽位,和 ACK 界定符位。类似 I2C 总线,在 ACK 槽位中,发送节点发送的是隐性位,而接收节点则在这一位中发送显性位以示应答。在 ACK 槽和帧结束之间由 ACK 界定符间隔开。

帧结束:

      EOF (End Of Frame),译为帧结束,帧结束段由发送节点发送的 7 个隐性位表示结束


其他帧,如遥控帧、错误帧等见下图

stm32-CAN

3、STM32外设CAN

特点:

  • 自动地接收和发送 CAN 报文
  • 支持使用标准 ID 和扩展 ID 的报文;
  • 外设中具有 3 个发送邮箱,
  • 发送报文的优先级可以使用软件控制,还可以记录发送的时间;
  • 具有 2 3 级深度的接收 FIFO,可使用过滤功能只接收或不接收某些 ID 号的报文;
  • 可配置成自动重发;
  • 不支持使用 DMA 进行数据收发

stm32-CAN

3.1stm32的CAN测试模式:

stm32-CAN

3.2 CAN外设的位时序与波特率

STM32 外设定义的位时序与CAN 标准时序有一点区别 :

  • CAN标准时序:        SS            |          PTS      |      PBS1       |             PBS2
  • stm32CAN时序:  STNC_SEG  |                     BS1                 |              BS2

stm32的CAN时序的BS1可以看成标准CAN的PTS和PBS1之和

因此:

stm32-CAN

其中单个时间片的长度 Tq CAN 外设的所挂载的时钟总线及分频器配置有关,CAN1 CAN2 外设都是挂载在 APB1 总线上的,而位时序寄存器 CAN_BTR 中的 BRP[9:0]

寄存器位可以设置 CAN 外设时钟的分频值 ,所以:

                                                        Tq = (BRP[9:0]+1) x TPCLK
其中的 PCLK 指 APB1 时钟,默认值为 45MHz。
最终可以计算出 CAN 通讯的波特率:

                                                            BaudRate = 1/N Tq

例:

stm32-CAN


3.3 CAN的筛选器

stm32-CAN

代码:

bsp_can.h

#include "stm32f4xx.h"
//CAN1 配置
#define 	CAN_CLK  							RCC_APB1Periph_CAN1
//接收中断号
#define   CAN1_RX_IRQ   				CAN1_RX0_IRQn  //处理来自FIFO0的中断
//接收中断函数
#define   CAN1_RX_IRQHandler    CAN1_RX0_IRQHandler

//GPIO_CAN引脚
#define   CAN1_RX_Pin 					GPIO_Pin_8
#define   CAN1_TX_Pin						GPIO_Pin_9
#define   CAN1_RX_GPIO_PORT     GPIOB
#define   CAN1_TX_GPIO_PORT     GPIOB
#define   CAN1_RX_GPIO_CLK      RCC_AHB1Periph_GPIOB 
#define   CAN1_TX_GPIO_CLK      RCC_AHB1Periph_GPIOB 
#define   CAN1_AF_PORT					GPIO_AF_CAN1  					//复用
#define 	CAN1_RX_Source				GPIO_PinSource8
#define 	CAN1_TX_Source				GPIO_PinSource9

//配置筛选器 stm32f4xx_can.h已经定义
//#define   CAN_ID_STD            (uint32_t)0x00000000
//#define 	CAN_Id_EXT            (uint32_t)0x00000004 
//CAN_RTR_DATA
void CAN_Config(void);
void CAN_SetMsg(CanTxMsg *TxMessage);

bsp_can.c

#include "./can/bsp_can.h"
#include "stm32f4xx.h"
//static  只在此文件能访问该函数
//配置CAN1的引脚
static void CAN1_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	//使能时钟
	RCC_AHB1PeriphClockCmd(CAN1_RX_GPIO_CLK|CAN1_TX_GPIO_CLK,ENABLE);
	//引脚源
	GPIO_PinAFConfig(CAN1_RX_GPIO_PORT ,CAN1_RX_Source,CAN1_AF_PORT);
	GPIO_PinAFConfig(CAN1_TX_GPIO_PORT ,CAN1_TX_Source,CAN1_AF_PORT);
	//结构体	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;//推挽输出
	GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	
	GPIO_InitStructure.GPIO_Pin=CAN1_RX_Pin;
	GPIO_Init(CAN1_RX_GPIO_PORT,&GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin=CAN1_TX_Pin;
	GPIO_Init(CAN1_TX_GPIO_PORT,&GPIO_InitStructure);
}


//配置CAN1的工作模式
static void CAN1_Mode_Config(void)
{
	CAN_InitTypeDef CAN_InitStructure;
	//开启 CAN CLOCK
	RCC_APB1PeriphClockCmd(CAN_CLK,ENABLE);
	//CAN寄存器初始化
	CAN_DeInit(CAN1);
	CAN_StructInit(&CAN_InitStructure);//结构体内全部变为初始值
	//CAN单元初始化
	CAN_InitStructure.CAN_ABOM=ENABLE;//自动离线管理,(出错时离线后,适时自动恢复)
	CAN_InitStructure.CAN_AWUM=ENABLE;//自动唤醒
	CAN_InitStructure.CAN_Mode=CAN_Mode_Normal;//正常模式。不使用回环
	
	CAN_InitStructure.CAN_NART=DISABLE;//自动重传禁用
	CAN_InitStructure.CAN_RFLM=DISABLE;//若为ENABLE,则FIFO被锁定;此时不锁定
	CAN_InitStructure.CAN_SJW=CAN_SJW_2tq;//重新同步宽度
	CAN_InitStructure.CAN_TTCM=DISABLE;//关闭之间自动触发功能
	CAN_InitStructure.CAN_TXFP=DISABLE;//配置报文发送优先级,ENABLE:根据存进邮箱时间决定;DISABLE:根据报文ID决定
	//ss =1 ; BS1=5 ;BS2=3 ;  9tq
	CAN_InitStructure.CAN_BS1=CAN_BS1_5tq;//5个tq
	CAN_InitStructure.CAN_BS2=CAN_BS2_3tq;//3  tq
	//APB1=45MHz,5分频    
	CAN_InitStructure.CAN_Prescaler=5;
	//45/5=9MHz, (9MHz,1tq) = (1MHz,9tq)
	CAN_Init(CAN1,&CAN_InitStructure);
}
//配置筛选器
static void CAN_Filter_config(void)
{
	CAN_FilterInitTypeDef CAN_FilterInitStructure;
	CAN_FilterInitStructure.CAN_FilterNumber=0;
	CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;//工作在掩码模式
	CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;//32位模式
	//使用扩展帧(CAN_ID_EXT),数据帧(CAN_RTR_DATA),ID=0x1314
	CAN_FilterInitStructure.CAN_FilterIdHigh=(((u32)0x1314|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF0000)>>16;//取高16位
	CAN_FilterInitStructure.CAN_FilterIdLow=((u32)0x1314|CAN_ID_EXT|CAN_RTR_DATA)&0x0000FFFF;
	CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0xFFFF;
	CAN_FilterInitStructure.CAN_FilterMaskIdLow=0xFFFF;
	CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;//使能筛选器
	CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0 ;
	
	CAN_FilterInit(&CAN_FilterInitStructure);
	CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE);//使能FIFO0接收中断
}
//配置中断优先级
static void CAN_NVIC_Config(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
	//中断设置
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//优先级最高
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannel=CAN1_RX_IRQ ;
	NVIC_Init(&NVIC_InitStructure);
}

//设置发送的报文
void CAN_SetMsg(CanTxMsg *TxMessage)
{
	uint8_t ubCounter=0;
	//使用扩展帧
	TxMessage->ExtId=0x1314;//扩展ID
	TxMessage->IDE=CAN_ID_EXT;//使用扩展帧
	TxMessage->RTR=CAN_RTR_DATA;//是数据帧不是远程帧
	TxMessage->DLC =8;//数据长度8字节
	for(ubCounter=0;ubCounter<8;ubCounter++)
	{
		TxMessage->Data[ubCounter]=ubCounter;		
	}	
}
void CAN_Config(void)
{
	CAN1_GPIO_Config();
	CAN1_Mode_Config();
	CAN_Filter_config();
	CAN_NVIC_Config();
}


中断函数:
//CAN接收中断
void CAN1_RX_IRQHandler(void)
{
	printf("Receive the can message");
	CAN_Receive(CAN1,CAN_FIFO0,&RxMessage);
	if((RxMessage.ExtId==0x1314)&&(RxMessage.IDE==CAN_ID_EXT)&&(RxMessage.DLC==8))
	{
		CAN_Flag=1;
	}
	else 
		CAN_Flag=0;
}



相关标签: CAN

推荐阅读