38.CAN通讯原理与配置

CAN通讯原理与配置

参考资料
探索者STM32F407开发板
《STM32F4开发指南-库函数版本》——CAN通讯实验
CAN参考资料
《STM32中文参考手册_V10》-第22章 控制器局域网(bxCAN)
《STM32F4xx中文参考手册》-第24章 控制器区域网络(bxCAN)
《CAN入门教程》,见光盘:软件资料→ CAN学习资料→ can入门教程.pdf

注意:本案例基于STM32F407
注意:正点原子mini板需要添加TJA1050模块才可以进行CAN实验

一、CAN基础知识介绍

1.什么是CAN

CAN是Controller Area Network 的缩写(以下称为CAN),是ISO国际标准化的串行通信协议。由德国电气商博世公司在1986 年率先提出。此后,CAN 通过ISO11898 及ISO11519 进行了标准化。现在在欧洲已是汽车网络的标准协议。
CAN协议经过ISO标准化后有两个标准:ISO11898标准和ISO11519-2标准。其中ISO11898是针对通信速率为25Kbps~1Mbps的高速通信标准,而ISO11519-2是针对通信速率为125Kbps以下的低速通信标准。
CAN具有很高的可靠性,广泛应用于:汽车电子、工业自动化、船舶、医疗设备、工业设备等方面。

2.CAN协议的特点

①,多主控制。总线空闲时,所有单元都可发送消息,而两个以上的单元同时开始发送消息时,根据标识符(ID,非地址)决定优先级。两个以上的单元同时开始发送消息时,对各消息ID 的每个位进行逐个仲裁比较。仲裁获胜(优先级最高)的单元可继续发送消息,仲裁失利的单元则立刻停止发送而进行接收工作。
②,系统柔软性。连接总线的单元,没有类似“地址”的信息,因此,在总线上添加单元时,已连接的其他单元的软硬件和应用层都不需要做改变。
③,速度快,距离远。最高1Mbps(距离<40M),最远可达10KM(速率<5Kbps)。
④ ,具有错误检测、错误通知和错误恢复功能。所有单元都可以检测错误(错误检测功能),检测出错误的单元会立即同时通知其他所有单元(错误通知功能),正在发送消息的单元一旦检测出错误,会强制结束当前的发送。强制结束发送的单元会不断反复地重新发送此消息直到成功发送为止(错误恢复功能)。
⑤,故障封闭功能。CAN 可以判断出错误的类型是总线上暂时的数据错误(如外部噪声等)还是持续的数据错误(如单元内部故障、驱动器故障、断线等)。由此功能,当总线上发生持续数据错误时,可将引起此故障的单元从总线上隔离出去。
⑥,连接节点多。CAN 总线是可同时连接多个单元的总线。可连接的单元总数理论上是没有限制的。但实际上可连接的单元数受总线上的时间延迟及电气负载的限制。降低通信速度,可连接的单元数增加;提高通信速度,则可连接的单元数减少。

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

3.CAN物理层特征

使用ISO11898标准,物理层特征如图所示
在这里插入图片描述

CAN 控制器根据CAN_L和CAN_H上的电位差来判断总线电平。总线电平分为显性电平和隐性电平,二者必居其一。发送方通过使总线电平发生变化,将消息发送给接收方。
显性电平对应逻辑:0
CAN_H和CAN_L之差为2V左右。
隐性电平对应逻辑:1
CAN_H和CAN_L之差为0V。

显性电平具有优先权,只要有一个单元输出显性电平,总线上即为显性电平。而隐形电平则具有包容的意味,只有所有的单元都输出隐性电平,总线上才为隐性电平(显性电平比隐性电平更强)。另外,在CAN总线的起止端都有一个120Ω的终端电阻,来做阻抗匹配,以减少回波反射。

4.帧种类介绍

CAN通信是以以下5种类型的帧进行的
在这里插入图片描述

