STM32---CAN通信协议实验详解(两块STM32F405之间CAN通信)

CAN 简介

CAN 是控制器区域网络(Controller Area Network) 的缩写(以下称为CAN),是ISO国际标准化的串行通信协议。在当前的汽车产业中,出于对安全性、舒适性、方便性、低公害、低成本的要求,各种各样的电子控制系统被开发了出来。由于这些系统之间通信所用的数据类型及对可靠性的要求不尽相同,由多条总线构成的情况很多,线束的数量也随之增加。为适应“减少线束的数量”、“通过多个LAN,进行大量数据的高速通信”的需要,1986 年德国电气商博世公司开发出面向汽车的CAN 通信协议。此后,CAN 通过ISO11898 及ISO11519 进行了标准化,现在在欧洲已是汽车网络的标准协议。

现在,CAN 的高性能和可靠性已被认同,并被广泛地应用于工业自动化、船舶、医疗设备、工业设备等方面。现场总线是当今自动化领域技术发展的热点之一,被誉为自动化领域的计算机局域网。它的出现为分布式控制系统实现各节点之间实时、可靠的数据通信提供了强有力的技术支持。

CAN 控制器根据两根线上的电位差来判断总线电平。总线电平分为显性电平和隐性电平,二者必居其一。发送方通过使总线电平发生变化,将消息发送给接收方

CAN 主要特性

● 支持 2.0 A 及 2.0 B Active 版本 CAN 协议
比特率高达 1 Mb/s
● 支持时间触发通信方案
发送
● 三个发送邮箱
● 可配置的发送优先级
● SOF 发送时间戳
接收
● 两个具有三级深度的接收 FIFO
● 可调整的筛选器组:
CAN1 和 CAN2 之间共享 28 个筛选器组
● 标识符列表功能
● 可配置的 FIFO 上溢
● SOF 接收时间戳
时间触发通信方案
● 禁止自动重发送模式
● 16 位自由运行定时器
● 在最后两个数据字节发送时间戳
管理
● 可屏蔽中断
● 在唯一地址空间通过软件实现高效的邮箱映射
双 CAN
● CAN1:主 bxCAN,用于管理 bxCAN 与 512 字节 SRAM 存储器之间的通信。
● CAN2:从 bxCAN,无法直接访问 SRAM 存储器。
● 两个 bxCAN 单元共享 512 字节 SRAM 存储器(请参见 图 224 :双 CAN 框图 )。

CAN控制、状态和配置寄存器

应用程序使用这些寄存器进行以下操作:
● 配置 CAN 参数,例如波特率
● 请求发送
● 处理接收
● 管理中断
● 获取诊断信息
在这里插入图片描述

CAN协议的特点

1) 多主控制。在总线空闲时,所有单元都可以发送消息(多主控制),而两个以上的单元同时开始发送消息时,根据标识符(Identifier 以下称为 ID)决定优先级。ID 并不是表示发送的目的地址,而是表示访问总线的消息的优先级。两个以上的单元同时开始发送消息时,对各消息ID 的每个位进行逐个仲裁比较。仲裁获胜(被判定为优先级最高)的单元可继续发送消息,仲裁失利的单元则立刻停止发送而进行接收工作。

2) 系统的若软性。与总线相连的单元没有类似于“地址”的信息。因此在总线上增加单元时,连接在总线上的其它单元的软硬件及应用层都不需要改变。

3) 通信速度较快,通信距离远。最高1Mbps(距离小于40M),最远可达10KM(速率低于5Kbps)。

4) 具有错误检测、错误通知和错误恢复功能。所有单元都可以检测错误(错误检测功能),检测出错误的单元会立即同时通知其他所有单元(错误通知功能),正在发送消息的单元一旦检测出错误,会强制结束当前的发送。强制结束发送的单元会不断反复地重新发送此消息直到成功发送为止(错误恢复功能)。

5) 故障封闭功能。CAN 可以判断出错误的类型是总线上暂时的数据错误(如外部噪声等)还是持续的数据错误(如单元内部故障、驱动器故障、断线等)。由此功能,当总线上发生持续数据错误时,可将引起此故障的单元从总线上隔离出去。

6) 连接节点多。CAN 总线是可同时连接多个单元的总线。可连接的单元总数理论上是没有限制的。但实际上可连接的单元数受总线上的时间延迟及电气负载的限制。降低通信速度,可连接的单元数增加;提高通信速度,则可连接的单元数减少。

正是因为CAN协议的这些特点,使得CAN特别适合工业过程监控设备的互连,因此,越来越受到工业界的重视,并已公认为最有前途的现场总线之一。

