IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 嵌入式工程师成长之路(三十九)之CAN通讯以及STM32F1单片机程序 -> 正文阅读

[嵌入式]嵌入式工程师成长之路(三十九)之CAN通讯以及STM32F1单片机程序

CAN通讯以及STM32F1单片机程序

1、CAN通讯基本概念:

(1)定义:
CAN是Controller Area Network 的缩写(以下称为CAN),是ISO国际标准化的串行通信协议。由德国电气商博世公司在1986 年率先提出。此后,CAN 通过ISO11898 及ISO11519 进行了标准化。现在在欧洲已是汽车网络的标准协议。
CAN协议经过ISO标准化后有两个标准:ISO11898标准和ISO11519-2标准。
其中ISO11898是针对通信速率为125Kbps~1Mbps的高速通信标准(闭环),而ISO11519-2是针对通信速率为125Kbps以下的低速通信标准(开环)。
注:
Kbps:总线的通信速率,指的是位速率。或称为比特率(和波特率不是一回事),表示的是:单位时间内,通信线路上传输的二进制位的数量,其基本单位是 bps 或者 b/s (bit per second)。(依据位时序实现传输数据的同步)
CAN具有很高的可靠性,广泛应用于:汽车电子、工业自动化、船舶、医疗设备、工业设备等方面。
(2)特点:
①,多主控制。总线空闲时,所有单元都可发送消息,而两个以上的单元同时开始发送消息时,根据标识符(ID,非地址)决定优先级。两个以上的单元同时开始发送消息时,对各消息ID 的每个位进行逐个仲裁比较。仲裁获胜(优先级最高)的单元可继续发送消息,仲裁失利的单元则立刻停止发送而进行接收工作。
②,系统柔软性。 连接总线的单元,没有类似“地址”的信息,因此,在总线上添加单元时,已连接的其他单元的软硬件和应用层都不需要做改变。
③,速度快,距离远。 最高1Mbps(距离<40M),最远可达10KM(速率<5Kbps)。
④ ,具有错误检测、错误通知和错误恢复功能。 所有单元都可以检测错误(错误检测功能),检测出错误的单元会立即同时通知其他所有单元(错误通知功能),正在发送消息的单元一旦检测出错误,会强制结束当前的发送。强制结束发送的单元会不断反复地重新发送此消息直到成功发送为止(错误恢复功能)。
⑤,故障封闭功能。 CAN 可以判断出错误的类型是总线上暂时的数据错误(如外部噪声等)还是持续的数据错误(如单元内部故障、驱动器故障、断线等)。由此功能,当总线上发生持续数据错误时,可将引起此故障的单元从总线上隔离出去。
⑥,连接节点多。 CAN 总线是可同时连接多个单元的总线。可连接的单元总数理论上是没有限制的。但实际上可连接的单元数受总线上的时间延迟及电气负载的限制。降低通信速度,可连接的单元数增加;提高通信速度,则可连接的单元数减少。
(3)硬件构成:
CAN的组成一般有两种方式:一种是CPU与CAN控制器集成到一起、再外接CAN收发器;另一种是CPU与CAN控制器分开的,使用的时候需要配置CAN接口电路。STM32中就是采用第一种方式,将CAN接口集成在芯片内,使用的时候再外接CAN收发器(顾名思义,可发送,可接收),常用的有TJA1050或者82C250。
在这里插入图片描述***(4)数据传输原理:***
CAN通信只由两根线完成的,一条称为CAN_H(CAN High),一条称为CAN_L(CAN Low),共同构成一组差分信号线。整个过程是以差分信号的形式进行通讯的,即信号的逻辑 0 和逻辑 1 由两根差分信号线的电压差来表示。
在这里插入图片描述
CAN 控制器根据CAN_L和CAN_H上的电位差来判断总线电平。总线电平分为显性电平(对应逻辑:0,CAN_H和CAN_L之差为2V左右。)和隐性电平(隐性电平对应逻辑:1,CAN_H和CAN_L之差为0V。),二者必居其一。发送方通过使总线电平发生变化,将消息发送给接收方。
CAN_Rx和CAN_Tx分别是从MCU中接出来的引脚,比如MCU要发送一个逻辑1,则只要将CAN_Tx设置为1,经过CAN收发器转换,CAN_High和CAN_Low 线上的电压均为 2.5v,即传到总线的电压差 Vh-Vl=0V,总线上的状态则就是逻辑1。同样,当CAN_High和CAN_Low 读取到 CAN总线电压分别3.5V和1.5V,即压差为2V,经过收发器转换,MCU则可通过CAN_Rx读取到信号0。
显性电平具有优先权,只要有一个单元输出显性电平,总线上即为显性电平。而隐形电平则具有包容的意味,只有所有的单元都输出隐性电平,总线上才为隐性电平(显性电平比隐性电平更强)。另外,在CAN总线的起止端都有一个120Ω的终端电阻,来做阻抗匹配,以减少回波反射。
注:
–发送方通过使总线电平发生变化,将其信息传递到CAN总线上。
–接收方通过监听总线电平,将总线上的消息读入自己的接收器
(5)空闲状态:
所谓的空闲状态就是指没有节点正在传输数据的时候,在CAN协议中,当总线上的上出现连续的11位隐性电平,那么总线就处于空闲状态。也就是说对于任意一个节点而言,只要它监听到总线上连续出现了11位隐性电平,那么该节点就会认为总线当前处于空闲状态。(可以简单的理解为总线在初始状态,每个节点输出的都是隐性电平
(6)帧:
CAN通信是以以下5种类型的帧进行的:
在这里插入图片描述其中,数据帧和遥控帧有标准格式和扩展格式两种格式。标准格式有11 个位的标识符(ID),扩展格式有29 个位的ID 。
(7)数据帧:

在这里插入图片描述 图中D表示显性电平,R表示隐形电平
数据帧由7个段组成:
①,帧起始。
表示数据帧开始的段。标准帧和扩展帧都是由1个位(SOF)的显性电平(0)表示帧起始。(也就是总线出现显性电平时开始传输数据)
②,仲裁段。
在这里插入图片描述
表示该帧优先级的段。
ID:高位在前,低位在后。基本ID,禁止高7位都为隐性,即不能:ID=1111111XXXX。
RTR,远程请求位。0,数据帧;1, 远程帧(命令帧);
SRR,替代远程请求位。设置为1(隐性电平);
IDE,标识符选择位。0,标准标识符;1,扩展标识符;
③,控制段。
表示数据的字节数及保留位的段。
r0,r1:保留位。必须以显现电平发送,但是接收可以是隐性电平。
DLC:数据长度码。0~8,表示发送/接收的数据长度(字节)。
IDE,标识符选择位。0,标准标识符;1,扩展标识符;
④,数据段。
数据的内容,一帧可发送0~8个字节的数据。从最高位(MSB)开始输出
⑤,CRC段。
检查帧的传输错误的段。该段用于检查帧传输错误。由15个位的CRC顺序和1个位的CRC界定符(用于分隔的位)组成。CRC的值计算范围包括:帧起始、仲裁段、控制段、数据段。
接收方以同样的算法计算 CRC 值并进行比较,不一致时会通报错误。
⑥,ACK段。
表示确认正常接收的段。此段用来确认是否正常接收。由ACK槽(ACK Slot)和ACK界定符2个位组成。
发送单元ACK段:发送2个隐性位。
接收单元ACK段:接收到正确消息的单元,在ACK槽发送显性位,通知发送单元,正常接收结束。称之为 发送ACK /返回ACK。
注意: 发送 ACK的是 既不处于总线关闭态也不处于休眠态的所有接收单元中,接收到正常消息的单元(发送单元不发送ACK)。正常消息是指:不含填充错误、格式错误、CRC 错误的消息。

⑦,帧结束(EOF)。
表示数据帧结束的段。由 7个位的隐性位 组成。
(8)同时多个单元发送数据时,总线仲裁过程(决定发送优先级):
在这里插入图片描述规律:1,总线空闲时,最先发送的单元获得发送优先权,一但发送,其他单元无法抢占。
2,如果有多个单元同时发送,则连续输出显性电平多的单元,具有较高优先级。(仲裁失利的单元会在下一位自动转为接收状态)
从ID开始比较,如果ID相同,还可能会比较RTR和SRR等位。
(9)实现传输发送数据与接收数据同步的核心——位时序
位时序的作用就是为了保证数据同步发送与接收。
位速率。由发送单元在非同步的情况下发送的每秒钟的位数称为位速率。一个位一般可以分为如下四段:
同步段(SS)
传播时间段(PTS)
相位缓冲段1(PBS1)
相位缓冲段2(PBS2)
这些段又由可称为 Time Quantum(以下称为Tq)的最小时间单位构成。
1 位分为4 个段,每个段又由若干个Tq 构成,这称为位时序。
位时间=1/波特率,因此,知道位时间,我们就可以知道波特率。
1 位由多少个Tq 构成、每个段又由多少个Tq 构成等,可以任意设定位时序。通过设定位时序,多个单元可同时采样,也可任意设定采样点。
在这里插入图片描述
?SS 段(SYNC SEG):同步段,比如当总线上出现帧起始信号(SOF)时,其它节点上的控制器根据总线上的这个下降沿,对自己的位时序进行调整,把该下降沿包含到 SS 段内,这样根据起始帧来进行同步的方式称为硬同步。其中 SS 段的大小为 1Tq。总线上信号的跳变沿被包含在节点的 SS 段的范围之内,则表示节点与总线的时序是同步的,采样点采集到的总线电平即可被确定为该位的电平。
?PTS 段(PROP SEG):传播时间段,这个时间段是用于补偿网络的物理延时时间,包括发送单元的输出延迟、总线上信号的传播延迟、接收单元的输入延迟,这个段的时间为以上各延迟时间的和的两倍。大小可以为 1~8Tq。
?PBS1 段(PHASE SEG1):相位缓冲段,主要用来补偿边沿阶段的误差,它的时间长度在重新同步的时候可以加长。 PBS1 段的初始大小可以为 1~8Tq。
?PBS2 段(PHASE SEG2):另一个相位缓冲段,也是用来补偿边沿阶段误差的,它的时间长度在重新同步时可以缩短。 PBS2 段的初始大小可以为 2~8Tq。
(对于PBS段而言,当信号边沿不能被包含于 SS 段中时,可在此段进行补偿,以及可以吸收时钟误差)
?SJW (reSynchronization Jump Width):重新同步补偿宽度,即在重新同步的时候,PBS1 和 PBS2 段的允许加长或缩短的时间长度,SJW 加大后允许误差加大,但通信速度下降。SJW 为补偿此误差的最大值(即每一次误差补偿都不能超过这个值,1~4Tq)。
在这里插入图片描述注: 图中采样时间加大或减少量的最大值就是SJW
?CAN 的同步分为硬同步和重新同步:

硬同步:在帧起始信号时同步总线上所有器件的位时序,无法确保后续一连串的位时序都是同步的。
重新同步:在检测到总线上的时序与节点使用的时序有相位差时(即总线上的跳变沿不在节点时序的 SS 段范围),通过延长 PBS1 段或缩短 PBS2 段,来获得同步。
采样点: 读取总线电平的时刻,并将读到的电平作为位值的点。位置在 PBS1 结束处。
延长/缩短PBS段来达到同步: PTS+PBS1小而PBS2加大时采样点前移,PTS+PBS1大而PBS2减小时采样点后移。
(10)STM32自带了基本扩展CAN外设,又称bxCAN,bxCAN的特点如下:
支持CAN协议2.0A和2.0B主动模式
–波特率最高达1Mbps
–支持时间触发通信
–具有3个发送邮箱
–具有3级深度的2个接收FIFO
–可变的筛选器组(也称过滤器组,最多28个)
CAN控制器框图:
在这里插入图片描述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]位
筛选器 可配置为:屏蔽位模式标识符列表模式
在屏蔽位模式下,标识符寄存器和屏蔽寄存器一起,指定报文标识符的任何一位,应该按照“必须匹配”或“不用关心”处理。屏蔽寄存器相应位为1代表必须匹配位,相应位为0代表不用关心。比如:屏蔽寄存器为101,标识符寄存器为001,那么FIFO可接收的报文(数据帧)ID可以为001或者011;
而在标识符列表模式下,屏蔽寄存器也被当作标识符寄存器用。因此,不是采用一个标识符加一个屏蔽位的方式,而是使用2个标识符寄存器。接收报文标识符的每一位都必须跟筛选器标识符相同。比如,标识符寄存器为001,屏蔽寄存器也必须为001,FIFO可接收的报文(数据帧)ID也必须为001。
通过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表示不关心)。
Tx Mailboxes(发送邮箱)
STM32 的 CAN 中共有 3 个发送邮箱供软件来发送报文。发送调度器根据优先级决定哪个邮箱的报文先被发送。
Accepttance Filters( 接收过滤器 )
STM32 的 CAN 中共有 14 个位宽可变/可配置的标识符过滤器组,软件通过对它们编程,从而在 CAN 收到的报文中选择它需要的报文,而把其它报文丢弃掉。
Receive FIFO( 接收 FIFO )
STM32 的 CAN 中共有 2 个接收 FIFO,每个 FIFO 都可以存放 3 个完整的报文。它们完全由硬件来管理。

