0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

CAN通信实验

汽车电子技术 来源:滑小稽笔记 作者:电子技术园地 2023-03-01 14:50 次阅读

18.1 CAN协议

18.1.1 协议概述

CAN是Controller Area Network的缩写,最初是专门用于汽车网络的通信协议,与485协议相似,CAN也是一种2线制,采用两根线的电压差进行数据传输的协议,随着CAN协议的高性能与可靠性被认同,现在被广泛运用在工业自动化,船舶等当面,目前最热门的国六标准,J1939通信协议就是以CAN协议为基础设计的。

CAN协议最远通信距离可达10km,与485相似,CAN也有两种电平,分别为显性电平与隐性电平,当CAN_H与CAN_L电压一致的时候为隐性电平,反之为显性电平。实际上隐性电平代表逻辑电平1,显性电平代表逻辑电平0,CAN在通信的时候,也需要在每个设备输出端并联1120Ω的终端匹配电阻,用于进行阻抗匹配。

18.1.2 通信组成

CAN协议通过5种类型的帧进行数据通信:数据帧,遥控帧,错误帧,过载帧和间隔帧,其中数据帧与遥控帧具有标准格式与扩展格式两种,标准格式有11个位的标识符,扩展格式则有29个位的标识符,五种帧的功能如下所示。

(1)数据帧:用于实际数据传输

(2)遥控帧:用于接收端向具有相同ID的发送端请求数据

(3)错误帧:用于检测报错时通知其他设备

(4)过载帧:用于接收端通报尚未准备好接收准备

(5)间隔帧:用于将数据帧与遥控帧和前面的帧分割

完整的数据帧构成如下图所示。

图片

数据帧一般由7段组成:

(1)帧起始:即数据帧开始的段,标准帧和扩展帧都是由1个位的显性电平表示帧起始

(2)仲裁段:表示该帧优先级,标准帧和扩展帧格式在这一段结构如下图所示。

图片

其中RTR位用于标识是否是远程帧(0代表数据帧;1代表远程帧),IDE位为标识符选择位(0表示使用标准标识符;1表示使用扩展标识符),SRR位为代替远程请求位,为隐性位,代替了标准帧中的RTR位。

(3)控制段:表示数据的字节数即保留位,由6个位构成,表示数据段的字节数。标准帧和扩展帧的控制段结构如下图所示。

图片

r0和r1为保留位,必须全部以显性电平发送,但是接收端可以接收显性、隐性及任意组合的电平。DLC段为数据长度表示段,高位在前,DLC段有效值为08,但是接收方接收到915的时候并不认为是错误。

(4)数据段:数据段内容,一段可以发送0~8个字节的数据,从最高位MSB开始输出

(5)CRC校验:CRC校验数据完整性

(6)应答段:表示正常接收

(7)帧结束:表示该帧结束

18.1.3 通信参数

由发送单元在非同步的情况下发送的每秒钟的位数称为位速率。一个位可分为4段。

(1)同步段(SS)

(2)传播时间段(PTS)

(3)相位缓冲段1(PBS1)

(4)相位缓冲段2(PBS2)

这些段又由可称为Tq的最小时间单位构成。1位分为4个段,每个段又由若干个Tq构成,这称为位时序。1位由多少个Tq构成、每个段又由多少个Tq构成等,可以任意设定位时序。通过设定位时序,多个单元可同时采样,也可任意设定采样点。各段的作用和Tq数如下表所示。

图片

18.1.4 总线仲裁

在总线空闲态,最先开始发送消息的单元获得发送权。当多个单元同时开始发送时,各发送单元从仲裁段的第一位开始进行仲裁。连续输出显性电平最多的单元可继续发送。实现过程如下图所示。

图片

单元1和单元2同时开始向总线发送数据,开始部分他们的数据格式是一样的,故无法区分优先级,直到T时刻,单元1输出隐性电平,而单元2输出显性电平,此时单元1仲裁失利,立刻转入接收状态工作,不再与单元2竞争,而单元2则顺利获得总线使用权,继续发送自己的数据。这就实现了仲裁,让连续发送显性电平多的单元获得总线使用权

18.1.5 STM32 CAN模组简介

STM32F1系列自带的是基本扩展CAN,支持CAN协议的2.0A和2.0B,支持报文发送的优先级要求可软件配置,最大通信速率1Mbps,3个发送邮箱和3级深度的2个接收缓存器FIFO,28个可变的滤波器组,STM32的CAN模组结构如下图所示。

图片

从上面结构可以看出,两个CAN都分别具备自己的发送与接收邮箱,但是28个滤波器却是公用的,通过FMR寄存器可以设置滤波器的分配方式,STM32的每个滤波器组的位宽都可以独立配置,根据位宽的不同,每个滤波器组可以提供

