1 CAN物理层
CAN是一种异步通讯,只具有CAN_High和CAN_Low两条信号线,共同构成一组差分信号线,以差分信号的形式进行通讯。
物理层形式分为两种:
- 闭环总线网络:适用于高速通讯;(最高1Mbps,距离40m)
数据传输终端实际上就是两个阻抗为120欧姆的电阻,也称为终端电阻。总线上的总阻抗大概是60-70欧姆左右。 终端电阻的大小和传输线相关。
- 开环总线网络:适合于远距离通讯。(最高125kbps,距离1km)
1.1 通讯节点
CAN总线上可以挂载多个通讯节点,节点之间的信号经过总线传输,实现节点间通讯。
由于CAN通讯协议不对节点进行地址编码,而是对数据内容进行编码,所以网络中的节点个数理论上不受限制,只要总线的负载足够即可,可以通过中继器增强负载。
1.2 差分信号
差分(差模)信号,与传统使用单根信号线电压表示逻辑的方式有区别,使用差分信号传输时,需要两根信号线,这两个信号线的振幅相等,相位相反,通过两根信号线的电压差值来表示逻辑0和逻辑1。
差分信号传输优点:
- 抗干扰能力强,外界的共模噪声可以被完全抵消;
- 能有效抑制本身对外部的电磁干扰;
- 时序定位精确;(由于差分信号的开关变化是位于两个信号的交点,而不像普通单端信号依靠高低两个阈值电压判断)
基于以上优点,USB协议、485协议、以太网协议及CAN协议的物理层中,都使用了差分信号传输。
1.2.1 CAN协议中的差分信号
CAN协议中对它使用的CAN_High
及CAN_Low
表示的差分信号做了规定。以高速CAN协议为例:
- 当表示逻辑1时(隐性电平),CAN_High和CAN_Low线上的电压均为2.5v,即它们的电压差VH-VL=0V;
- 当表示逻辑0时(显性电平),CAN_High的电平为3.5V,CAN_Low线的电平为1.5V,即它们的电压差为VH-VL=2V。
CAN总线遵循如下逻辑:高速CAN和低速CAN的电压差大于0.9V时为显性电平(逻辑0),小于0.5V为隐性电平(逻辑1)。
CAN网络基于**“线与”**逻辑。显性总线电平会覆盖隐性总线电平。如果不同的CAN节点同时发送显性电平和隐性电平,那么CAN总线会处于显性电平的逻辑“0”。隐性电平的逻辑“1”仅在所有CAN节点隐性传输时才发生。
在CAN总线中,必须使它处于隐性电平(逻辑1)或显性电平(逻辑0)中的其中一个状态。假如有三个CAN通讯节点,在同一时间,A/B输出隐性电平,C输出显性电平,类似I2C总线的“线与”特性将使它处于显性电平状态,即可认为显性具有优先的意味。
2 CAN协议层
2.1 CAN的报文种类及结构
什么是报文?
当使用CAN协议进行通讯时,需要对数据、操作命令(如读/写)以及同步信号进行打包,打包后的这些内容称为报文。
当总线空闲时任何连接的单元都可以开始发送新的报文。
2.1.1 报文(帧)的种类
帧 | 帧用途 |
---|---|
数据帧 | 用于发送单元向接收单元传送数据的帧 |
遥控帧 | 用于接收单元向具有相同 ID 的发送单元请求数据的帧。 |
错误帧 | 用于向远端节点通知校验错误,请求重新发送上一个数据 |
过载帧 | 用于接收单元通知其尚未做好接收准备的帧 |
帧间隔 | 用于将数据帧及遥控帧与前面的帧分离开来 |
发送单元(TRANSMITTER):产生报文的单元被称之为报文的“发送单元”。此单元保持作为报文发送单元直到总线出现空闲或此单元失去仲裁(ARBITRATION)为止。
接收单元(RECEIVER):如果有一单元不作为报文的发送单元并且总线也不空闲,则这一单元就被称之为报文的“接收单元”。
2.1.1.1 数据帧
D-Dominant :显性电平;R-Recessive:隐性电平
白块:显性位逻辑1;黑块:隐性位逻辑0
数据帧具有标准格式和扩展格式两种,主要区别在于ID信息的长度,标准格式的ID为11
位,扩展格式的ID为29
位,相对于标准帧其ID多出18位。
数据帧以一个显性位(逻辑0)开始,以7个连续的隐性位(逻辑1)结束,在它们之间,分别有仲裁段、控制段、数据段、CRC段和ACK段。
名称 | 描述 |
---|---|
帧起始 | 表示帧的开始,产生一个bit的显性电平。 |
仲裁段 | 表示帧的优先级, 由标识符(ID)和传送帧类型(RTR)组成。 |
控制段 | 表示数据的字节数,由6个bit构成 |
数据段 | 数据的具体内容,可发送0~8 个字节的数据。 |
CRC段 | 用于校验传输是否正确。 |
ACK段 | 表示确认是否正常接收。 |
帧结束 | 表示此帧结束。 |
「帧起始 SOF(Start Of Frame)段」
帧起始信号(图中红色部分)只有一个数据位,是一个显性电平,它用于通知各个节点将有数据传输,其它节点通过帧起始信号的电平跳变沿来进行硬同步。
「仲裁段」
仲裁段的内容主要为本数据帧的ID信息(标识符)和扩展/请求位,表示数据的优先级,通过对ID的仲裁来确定的。如果总线上同时出现显性电平和隐性电平,总线的状态会被置为显性电平,总线会根据仲裁段的内容决定哪个数据包能被传输。
因而CAN控制器大多具有根据ID过滤报文的功能,它可以控制自己只接收某些ID的报文。
【注】标准格式ID有11
位,从ID28~ID18被依次发送,禁止高7位为隐性逻辑电平1(禁止格式:ID=1111111XXXX);扩展格式ID有29
位,基本ID从ID28~ID18(与标准格式相同),扩展ID从ID17~ID0,同样禁止高位全为隐性。(禁止设定:基本ID=1111111XXXX)
RTR、IDE、SRR位:
-
RTR
(Remote Transmission Request Bit) 远程传输请求位:它是用于区分数据帧和遥控帧的,当它全为显性电平时表示数据帧,全为隐性电平时则表示遥控帧。 -
IDE
(Identifier Extension Bit) 标识符扩展位:它是用于区分标准格式与扩展格式,当它为显性电平时表示标准格式,隐性电平时表示扩展格式。 -
SRR
(Substitute Remote Request Bit)替换远程请求位:只存在于扩展格式,它用于替代标准格式中的RTR位。由于扩展帧中的SRR位为隐性位,RTR在数据帧为显性位,所以在两个ID相同的标准格式报文与扩展格式报文中,标准格式的优先级较高。
「控制段」
控制段由 6
个位构成,表示数据段的字节数。
r1
和r0
为保留位,必须全部以显性电平发送。但接收方可以接收显性、隐性及其任意组合的电平。DLC
(Data Length Code)数据长度码,它由4个数据位组成,用于表示本报文中的数据段含有多少个字节,DLC段表示的数据字节数必须为 0~8 字节。但接收方对DLC = 9~15 的情况并不视为错误。数据长度码DLC和字节数的关系如下图所示:
「数据段」
数据段可包含 0~8 个字节的数据。从MSB(最高位)开始输出。
「CRC段」
CRC段是检查帧传输错误的帧。由15
个位的CRC 顺序1和1
个位的CRC 界定符(隐性位,用于分隔CRC校验码与后面的ACK段)构成。
CRC部分的计算一般由CAN控制器硬件完成,出错时的处理则由软件控制最大重发数。
一旦接收节点算出的CRC码跟接收到的CRC码不同,则它会向发送节点反馈出错信息,利用错误帧请求它重新发送。
「ACK段」
ACK段包括1个ACK应答间隙(ACK slot)位和1个ACK界定符位2。
- 发送单元的ACK段:发送2个位的隐性位。
- 接收单元的ACK段:接收到正确消息3的单元在ACK槽发送显性位,通知发送单元正常接收结束。
「帧结束」
EOF(End Of Frame)帧结束段,由发送节点发送的7个隐性位表示结束。
2.1.1.2 遥控帧
接收单元向发送单元请求发送数据所用的帧,由 6 个段组成,是没有数据帧的数据段。
遥控帧和数据帧区别:遥控帧RTR位全为隐性电平,数据帧RTR位全为显性电平。此外,遥控帧没有数据端。
【Q&A】
- 遥控帧没有数据段,其控制段中的数据长度码DLC如何表示?
遥控帧用所请求数据帧的DLC作为其DLC。
2.1.1.3 错误帧
用于在接收和发送消息时检测并通知错误的帧,由错误标志和错误界定符构成。
- 主动错误标志:6 个连续的显性位,检测到错误条件的“错误主动”的单元通过发送主动错误标志,以指示错误。
主动错误标志的形式破坏了从起始段到CRC 界定符的位填充规则(参见“编码”),或者破坏了应答段或结束段的固定形式。导致其他单元产生错误,它们此时会检测到错误条件并与此同时开始发送错误标志。
因此,由于主动错误标志的“显性”特点,各个单独单元发送的不同的错误标志叠加在一起,该错误序列总长度最小为6个位,最大为12个位。(6 + 0~6)
- 被动错误标志:6 个连续的极性相同的位,若为隐性位可能被其他单元的显性位重写。
错误界定符:由 8 个位的隐性位构成。错误标志传送了以后,每一单元就发送“隐性”的位并一直监视总线直到检测出一个“隐性”的位为止,然后就开始发送7位以上的“隐性”位。
2.1.1.4 过载帧
过载帧是用于接收单元通知其尚未完成接收准备的帧,由过载标志和过载界定符构成。
过载标志:6 个“显性”的位组成,其形式和主动错误标志的一样。
过载界定符:包括8 个“隐性”的位,其形式和错误界定符的一样。
2.1.1.5 帧间隔
帧间隔是用于分隔数据帧与其前面帧(数据帧、远程帧、错误帧、过载帧)的帧。
其中,过载帧和错误帧前不能插入帧间隔。
帧间隔的构成如图所示:
间隔:3 个位的隐性位。间隔期间,所有单元均不允许传送数据帧或远程帧,唯一要做的是标示一个过载条件。
总线空闲:总线空闲的(时间)长度是任意的。只要总线被认定为空闲,任何等待发送信息的单元就会访问总线。总线上检测到的“显性”的位可被解释为帧的起始。
延迟传送(发送暂时停止):“错误被动”的单元发送报文后,会在下一报文开始传送之前或总线空闲之前发出8 个“隐性”的位跟随在间隔的后面。若与此同时另一单元开始发送报文(由另一单元引起),则此站就作为这个报文的接收器。
2.2 优先级仲裁
在总线空闲态,最先开始发送消息的单元获得发送权。
多个单元同时开始发送时,各发送单元从仲裁段的第一位开始进行仲裁。连续输出显性电平最多的单元可继续发送。其仲裁的过程如图所示:
2.2.1 数据帧和遥控帧的优先级
具有相同 ID 的数据帧和遥控帧在总线上竞争时,仲裁段的最后一位(RTR)为显性位的数据帧具有优先权,可继续发送。
仲裁过程如下图所示,其中数据帧的RTR位为显性位,获得发送权。
2.2.2 标准格式和扩展格式的优先级
ID相同的标准格式的数据帧与扩展格式的数据在总线上竞争时,扩展格式的SRR位为隐性,而标准格式的RTR 位为显性位,具有优先权,可继续发送。
2.3 位时序与位同步
由于CAN属于异步通讯,没有时钟信号线,连接在同一个总线网络中的各节点使用约定好的波特率进行通讯,特别地,CAN还会使用“位同步”的方式来抗干扰、吸收误差,实现对总线电平信号进行正确的采样,确保通讯正常。
2.3.1 位时序
由发送单元在非同步的情况下发送的每秒钟的位数称为位速率。一个位可分为 4 段,分别为同步段、传播时间段、相位缓冲段1、相位缓冲段2。
每个段又由若干个Tq4组成,这称为位时序。
采样点是读取总线电平,并将读到的电平作为位值的点。信号的采样点位于PBS1段之后,通过控制各段的长度,可以对采样点的位置进行偏移,以便准确地采样。
位时序分解:
图中表示的CAN通讯信号每一个数据位的长度为19Tq,其中SS段占1Tq,PTS段占6Tq,PBS1段占5Tq,PBS2段占7Tq。因此,总线上的各个通讯节点只要约定好1个Tq的时间长度以及每一个数据位占据多少个Tq,就可以确定CAN通讯的波特率。
假设上图中的1Tq=1us,而每个数据位由19个Tq组成,则传输一位数据需要时间T1bit =19us,从而每秒可传输的数据位个数为:1x10^6/19 = 52631.6 (bps)
2.3.2 位同步
CAN 协议的通信方法为NRZ(Non-Return to Zero)方式。各个位的开头或者结尾都没有附加同步信号。发送单元以与位时序同步的方式开始发送数据。另外,接收单元根据总线上电平的变化进行同步并进行接收工作。
但是,发送单元和接收单元存在的时钟频率误差及传输路径上的(电缆、驱动器等)相位延迟会引起同步偏差。因此接收单元通过硬件同步或者再同步的方法调整时序进行接收。
2.3.2.1 硬同步
接收单元在总线空闲状态检测出帧起始时进行同步调整,硬同步后,内部的位时间从同步段SS重新开始。
2.3.2.2 再同步
在接收过程中检测出总线上的电平变化时进行的同步调整。(接收数据时电平理应处于稳态)
每当检测出边沿时,根据 SJW 值通过加长PBS1段,或缩短PBS2 段,以调整同步。但最大调整量不能超过SJW最大值4tq。
2.3.2.3 调整同步的规则
硬同步和再同步遵从如下规则:
(1) 1 个位中只进行一次同步调整。
(2) 只有当上次采样点的总线值和边沿后的总线值不同时,该边沿才能用于调整同步。
(3) 在总线空闲且存在隐性电平到显性电平的边沿时,则一定要进行硬件同步。
(4) 在总线非空闲时检测到的隐性电平到显性电平的边沿如果满足条件(1)和(2),将进行再同步。但还要满足下面条件。
(5) 发送单元观测到自身输出的显性电平有延迟时不进行再同步。
(6) 发送单元在帧起始到仲裁段有多个单元同时发送的情况下,对延迟边沿不进行再同步。
时序与同步等小节中某些知识点仅浅尝辄止,具体可参看文件夹中相关手册。
3 STM32的CAN外设
STM32的芯片中具有bxCAN控制器 (Basic Extended CAN),它支持CAN协议2.0A和2.0B标准,最高通讯速率为1Mb/s,两个 bxCAN 单元共享 512
字节 SRAM 存储。(注意CAN2为从 bxCAN,无法直接访问 SRAM 存储器。)
「发送」
- 三个发送邮箱
- 可配置发送报文的优先级,由报文标识符或发送的请求次序决定
- 可记录发送报文SOF时刻的时间戳
「接收」
-
2个具有三级深度的接收 FIFO
-
28个可配置的报文过滤(筛选)器组,CAN1和CAN2共用28个过滤器。可设为32或16位模式,屏蔽位或标识符列表模式。
-
可配置FIFO上溢与SOF接收时间戳
3.1 CAN工作模式
bxCAN 有三种主要的工作模式:初始化、正常和睡眠。 跳转到的地方
3.1.1 睡眠模式
进入条件:
- 硬件复位后
- CAN_MCR 寄存器的SLEEP位置1。
功能特点:bxCAN时钟停止,同时 CANTX 上的内部上拉电阻激活,但软件仍可访问 bxCAN邮箱。
退出条件:
- bxCAN 处于睡眠模式时,软件将INRQ位置1请求进入初始化模式,与此同时必须将SLEEP位清零。
- 软件将 SLEEP 位清零或是检测到 CAN 总线活动时,bxCAN 即被唤醒。其中当检测CAN活动时:
- 若CAN_MCR寄存器AWUM位置1(使能硬件自动唤醒)或CAN_IER寄存器的WKUIE位置1(使能唤醒中断),硬件清除SLEEP位自动执行唤醒序列
- 若上述两位均为0,发生唤醒中断时,软件必须将 SLEEP 位清 零才能退出睡眠模式。
SLEEP位清零后,一旦 bxCAN与CAN 总线同步,硬件将 SLAK 位清零,即退出睡眠模式。
3.1.2 初始化模式
进入条件:软件将 CAN_MCR 寄存 器的 INRQ 位置 1,并等待硬件通过将 CAN_MCR 寄存器的 INAK 位置 1 来确认请求。
功能特点:
- 所有从 CAN 总线传入和传出的消息都将停止,并且 CAN 总线输出 CANTX 的状态为隐性(高)
- 为初始化CAN控制器,软件必须设置位定时 (CAN_BTR) 和 CAN 选项 (CAN_MCR) 寄存器。
- 为初始化与CAN筛选器组相关的寄存器),软件必须将
FINIT
位 (CAN_FMR) 置 1。(FINIT位置1时,CAN收发停用)。筛选器值也可通过停用(CAN_FA1R 寄存器的)相关筛选器激活位来修改。
筛选器的初始化也可以在初始化模式之外进行。
如果某个筛选器组未使用,建议将其保持未激活状态(将相应 FACT 位保持清零)。
退出条件:软件将 INQR 位清零,一旦硬件将 INAK 位清零,bxCAN 即退出初始化
3.1.3 正常模式
初始化完成,软件必须向硬件请求进入正常模式,这样才能在 CAN 总线上进行同步, 并开始接收和发送。
进入条件:CAN_MCR 寄存器的 INRQ 位清零,并等待出现一个由 11 个连续隐性位(总线空闲状态)组成的序列,即进入正常模式。硬件通过将 CAN_MSR 寄存器的 INAK 位清零,来确认切换到正常模式。
功能特点:正常通信收发数据
退出条件:SLEEP或INRQ位置1
3.2 CAN控制内核
CAN框图标号①处的CAN控制内核包含了各种控制寄存器及状态寄存器,主要分析主控制寄存器CAN_MCR和CAN_BTR寄存器。
3.2.1 主控制寄存器CAN_MCR
- DBF调试冻结功能
DBF(Debug freeze)调试冻结,调试模式下(DBG外设模块中CAN1 的 DBG_CAN1_STOP 位或者 CAN2 的 DBG_CAN2_STOP置位),使CAN处于工作状态或禁止收发的状态,禁止收发时仍可访问接收FIFO中的数据。(注意该位不会影响正常模式)
- TTCM时间戳触发模式
【Why】CAN通信总线繁忙时,容易造成低优先级的消息长时间阻塞,甚至无法满足其时限要求,时间戳触发模式可解决该瓶颈。
【What】TTCM(Time triggered communication mode)时间触发模式,它用于配置CAN的时间触发通信模式,在此模式下,CAN使用它内部定时器产生时间戳,并把它保存在CAN_RDTxR、CAN_TDTxR寄存器中。内部定时器在每个CAN位时间累加,在接收和发送的帧起始位被采样,并生成时间戳。利用它可以实现ISO 11898-4 CAN标准的分时同步通信功能。
- ABOM自动离线管理
ABOM(Automatic bus-off management) 自动离线管理,它用于设置是否使用自动离线管理功能。当节点检测到它发送错误或接收错误超过一定值时,会自动进入离线状态,在离线状态中,CAN不能接收或发送报文。处于离线状态的时候,可以软件控制恢复或者直接使用这个自动离线管理功能,它会在适当的时候自动恢复。
- AWUM自动唤醒
AWUM(Automatic bus-off management),自动唤醒功能,CAN外设可以使用软件进入低功耗的睡眠模式,如果使能了这个自动唤醒功能,当CAN检测到总线活动的时候,会自动唤醒。
- NART自动重传
NART(No automatic retransmission)报文自动重传功能,设置这个功能后,当报文发送失败时会自动重传至成功为止。若不使用这个功能,无论发送结果如何,消息只发送一次。
- RFLM锁定模式
RFLM(Receive FIFO locked mode)FIFO锁定模式,该功能用于锁定接收FIFO。锁定后,当接收FIFO溢出时,会丢弃下一个接收的报文。若不锁定,则下一个接收到的报文会覆盖原报文。
- TXFP报文发送优先级的判定方法
TXFP(Transmit FIFO priority)报文发送优先级的判定方法,当CAN外设的发送邮箱中有多个待发送报文时,本功能可以控制它是根据报文的ID优先级还是报文存进邮箱的顺序来发送。
3.2.2 测试模式
为方便调试,STM32的CAN提供了测试模式,配置位时序寄存器CAN_BTR的SILM及LBKM寄存器位可以控制使用正常(normal)模式、静默(silent)模式、回环(loopback)模式及静默回环(silent loopback)模式。
- 静默模式
CAN_BTR的SILM
位置1。
节点不能向总线发送显性位(逻辑0),若发送会将其输出端的逻辑0传回至自己的输入端;隐性位(逻辑1)可正常发送至总线。因此,此模式不会影响总线状态,所以称之为silent mode。
- 回环模式
CAN_BTR的LBKM
位置1。
输出端消息会传回输入端,同时会发送至总线。输入端只接收自己发送端的内容,不接收来自总线上的内容。使用回环模式可以进行自检。
- 静默回环模式
CAN_BTR的SILM
和LBKM
位均置1。
给回环模式加了个限制条件:仅可向总线发送隐性位(向自己发送不收影响)。此方式可在“热自检”时使用,即自我检查的时候,不会干扰总线。
3.3 位时序与波特率
3.3.1 位时序
STM32外设位时序与CAN标准时序有一点区别:
- 同步段 (SYNC_SEG):长度固定为1tq,位变化应该在此时间段内发生。
- 位段 1 (BS1):定义采样点的位置。包括标准CAN中的PTS 与 PBS1,长度为1~16 Tq,通过
CAN_BTR
位时序寄存器的TS1[3:0]设置。其长度可在再同步期间增长或缩短,以补偿负相位漂移。 - 位段 2 (BS2):定义发送点的位置。包括标准CAN中PBS2,长度为1~8 Tq,通过
CAN_BTR
位时序寄存器的TS2[2:0]设置。其长度可在再同步期间增长或缩短,以补偿负相位漂移。
时序调整
再同步跳转宽度 (SJW) 定义位段加长或缩短的上限。它可在1~4 Tq间调整,在CAN_BTR
中配置。
- 若在 BS1 而不是 SYNC_SEG 中检测到有效边沿,则 BS1 会延长最多 SJW,以便延迟采样点。
- 若在 BS2 而不是 SYNC_SEG 中检测到有效边沿,则 BS2 会缩短最多 SJW,以便提前发送点。
为了避免编程错误,位时序寄存器 (CAN_BTR
) 只能在器件处于睡眠(待机)模式时进行配置。
「有效边沿」指一个位时间内总线电平从显性到隐性的第一次转换(前提是控制器本身不发送隐性位)。
3.3.2 CAN通讯波特率计算
-
BS1段时间:TS1=Tq x (TS1[3:0] + 1)
-
BS2段时间:TS2= Tq x (TS2[2:0] + 1)
-
一个数据位的时间:T1bit =1 +TS1+TS2 =
[1+ (TS1[3:0] + 1)+ (TS2[2:0] + 1)]
= N Tq
Tq与CAN外设的所挂载的时钟总线及分频器配置有关。其中,CAN1和CAN2外挂载在APB1总线上,CAN_BTR寄存器中的BRP[9:0]位配置CAN外设时钟分频值。因此:
则CAN通讯的波特率:Bps = 1 / (N * Tq) = Fq / N
Fq为时间长度的倒数, Fq = FPCLK / (BRP[9:0]+1),个人倾向于此种计算方法。
一种把波特率配置为1Mbps的方式:
参数 | 说明 |
---|---|
SYNC_SE段 | 固定为1Tq |
BS1段 | 设置为5Tq (实际写入TS1[3:0]的值为4) |
BS2段 | 设置为3Tq (实际写入TS2[2:0]的值为2) |
TPCLK | APB1按默认配置为F=36MHz,TPCLK=1/36M |
CAN外设时钟分频 | 设置为4分频(实际写入BRP[9:0]的值为3) |
1Tq时间长度 | Tq = (BRP[9:0]+1) x TPCLK = 4 x 1/36M=1/9M |
1位的时间长度 | T1bit =1Tq+TS1+TS2 = 1+5+3 = 9Tq |
波特率 | BaudRate = 1/(N * Tq) = 1/(1/9M x 9)=1Mbps |
用频率计算:FPCLK / (BRP[9:0]+1) * T1bit = 36 / 4 / (1+5+3) = 1Mbps
对应代码:
//配置成1Mbps
CAN_InitTypeStruct.CAN_BS1 = CAN_BS1_5tq; // 实际写入TS1[3:0]的值为4
CAN_InitTypeStruct.CAN_BS2 = CAN_BS2_3tq; // 实际写入TS2[3:0]的值为2
CAN_InitTypeStruct.CAN_SJW = CAN_SJW_2tq;
CAN_InitTypeStruct.CAN_Prescaler = 4; // 实际写入BRP[9:0]的值为3
3.4 CAN发送邮箱
CAN外设一共有3
个发送邮箱,即最多可以缓存3个待发送的报文。
每个发送邮箱中包含有标识符寄存器CAN_TIxR
、数据长度控制寄存器CAN_TDTxR
及2个数据寄存器CAN_TDLxR
、CAN_TDHxR
,它们的功能如下:
寄存器名 | 功能 |
---|---|
标识符寄存器CAN_TIxR | 存储待发送报文的ID、扩展ID、IDE位及RTR位 |
数据长度控制寄存器CAN_TDTxR | 存储待发送报文的DLC段 |
低位数据寄存器CAN_TDLxR | 存储待发送报文数据段的Data0-Data3这四个字节的内容 |
高位数据寄存器CAN_TDHxR | 存储待发送报文数据段的Data4-Data7这四个字节的内容 |
发送状态寄存器CAN_TSR | 设置发送/中止请求、发送优先级,指示发送成功/失败、仲裁丢失、邮箱空标志 |
标识符:对于CAN_TIxR的
STID
位。当报文使用扩展标识符(IDE位置1)时,STDID[10:0]=EXTID[18:28]位,它与EXTID[17:0]组成完整的29位扩展标识符。
发送流程
将CAN_TIxR中的发送请求寄存器位TXRQ
置1,选择一个空发送邮箱,此时邮箱会进入挂起状态,等待成为「发送优先级」最高的邮箱,一但此邮箱拥有最高优先级,将被安排发送,当CAN总线空闲时,即可将标识符、数据长度码DLC和0~8位数据组成报文发送出去。邮箱 一旦发送成功,即恢复空状态。
发送标志
- 若发送成功,硬件通过将 CAN_TSR 寄存器的
RQCP
位(发送邮箱请求完成)和TXOK
位(发送成功)置1。 - 若发送失败,将由 CAN_TSR 寄存器的
ALST
位(仲裁丢失)and/orTERR
位(检 测到发送错误)指示失败原因。
发送优先级
当多个发送邮箱挂起时,按以下规则决定发送顺序。
- 按发送请求顺序(CAN_MCR寄存器的
TXFP
置1):此时各发送邮箱按请求先后次序发送消息; - 按报文标识符(CAN_MCR寄存器的
TXFP
置0):发送顺序由邮箱中所存消息的标识符确定,由CAN协议知,标识符(ID)值最低的消息拥有最高的优先级(显性位0占比高)。若标识符相等,低编号的邮箱有更高的优先级。
发送中止
将CAN_TSR 寄存器的 ABRQ
位置 1,即可中止发送请求。
- 此时若邮箱为挂起或预发送状态,则立即停止发送
- 若邮箱处于发送状态请求中止,则:
- 若邮箱已成功发送,变为空状态,CAN_TSR 寄存器的
TXOK
位置 1 - 若发送失败,邮箱变为预发送状态,发送中止并变为空状态,同时
TXOK
位清零
- 若邮箱已成功发送,变为空状态,CAN_TSR 寄存器的
上述所有情况,邮箱均为变为空状态。
3.5 CAN接收FIFO
CAN外设一共有2个接收FIFO,每个FIFO中有3个邮箱,即最多可缓存6个接收到的报文。FIFO完全由硬件进行管理,应用程序通过 FIFO 输出邮箱访问 FIFO中所存储 的消息。
下列寄存器
FIFO0
和FIFO1
各拥有1个。
寄存器名 | 功能 |
---|---|
标识符寄存器CAN_RIxR | 存储收到报文的基本ID、扩展ID、IDE位及RTR位 |
数据长度控制寄存器CAN_RDTxR | 存储收到报文的DLC段 |
低位数据寄存器CAN_RDLxR | 存储收到报文数据段的Data0-Data3 |
高位数据寄存器CAN_RDHxR | 存储收到报文数据段的Data4-Data7 |
接收FIFO寄存器CAN_RFxR | 指示挂起的消息数(0~3)、FIFO满/上溢标志,释放邮箱位控制 |
FIFO管理
有效消息:消息依据 CAN 协议正确接收(直到 EOF 字段的倒数第二位都没有发送错误)并成功通过标识符筛选,即视为有效。
初始状态FIFO为空,在接收到第一条有效消息存至其中后,变为Pending_1状态。CAN_RFxR的FMPx
位(FIFO的报文计数器)会自增1,即FMP=0x01。软件读取FIFO数据后,通过将 CAN_RFxR
寄存器的 RFOM
位置 1,以释放邮箱(释放完后RFOM
自动清零),此时FMPx
自减1。邮箱恢复至空状态。(如果同时接收到新的有效消息,FIFO 将保持 Pending_1 状态,新消息将在输出邮箱中供读取。)
若软件未释放邮箱,下一条有效消息存至FIFO后,进入Pending_2状态(FMP=0x02)。以此类推,下一条有效消息使其进入Pending_3状态(FMP=0x03),3个邮箱均为满。
上溢
当邮箱均为满时,下一条消息会使其发生上溢,FOVR
位置1,导致该消息丢失,因此一般读完FIFO后就要释放邮箱。其中,消息丢弃方式取决于CAN_MCR的RFLM
位:
- 锁定模式(RFLM=1):FIFO溢出时会丢弃新报文
- 非锁定模式(RFLM=0):FIFO溢出时会新报文会覆盖旧报文
3.6 标识符筛选
CAN外设的验收筛选器,一共有28
个筛选器组,每个筛选器组有2
个寄存器,CAN1和CAN2共用一个筛选器。
CAN中标识符与节点地址无关,而与报文内容(仲裁段ID)有关,接收器根据报文标识符ID的值来确定是否接收,CAN外设中的筛选器可调整筛选ID的长度及过滤模式,不符合规则的报文被硬件丢弃。
筛选器尺度寄存器(CAN_FS1R
)的FSCx
位可设置28个筛选器的筛选ID长度,仅当筛选器在初始化模式(CAN_FMR的FINIT位为1)才可以设置:
- FSCx = 1:单32位尺度,检查STDID[10:0]、 EXTID[17:0]、 IDE 和 RTR 位,末尾补个0位,一共32位
- FSCx = 0:单16位尺度,检查STDID[10:0]、 RTR、 IDE 和 EXTID[17:15],一共16位
筛选器模式寄存器 (CAN_FM1R
)的FBMx
位可设置过滤方法(筛选器在初始化模式):
- FBMx= 1:标识符列表模式,CAN_FxR1和CAN_FxR2均作为标识符寄存器,接收报文标识符ID的必须与其中一个标识符寄存器完全一致才通过筛选。
- FBMx= 0:标识符屏蔽(掩码)模式,两个寄存器分别作为标识符寄存器和屏蔽寄存器,两者配合使用,可设置屏蔽寄存器某几位可作为掩码,只要掩码相同即通过筛选。
要筛选一组标识符,应将掩码/标识符寄存器配置为掩码模式。
要选择单个标识符,应将掩码/标识符寄存器配置为标识符列表模式。
未使用的筛选器应保持停用。
模式 | 说明 |
---|---|
32位掩码模式 | CAN_FxR1存储要筛选的ID,CAN_FxR2存储掩码,掩码为1的部分表示该位必须与ID中的内容一致。 |
32位标识符模式 | CAN_FxR1和CAN_FxR2各存储1个ID,2个寄存器表示2个筛选的ID |
16位掩码模式 | CAN_FxR1低16位存储要筛选的ID,高16位存储掩码,掩码为1的部分表示必须要与低16位的ID一致;CAN_FxR2低16位存储要筛选的ID,高16位存储掩码,掩码为1的部分必须要与低16位的ID一致。 |
16位标识符模式 | CAN_FxR1和CAN_FxR2各存储2个ID,2个寄存器表示4个筛选的ID |
3.6.1 筛选器匹配索引
【why】消息接收到FIFO后,应用程序为将数据复制到正确的位置,需要根据标识符来识别数据。为了方便APP区分报文数据、方便访问SRAM位置,CAN控制器提供了一个筛选器匹配索引。
【what】CAN控制器给通过筛选器的报文提供了筛选器编号,该编号存放在CAN_RDTxR
寄存器的FMI[7:0]
位中,编号时不考虑过滤器是否激活。因此,每条成功接收的报文都有与之匹配的筛选器编号。
【how】
筛选器匹配索引有两种用法:
- 将筛选器匹配索引用作阵列索引,以访问数据目标位置。
- 将筛选器匹配索引与预期值列表进行比较。
对于标识符列表模式的筛选器,APP不再需要比较标识符,可直接比较筛选器索引。
对于标识符掩码模式的筛选器,APP只需比较掩码(屏蔽)位。
CAN控制器对于两个FIFO使用独立的编号方案,示例如下图所示:
3.6.2 筛选器优先级
当出现某个报文标识符通过多个筛选器时,将根据以下优先级规则选择接收邮箱中存储的筛选器匹配值:
- 32位筛选器优先于16位筛选器。
- 对于尺度(16/32位)相等的筛选器,标识符列表模式优先于标识符掩码模式。
- 对于尺度和模式均相等的筛选器,则按筛选器编号确定优先级(编号越低,优先级越高)。
筛选优先级机制如下图所示,有3个筛选器组为32位标识符列表模式,其余处于32位标识符掩码模式。
根据筛选器优先级规则,当前接收到的消息首先与32位标识符列表模式的筛选器组比较,若匹配则将消息存储到相应的FIFO中,将该筛选器的匹配索引存至CAN_RDTxR
寄存器的FMI[7:0]
中。若不匹配,则接收消息的标识符继续与后面的32位标识符掩码模式的筛选器比较,若标识符与筛选器中配置的任何标识符均不匹配,则丢弃该消息。
图中接收消息的标识符与标识符#4匹配。
3.7 CAN中断
CAN有四个中断向量:发送中断、FIFO0中断、FIFO1中断、错误和状态改变中断,每个中断通过中断使能寄存器 (CAN_IER
) 来单独使能或禁止。
发送中断可由以下事件产生:
- 发送邮箱 0 变为空,CAN_TSR 寄存器的 RQCP0 位置 1。
- 发送邮箱 1 变为空,CAN_TSR 寄存器的 RQCP1 位置 1。
- 发送邮箱 2 变为空,CAN_TSR 寄存器的 RQCP2 位置 1。
FIFO 0中断可由以下事件产生:
- 接收到新消息,CAN_RF0R 寄存器的 FMP0 位不是“00”。
- FIFO0 满,CAN_RF0R 寄存器的 FULL0 位置 1。
- FIFO0上溢,CAN_RF0R 寄存器的 FOVR0 位置 1。
FIFO 1中断可由以下事件产生:
- 接收到新消息,CAN_RF1R 寄存器的 FMP1 位不是“00”。
- FIFO0 满,CAN_RF1R 寄存器的 FULL1 位置 1。
- FIFO0上溢,CAN_RF1R 寄存器的 FOVR1 位置 1。
错误和状态改变中断可由以下事件产生:
- 出错状况,具体参考CAN_ESR寄存器中的LEC上次错误代号。BOFF离线状态标志、EPVF错误被动标志。EWGF错误警告标志。
- 唤醒状况,CAN Rx 信号上监测到 SOF(start of frame 帧起始,详见前文CAN时序),于此同时CAN_MSR的
WUKI
被硬件置1。 - 进入睡眠模式(CAN_MSR的
SLAKI
置1进入)
4 STM32的CAN控制相关结构体
4.1 CAN初始化结构体
typedef struct
{
uint16_t CAN_Prescaler; /* 配置CAN外设的时钟分频,可设置为1-1024 注意:固件库做了-1处理!*/
uint8_t CAN_Mode; /* 配置CAN的工作模式,(静默)回环或(静默)正常模式 */
uint8_t CAN_SJW; /* 配置SJW的极限长度,即CAN重新同步时单次可增加或缩短的最大长度,可配置为1~4Tq 写入0即为1Tq */
uint8_t CAN_BS1; /* 配置BS1段的长度,可配置为1~16Tq 写入0即为1Tq */
uint8_t CAN_BS2; /* 配置BS2段的长度,可配置为1~8Tq 写入0即为1Tq */
FunctionalState CAN_TTCM; /* 是否使能TTCM时间触发功能 */
FunctionalState CAN_ABOM; /* 是否使能ABOM自动离线管理功能 */
FunctionalState CAN_AWUM; /* 是否使能AWUM自动唤醒功能 */
FunctionalState CAN_NART; /* 是否使能NART自动重传功能 */
FunctionalState CAN_RFLM; /* 是否使能RFLM锁定FIFO功能 */
FunctionalState CAN_TXFP; /* 配置TXFP报文优先级的判断方法*/
} CAN_InitTypeDef;
- CAN_Prescaler
设置CAN外设的时钟分频,它可控制时间片Tq的时间长度,这里设置的值最终会减1后再写入BRP寄存器位,即前面介绍的Tq计算公式: Tq = (BRP[9:0]+1) x TPCLK 等效于:Tq = CAN_Prescaler x TPCLK
- CAN_Mode
设置CAN的工作模式,可设置为正常模式(CAN_Mode_Normal)、回环模式(CAN_Mode_LoopBack)、静默模式(CAN_Mode_Silent)以及回环静默模式(CAN_Mode_Silent_LoopBack)。
- CAN_SJW
配置SJW的极限长度,即CAN重新同步时单次可增加或缩短的最大长度,它可以被配置为1-4Tq
- CAN_BS1
设置CAN位时序中的BS1段的长度,它可以被配置为1-16个Tq长度。
- CAN_BS2
设置CAN位时序中的BS2段的长度,它可以被配置为1-8个Tq长度
- CAN_TTCM
设置是否使用时间触发功能(ENABLE/DISABLE),时间触发功能在某些CAN标准中会使用到。
- CAN_ABOM
设置**是否使用自动离线管理(**ENABLE/DISABLE),使用自动离线管理可以在节点出错离线后适时自动恢复,不需要软件干预。
- CAN_ AWUM
设置是否使用自动唤醒功能(ENABLE/DISABLE),使能自动唤醒功能后它会在监测到总线活动后自动唤醒。
- CAN_NART
设置是否使用自动重传功能(ENABLE/DISABLE),使用自动重传功能时,会一直发送报文直到成功为止。
- CAN_RFLM
设置是否使用锁定接收FIFO(ENABLE/DISABLE),锁定接收FIFO后,若FIFO溢出时会丢弃新数据,否则在FIFO溢出时以新数据覆盖旧数据。
- CAN_TXFP
设置发送报文的优先级判定方法(ENABLE/DISABLE),使能时,以报文存入发送邮箱的先后顺序来发送,否则按照报文ID的优先级来发送。
4.2 CAN发送及接收结构体
4.2.1 发送结构体
typedef struct
{
uint32_t StdId; /* 存储报文的标准标识符11位,0-0x7FF */
uint32_t ExtId; /* 存储报文的扩展标识符29位,0-0x1FFFFFFF */
uint8_t IDE; /* 存储IDE扩展标志 */
uint8_t RTR; /* 存储RTR远程帧标志 */
uint8_t DLC; /* 存储报文数据段的长度,0-8 */
uint8_t Data[8]; /* 存储报文数据段的内容 */
} CanTxMsg;
- StdId
存储报文的11位标准标识符,范围是0-0x7FF
。
ExtId
存储报文的29位扩展标识符,范围是0-0x1FFFFFFF
。ExtId与StdId这两个成员根据下面的IDE位配置,只有一个是有效的。
- IDE
存储扩展标志IDE位,当它的值为宏CAN_ID_STD时表示本报文是标准帧,使用StdId成员存储报文ID;当它的值为宏CAN_ID_EXT时表示本报文是扩展帧,使用ExtId成员存储报文ID。
- RTR
存储报文类型标志RTR位,当它的值为宏CAN_RTR_Data时表示本报文是数据帧;当它的值为宏CAN_RTR_Remote时表示本报文是遥控帧,由于遥控帧没有数据段,所以当报文是遥控帧时,下面的Data[8]成员的内容是无效的。
- DLC
存储数据帧数据段的长度,它的值的范围是0-8,当报文是遥控帧时DLC值为0。
- Data[8]
存储数据帧中数据段的数据。
4.2.2 接收结构体
typedef struct
{
uint32_t StdId; /* 存储了报文的标准标识符11位,0-0x7FF */
uint32_t ExtId; /* 存储了报文的扩展标识符29位,0-0x1FFFFFFF */
uint8_t IDE; /* 存储了IDE扩展标志 */
uint8_t RTR; /* 存储了RTR远程帧标志 */
uint8_t DLC; /* 存储了报文数据段的长度,0-8 */
uint8_t Data[8]; /* 存储了报文数据段的内容 */
uint8_t FMI; /* 存储了筛选器的编号 */
} CanRxMsg
4.3 CAN筛选器结构体
typedef struct
{
uint16_t CAN_FilterIdHigh; /* CAN_FxR1寄存器的高16位 */
uint16_t CAN_FilterIdLow; /* CAN_FxR1寄存器的低16位 */
uint16_t CAN_FilterMaskIdHigh; /* CAN_FxR2寄存器的高16位 */
uint16_t CAN_FilterMaskIdLow; /* CAN_FxR2寄存器的低16位 */
uint16_t CAN_FilterFIFOAssignment; /* 设置经过筛选后数据存储到哪个接收FIFO FIFO0/FIFO1*/
uint8_t CAN_FilterNumber; /* 筛选器编号,范围0~27 F103:0~13 */
uint8_t CAN_FilterMode; /* 筛选器模式 标识符列表/标识符掩码*/
uint8_t CAN_FilterScale; /* 设置筛选器的尺度 32位/16位*/
FunctionalState CAN_FilterActivation; /* 是否使能本筛选器 */
} CAN_FilterInitTypeDef;
模式 | CAN_FilterIdHigh | CAN_FilterIdLow | CAN_FilterMaskIdHigh | CAN_FilterMaskIdLow |
---|---|---|---|---|
32位列表模式 | ID1的高16位 | ID1的低16位 | ID2的高16位 | ID2的低16位 |
16位列表模式 | ID1的完整数值 | ID2的完整数值 | ID3的完整数值 | ID4的完整数值 |
32位掩码模式 | ID1的高16位 | ID1的低16位 | ID1掩码的高16位 | ID1掩码的低16位 |
16位掩码模式 | ID1的完整数值 | ID2的完整数值 | ID1掩码的完整数值 | ID2掩码完整数值 |
5 CAN通讯实验
将CAN配置为回环模式,采用串口打印出在CAN中断接收到的消息。
5.1 外设初始化
- GPIO初始化
void CAN_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能CAN时钟 */
RCC_APB1PeriphClockCmd (RCC_APB1Periph_CAN1 , ENABLE );
/* 使能CAN引脚相关的时钟 */
RCC_APB2PeriphClockCmd ( CAN_GPIO_CLK|RCC_APB2Periph_AFIO, ENABLE );
/* 配置CAN的引脚,普通IO即可 */
GPIO_InitStructure.GPIO_Pin = CAN_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(CAN_TX_GPIO_PROT, &GPIO_InitStructure);
/* 配置CAN的引脚,普通IO即可 */
GPIO_InitStructure.GPIO_Pin = CAN_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(CAN_RX_GPIO_PORT, &GPIO_InitStructure);
}
- CAN模式配置
void CAN_Mode_Config(void)
{
CAN_InitTypeDef CAN_InitTypeStruct;
CAN_InitTypeStruct.CAN_ABOM = ENABLE; /* 是否使能ABOM自动离线管理功能 */
CAN_InitTypeStruct.CAN_AWUM = ENABLE; /* 是否使能AWUM自动唤醒功能 */
CAN_InitTypeStruct.CAN_Mode = CAN_Mode_LoopBack;//CAN_Mode_Normal;//调试时建议使用回环模式,调试完再改成NORMAL
CAN_InitTypeStruct.CAN_NART = ENABLE; /* 是否使能NART自动重传功能 */ //错误重传
CAN_InitTypeStruct.CAN_RFLM = ENABLE; /* 是否使能RFLM锁定FIFO功能 */
CAN_InitTypeStruct.CAN_TTCM = DISABLE; /* 是否使能TTCM时间触发功能 */
CAN_InitTypeStruct.CAN_TXFP = DISABLE; /* 配置TXFP报文优先级的判断方法*/ //DISABLE 按报文ID优先级来发送
//配置成1Mbps
CAN_InitTypeStruct.CAN_BS1 = CAN_BS1_5tq; // 实际写入TS1[3:0]的值为4
CAN_InitTypeStruct.CAN_BS2 = CAN_BS2_3tq; // 实际写入TS2[3:0]的值为2
CAN_InitTypeStruct.CAN_SJW = CAN_SJW_2tq;
CAN_InitTypeStruct.CAN_Prescaler = 4; // 实际写入BRP[9:0]的值为3
CAN_Init(CAN1,&CAN_InitTypeStruct);
}
波特率配置为1M
- CAN筛选器配置
#define PASS_ID ((uint32_t)0x1314)
void CAN_Filter_Config(void)
{
CAN_FilterInitTypeDef CAN_FilterInitTypeStruct;
CAN_FilterInitTypeStruct.CAN_FilterActivation = ENABLE; /* 是否使能本筛选器 */
CAN_FilterInitTypeStruct.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; /* 设置经过筛选后数据存储到哪个接收FIFO */
CAN_FilterInitTypeStruct.CAN_FilterNumber = 0; /* 筛选器编号,范围0~27 F103:0~13 */
CAN_FilterInitTypeStruct.CAN_FilterScale = CAN_FilterScale_32bit; /* 设置筛选器的尺度 */
CAN_FilterInitTypeStruct.CAN_FilterMode = CAN_FilterMode_IdMask; /* 筛选器模式 */
// CAN_Id_Extended使用扩展帧, CAN_RTR_Data为数据帧
// 左移3位是因为此摸下低3位分别为0、RTR、IDE(详见标识符筛选)
CAN_FilterInitTypeStruct.CAN_FilterIdHigh = ((PASS_ID << 3 | CAN_Id_Extended | CAN_RTR_Data) & 0xFFFF0000) >> 16; /* CAN_FxR1寄存器的高16位 */
CAN_FilterInitTypeStruct.CAN_FilterIdLow = ((PASS_ID << 3 | CAN_Id_Extended | CAN_RTR_Data) & 0xFFFF); /* CAN_FxR1寄存器的低16位 */
//掩码均为1表示报文标识符必须全部与筛选器中存储的ID一致, 即可视为列表模式
CAN_FilterInitTypeStruct.CAN_FilterMaskIdHigh = 0xFFFF; /* CAN_FxR2寄存器的高16位 */
CAN_FilterInitTypeStruct.CAN_FilterMaskIdLow = 0xFFFF; /* CAN_FxR2寄存器的低16位 */
CAN_FilterInit(&CAN_FilterInitTypeStruct);
CAN_ITConfig (CAN1, CAN_IT_FMP0, ENABLE); // CAN_IT_FMP0 接收邮箱0中断
}
- CAN接收NVIC中断配置
void CAN_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 配置NVIC为优先级组1 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn; //接收邮箱0
/* 配置抢占优先级 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 配置子优先级 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断通道 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
5.2 收发数据
- 发送数据
uint8_t mailbox;
//发送扩展帧
CAN_Tran_Data.StdId = 0; /* 存储报文的标准标识符11位,0-0x7FF */
CAN_Tran_Data.ExtId = PASS_ID;
CAN_Tran_Data.RTR = CAN_RTR_Data; // 数据帧(CAN_RTR_Remote为遥控帧)
CAN_Tran_Data.IDE = CAN_Id_Extended;
CAN_Tran_Data.DLC = 8; /* 存储了报文数据段的长度,0-8 */
memset(CAN_Tran_Data.Data, 'a', 8);
mailbox = CAN_Transmit(CAN1, &CAN_Tran_Data); //CAN_Transmit 函数返回值为邮箱编号
while(CAN_TransmitStatus(CAN1, mailbox) == CAN_TxStatus_Failed); //检测数据是否传输完成
printf("\r\n 数据包发送完成\r\n");
- CAN中断接收数据
void USB_LP_CAN1_RX0_IRQHandler(void)
{
if (CAN_GetITStatus(CAN1, CAN_IT_FMP0) != RESET)
{
CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0);
CAN_Receive(CAN1, CAN_FIFO0, &CAN_Rece_Data);
if(CAN_Rece_Data.ExtId == PASS_ID && CAN_Rece_Data.IDE == CAN_ID_EXT && CAN_Rece_Data.DLC == 8) //确定ID 扩展帧 报文数据段的长度
rec_flag = 1;
}
}
打印出接收的数据:
if(rec_flag == 1)
{
printf("\r\n接收到的数据: ");
for(int i = 0; i < 8; i++)
printf("%c ",CAN_Rece_Data.Data[i]); //在USB_LP_CAN1_RX0_IRQHandler中接收
printf("\r\n");
rec_flag = 0;
}
参考
- CAN协议中文版.pdf
- CAN总线入门.pdf
- 「野火」CAN—通讯实验(第1~3节).pptx
- STM32F4xx中文参考手册.pdf