(11)模式:
–工作模式
①初始化模式(INRQ=1,SLEEP=0)
②正常模式(INRQ=0,SLEEP=0)
③睡眠模式(SLEEP=1)
通过CAN_MCR寄存器控制INRQ和SLEEP
–测试模式
①静默模式( LBKM=0,SILM=1 )
②环回模式( LBKM=1,SILM=0 )
③环回静默模式(LBKM=1,SILM=1)
通过CAN_BTR寄存器控制LBKM和SILM
–调试模式
(12)发送与接收数据流程:
CAN发送流程为:
程序选择1个空置的邮箱(TME=1)?设置标识符(ID),数据长度和发送数据>>设置CAN_TIxR的TXRQ位为1,请求发送>>邮箱挂号(等待成为最高优先级)>>预定发送(等待总线空闲)>>发送>>邮箱空置。
在这里插入图片描述CAN接收流程为:
FIFO空>>收到有效报文?挂号_1(存入FIFO的一个邮箱,这个由硬件控制,我们不需要理会)>>收到有效报文>>挂号_2>>收到有效报文>>挂号_3>>收到有效报文>>溢出。
在这里插入图片描述CAN收到的有效报文,存储在3级邮箱深度的FIFO中。FIFO接收到的报文数,我们可以通过查询CAN_RFxR的FMP寄存器来得到,只要FMP不为0,我们就可以从FIFO读出收到的报文。
报文FIFO具有锁定功能(由CAN_MCR,RFLM位控制),锁定后,新数据将丢弃,不锁定则新数据将替代老数据
(13)波特率设置:
在这里插入图片描述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。
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