(1)1个32位过滤器:包含STDID[10:0],EXTID[17:0],18位扩展ID,IDE和RTR位

(2)1个16位过滤器:包含STDID[10:0],IDE,RTR和EXTID[17:15]

此外过滤器可以配置为屏蔽位模式与标识符列表模式。

(1)在屏蔽位模式下,标识符寄存器和屏蔽寄存器一起,指定报文标识符的任何一位,应该按照“必须匹配”或“不用关心”处理

(2)标识符列表模式下,屏蔽寄存器也被当作标识符寄存器用。因此,不是采用一个标识符加一个屏蔽位的方式,而是使用2个标识符寄存器。接收报文标识符的每一位都必须跟过滤器标识符相同。

18.1.6 CAN的发送流程

第1步:程序选择1个空置的邮箱(TME=1),设置标识符(ID),数据长度和发送数据

第2步:设置CAN_TIxR的TXRQ位为1,请求发送

第3步:邮箱挂号(等待成为最高优先级)

第4步:预定发送(等待总线空闲)

第5步:发送

第6步:邮箱空置

整个发送流程如下图所示。

图片

18.1.7 CAN的接收流程

CAN接收到的有效报文,被存储在3级邮箱深度的FIFO中。FIFO完全由硬件来管理,从而节省了CPU的处理负荷,简化了软件并保证了数据的一致性。应用程序只能通过读取FIFO输出邮箱,来读取FIFO中最先收到的报文。这里的有效报文是指那些正确被接收的(直到EOF都没有错误)且通过了标识符过滤的报文。前面我们知道CAN的接收有2个FIFO,我们每个滤波器组都可以设置其关联的FIFO,通过CAN_FFA1R的设置,可以将滤波器组关联到FIFO0/FIFO1。CAN接收流程为:

FIFO空->收到有效报文->挂号1(存入FIFO的一个邮箱,这个由硬件控制,我们不需要理会)->收到有效报文->挂号2->收到有效报文->挂号3->收到有效报文->溢出

这个流程里面,我们没有考虑从FIFO读出报文的情况,实际情况是:我们必须在FIFO溢出之前,读出至少1个报文,否则下个报文到来,将导致FIFO溢出,从而出现报文丢失。每读出1个报文,相应的挂号就减1,直到FIFO空,完整的接收流程图如下图所示。

图片

FIFO接收到的报文数,我们可以通过查询CAN_RFxR的FMP寄存器来得到,只要FMP不为0,我们就可以从FIFO读出收到的报文。

18.1.8 通信速率计算

根据通信参数小节我们可以知道1个位是由4个段组成,分别为同步段(SS),传播时间段(PTS),相位缓冲段1(PBS1),相位缓冲段2(PBS2),STM32内部将传播时间段与相位缓冲段1合并为时间段1,这样1个位就是有3个段组成,即同步段SS,时间段1和时间段2(即相位缓冲段),由于波特率的定义是1秒内发送二进制位的个数,所以,CAN波特率的计算公式为

图片

18.1.9 STM32F1系列CAN测试模式

(1)静默模式

通过对CAN_BTR寄存器的SILM位置1,来选择静默模式。在静默模式下,CAN可以正常地接收数据帧和远程帧,但只能发出隐性位,而不能真正发送报文。如果bxCAN需要发出显性位(确认位、过载标志、主动错误标志),那么这样的显性位在内部被接回来从而可以被CAN内核检测到,同时CAN总线不会受到影响而仍然维持在隐性位状态。因此,静默模式通常用于分析CAN总线的活动,而不会对总线造成影响-显性位(确认位、错误帧)不会真正发送到总线上,静默模式等效图如下图所示。

图片

(2)环回模式

通过对CAN_BTR寄存器的LBKM位置1,来选择环回模式。在环回模式下,CAN把发送的报文当作接收的报文并保存(如果可以通过接收过滤)在接收邮箱里,环回模式等效图如下图所示。

图片

(3)环回静默模式

该模式可用于“热自测试”,即可以像环回模式那样测试CAN,但却不会影响CANTX和CANRX所连接的整个CAN系统。在环回静默模式下,CANRX引脚与CAN总线断开,同时CANTX引脚被驱动到隐性位状态,环回静默模式等效图如下图所示。

图片

18.2 相关寄存器

18.2.1 CAN主控制器:CAN_MCR

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
- DBF
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
RESET - TTCM ABOM AWUM NART RFLM TXFP SLEEP INRQ

Bit 16:DBF:调试冻结

0:在调试时,CAN照常工作

1:在调试时,冻结CAN的接收/发送。仍然可以正常地读写和控制接收FIFO

Bit 15:bxCAN软件复位

0:本外设正常工作

1:对bxCAN进行强行复位,复位后bxCAN进入睡眠模式。此后硬件自动对该位清0