其中,数据帧和遥控帧有标准格式和扩展格式两种格式。标准格式有11 个位的标识符(ID),扩展格式有29 个位的ID 。
这里只介绍数据帧,其他帧格式,请参考《can入门教程.pdf》

5.数据帧介绍

数据帧的组成(7段)
①,帧起始。
表示数据帧开始的段。
②,仲裁段。
表示该帧优先级的段。
③,控制段。
表示数据的字节数及保留位的段。
④,数据段。
数据的内容,一帧可发送0~8个字节的数据。
⑤,CRC段。
检查帧的传输错误的段。
⑥,ACK段。
表示确认正常接收的段。
⑦,帧结束。
表示数据帧结束的段。
在这里插入图片描述

  • 1,帧起始。标准帧和扩展帧都是由1个位的显性电平表示帧起始。

  • 2,仲裁段。表示数据优先级的段,标准帧和扩展帧格式在本段有所区别,如图所示:
    在这里插入图片描述

  • 3,控制段。由6个位构成,表示数据段的字节数。标准帧和扩展帧的控制段稍有不同,如图所示:
    在这里插入图片描述

  • 4,数据段。该段可包含0~8个字节的数据,从最高位(MSB)开始输出。标准帧和扩展帧在这个段的格式完全一样:
    在这里插入图片描述

  • 5,CRC段。该段用于检查帧传输错误。由15个位的CRC顺序和1个位的CRC界定符(用于分隔的位)组成,标准帧和扩展帧在这个段的格式也是相同的:
    在这里插入图片描述

  • 6,ACK段。此段用来确认是否正常接收。由ACK槽(ACK Slot)和ACK界定符2个位组成。标准帧和扩展帧在这个段的格式也是相同的:
    在这里插入图片描述

  • 7,帧结束。由7个位的隐性位组成。标准帧和扩展帧在这个段格式完全一样。

6.总线总裁

同时多个单元发送数据时,总线仲裁过程:
在这里插入图片描述

规律:1,总线空闲时,最先发送的单元获得发送优先权,一但发送,其他单元无法抢占。2,如果有多个单元同时发送,则连续输出显性电平多的单元,具有较高优先级。
从ID开始比较,如果ID相同,还可能会比较RTR和SRR等位。

7.位时序
  • 位速率。由发送单元在非同步的情况下发送的每秒钟的位数称为位速率。一个位一般可以分为如下四段:
    同步段(SS)
    传播时间段(PTS)
    相位缓冲段1(PBS1)
    相位缓冲段2(PBS2)
    这些段又由可称为 Time Quantum(以下称为Tq)的最小时间单位构成。1 位分为4 个段,每个段又由若干个Tq 构成,这称为位时序。
    位时间=1/波特率,因此,知道位时间,我们就可以知道波特率。
    1 位由多少个Tq 构成、每个段又由多少个Tq 构成等,可以任意设定位时序。通过设定位时序,多个单元可同时采样,也可任意设定采样点。

  • 位时序各段的作用和 Tq 数如下表:
    在这里插入图片描述

  • 一个位的构成:
    在这里插入图片描述

图中采样时间加大或减少量的最大值就是SJW

二、STM32 CAN控制器简介

1.bxCAN

STM32自带了基本扩展CAN外设,又称bxCAN,bxCAN的特点如下:

  • 支持CAN协议2.0A和2.0B主动模式
  • 波特率最高达1Mbps
  • 支持时间触发通信
  • 具有3个发送邮箱
  • 具有3级深度的2个接收FIFO
  • 可变的筛选器组(也称过滤器组,最多28个)
2.模式
  • 工作模式
    ①初始化模式(INRQ=1,SLEEP=0)
    ②正常模式(INRQ=0,SLEEP=0)
    ③睡眠模式(SLEEP=1)
    通过CAN_MCR寄存器控制INRQ和SLEEP

  • 测试模式
    ①静默模式( LBKM=0,SILM=1 )
    在这里插入图片描述