CAN协议经过ISO标准化后有两个标准:ISO11898标准和ISO11519-2标准。其中ISO11898是针对通信速率为125Kbps~1Mbps的高速通信标准,而ISO11519-2是针对通信速率为125Kbps以下的低速通信标准。

CAN 工作模式

bxCAN 有三种主要的工作模式: 初始化、 正常和 睡眠。硬件复位后,bxCAN 进入睡眠模式以降低功耗,同时 CANTX 上的内部上拉电阻激活。**软件将 CAN_MCR 寄存器的 INRQ 或SLEEP 位置 1,以请求 bxCAN 进入 初始化或 睡眠模式。**一旦进入该模式,bxCAN 即将CAN_MSR 寄存器的 INAK 或 SLAK 位置 1,以确认该模式,同时禁止内部上拉电阻。如果INAK 和 SLAK 均未置 1,则 bxCAN 将处于 正常模式。进入 正常模式之前,bxCAN 必须始终在 CAN 总线上实现 同步。为了进行同步,bxCAN 将等待 CAN 总线空闲(即,已监测到CANRX 上的 11 个隐性位)。
在这里插入图片描述

CAN 功能说明

发送处理

为了发送消息,应用程序必须在请求发送前,通过将 CAN_TIxR 寄存器的相应 TXRQ 位置1,选择一个 空发送邮箱,并设置标识符、数据长度代码 (DLC) 和数据。一旦邮箱退出 空状态,软件即不再具有对邮箱寄存器的写访问权限。TXRQ 位置 1 后,邮箱立即进入 挂起状态,等待成为优先级最高的邮箱,请参见 发送优先级 。一旦邮箱拥有最高优先级,即被 安排发送。CAN 总线变为空闲后,被安排好的邮箱中的消息即开始发送(进入 发送状态)。邮箱一旦发送成功,即恢复 空状态。硬件通过将 CAN_TSR 存器的 RQCP 和 TXOK 位置 1,来表示发送成功。
如果发送失败,失败原因将由 CAN_TSR 寄存器的 ALST 位(仲裁丢失)和/或 TERR 位(检测到发送错误)指示。

发送优先级

按标识符
当多个发送邮箱挂起时,发送顺序由邮箱中所存储消息的标识符来确定。根据 CAN 协议的仲裁,标识符值最低的消息具有最高的优先级。如果标识符值相等,则首先安排发送编号较小的邮箱。
按发送请求顺序
可以通过设置 CAN_MCR 寄存器中的 TXFP 位,将发送邮箱配置为发送 FIFO。在此模式下,优先级顺序按照发送请求顺序来确定。
中止
可以通过将 CAN_TSR 寄存器的 ABRQ 位置 1,来中止发送请求。在 挂起或 已安排状态下,邮箱立即中止。如果在邮箱处于 发送状态时请求中止,则会出现两种结果。如果邮箱发送成功,将变为 空状态,同时 CAN_TSR 寄存器的 TXOK 位置 1。如果发送失败,邮箱变为已安排已安排状态,发送中止并变为 空状态,同时 TXOK 位清零。在所有情况下,邮箱至少在当前发送结束时都会恢复空状态。

在这里插入图片描述

接收处理
为了接收 CAN 消息,提供了构成 FIFO 的三个邮箱。为了节约 CPU 负载,简化软件并保证数据一致性,FIFO 完全由硬件进行管理。应用程序通过 FIFO 输出邮箱访问 FIFO 中所存储的消息。

有效消息
当消息依据 CAN 协议正确接收(直到 EOF 字段的倒数第二位都没有发送错误) 并且成功通
过标识符筛选后,该消息将视为有效。

FIFO 管理
FIFO 开始时处于 空状态,在接收的第一条有效消息存储在其中后,变为 Pending_1 状态。硬件通过将 CAN_RFR 寄存器的 FMP[1:0] 位置为 01b 来指示该事件。消息将在 FIFO 输出邮箱中供读取。软件将读取邮箱内容,并通过将 CAN_RFR 寄存器的 RFOM 位置 1,来将邮箱释放。FIFO 随即恢复 空状态。如果同时接收到新的有效消息,FIFO 将保持 Pending_1状态,新消息将在输出邮箱中供读取。

如果应用程序未释放邮箱,下一条有效消息将存储在 FIFO 中,使其进入 Pending_2 状态(FMP[1:0] = 10b)。下一条有效消息会重复该存储过程,同时将 FIFO 变为 Pending_3 状态(FMP[1:0] = 11b)。此时,软件必须通过将 RFOM 位置 1 来释放输出邮箱,从而留出一个空邮箱来存储下一条有效消息。否则,下一次接收到有效消息时,将导致消息丢失。
在这里插入图片描述