Bit 7:时间触发通信模式

0:禁止时间触发通信模式

1:允许时间触发通信模式

Bit 6:自动离线管理

0:离线状态的退出过程是,软件对INRQ位进行置1随后清0后,一旦硬件检测到128次11位连续的隐性位,则退出离线状态

1:一旦硬件检测到128次11位连续的隐性位,则自动退出离线状态

Bit 5:自动唤醒模式

0:睡眠模式通过清除CAN_MCR寄存器的SLEEP位,由软件唤醒

1:睡眠模式通过检测CAN报文,由硬件自动唤醒。唤醒的同时,硬件自动对SLEEP和SLAK位清0

Bit 4:禁止报文自动重传

0:按照CAN标准,CAN硬件在发送报文失败时会一直自动重传直到发送成功

1:CAN报文只被发送1次,不管发送的结果如何

Bit 3:接收FIFO锁定模式

0:在接收溢出时FIFO未被锁定,当接收FIFO的报文未被读出,下一个收到的报文会覆盖原有的报文

1:在接收溢出时FIFO被锁定,当接收FIFO的报文未被读出,下一个收到的报文会被丢弃

Bit 2:发送FIFO优先级

0:优先级由报文的标识符来决定

1:优先级由发送请求的顺序来决定

Bit 1:睡眠模式请求(在复位后该位被置1)

1:可以请求CAN进入睡眠模式,一旦当前的CAN活动结束,CAN就进入睡眠

0:使CAN退出睡眠模式

Bit 0:初始化请求

0:当CAN在接收引脚检测到连续的11个隐性位后,CAN就达到同步,并为接收和发送数据作好准备了。为此,硬件相应地对INAK位清0

1:一旦当前的CAN活动结束,CAN就进入初始化模式。相应地,硬件对INAK位置1

18.2.2 CAN主状态寄存器:CAN_MSR

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
- RX SAMP RXM TXM - SLAKI WKUI ERRI SLAK INAK

Bit 11:CAN接收电平,该位反映CAN接收引脚的实际电平

Bit 10:上次采样值,CAN接收引脚的上次采样值(对应于当前接收位的值)

Bit 9:接收模式,该位为1表示CAN当前为接收器

Bit 8:发送模式,该位为1表示CAN当前为发送器

Bit 4:睡眠确认中断,当SLKIE=1,一旦CAN进入睡眠模式硬件就对该位置1,紧接着相应的中断被触发软件可对该位清0,当SLAK位被清0时硬件也对该位清0

Bit 3:唤醒中断挂号,当CAN处于睡眠状态,一旦检测到帧起始位,硬件就置该位为1

Bit 2:出错中断挂号,当检测到错误时,CAN_ESR寄存器的某位被置1,如果CAN_IER寄存器的相应中断使能位也被置1时,则硬件对该位置1

Bit 1:睡眠模式确认

0:CAN退出睡眠模式

1:CAN模块正处于睡眠模式

Bit 0:初始化确认

0:CAN退出初始化模式时

1:CAN模块正处于初始化模式

18.2.3 CAN位时序寄存器:CAN_BTR

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
SILM LBKM - SJW[1:0] - TS2[2:0] TS1[3:0]
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
- BRP[9:0]

Bit 31:静默模式(用于调试)

0:正常状态

1:静默模式

Bit 30:环回模式(用于调试)

0:禁止环回模式

1:允许环回模式

Bit 25~Bit 24:重新同步跳跃宽度,该位域定义了CAN硬件在每位中可以延长或缩短多少个时间单元的上限,t RJW =t CAN ×(SJW[1:0]+1)

Bit 22~Bit 20:时间段2,该位域定义了时间段2占用了多少个时间单元,t BS2 =t CAN ×(TS2[2:0]+1)

Bit 19~Bit 16:时间段1,该位域定义了时间段1占用了多少个时间单元,t BS1 =t CAN ×(TS1[3:0]+1)

Bit 9Bit 0:波特率分频器,该位域定义了时间单元tq的时间长度,t q =(BRP[9:0]+1)×tPCLK~

18.2.4 CAN发送状态寄存器:CAN_TSR

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
LOW2 LOW1 LOW0 TME2 TME1 TME0 CODE[1:0] ABRQ2 - TERR2 ALST2 TXOK2 RQCP2
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
ABRQ1 - TERR1 ALST1 TXOK1 RQCP1 ABRQ0 - TERR0 ALST0 TXOK0 RQCP0

Bit 31,Bit30,Bit 29:邮箱x最低优先级标志

当多个邮箱在等待发送报文,且邮箱x的优先级最低时,硬件对该位置1

Bit 28,Bit 27,Bit 26:发送邮箱x空

Bit 25~Bit 24:邮箱号

当有至少1个发送邮箱为空时,表示下一个空的发送邮箱号。