静默模式下,bxCAN可以接收CANRX的数据,但是不会发送任何数据到CANTX,可以用来监控总线数据而不影响总线(只接收不发送)。

②环回模式( LBKM=1,SILM=0 )
在这里插入图片描述

环回模式下,bxCAN不接收CANRX的数据,但是会发送数据到CANTX,因为内部有个环路,即实现了自己发送的数据同时又可以自己接收(自发自收)。

③环回静默模式(LBKM=1,SILM=1)
在这里插入图片描述
自发自收,与总线数据无关。

通过CAN_BTR寄存器控制LBKM和SILM

  • 调试模式
    少用,这里不介绍。
3.bxCAN框图

4.标识符筛选器
  • CAN的标识符不表示目的地址而是表示发送优先级。接收节点根据标识符的值,来决定是否接收对应消息。

  • STM32 CAN控制器,提供了28个可配置的筛选器组(F1仅互联型才有28个,其他的只有14个),可降低CPU处理CAN通信的开销。

  • STM32 CAN控制器每个筛选器组由2个32位寄存器组成(CAN_FxR1和CAN_FxR2,x=0~27)。根据位宽不同,每个筛选器组可提供:

    • 1个32位筛选器,包括:STDID[10:0]、EXTID[17:0]、IDE和RTR位
    • 2个16位筛选器,包括:STDID[10:0]、IDE、RTR和EXTID[17:15]位
  • 筛选器可配置为:屏蔽位模式标识符列表模式。在屏蔽位模式下,标识符寄存器和屏蔽寄存器一起,指定报文标识符的任何一位,应该按照“必须匹配”或“不用关心”处理。而在标识符列表模式下,屏蔽寄存器也被当作标识符寄存器用。因此,不是采用一个标识符加一个屏蔽位的方式,而是使用2个标识符寄存器。接收报文标识符的每一位都必须跟筛选器标识符相同。

  • 通过CAN_FM1R和CAN_FS1R可配置筛选器的位宽和模式
    在这里插入图片描述

  • 为了过滤出一组标识符,应该设置筛选器组工作在屏蔽位模式。

  • 为了过滤出一个标识符,应该设置过滤器组工作在标识符列表模式。

  • 应用程序不用的筛选器组,应该保持在禁用状态(通过CAN_FA1R设置)。

  • 筛选器组中的每个筛选器,都被编号为(即:筛选器编号)从0开始,到某个最大数值-取决于筛选器组的模式和位宽的设置。

  • 通过CAN_FFA1R的设置,可以将筛选器组关联到FIFO0/FIFO1
    例:设置筛选器组0工作在:1个32位筛选器-标识符屏蔽模式,然后设置CAN_F0R1=0XFFFF0000,CAN_F0R2=0XFF00FF00。其中存放到CAN_F0R1的值就是期望收到的ID,即(STID+EXTID+IDE+RTR)最好是:0XFFFF0000。而0XFF00FF00就是设置我们需要必须关心的ID,表示收到的映像,其位[31:24]和位[15:8]这16个位的必须和CAN_F0R1中对应的位一模一样,而另外的16个位则不关心,可以一样,也可以不一样,都认为是正确的ID,即收到的映像必须是0XFFxx00xx,才算是正确的(x表示不关心)。
    关于标识符筛选器的其他介绍,请看《STM32中文参考手册》,CAN相关章节。

5.发送流程

CAN发送流程为:
程序选择1个空置的邮箱(TME=1)→设置标识符(ID),数据长度和发送数据→设置CAN_TIxR的TXRQ位为1,请求发送→邮箱挂号(等待成为最高优先级)→预定发送(等待总线空闲)→发送→邮箱空置。
在这里插入图片描述

6.接收流程

CAN接收流程为:
FIFO空→收到有效报文→挂号_1(存入FIFO的一个邮箱,这个由硬件控制,我们不需要理会)→收到有效报文→挂号_2→收到有效报文→挂号_3→收到有效报文→溢出。