2、CAN相关寄存器:

(1)CAN主控制寄存器(CAN_MCR)
在这里插入图片描述该寄存器的我们仅介绍下INRQ位,该位用来控制初始化请求。
设置INRQ=0,可使CAN从初始化模式进入正常工作模式。
设置INRQ=1,可使CAN从正常工作模式进入初始化模式 。
CAN初始化时,先设置INRQ=1 ,进入初始化模式,进行初始化(尤其是CAN_BTR的设置,该寄存器,必须在CAN正常工作之前设置),之后再设置INRQ=0,进入正常工作模式。
(2)CAN位时序寄存器(CAN_BTR)
在这里插入图片描述用于设置分频、Tbs1、Tbs2以及 Tsjw 等非常重要的参数,直接决定了 CAN 的波特率,还可以设置 CAN 的工作模式。(保留位由硬件强制为0)
–正常模式,此时SILM=0,LBKM=0,这种状态时节点可以正常向节点发送和接收数据。
–静默模式下,节点的输出端的逻辑0数据会直接传输到自己的输入端,逻辑1可以被发送到总线,所以它不能向总线发送显
性位(逻辑0), 只能发送隐性位(逻辑1)。输入端可以从总线接收内容。
在这里插入图片描述

–回环模式下,节点输出端的所有内容都直接传输到自己的输入端,输出端的内容同时也会被传输到总线上,即也可使用总线监测它的发送内容。输入端只接收自己发送端的内容,不接收来自总线上的内容。使用回环模式可以进行自检。
??CAN 内核忽略确认错误(在数据/远程帧的确认位时刻,不检测是否有显性位)。在环回模式下,bxCAN 在内部把 Tx 输出回馈到 Rx 输入上,而完全忽略 CANRX 引脚的实际状态。发送的报文可以在 CANTX 引脚上检测到。
在这里插入图片描述–回环静默模式是以上两种模式的结合,节点的输出端的所有内容都直接传输到自己的输入端,并且不会向总线发送显性位影响总线,不能通过总线监测它的发送内容。输入端只接收自己发送端的内容,不接收来自总线上的内容。这种方式可以在“热自检”时使用,即自我检查的时候,不会干扰总线。
在这里插入图片描述(3)CAN接收FIFO寄存器(CAN_RF0R/CAN_RF1R)
在这里插入图片描述
CAN_RF0R用于FIFO0控制
CAN_RF1R用于FIFO1控制