当所有的发送邮箱都为空时,表示优先级最低的那个发送邮箱号

Bit 23,Bit 15,Bit 7:邮箱x终止发送

Bit 19,Bit 11,Bit 3:邮箱x发送失败

Bit 18,Bit 10,Bit 2:邮箱x仲裁丢失

Bit 17,Bit 9,Bit 1:邮箱x发送成功

Bit 16,Bit 8,Bit 0:邮箱x请求完成

18.2.5 CAN接收FIFOx寄存器:CAN_RFxR

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
- ROFM FOVR FULL - FMP[1:0]

Bit 5:释放接收FIFO,输出邮箱软件通过对该位置1来释放接收FIFO的输出邮箱。如果接收FIFO为空,那么对该位置1没有任何效果,即只有当FIFO中有报文时对该位置1才有意义。如果FIFO中有2个以上的报文,由于FIFO的特点,软件需要释放输出邮箱才能访问第2个报文,当输出邮箱被释放时,硬件对该位清0

Bit 4:FIFO溢出,当FIFO0已满,又收到新的报文且报文符合过滤条件,硬件对该位置1,该位由软件清0

Bit 3:FIFO满,当FIFO0中有3个报文时,硬件对该位置1,该位由软件清0

Bit 1~Bit 0:FIFO报文数目,表示当前接收FIFO0中存放的报文数目

每当1个新的报文被存入接收FIFO0,硬件就对FMP0加1

每当软件对RFOM位写1来释放输出邮箱,FMP就被减1,直到其为0

18.2.6 CAN过滤器主控寄存器:CAN_FMR

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
- FINIT

Bit 0:过滤器初始化模式

0:过滤器组工作在正常模式

1:过滤器组工作在初始化模式

18.2.7 CAN过滤器模式寄存器:CAN_FM1R

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
- FBM[27:16]
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
FBM[15:0]

Bit 29~Bit 0:过滤器模式

0:过滤器组x的2个32位寄存器工作在标识符屏蔽位模式

1:过滤器组x的2个32位寄存器工作在标识符列表模式

18.2.8 CAN过滤器位宽寄存器:CAN_FS1R

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
- FSC[27:16]
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
FSC[15:0]

Bit 29~Bit 0:过滤器位宽设置

0:过滤器位宽为2个16位

1:过滤器位宽为单个32位

18.2.9 CAN过滤器FIFO关联寄存器:CAN_FFA1R

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
FFA[27:16]
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
FFA[15:0]

Bit 29~Bit 0:过滤器关联设置

0:过滤器被关联到FIFO0

1:过滤器被关联到FIFO1

18.2.10 CAN过滤器激活寄存器:CAN_FA1R

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
FACT[27:16]
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
FACT[15:0]

Bit 29~Bit 0:过滤器激活

0:过滤器被禁用

1:过滤器被激活

18.2.11 发送邮箱标识符寄存器:CAN_TIxR

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
STID[10:0]/EXID[28:18] EXID[17:13]
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
EXID[12:0] IDE RTR TXRQ

Bit 31~Bit 21:标准标识符或扩展标识符,依据IDE位的内容,这些位或是标准标识符,或是扩展身份标识的高字节

Bit 20~Bit 3:扩展标识符,扩展身份标识的低字节

Bit 2:标识符选择

0:使用标准标识符

1:使用扩展标识符

Bit 1:远程发送请求

0:数据帧

1:远程帧

Bit 0:发送数据请求,由软件对其置1,来请求发送邮箱的数据。当数据发送完成,邮箱为空时,硬件对其清0

18.2.12 发送邮箱数据长度和时间戳寄存器:CAN_TDTxR

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
TIME[15:0]
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
- TGT - DLC[3:0]

Bit 31~Bit 16:报文时间戳,该域包含了,在发送该报文SOF的时刻,16位定时器的值

Bit 8:发送时间戳(只有在CAN处于时间触发通信模式才有效)

0:不发送时间戳TIME[15:0]

1:发送时间戳TIME[15:0]

注:在长度为8的报文中,时间戳TIME[15:0]是最后2个发送的字节:TIME[7:0]作为第7个字节,TIME[15:8]为第8个字节,替换了写入CAN_TDHxR[31:16]的数据。为了把时间戳的2个字节发送出去,DLC必须编程为8。

Bit 3~Bit 0:发送数据长度,指定数据报文的数据长度或者远程帧请求的数据长度

18.2.13 发送邮箱低字节数据寄存器:CAN_TDLxR

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
DATA3[7:0] DATA2[7:0]
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
DATA1[7:0] DATA0[7:0]

Bit 31~Bit 24:数据字节3

Bit 23~Bit 16:数据字节2

Bit 15~Bit 8:数据字节1

Bit 7~Bit 0:数据字节0