CAN收到的有效报文,存储在3级邮箱深度的FIFO中。FIFO接收到的报文数,我们可以通过查询CAN_RFxR的FMP寄存器来得到,只要FMP不为0,我们就可以从FIFO读出收到的报文。
报文FIFO具有锁定功能(由CAN_MCR,RFLM位控制),锁定后,新数据将丢弃,不锁定则新数据将替代老数据
在这里插入图片描述

7.位时序

在这里插入图片描述

提示:STM32的CAN将传播时间段和相位缓冲时间段1合并成时间段1

STM32F103,设TS1=8、TS2=7、BRP=3,波特率=36000/[(9+8+1)*4]=500Kbps。
STM32F407,设TS1=6、TS2=5、BRP=5,波特率=42000/[(7+6+1)*6]=500Kbps。

三、寄存器简介

1.CAN主控制寄存器(CAN_MCR)

在这里插入图片描述

该寄存器的我们仅介绍下INRQ位,该位用来控制初始化请求。
设置INRQ=0,可使CAN从初始化模式进入正常工作模式。
设置INRQ=1,可使CAN从正常工作模式进入初始化模式 。
CAN初始化时,先设置INRQ=1 ,进入初始化模式,进行初始化(尤其是CAN_BTR的设置,该寄存器,必须在CAN正常工作之前设置),之后再设置INRQ=0,进入正常工 作模式。

2.CAN位时序寄存器(CAN_BTR)

在这里插入图片描述

3.CAN接收FIFO寄存器(CAN_RF0R/CAN_RF1R)

在这里插入图片描述

4.CAN发送邮箱标识符寄存器(CAN_TIxR)(x=0~2)

在这里插入图片描述

5.CAN发送邮箱数据长度和时间戳寄存器 (CAN_TDTxR) (x=0~2)

在这里插入图片描述

TIME[15:0]和TGT位,用于时间戳相关设置,这里不做介绍。请大家参考《STM32中文参考手册》相关章节。

6.CAN发送邮箱数据寄存器(CAN_TDLxR/CAN_TDHxR) (x=0~2)

在这里插入图片描述

7.CAN接收FIFO邮箱标识符寄存器(CAN_RIxR)(x=0/1)

在这里插入图片描述

8.CAN接收FIFO邮箱数据长度和时间戳寄存器(CAN_RDTxR) (x=0/1)

在这里插入图片描述

TIME[15:0],用于时间戳相关设置,FMI用于存储筛选器匹配索引,这里不做介绍。请大家参考《STM32中文参考手册》相关章节。

9.CAN接收FIFO邮箱邮箱数据寄存器(CAN_RDLxR/CAN_RDHxR) (x=0/1)

在这里插入图片描述

10.CAN筛选器模式寄存器(CAN_FM1R)

在这里插入图片描述

该寄存器设置筛选器的工作模式,必须在CAN_FMR寄存器FINIT=1时配置,对于STM32F103ZET6,只有[13:0]位有效,对于互联性STM32F1或者STM32F407,则全部有效。

11.CAN筛选器尺度寄存器(CAN_FS1R)

在这里插入图片描述

该寄存器用于设置筛选器的位宽,必须在CAN_FMR寄存器FINIT=1时配置,对于STM32F103ZET6,只有[13:0]位有效,对于互联性STM32F1或者STM32F407,则全部有效。

12.CAN筛选器FIFO关联寄存器(CAN_FFA1R)

在这里插入图片描述

该寄存器设置报文通过筛选器组之后,被存入的FIFO,如果对应位为0,则存放到FIFO0;如果为1,则存放到FIFO1。该寄存器也只能在过滤器处于初始化模式( CAN_FMR 寄存器的FINIT=1 )下配置。

13.CAN筛选器激活寄存器(CAN_FA1R)

在这里插入图片描述

该寄存器用于设置筛选器组的开启和关闭。对对应位置1,即开启对应的筛选器组;置0则关闭该筛选器组。

14.CAN筛选器组i寄存器x(CAN_FiRx)(i=0~27,x=1/2)