CAN总线结构示意图:

在这里插入图片描述

说明: 1:CAN收发器(示意图中的单元)根据两总线CAN_H和CAN_L的电位差来判断总线电平;

        2:实际中CAN_H与CAN_L由双绞线组成;

        3:数据传递终端的电阻器,是为了避免数据传输反射回来,使数据遭到破坏;

        4:电阻阻值为120Ω;

        5:CAN通信实际上为单元之间的数据传输

CAN通信单元的组成:

每个通信单元软件部分由数据帧、遥控帧、错误帧、过载帧、帧间隔组成;但不是上述5种帧都包含,具体看软件怎样编写。

1 数据帧介绍

  1. 数据帧的组成(遥控帧与数据帧的组成类似,只是不包含数据帧的数据段)

在这里插入图片描述
我们这里仅对数据帧进行详细介绍,数据帧一般由7个段构成,即:

(1) 帧起始。表示数据帧开始的段。

(2) 仲裁段。表示该帧优先级的段。

(3) 控制段。表示数据的字节数及保留位的段。

(4) 数据段。数据的内容,一帧可发送0~8个字节的数据。

(5) CRC段。检查帧的传输错误的段。

(6) ACK段。表示确认正常接收的段。

(7) 帧结束。表示数据帧结束的段。

另外,数据帧和遥控帧有标准格式和扩展格式两种格式。标准格式有11 个位的标识符(ID),扩展格式有29 个位的ID。各种帧的用途如表所示:

在这里插入图片描述
2 遥控帧介绍

接收单元向发送单元请求发送数据所用的帧。遥控帧由 6 个段组成。遥控帧没有数据帧的数据段。
遥控帧的构成如图 所示。
在这里插入图片描述
(1) 帧起始(SOF)
表示帧开始的段。
(2) 仲裁段
表示该帧优先级的段。可请求具有相同 ID 的数据帧。
(3) 控制段
表示数据的字节数及保留位的段。
(4) CRC 段
检查帧的传输错误的段。
(5) ACK 段
表示确认正常接收的段。
(6) 帧结束
表示遥控帧结束的段。

遥控帧和数据帧

数据帧和遥控帧的不同
 遥控帧的 RTR 位为隐性位,没有数据段。
 没有数据段的数据帧和遥控帧可通过 RTR 位区别开来。
遥控帧没有数据段,数据长度码该如何表示?
 遥控帧的数据长度码以所请求数据帧的数据长度码表示。
没有数据段的数据帧有何用途?
 例如,可用于各单元的定期连接确认/应答、或仲裁段本身带有实质性信息的情况下。

错误帧

用于在接收和发送消息时检测出错误通知错误的帧。错误帧由错误标志和错误界定符构成。
错误帧的构成如图所示。
在这里插入图片描述

过载帧

过载帧是用于接收单元通知其尚未完成接收准备的帧。过载帧由过载标志和过载界定符构成。
过载帧的构成如图 所示。
在这里插入图片描述

帧间隔

帧间隔是用于分隔数据帧和遥控帧的帧。数据帧和遥控帧可通过插入帧间隔将本帧与前面的任何帧(数据帧、遥控帧、错误帧、过载帧)分开。
过载帧和错误帧前不能插入帧间隔。
帧间隔的构成如图 所示。
在这里插入图片描述
以上就是对CAN通信单元的简单介绍,下面给大家介绍一下CAN代码配置。

两块STM32F405之间CAN通信

CAN协议代码配置

A板初始化配置