18.2.14 发送邮箱高字节数据寄存器:CAN_TDHxR

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
DATA7[7:0] DATA6[7:0]
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
DATA5[7:0] DATA4[7:0]

Bit 31~Bit 24:数据字节7

Bit 23~Bit 16:数据字节6

Bit 15~Bit 8:数据字节5

Bit 7~Bit 0:数据字节4

18.2.15 接收邮箱标识符寄存器:CAN_RIxR

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
STID[10:0]/EXID[28:18] EXID[17:13]
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
EXID[12:0] IDE RTR -

Bit 31~Bit 21:标准标识符或扩展标识符,依据IDE位的内容,这些位或是标准标识符,或是扩展身份标识的高字节

Bit 20~Bit 3:扩展标识符,扩展身份标识的低字节

Bit 2:标识符选择

0:使用标准标识符

1:使用扩展标识符

Bit 1:远程发送请求

0:数据帧

1:远程帧

18.2.16 接收邮箱数据长度和时间戳寄存器:CAN_RDTxR

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
TIME[15:0]
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
FMI[7:0] - DLC[3:0]

Bit 31~Bit 16:报文时间戳,该域包含了,在接收该报文SOF的时刻,16位定时器的值

Bit 15~Bit 8:过滤器匹配序号

Bit 3~Bit 0:接收数据长度

18.2.17 接收邮箱低字节数据寄存器:CAN_RDLxR

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
DATA3[7:0] DATA2[7:0]
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
DATA1[7:0] DATA0[7:0]

Bit 31~Bit 24:数据字节3

Bit 23~Bit 16:数据字节2

Bit 15~Bit 8:数据字节1

Bit 7~Bit 0:数据字节0

18.2.18 接收邮箱高字节数据寄存器:CAN_RDHxR

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
DATA7[7:0] DATA6[7:0]
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
DATA5[7:0] DATA4[7:0]

Bit 31~Bit 24:数据字节7

Bit 23~Bit 16:数据字节6

Bit 15~Bit 8:数据字节5

Bit 7~Bit 0:数据字节4

18.3 实验例程

实验内容:利用CAN的回环模式进行数据收发测试,通信速率500Kbps。

(1)创建can.h文件,并输入以下代码。

/*********************************************************************************************************
            CAN    通    信    驱    动    文    件
*********************************************************************************************************/
#ifndef _CAN_H_
#define _CAN_H_


#include "sys.h"                
/*********************************************************************************************************
                  函    数    列    表
*********************************************************************************************************/
u8 CAN_Init( u8 tsjw, u8 tbs2, u8 tbs1, u16 brp, u8 mode ) ;                      //CAN初始化
u8 CAN_Tx_Msg( u32 id, u8 ide, u8 rtr, u8 len, u8 *dat ) ;                        //CAN发送数据
void CAN_Rx_Msg( u8 fifox, u32 *id, u8 *ide, u8 *rtr, u8 *len, u8 *dat ) ;                //CAN接收数据


#endif

(2)创建can.c文件,并输入以下代码。