在这里插入图片描述

每个筛选器组的CAN_FiRx都由2个32位寄存器构成,即:CAN_FiR1和CAN_FiR2。根据过滤器位宽和模式的不同设置,这两个寄存器的功能也不尽相同。关于过筛选器的映射,功能描述和屏蔽寄存器的关联,在前面已经介绍过了。

四、源码讲解

1.初始化流程

①配置相关引脚的复用功能,使能CAN时钟。
要用CAN,先要使能CAN的时钟,CAN的时钟通过APB1ENR的第25位来设置。其次要设置CAN的相关引脚为复用输出,这里我们需要设置PA11为上拉输入(CAN_RX引脚)PA12为复用输出(CAN_TX引脚),并使能PA口的时钟

②设置CAN工作模式及波特率等。
通过先设置CAN_MCR寄存器的INRQ位,让CAN进入初始化模式,然后设置CAN_MCR的其他相关控制位。再通过CAN_BTR设置波特率和工作模式(正常模式/环回模式)等信息。 最后设置INRQ为0,退出初始化模式。

③设置滤波器。
本例程,我们将使用筛选器组0,并工作在32位标识符屏蔽位模式下。先设置CAN_FMR的FINIT位,进入初始化模式,然后设置筛选器组0的工作模式以及标识符ID和屏蔽位。最后激活筛选器,并退出初始化模式。

2.重点掌握以下内容

1,标识符筛选器
2,数据收发流程
3,位时序(波特率设置)
4,了解相关寄存器及其作用
5,了解CAN初始化流程

五、CAN实验测试

1.硬件连接

用跳线帽将指定GPIO与CAN_RX和CAN_TX连接在一起
在这里插入图片描述

2.can.c函数
#include "can.h"
#include "usart.h"
#include "delay.h"
#include "led.h"

CAN_HandleTypeDef	CAN1_Handler;   //CAN1句柄
CAN_TxHeaderTypeDef	TxHeader;      //发送
CAN_RxHeaderTypeDef	RxHeader;      //接收

CAN初始化
//tsjw:重新同步跳跃时间单元.范围:CAN_SJW_1TQ~CAN_SJW_4TQ
//tbs2:时间段2的时间单元.   范围:CAN_BS2_1TQ~CAN_BS2_8TQ;
//tbs1:时间段1的时间单元.   范围:CAN_BS1_1TQ~CAN_BS1_16TQ
//brp :波特率分频器.范围:1~1024; tq=(brp)*tpclk1
//波特率=Fpclk1/((tbs1+tbs2+1)*brp); 其中tbs1和tbs2我们只用关注标识符上标志的序号,例如CAN_BS2_1TQ,我们就认为tbs2=1来计算即可。
//mode:CAN_MODE_NORMAL,普通模式;CAN_MODE_LOOPBACK,回环模式;
//Fpclk1的时钟在初始化的时候设置为42M,如果设置CAN1_Mode_Init(CAN_SJW_1tq,CAN_BS2_6tq,CAN_BS1_7tq,6,CAN_MODE_LOOPBACK);
//则波特率为:42M/((6+7+1)*6)=500Kbps
//返回值:0,初始化OK;
//    其他,初始化失败; 