(4)CAN发送邮箱标识符寄存器(CAN_TIxR)(x=0~2)
在这里插入图片描述(5)CAN发送邮箱数据长度和时间戳寄存器 (CAN_TDTxR) (x=0~2)
在这里插入图片描述(6)CAN发送邮箱数据寄存器(CAN_TDLxR/CAN_TDHxR) (x=0~2)
在这里插入图片描述图为CAN_TDLxR寄存器的描述,用于存储低4个字节的数据。CAN_TDHxR寄存器与之类似,用于存储高4个字节的数据。要发送的数据就是存储在这两个寄存器。
(7)CAN接收FIFO邮箱标识符寄存器(CAN_RIxR)(x=0/1)

在这里插入图片描述(8)CAN接收FIFO邮箱数据长度和时间戳寄存器(CAN_RDTxR) (x=0/1)
在这里插入图片描述(9)CAN接收FIFO邮箱邮箱数据寄存器(CAN_RDLxR /CAN_RDHxR) (x=0/1)

在这里插入图片描述图为CAN_RDLxR寄存器的描述,用于存储低4个字节的数据。CAN_RDHxR寄存器与之类似,用于存储高4个字节的数据。接收到的数据就存储在这两个寄存器。
(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。根据过滤器位宽和模式的不同设置,这两个寄存器的功能也不尽相同。关于过筛选器的映射,功能描述和屏蔽寄存器的关联,在前面已经介绍过了。
STM32F103ZET6只有0~13

3、CAN相关函数结构体:

(1) CAN_Init()函数:
定义:uint8_t CAN_Init(CAN_TypeDef* CANx, CAN_InitTypeDef* CAN_InitStruct);
结构体:

typedef struct
{
 uint16_t CAN_Prescaler; 
 uint8_t CAN_Mode; 
 uint8_t CAN_SJW; 
 uint8_t CAN_BS1; 
 uint8_t CAN_BS2; 
 FunctionalState CAN_TTCM; 
 FunctionalState CAN_ABOM; 
 FunctionalState CAN_AWUM; 
 FunctionalState CAN_NART; 
 FunctionalState CAN_RFLM; 
 FunctionalState CAN_TXFP; 
} CAN_InitTypeDef;

前面 5 个参数是用来设置寄存器 CAN_BTR,用来设置模式以及波特率相关的参数
后面 6 个成员变量用来设置寄存器 CAN_MCR,也就是设置 CAN 通信相关的控制位。
初始化实例:

CAN_InitStructure.CAN_TTCM=DISABLE; //非时间触发通信模式
CAN_InitStructure.CAN_ABOM=DISABLE; //软件自动离线管理
CAN_InitStructure.CAN_AWUM=DISABLE; //睡眠模式通过软件唤醒
CAN_InitStructure.CAN_NART=ENABLE; //禁止报文自动传送
CAN_InitStructure.CAN_RFLM=DISABLE; //报文不锁定,新的覆盖旧的
CAN_InitStructure.CAN_TXFP=DISABLE; //优先级由报文标识符决定
CAN_InitStructure.CAN_Mode= CAN_Mode_LoopBack; //模式设置: 1,回环模式;
//设置波特率
CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;//重新同步跳跃宽度为个时间单位 
CAN_InitStructure.CAN_BS1=CAN_BS1_8tq; //时间段 1 占用 8 个时间单位
CAN_InitStructure.CAN_BS2=CAN_BS2_7tq;//时间段 2 占用 7 个时间单位
CAN_InitStructure.CAN_Prescaler=5; //分频系数(Fdiv)
CAN_Init(CAN1, &CAN_InitStructure); // 初始化 CAN1

(2)滤波器结构体:
CAN_FilterInit ()函数的定义:
void CAN_FilterInit(CAN_FilterInitTypeDef* CAN_FilterInitStruct);
结构体:

typedef struct
{
 uint16_t CAN_FilterIdHigh; 
 uint16_t CAN_FilterIdLow; 
 uint16_t CAN_FilterMaskIdHigh; 
 uint16_t CAN_FilterMaskIdLow; 
 uint16_t CAN_FilterFIFOAssignment; 
 uint8_t CAN_FilterNumber; 
 uint8_t CAN_FilterMode; 
 uint8_t CAN_FilterScale; 
 FunctionalState CAN_FilterActivation; 
} CAN_FilterInitTypeDef;

第 1 个至第 4 个是用来设置过滤器的 32 位 id 以及 32 位 mask id

比如对于32位列表模式的配置:
??已知这种情况下可筛选2个ID,第一个ID有32位,分别由CAN_FilterldHigh和CAN_FilterldLow存放,第二个ID也是32位,分别由CAN_FilterMaskldHigh和CAN_FilterMaskldLow存放,所以说此时CAN_FilterMaskldHigh和CAN_FilterldHigh功能相同。
比如对于16位掩码模式的配置:
??已知这种情况下可筛选2个ID,对于ID1,由CAN_FilterldHigh存放,则由CAN_FilterMaskldHigh存放ID1对应的掩码;对于ID2,由CAN_FilterldLow,则由CAN_FilterMaskldLow存放,即:CAN_FilterldHigh和CAN_FilterMaskldHigh组成一组筛选器。

第 5 个成员变量 CAN_FilterFIFOAssignment 用来设置 FIFO 和过滤器的关联关系,比如关联过滤器 0 到 FIFO0,值为 CAN_Filter_FIFO0。(报文会被存储到哪一个接收FIFO,它的可选值为FIFO0或FIFO1)
第 6 个成员变量 CAN_FilterNumber 用来设置初始化的过滤器组,取值范围为 0~13。
第 7 个成员变量 FilterMode 用来设置过滤器组的模式,取值为标识符列表模式CAN_FilterMode_IdList 和标识符屏蔽位模式 CAN_FilterMode_IdMask。
第 8 个成员变量 FilterScale 用来设置过滤器的位宽为 2 个 16 位 CAN_FilterScale_16bit 还是 1 个32 位 CAN_FilterScale_32bit。
第 9 个成员变量 CAN_FilterActivation ,用来激活该过滤器。
(3)发送、接收函数以及结构体:
发送消息的函数是:
uint8_t CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage);
第一个参数是 CAN 标号,如CAN1。第二个参数是相关消息结构体 CanTxMsg 指针类型,CanTxMsg 结构体的成员变量用来设置标准标识符,扩展标示符,消息类型和消息帧长度等信息。
CanTxMsg 结构体:

