需要了解的知识:
1.CAN:Controller Area Network,控制器局域网
2.CAN-bus: Controller Area Network-bus,控制器局 域网总线技术
3.运行流程:
4.显性隐性:
CAN-bus通信帧:数据帧、远程帧、错误帧、过载帧和帧间隔
由7个段组成,其中根据仲裁段ID码长度的不同,分为标准帧(CAN2.0A)和扩展帧(CAN2.0B)。
1.1 帧起始、帧结束:
1.2 仲裁段
1.5 CRC段
数据帧和远程帧最大的区别在于:远程帧没有数据域。数据帧分为标准数据帧和扩展数据帧,它们之间最大的区别在于:标识符(ID)长度不同(标准帧为11 位,扩展帧为29 位)。
为了能更好地理解下面的内容,让我们先来回忆一下标准数据帧是什么样子的:
前面已经强调了CAN 总线上的节点接收或者发送数据都是以帧为单位。假如我们要发送一个字节的数据:0x5A,是不是像串口发送数据那样,直接把0x5A 写入发送缓冲寄存器,然后发送就可以了呢?
NO! 刚才已经强调了,CAN 总线上的数据都是以帧为单位的!!!我们必须按照帧的格式填充它!TNND,
这么复杂?没关系,ST 库函数已经提供了一个结构,我们只需要填充该结构就可以了。我们来看一下
这个结构的样子:
typedef struct
{
uint32_t StdId; //标准帧ID,如果您要发送扩展帧。可以不管它
uint32_t ExtId; //扩展帧ID,如果您要发送标准帧。可以不管它
uint8_t IDE; //您是想发送标准帧还是扩展帧?
uint8_t RTR; //您是想发送数据帧还是远程帧?
uint8_t DLC; // 您想发送数据的长度。
uint8_t Data[8]; //您想要发送的数据。
} CanTxMsg;
IDE | 描述 |
---|---|
CAN_Id_Standard | 使用标准标识符 |
CAN_Id_Extended | 使用扩展标识符 |
RTR | 描述 |
---|---|
CAN_RTR_Data | 数据帧 |
CAN_RTR_Remote | 远程帧 |
1. 先声明一个CanTxMsg 类型的变量,然后按照自己具体的需要,填充此结构变量就可
以发送了(帧结构中其他没有填充的部分由硬件自动完成)。
2. 强调:这里的StdId 或者 ExtId 是根据自己的实际需要设置的!我们先抛开它们所
代表的实际意义,认为它们存在的目的是为了“进攻”。(发送出去让别人过滤)
7.2 接收
STM32 参考手册中提到:bxCAN 控制器为应用程序提供了14 个位宽可变的、可配置的过滤器组(13~0)。(互联型有28 个)。每个过滤器组的位宽都可以独立配置。可以配置成16
位或者32 位。过滤器组还可配置为屏蔽位模式或标识符列表模式。TNND,看起来好像很复杂!
先来理解一句话:共有14个过滤器组,每个过滤器组x由2个32位寄存器,CAN_FxR1和CAN_FxR2组成。搞定一个过滤器组,其他的都可以以此类推。
我们先来看1个32位过滤器-标示符屏蔽的情况CAN_FxR1作ID,CAN_FxR2作屏蔽:
先看图:
这里的ID 是什么意思?难道就是前面所说的发送数据帧里面的ID?NO!这里的ID 和自己发送的帧里面的ID 没有一点关系。完全是两个东西,在硬件上属于不同的寄存器。强调:这里的ID也是根据自己的实际需要设置的!我们先抛开它们所代表的实际意义,认为它们存在的目的是为了“防守”。(过滤别人发送过来的帧的 ID)
这里的屏蔽是什么意思?这里的屏蔽和ID 共同配合完成过滤。
这里的映像是什么意思?映像的意思就是假定收到的帧的ID 信息。
下面举个例子,一切都会一目了然:
1.: 假如我们只想收到别人发过来的ID 为0x317 的标准数据帧:
0X317 二进制位:011 0001 0111
那么可以这样设置:
CAN_FxR1:0110 0010 111X XXXX XXXX XXXX XXXX X00X (ID)
CAN_FxR2:1111 1111 1110 0000 0000 0000 0000 0110 (屏蔽)
这里是我们设置的想要收到的数据帧的ID。
这里为1 的位,意味着收到的数据帧中相应的ID 位必须和设置的ID 位一样(必须匹
配)。
2.: 假如我们想收到别人发过来的ID 为0x310 到0x317 的标准数据帧:
那么可以这样设置:
CAN_FxR1:0110 0010 xxxX XXXX XXXX XXXX XXXX X00X (ID)
CAN_FxR2:1111 1111 0000 0000 0000 0000 0000 0110 (屏蔽)
这里的红色xxx 就代表000 到111 的任意组合。
这里为0 的位,意味着收到的数据帧中的相应位的ID 不一定非要与设置的ID 一样(不用关心)。
这里红色部分表明必须是标准数据帧。
3.: 假如我们想收到别人发过来的ID 为0x000 到0x7FF 的标准数据帧:
那么可以这样设置:
CAN_FxR1:xxxx xxxx xxxX XXXX XXXX XXXX XXXX X00X (ID)
CAN_FxR2:0000 0000 0000 0000 0000 0000 0000 0110 (屏蔽)
接下来看2个32位过滤器-标识符列表模式,CAN_FxR1和 CAN_FxR2都作为 ID.
这种情况就很简单了。只有收到的帧的ID 必须和CAN_FxR1 或者CAN_FxR2 完全一样才接收。这样的话,就只能接收两种不同的ID。
举个例子:
如果我们把CAN_FxR1 和CAN_FxR2 设置为下面的样子。
CAN_FxR1:0110 0010 1110 0000 0000 0000 0000 0000 (ID)
CAN_FxR2:0000 0001 1110 0000 0000 0000 0000 0000 (ID)
这样的话,就只能过滤出ID 为0x317(与CAN_FxR1 必须一样)和0x00F(与CAN_FxR2 必须一样)两种标准数据帧了。
以上是32 位模式下标识符屏蔽模式和标识符列表模式下的设置方法。
在16 位模式下,只不过把两个32 位寄存器拆成了4 个16 位的而已,原理和32 位模式下是一样的。就不赘述了。
接收数据是通过指向CanRxMsg 结构体变量的指针传递的。直接调用 CAN_Receive 即可轻松完成。
typedef struct
{
uint32_t StdId;
uint32_t ExtId;
uint8_t IDE;
uint8_t RTR;
uint8_t DLC;
uint8_t Data[8];
uint8_t FMI;
} CanRxMsg;
CanRxMsg 结构与CanTxMsg 差不多。只是多了一个FMI 域。