void CAN2_Configuration(void)
{
    
    
    CAN_InitTypeDef        can;
    CAN_FilterInitTypeDef  can_filter;
    GPIO_InitTypeDef       gpio;
    NVIC_InitTypeDef       nvic;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN2, ENABLE);

    GPIO_PinAFConfig(GPIOB, GPIO_PinSource12, GPIO_AF_CAN2);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_CAN2); 

    gpio.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 ;
    gpio.GPIO_Mode = GPIO_Mode_AF;
    GPIO_Init(GPIOB, &gpio);

    nvic.NVIC_IRQChannel = CAN2_RX0_IRQn;
    nvic.NVIC_IRQChannelPreemptionPriority = 0;
    nvic.NVIC_IRQChannelSubPriority = 3;
    nvic.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&nvic);
    
    nvic.NVIC_IRQChannel = CAN2_TX_IRQn;
    nvic.NVIC_IRQChannelPreemptionPriority = 0;
    nvic.NVIC_IRQChannelSubPriority = 1;
    nvic.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&nvic);

    CAN_DeInit(CAN2);
    CAN_StructInit(&can);

    can.CAN_TTCM = DISABLE;
    can.CAN_ABOM = DISABLE;    
    can.CAN_AWUM = DISABLE;    
    can.CAN_NART = DISABLE;    
    can.CAN_RFLM = DISABLE;    
    can.CAN_TXFP = ENABLE;     
    can.CAN_Mode = CAN_Mode_Normal; 
    can.CAN_SJW  = CAN_SJW_1tq;
    can.CAN_BS1 = CAN_BS1_9tq;
    can.CAN_BS2 = CAN_BS2_4tq;
    can.CAN_Prescaler = 3;   //CAN BaudRate 42/(1+9+4)/3=1Mbps
    CAN_Init(CAN2, &can);
    
    can_filter.CAN_FilterNumber=14;
    can_filter.CAN_FilterMode=CAN_FilterMode_IdMask;
    can_filter.CAN_FilterScale=CAN_FilterScale_32bit;
    can_filter.CAN_FilterIdHigh=0x0000;
    can_filter.CAN_FilterIdLow=0x0000;
    can_filter.CAN_FilterMaskIdHigh=0x0000;
    can_filter.CAN_FilterMaskIdLow=0x0000;
    can_filter.CAN_FilterFIFOAssignment=0;//the message which pass the filter save in fifo0
    can_filter.CAN_FilterActivation=ENABLE;
    CAN_FilterInit(&can_filter);
    
    CAN_ITConfig(CAN2,CAN_IT_FMP0,ENABLE);
    CAN_ITConfig(CAN2,CAN_IT_TME,ENABLE);
}

void CAN2_TX_IRQHandler(void) //CAN TX
{
    
    
  if (CAN_GetITStatus(CAN2,CAN_IT_TME)!= RESET)    //if transmit mailbox is empty 
  {
    
    
	   CAN_ClearITPendingBit(CAN2,CAN_IT_TME);   
  }
}


void CAN2_RX0_IRQHandler(void)
{
    
    
    CanRxMsg rx_message;
    if (CAN_GetITStatus(CAN2,CAN_IT_FMP0)!= RESET) 
    {
    
    
        CAN_ClearITPendingBit(CAN2, CAN_IT_FMP0);
        CAN_Receive(CAN2, CAN_FIFO0, &rx_message);  
       //电机编码器数据处理
       Can2ReceiveMsgProcess(&rx_message);
    }
}

A板CAN发送函数

void Relay_Control(u8 Relay)
{
    
    
    CanTxMsg tx_message;
  
    tx_message.StdId = 0x405;//send to  controll board
    tx_message.IDE = CAN_Id_Standard;
    tx_message.RTR = CAN_RTR_Data;
    tx_message.DLC = 0x08;
    
    tx_message.Data[0] = Relay;
    tx_message.Data[1] = 0x01;
    tx_message.Data[2] = 0x02;
    tx_message.Data[3] = 0x03;
    tx_message.Data[4] = 0x04;
    tx_message.Data[5] = 0x05;
    tx_message.Data[6] = 0x06;
    tx_message.Data[7] = 0x07;
    
    CAN_Transmit(CAN2,&tx_message);
}

B板的CAN协议配置和A板一样,所以就不详解了。

B板CAN接受函数

void USB_LP_CAN1_RX0_IRQHandler(void)
{
    
    
  CanRxMsg RxMessage;  
	if (CAN_GetITStatus(CAN1,CAN_IT_FMP0)!= RESET)
	{
    
    
		CAN_ClearITPendingBit(CAN1, CAN_IT_FF0);
		CAN_ClearFlag(CAN1, CAN_FLAG_FF0); 
        CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);//读取数据	
		RxMessage2=RxMessage;
		if(RxMessage.StdId == 0x405)
		{
    
    
			RY1 = (RxMessage.Data[0]&0x01);
			RY2 = (RxMessage.Data[0]&0x02)>>1;
			RY3 = (RxMessage.Data[0]&0x04)>>2;
			RY4 = (RxMessage.Data[0]&0x08)>>3;
			RY5 = (RxMessage.Data[0]&0x10)>>4;
			RY6 = (RxMessage.Data[0]&0x20)>>5;
		}
	}


	for(i=0;i<8;i++)
	printf("rxbuf[%d]:%d\r\n",i,RxMessage.Data[i]);
}

通过以上代码就可以实现CAN通信·,并实现数据传输。

猜你喜欢

转载自blog.csdn.net/weixin_43491077/article/details/110306660
今日推荐