typedef struct
{
  uint32_t StdId;  /*!< Specifies the standard identifier.
                        This parameter can be a value between 0 to 0x7FF. */

  uint32_t ExtId;  /*!< Specifies the extended identifier.
                        This parameter can be a value between 0 to 0x1FFFFFFF. */

  uint8_t IDE;     /*!< Specifies the type of identifier for the message that 
                        will be transmitted. This parameter can be a value 
                        of @ref CAN_identifier_type */

  uint8_t RTR;     /*!< Specifies the type of frame for the message that will 
                        be transmitted. This parameter can be a value of 
                        @ref CAN_remote_transmission_request */

  uint8_t DLC;     /*!< Specifies the length of the frame that will be 
                        transmitted. This parameter can be a value between 
                        0 to 8 */

  uint8_t Data[8]; /*!< Contains the data to be transmitted. It ranges from 0 
                        to 0xFF. */
} CanTxMsg;

接受消息的函数是:
void CAN_Receive(CAN_TypeDef* CANx, uint8_t FIFONumber, CanRxMsg* RxMessage);
第一第二个参数CAN 标号和 FIFO 号。第三个参数 RxMessage 是用来存放接受到的消息信息。
CanRxMsg 结构体:

typedef struct
{
  uint32_t StdId;  /*!< Specifies the standard identifier.
                        This parameter can be a value between 0 to 0x7FF. */

  uint32_t ExtId;  /*!< Specifies the extended identifier.
                        This parameter can be a value between 0 to 0x1FFFFFFF. */

  uint8_t IDE;     /*!< Specifies the type of identifier for the message that 
                        will be received. This parameter can be a value of 
                        @ref CAN_identifier_type */

  uint8_t RTR;     /*!< Specifies the type of frame for the received message.
                        This parameter can be a value of 
                        @ref CAN_remote_transmission_request */

  uint8_t DLC;     /*!< Specifies the length of the frame that will be received.
                        This parameter can be a value between 0 to 8 */

  uint8_t Data[8]; /*!< Contains the data to be received. It ranges from 0 to 
                        0xFF. */

  uint8_t FMI;     /*!< Specifies the index of the filter the message stored in 
                        the mailbox passes through. This parameter can be a 
                        value between 0 to 0xFF */
} CanRxMsg;