u8 CAN1_Mode_Init(u32 tsjw,u32 tbs2,u32 tbs1,u16 brp,u32 mode)
{
	CAN_InitTypeDef		CAN1_InitConf;
    
    CAN1_Handler.Instance=CAN1;
	
	CAN1_Handler.Init = CAN1_InitConf;
	
    CAN1_Handler.Init.Prescaler=brp;				//分频系数(Fdiv)为brp+1
    CAN1_Handler.Init.Mode=mode;					//模式设置 
    CAN1_Handler.Init.SyncJumpWidth=tsjw;			//重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1TQ~CAN_SJW_4TQ
    CAN1_Handler.Init.TimeSeg1=tbs1;				//tbs1范围CAN_BS1_1TQ~CAN_BS1_16TQ
    CAN1_Handler.Init.TimeSeg2=tbs2;				//tbs2范围CAN_BS2_1TQ~CAN_BS2_8TQ
    CAN1_Handler.Init.TimeTriggeredMode=DISABLE;	//非时间触发通信模式 
    CAN1_Handler.Init.AutoBusOff=DISABLE;			//软件自动离线管理
    CAN1_Handler.Init.AutoWakeUp=DISABLE;			//睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)
    CAN1_Handler.Init.AutoRetransmission=ENABLE;	//禁止报文自动传送 
    CAN1_Handler.Init.ReceiveFifoLocked=DISABLE;	//报文不锁定,新的覆盖旧的 
    CAN1_Handler.Init.TransmitFifoPriority=DISABLE;	//优先级由报文标识符决定 
	
    if(HAL_CAN_Init(&CAN1_Handler)!=HAL_OK)			//初始化
		return 1;
    return 0;
}

//CAN底层驱动,引脚配置,时钟配置,中断配置
//此函数会被HAL_CAN_Init()调用
//hcan:CAN句柄
void HAL_CAN_MspInit(CAN_HandleTypeDef* hcan)
{
    GPIO_InitTypeDef GPIO_Initure;
    
    __HAL_RCC_CAN1_CLK_ENABLE();                //使能CAN1时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();			    //开启GPIOA时钟
	
    GPIO_Initure.Pin=GPIO_PIN_11|GPIO_PIN_12;   //PA11,12
    GPIO_Initure.Mode=GPIO_MODE_AF_PP;          //推挽复用
    GPIO_Initure.Pull=GPIO_PULLUP;              //上拉
    GPIO_Initure.Speed=GPIO_SPEED_FAST;         //快速
    GPIO_Initure.Alternate=GPIO_AF9_CAN1;       //复用为CAN1
    HAL_GPIO_Init(GPIOA,&GPIO_Initure);         //初始化
}