/*********************************************************************************************************
            CAN    通    信    驱    动    程    序
*********************************************************************************************************/
#include "can.h"
/*******************************************************
Name    :CAN_Mode_Init
Function  :CAN初始化
Paramater  :
      tsjw:重新同步跳跃时间单元.范围:1~3
      tbs2:时间段2的时间单元.范围:1~8
      tbs1:时间段1的时间单元.范围:1~16
      brp:波特率分频器.范围:1~1024
      mode:工作模式
        0:普通模式
        1:回环模式
Return    :
      0:成功
      其他:失败
*******************************************************/
u8 CAN_Init( u8 tsjw, u8 tbs2, u8 tbs1, u16 brp, u8 mode )
{
  u16 i=0;
   if( ( tsjw==0 )||( tbs2==0 )||( tbs1==0 )||( brp==0 ) )
    return 1 ;
  //先减去1.再用于设置
  tsjw -= 1 ;
  tbs2 -= 1 ;
  tbs1 -= 1 ;
  brp -= 1 ;


  RCC->APB2ENR |= 1<<2 ;                                        //使能PA时钟
  GPIOA->CRH &= 0xFFF00FFF ;
  GPIOA->CRH |= 0x000B8000 ;
    GPIOA->ODR |= 3<<11 ;
  RCC->APB1ENR |= 1<<25 ;                                        //使能CAN时钟
  CAN1->MCR = 0x0000 ;                                        //退出睡眠模式
  CAN1->MCR |= 1<<0 ;                                          //请求CAN进入初始化模式
  while( ( CAN1->MSR&0x01 )==0 )
  {
    i ++ ;
    if( i>100 )
      return 2 ;                                          //进入初始化模式失败
  }
  CAN1->MCR |= 0<<7 ;                                          //非时间触发通信模式
  CAN1->MCR |= 0<<6 ;                                          //软件自动离线管理
  CAN1->MCR |= 0<<5 ;                                          //睡眠模式通过软件唤醒
  CAN1->MCR |= 1<<4 ;                                          //禁止报文自动传送
  CAN1->MCR |= 0<<3 ;                                          //报文不锁定,新的覆盖旧的
  CAN1->MCR |= 0<<2 ;                                          //优先级由报文标识符决定
  CAN1->BTR = 0x00000000 ;                                      //清除原来的设置
  CAN1->BTR |= mode<<30 ;                                        //模式设置
  CAN1->BTR |= tsjw<<24 ;                                        //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位
  CAN1->BTR |= tbs2<<20 ;                                        //Tbs2=tbs2+1个时间单位
  CAN1->BTR |= tbs1<<16 ;                                        //Tbs1=tbs1+1个时间单位
  CAN1->BTR |= brp<<0 ;                                        //分频系数(Fdiv)为brp+1
  CAN1->MCR &= ~( 1<<0 ) ;                                      //请求CAN退出初始化模式
  while( ( CAN1->MSR&0x01 )!=0 )
  {
    i ++ ;
    if( i>0xFFF0 )
      return 3 ;                                          //退出初始化模式失败
  }
  //过滤器初始化
  CAN1->FMR |= 1<<0 ;                                          //过滤器组工作在初始化模式
  CAN1->FA1R &= ~( 1<<0 ) ;                                      //过滤器0不激活
  CAN1->FS1R |= 1<<0 ;                                        //过滤器位宽为32位
  CAN1->FM1R |= 0<<0 ;                                        //过滤器0工作在标识符屏蔽位模式
  CAN1->FFA1R |= 0<<0 ;                                        //过滤器0关联到FIFO0
  CAN1->sFilterRegister[ 0 ].FR1 = 0x00000000 ;                            //32位ID
  CAN1->sFilterRegister[ 0 ].FR2 = 0x00000000 ;                            //32位MASK
  CAN1->FA1R |= 1<<0 ;                                        //激活过滤器0
  CAN1->FMR &= 0<<0 ;                                          //过滤器组进入正常模式
  return 0 ;
}
/*******************************************************
Name    :CAN_Tx_Msg
Function  :CAN发送数据
Paramater  :
      id:标准ID(11位)/扩展ID(11位+18位)
      ide:ID类型
        0:标准帧
        1:扩展帧
      rtr:数据类型
        0:数据帧
        1:远程帧
      len:要发送的数据长度
      *dat:数据指针
Return    :
      0~3:邮箱编号
      0xFF:无有效邮箱
*******************************************************/
u8 CAN_Tx_Msg( u32 id, u8 ide, u8 rtr, u8 len, u8 *dat )
{
  u16 i;
  u8 mbox, sta=0 ;
  //邮箱0为空
  if( CAN1->TSR&( 1<<26 ) )
    mbox = 0 ;
  //邮箱1为空
  else if( CAN1->TSR&( 1<<27 ) )
    mbox = 1 ;
  //邮箱2为空
  else if( CAN1->TSR&( 1<<28 ) )
    mbox = 2 ;
  //无空邮箱,无法发送
  else
    return 0xFF ;
  CAN1->sTxMailBox[ mbox ].TIR = 0 ;                                  //清除之前的设置
  //标准帧
  if( ide==0 )
  {
    id &= 0x7FF ;                                          //取低11位stdid
    id <<= 21 ;
  }
  //扩展帧
  else
  {
    id &= 0x1FFFFFFF ;                                        //取低32位extid
    id <<= 3 ;
  }
  CAN1->sTxMailBox[ mbox ].TIR |= id ;
  CAN1->sTxMailBox[ mbox ].TIR |= ide<<2 ;
  CAN1->sTxMailBox[ mbox ].TIR |= rtr<<1 ;
  len &= 0x0F ;                                            //得到低四位
  CAN1->sTxMailBox[ mbox ].TDTR &= 0xFFFFFFF0 ;
  CAN1->sTxMailBox[ mbox ].TDTR |= len ;                                //设置DLC
  //待发送数据存入邮箱
  CAN1->sTxMailBox[ mbox ].TDHR = ( ( (u32)dat[7]<<24 )|( (u32)dat[6]<<16 )|( (u32)dat[5]<<8 )|( (u32)dat[4] ) ) ;
  CAN1->sTxMailBox[ mbox ].TDLR = ( ( (u32)dat[3]<<24 )|( (u32)dat[2]<<16 )|( (u32)dat[1]<<8 )|( (u32)dat[0] ) ) ;
  CAN1->sTxMailBox[ mbox ].TIR |= 1<<0 ;                                //请求发送邮箱数据
  //获取发送状态
  while( ( sta!=0x07 )&&( i<0xFFF ) )
  {
    i ++ ;
    switch( mbox )
    {
      //邮箱0
      case 0:
        sta |= CAN1->TSR&( 1<<0 ) ;                                //RQCP0
        sta |= CAN1->TSR&( 1<<1 ) ;                                //TXOK0
        sta |= CAN1->TSR&( 1<<26 )>>24 ;                            //TME0
        break;
      //邮箱1
      case 1:
        sta |= CAN1->TSR&( 1<<8 )>>8 ;                              //RQCP1
        sta |= CAN1->TSR&( 1<<9 )>>8 ;                              //TXOK1
        sta |= CAN1->TSR&( 1<<27 )>>25 ;                            //TME1
        break;
      //邮箱2
      case 2:
        sta |= CAN1->TSR&( 1<<16 )>>16 ;                            //RQCP2
        sta |= CAN1->TSR&( 1<<17 )>>16 ;                            //TXOK2
        sta |= CAN1->TSR&( 1<<28 )>>26 ;                            //TME2
        break;
      //邮箱号不对
      default:
        sta = 0x05 ;
      break ;
    }
  }
  if( i==0xFFF )
    mbox = 0xFF ;
  return mbox ;
}
/*******************************************************
Name    :CAN_Rx_Msg
Function  :CAN接收数据
Paramater  :
      fifox:邮箱号
      id:标准ID(11位)/扩展ID(11位+18位)
      ide:ID类型
        0:标准帧
        1:扩展帧
      rtr:数据类型
        0:数据帧
        1:远程帧
      len:要发送的数据长度
      *dat:数据指针
Return    :None
*******************************************************/
void CAN_Rx_Msg( u8 fifox, u32 *id, u8 *ide, u8 *rtr, u8 *len, u8 *dat )
{
  if( ( fifox==0 )&&( ( CAN1->RF0R&0x03 )==0 ) )
    len = 0 ;
  else if( ( fifox==1 )&&( ( CAN1->RF1R&0x03 )==0 ) )
    len = 0 ;
  else
  {
    //接收数据
    *ide = CAN1->sFIFOMailBox[ fifox ].RIR&0x04 ;                          //得到标识符选择位的值
    if( *ide==0 )
      *id = CAN1->sFIFOMailBox[ fifox ].RIR>>21 ;                          //标准标识符
    else
      *id = CAN1->sFIFOMailBox[ fifox ].RIR>>3 ;                          //扩展标识符
    *rtr = CAN1->sFIFOMailBox[ fifox ].RIR&0x02 ;                          //得到远程发送请求值
    *len = CAN1->sFIFOMailBox[ fifox ].RDTR&0x0F ;                          //得到DLC
    dat[ 0 ] = CAN1->sFIFOMailBox[ fifox ].RDLR&0xFF ;
    dat[ 1 ] = ( CAN1->sFIFOMailBox[ fifox ].RDLR>>8 )&0xFF ;
    dat[ 2 ] = ( CAN1->sFIFOMailBox[ fifox ].RDLR>>16 )&0xFF ;
    dat[ 3 ] = ( CAN1->sFIFOMailBox[ fifox ].RDLR>>24 )&0xFF ;
    dat[ 4 ] = CAN1->sFIFOMailBox[ fifox ].RDHR&0xFF ;
    dat[ 5 ] = ( CAN1->sFIFOMailBox[ fifox ].RDHR>>8 )&0xFF ;
    dat[ 6 ] = ( CAN1->sFIFOMailBox[ fifox ].RDHR>>16 )&0xFF ;
    dat[ 7 ] = ( CAN1->sFIFOMailBox[ fifox ].RDHR>>24 )&0xFF ;
    if( fifox==0 )
      CAN1->RF0R |= 0x20 ;                                    //释放FIFO0邮箱
    else if( fifox==1 )
      CAN1->RF1R |= 0x20 ;                                    //释放FIFO1邮箱
  }
}