检查发送状态函数:
uint8_t CAN_TransmitStatus(CAN_TypeDef* CANx, uint8_t TransmitMailbox);
将CAN_Transmit返回的邮箱编号放进此函数查看发送状态,返回值为0:代表发送失败;返回值为1:代表发送成功
检查FIFO里的邮箱挂起数目函数:
uint8_t CAN_MessagePending(CAN_TypeDef* CANx, uint8_t FIFONumber)
如:CAN_MessagePending(CAN1,CAN_FIFO0)
发送数据函数实例:

u8 Can_Send_Msg(u8* msg,u8 len)
{
 u8 mbox;
u16 i=0;
 CanTxMsg TxMessage;
 TxMessage.StdId=0x12; // 标准标识符为 0
 TxMessage.ExtId=0x12; // 设置扩展标示符(29 位)
 TxMessage.IDE=CAN_Id_Standard; // 标准帧
 TxMessage.RTR=CAN_RTR_Data; // 数据帧
 TxMessage.DLC=len; // 要发送的数据长度
 for(i=0;i<len;i++)
 TxMessage.Data[i]=msg[i]; 
 mbox= CAN_Transmit(CAN1, &TxMessage); 
 i=0;
while((CAN_TransmitStatus(CAN1, mbox)!= CAN_TxStatus_Ok)&&(i<0xfff))i++; //等待结束
 if(i>=0xfff)return 1;
 return 0;
}