void CAN_Config(void)
{
  CAN_FilterTypeDef  sFilterConfig;

  /*##-2- Configure the CAN Filter ###########################################*/
  sFilterConfig.FilterBank = 0;
  sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
  sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
  sFilterConfig.FilterIdHigh = 0x0000;
  sFilterConfig.FilterIdLow = 0x0000;
  sFilterConfig.FilterMaskIdHigh = 0x0000;
  sFilterConfig.FilterMaskIdLow = 0x0000;
  sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
  sFilterConfig.FilterActivation = ENABLE;
  sFilterConfig.SlaveStartFilterBank = 14;

  if (HAL_CAN_ConfigFilter(&CAN1_Handler, &sFilterConfig) != HAL_OK)
  {
    /* Filter configuration Error */
    while(1)
	  {
	  }
  }

  /*##-3- Start the CAN peripheral ###########################################*/
  if (HAL_CAN_Start(&CAN1_Handler) != HAL_OK)
  {
    /* Start Error */
    while(1)
	  {
	  }
  }

  /*##-4- Activate CAN RX notification #######################################*/
  if (HAL_CAN_ActivateNotification(&CAN1_Handler, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
  {
    /* Notification Error */
    while(1)
	  {
	  }
  }

  /*##-5- Configure Transmission process #####################################*/
  TxHeader.StdId = 0x321;
  TxHeader.ExtId = 0x01;
  TxHeader.RTR = CAN_RTR_DATA;
  TxHeader.IDE = CAN_ID_STD;
  TxHeader.DLC = 2;
  TxHeader.TransmitGlobalTime = DISABLE;
}

//can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)	
//len:数据长度(最大为8)				     
//msg:数据指针,最大为8个字节.
//返回值:0,成功;
//		 其他,失败;
u8 CAN1_Send_Msg(u8* msg,u8 len)
{	
    u8 i=0;
	u32 TxMailbox;
	u8 message[8];
    TxHeader.StdId=0X12;        //标准标识符
    TxHeader.ExtId=0x12;        //扩展标识符(29位)
    TxHeader.IDE=CAN_ID_STD;    //使用标准帧
    TxHeader.RTR=CAN_RTR_DATA;  //数据帧
    TxHeader.DLC=len;                
    for(i=0;i<len;i++)
    {
		message[i]=msg[i];
	}
    if(HAL_CAN_AddTxMessage(&CAN1_Handler, &TxHeader, message, &TxMailbox) != HAL_OK)//发送
	{
		return 1;
	}
	while(HAL_CAN_GetTxMailboxesFreeLevel(&CAN1_Handler) != 3) {}
    return 0;
}

//can口接收数据查询
//buf:数据缓存区;	 
//返回值:0,无数据被收到;
//		 其他,接收的数据长度;
u8 CAN1_Receive_Msg(u8 *buf)
{
 	u32 i;
	u8	RxData[8];

	if(HAL_CAN_GetRxFifoFillLevel(&CAN1_Handler, CAN_RX_FIFO0) != 1)
	{
		return 0xF1;
	}

	if(HAL_CAN_GetRxMessage(&CAN1_Handler, CAN_RX_FIFO0, &RxHeader, RxData) != HAL_OK)
	{
		return 0xF2;
	}
    for(i=0;i<RxHeader.DLC;i++)
    buf[i]=RxData[i];
	return RxHeader.DLC;
}

**注意:**在CAN文件里面有对应的注释
在这里插入图片描述

其对里面参数值进行了宏定义处理,对要填入寄存器的数据用变量进行替代
在这里插入图片描述
在这里插入图片描述

3.初始化函数:CAN1_Mode_Init

CAN初始化函数名:u8 CAN1_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
详细配置流程看代码注释和参考文档

4.数据发送函数:
//can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)	
//len:数据长度(最大为8)				     
//msg:数据指针,最大为8个字节.
//返回值:0,成功;
//		 其他,失败;
u8 CAN1_Send_Msg(u8* msg,u8 len)
{	
    u16 i=0;
    CAN1_Handler.pTxMsg->StdId=0X12;        //标准标识符
    CAN1_Handler.pTxMsg->ExtId=0x12;        //扩展标识符(29位)
    CAN1_Handler.pTxMsg->IDE=CAN_ID_STD;    //使用标准帧
    CAN1_Handler.pTxMsg->RTR=CAN_RTR_DATA;  //数据帧
    CAN1_Handler.pTxMsg->DLC=len;           //要发送的数据的长度       
    for(i=0;i<len;i++)
    CAN1_Handler.pTxMsg->Data[i]=msg[i];
    if(HAL_CAN_Transmit(&CAN1_Handler,10)!=HAL_OK) return 1;     //发送
    return 0;		
}

在这里插入图片描述

下面为CAN发送邮箱的位置
在这里插入图片描述
在这里插入图片描述

4.数据接收函数:
//can口接收数据查询
//buf:数据缓存区;	 
//返回值:0,无数据被收到;
//		 其他,接收的数据长度;
u8 CAN1_Receive_Msg(u8 *buf)
{		   		   
 	u32 i;
    if(HAL_CAN_Receive(&CAN1_Handler,CAN_FIFO0,0)!=HAL_OK) return 0;//接收数据,超时时间设置为0	
    for(i=0;i<CAN1_Handler.pRxMsg->DLC;i++)
    buf[i]=CAN1_Handler.pRxMsg->Data[i];
	return CAN1_Handler.pRxMsg->DLC;	
}

在这里插入图片描述

5.main.c函数:
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "lcd.h"
#include "sdram.h"
#include "can.h"

int main(void)
{
   	u8 key;
	u8 i=0,t=0;
	u8 cnt=0;
	u8 canbuf[8];
	u8 res;
	u8 mode=1; 
    HAL_Init();                     //初始化HAL库   
    Stm32_Clock_Init(360,25,2,8);   //设置时钟,180Mhz
    delay_init(180);                //初始化延时函数
    uart_init(115200);              //初始化USART
    LED_Init();                     //初始化LED 
    KEY_Init();                     //初始化按键
    SDRAM_Init();                   //初始化SDRAM
    LCD_Init();                     //初始化LCD
 	CAN1_Mode_Init(CAN_SJW_1TQ,CAN_BS2_6TQ,CAN_BS1_8TQ,6,CAN_MODE_LOOPBACK); //CAN初始化,波特率500Kbps      
    POINT_COLOR=RED;
	LCD_ShowString(30,50,200,16,16,"Apollo STM32F4/F7"); 
	LCD_ShowString(30,70,200,16,16,"CAN TEST");	
	LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
	LCD_ShowString(30,110,200,16,16,"2016/1/16");	 		
	LCD_ShowString(30,130,200,16,16,"LoopBack Mode");	 
	LCD_ShowString(30,150,200,16,16,"KEY0:Send WK_UP:Mode");//显示提示信息		
  	POINT_COLOR=BLUE;//设置字体为蓝色	  
	LCD_ShowString(30,170,200,16,16,"Count:");			//显示当前计数值	
	LCD_ShowString(30,190,200,16,16,"Send Data:");		//提示发送的数据	
	LCD_ShowString(30,250,200,16,16,"Receive Data:");	//提示接收到的数据		
    while(1)
    {
        key=KEY_Scan(0);
		if(key==KEY0_PRES)//KEY0按下,发送一次数据
		{
			for(i=0;i<8;i++)
			{
				canbuf[i]=cnt+i;//填充发送缓冲区
				if(i<4)LCD_ShowxNum(30+i*32,210,canbuf[i],3,16,0X80);	//显示数据
				else LCD_ShowxNum(30+(i-4)*32,230,canbuf[i],3,16,0X80);	//显示数据
 			}
			res=CAN1_Send_Msg(canbuf,8);//发送8个字节 
			if(res)LCD_ShowString(30+80,190,200,16,16,"Failed");		//提示发送失败
			else LCD_ShowString(30+80,190,200,16,16,"OK    ");	 		//提示发送成功								   
		}else if(key==WKUP_PRES)//WK_UP按下,改变CAN的工作模式
		{	   
			mode=!mode;
            if(mode==0)  CAN1_Mode_Init(CAN_SJW_1TQ,CAN_BS2_6TQ,CAN_BS1_8TQ,6,CAN_MODE_NORMAL);        //回环模式,波特率500Kbps
            else if(mode==1) CAN1_Mode_Init(CAN_SJW_1TQ,CAN_BS2_6TQ,CAN_BS1_8TQ,6,CAN_MODE_LOOPBACK);  //回环模式,波特率500Kbps
  			POINT_COLOR=RED;//设置字体为红色 
			if(mode==0)//普通模式,需要2个开发板
			{
				LCD_ShowString(30,130,200,16,16,"Nnormal Mode ");	    
			}else //回环模式,一个开发板就可以测试了.
			{
 				LCD_ShowString(30,130,200,16,16,"LoopBack Mode");
			}
 			POINT_COLOR=BLUE;//设置字体为蓝色 
		}		 
		key=CAN1_Receive_Msg(canbuf);
		if(key)//接收到有数据
		{			
			LCD_Fill(30,270,160,310,WHITE);//清除之前的显示
 			for(i=0;i<key;i++)
			{									    
				if(i<4)LCD_ShowxNum(30+i*32,270,canbuf[i],3,16,0X80);	//显示数据
				else LCD_ShowxNum(30+(i-4)*32,290,canbuf[i],3,16,0X80);	//显示数据
 			}
		}
		t++; 
		delay_ms(10);
		if(t==20)
		{
			LED0=!LED0;//提示系统正在运行	
			t=0;
			cnt++;
			LCD_ShowxNum(30+48,170,cnt,3,16,0X80);	//显示数据
		}		   
	} 		    
}
  • 8
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值