(3)创建1.c文件,并输入以下代码。

#include "sys.h"
#include "delay.h"
#include "usart1.h"
#include "can.h"


int main()
{
  u32 id;
  u8 ide, rtr, len, canbuf[ 8 ] ;
  STM32_Clock_Init( 9 ) ;                                        //STM32时钟初始化
  SysTick_Init( 72 ) ;                                        //SysTick初始化
  USART1_Init( 72, 115200 ) ;                                      //初始化串口1波特率115200
  CAN_Init( 1, 8, 9, 4, 1 ) ;                                      //CAN初始化
  CAN_Tx_Msg( 0x12, 0, 0, 8, "CAN Test" ) ;                              //发送8个字节
  while( 1 )
  {
    CAN_Rx_Msg( 0, &id, &ide, &rtr, &len, canbuf ) ;                        //读取数据
  }
}
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 通信协议
    +关注

    关注

    28

    文章

    856

    浏览量

    40252
  • CAN
    CAN
    +关注

    关注

    57

    文章

    2715

    浏览量

    463345
  • 工业自动化
    +关注

    关注

    17

    文章

    2297

    浏览量

    67176
收藏 人收藏

    评论

    相关推荐

    【GD32F303红枫派开发板使用手册】第三十讲 CAN -CAN通信实验

    CAN 是 Controller Area Network 的缩写,是由德国BOSCH公司开发的,已成为ISO 国际标准化的串行通信协议。其主要应用场合为汽车和工业控制。 CAN具有传输距离长,传输
    的头像 发表于 07-05 11:13 2859次阅读
    【GD32F303红枫派开发板使用手册】第三十讲 <b class='flag-5'>CAN</b> -<b class='flag-5'>CAN</b><b class='flag-5'>通信实验</b>

    #硬声创作季 #STM32 学STM32-58 CAN通信实验讲解-1

    水管工
    发布于 :2022年11月11日 01:29:06

    #硬声创作季 #STM32 学STM32-58 CAN通信实验讲解-2

    水管工
    发布于 :2022年11月11日 01:29:36

    #硬声创作季 #STM32 学STM32-58 CAN通信实验讲解-3

    水管工
    发布于 :2022年11月11日 01:30:05

    【NUCLEO-F412ZG试用体验】之————07 CAN通信实验

    关于CAN通信的问题由于不熟悉,试了好几天,现在终于解决了,先记下来担心时间长了又忘记了。一、硬件设置1.1 要进行CAN通信首先要建立通信
    发表于 01-06 15:53

    请问CAN收发实验怎么用esp8266在电脑上查看发送的数据?

    在用stm32f1战舰v3做CAN通信实验,资料里有完整的代码,挺好的。但是要求用ESP8266 WiFi模块,能在电脑上用网络助手可以查看我用CAN发送的数据。也就是说我用CAN
    发表于 05-09 06:35

    基于STM32F429HAL库的CAN通信代码分享

    基于STM32F429HAL库的CAN通信代码* 最近在学正点原子阿波罗STM32F429开发板的CAN通信章节,例程里只提供了查询方式实现的CAN
    发表于 02-08 07:00

    CAN通信实验报文对象的FIFO缓冲器应用

    关键词 CAN报文对象的FIFO模式应用摘 要 CAN通信实验
    发表于 11-07 14:18 33次下载

    CAN通信实验

    CAN通信实验
    发表于 03-30 16:48 0次下载

    正点原子开拓者FPGA Qsys视频:CAN通信实验

    CAN总线采用了多主竞争式总线结构,具有多主站运行和分散仲裁的串行总线以及广播通信的特点。CAN总线上任意节点可在任意时刻主动地向网络上其它节点发送信息而不分主次,因此可在各节点之间实现自由
    的头像 发表于 09-18 07:06 3139次阅读
    正点原子开拓者FPGA Qsys视频:<b class='flag-5'>CAN</b><b class='flag-5'>通信实验</b>

    STM32CubMX自学笔记(七)—CAN通信实验

    STM32CubMX自学笔记(七)—CAN通信实验主要介绍由于项目需求要用到CAN通信协议,因此学习一下CAN
    发表于 12-04 18:21 11次下载
    STM32CubMX自学笔记(七)—<b class='flag-5'>CAN</b><b class='flag-5'>通信实验</b>

    STM32入门学习笔记之CAN通信实验1

    CAN是Controller Area Network的缩写,最初是专门用于汽车网络的通信协议,与485协议相似,CAN也是一种2线制,采用两根线的电压差进行数据传输的协议,随着CAN
    的头像 发表于 02-16 14:37 1685次阅读
    STM32入门学习笔记之<b class='flag-5'>CAN</b><b class='flag-5'>通信实验</b>1

    STM32入门学习笔记之CAN通信实验2

    CAN是Controller Area Network的缩写,最初是专门用于汽车网络的通信协议,与485协议相似,CAN也是一种2线制,采用两根线的电压差进行数据传输的协议,随着CAN
    的头像 发表于 02-16 14:37 1671次阅读

    STM32入门学习笔记之CAN通信实验3

    CAN是Controller Area Network的缩写,最初是专门用于汽车网络的通信协议,与485协议相似,CAN也是一种2线制,采用两根线的电压差进行数据传输的协议,随着CAN
    的头像 发表于 02-16 14:37 929次阅读

    STM32入门学习笔记之CAN通信实验4

    CAN是Controller Area Network的缩写,最初是专门用于汽车网络的通信协议,与485协议相似,CAN也是一种2线制,采用两根线的电压差进行数据传输的协议,随着CAN
    的头像 发表于 02-16 14:37 1053次阅读