接收数据实例:

u8 Can_Receive_Msg(u8 *buf)
{ 
u32 i;
CanRxMsg RxMessage;
 if( CAN_MessagePending(CAN1,CAN_FIFO0)==0)return 0;//没有接收到数据,直接退出
 CAN_Receive(CAN1, CAN_FIFO0, &RxMessage); //读取数据
 for(i=0;i<8;i++)
 buf[i]=RxMessage.Data[i]; 
return RxMessage.DLC;
}

4、硬件原理图:

在这里插入图片描述

5、程序:

(1)can.c

#include "can.h"
#include "led.h"
#include "delay.h"
#include "usart.h"
//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+1+tbs2+1+1)*brp);
//mode:CAN_Mode_Normal,普通模式;CAN_Mode_LoopBack,回环模式;
//Fpclk1 的时钟在初始化的时候设置为 36M,如果设置
//CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_LoopBack);
//则波特率为:36M/((8+9+1)*4)=500Kbps
//返回值:0,初始化 OK;
// 其他,初始化失败;u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
{
GPIO_InitTypeDef GPIO_InitStructure; 
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
#if CAN_RX0_INT_ENABLE 
 NVIC_InitTypeDef NVIC_InitStructure;
#endif
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能 PORTA 时钟
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能 CAN1 时钟
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽
 GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 IO
 
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
 GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 IO
//CAN 单元设置
CAN_InitStructure.CAN_TTCM=DISABLE; //非时间触发通信模式
CAN_InitStructure.CAN_ABOM=DISABLE; //软件自动离线管理
 CAN_InitStructure.CAN_AWUM=DISABLE; //睡眠模式通过软件唤醒
 CAN_InitStructure.CAN_NART=ENABLE; //禁止报文自动传送
 CAN_InitStructure.CAN_RFLM=DISABLE; //报文不锁定,新的覆盖旧的
 CAN_InitStructure.CAN_TXFP=DISABLE; //优先级由报文标识符决定
 CAN_InitStructure.CAN_Mode= mode; //模式设置:0,普通模式;1,回环模式;
 //设置波特率
 CAN_InitStructure.CAN_SJW=tsjw; //重新同步跳跃宽度(Tsjw)
 CAN_InitStructure.CAN_BS1=tbs1; //时间段 1 占用时间单位
 CAN_InitStructure.CAN_BS2=tbs2; // 时间段 2 占用时间单位
 CAN_InitStructure.CAN_Prescaler=brp; //分频系数(Fdiv)为 brp+1
 CAN_Init(CAN1, &CAN_InitStructure); // 初始化 CAN1 
CAN_FilterInitStructure.CAN_FilterNumber=0; //过滤器 0
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; 
 CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //32 位
 CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000; //32 位 ID
 CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
 CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000; //32 位 MASK
 CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
 CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;// FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //激活过滤器 0
 CAN_FilterInit(&CAN_FilterInitStructure); //滤波器初始化
#if CAN_RX0_INT_ENABLE
CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE); //FIFO0 消息挂号中断允许.
 NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 主优先级为 1
 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 次优先级为 0
 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
 NVIC_Init(&NVIC_InitStructure);
#endif
return 0;
} 
#if CAN_RX0_INT_ENABLE //使能 RX0 中断
//中断服务函数 
void USB_LP_CAN1_RX0_IRQHandler(void)
{
 CanRxMsg RxMessage;
int i=0;
 CAN_Receive(CAN1, 0, &RxMessage);
for(i=0;i<8;i++)
printf("rxbuf[%d]:%d\r\n",i,RxMessage.Data[i]);
}
#endif
//can 发送一组数据(固定格式:ID 为 0X12,标准帧,数据帧)
//len:数据长度(最大为 8) 
//msg:数据指针,最大为 8 个字节.
//返回值:0,成功;
// 其他,失败;
u8 Can_Send_Msg(u8* msg,u8 len)
{
 u8 mbox;
u16 i=0;
 CanTxMsg TxMessage;
 TxMessage.StdId=0x12; // 标准标识符为 0
 TxMessage.ExtId=0x12; // 设置扩展标示符(29 位)
 TxMessage.IDE=CAN_Id_Standard; // 标准帧
 TxMessage.RTR=CAN_RTR_Data; // 数据帧
 TxMessage.DLC=len; // 要发送的数据长度
 for(i=0;i<len;i++)
 TxMessage.Data[i]=msg[i]; 
 mbox= CAN_Transmit(CAN1, &TxMessage); 
 i=0;
while((CAN_TransmitStatus(CAN1, mbox)!= CAN_TxStatus_Ok)&&(i<0xfff))i++; //等待结束
 if(i>=0xfff)return 1;
 return 0;
}
//can 口接收数据查询
//buf:数据缓存区;
//返回值:0,无数据被收到;
// 其他,接收的数据长度;
u8 Can_Receive_Msg(u8 *buf)
{ 
u32 i;
CanRxMsg RxMessage;
 if( CAN_MessagePending(CAN1,CAN_FIFO0)==0)return 0;//没有接收到数据,直接退出
 CAN_Receive(CAN1, CAN_FIFO0, &RxMessage); //读取数据
 for(i=0;i<8;i++)
 buf[i]=RxMessage.Data[i]; 
return RxMessage.DLC;
}

(2)main.c

int main(void)
{
u8 key;
u8 i=0,t=0;
u8 cnt=0;
u8 canbuf[8];
u8 res;
u8 mode=CAN_Mode_LoopBack;//CAN 工作模式;CAN_Mode_Normal(0):
//普通模式,CAN_Mode_LoopBack(1):环回模式
delay_init(); //延时函数初始化 
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置 NVIC 中断分组 2
uart_init(115200); //串口初始化波特率为 115200
LED_Init(); //初始化与 LED 连接的硬件接口
LCD_Init(); //初始化 LCD
KEY_Init(); //按键初始化
 
CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,
CAN_Mode_LoopBack);//CAN 初始化环回模式,波特率 500Kbps 
POINT_COLOR=RED; //设置字体为红色
LCD_ShowString(60,50,200,16,16,"ELITE STM32");
LCD_ShowString(60,70,200,16,16,"CAN TEST");
LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,110,200,16,16,"2015/1/15");
LCD_ShowString(60,130,200,16,16,"LoopBack Mode");
LCD_ShowString(60,150,200,16,16,"KEY0:Send WK_UP:Mode");//显示提示信息
 POINT_COLOR=BLUE; //设置字体为蓝色 
LCD_ShowString(60,170,200,16,16,"Count:"); //显示当前计数值
LCD_ShowString(60,190,200,16,16,"Send Data:"); //提示发送的数据
LCD_ShowString(60,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(60+i*32,210,canbuf[i],3,16,0X80); //显示数据
else LCD_ShowxNum(60+(i-4)*32,230,canbuf[i],3,16,0X80); //显示数据
}
res=Can_Send_Msg(canbuf,8); //发送 8 个字节
if(res)LCD_ShowString(60+80,190,200,16,16,"Failed"); //提示发送失败
else LCD_ShowString(60+80,190,200,16,16,"OK "); //提示发送成功
 
}else if(key== WKUP_PRES) //WK_UP 按下,改变 CAN 的工作模式
{ 
mode=!mode;
 CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,mode);
//CAN 普通模式初始化, 波特率 500Kbps 
POINT_COLOR=RED; //设置字体为红色
if(mode==0) //普通模式,需要 2 个开发板
{
LCD_ShowString(60,130,200,16,16,"Nnormal Mode "); 
}else //回环模式,一个开发板就可以测试了. {
LCD_ShowString(60,130,200,16,16,"LoopBack Mode");
}
POINT_COLOR=BLUE; //设置字体为蓝色
}
key=Can_Receive_Msg(canbuf);
if(key) //接收到有数据
{
LCD_Fill(60,270,130,310,WHITE);//清除之前的显示
for(i=0;i<key;i++)
{ 
if(i<4)LCD_ShowxNum(60+i*32,270,canbuf[i],3,16,0X80); //显示数据
else LCD_ShowxNum(60+(i-4)*32,290,canbuf[i],3,16,0X80); //显示数据
} }
t++; 
delay_ms(10);
if(t==20)
{
LED0=!LED0; //提示系统正在运行
t=0;
cnt++;
LCD_ShowxNum(60+48,170,cnt,3,16,0X80); //显示数据
} 
} }

总结:

简而言之,can数据传输就是将发送邮箱里的数据通过can控制器发送到收发器,收发器将单电平信号利用高低电平线产生电压差,其他收发器或自己收发器采集电压差并转换成单电平传送到接收FIFO的三级邮箱里。
TJA1050收发器:
在这里插入图片描述

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-08-27 12:01:45  更:2021-08-27 12:02:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/21 4:48:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码