目录
1. CAN外设简介
1.1 CAN网拓扑结构
1.2 引脚定义
1.3 CAN框图
1.4 发送流程
1.5 接收流程
1.6 标识符过滤器
1.6.1 2个32位过滤器-标识符列表
1.6.2 4个16位过滤器-标识符列表
1.6.3 1个32位过滤器-标识符屏蔽
1.6.4 2个16位过滤器-标识符屏蔽
1.7 测试模式
1.7.1 静默模式
1.7.2 环回模式
1.7.3 环回静默模式
1.8 工作模式
1.8.1 初始化模式
1.8.2 正常模式
1.8.3 睡眠模式
1.9 位时间特性
1.10 中断
1.10.1 发送中断
1.10.2 FIFO 0中断
1.10.3 FIFO 1中断
1.10.4 错误和状态变化中断
1.11 错误处理和离线恢复
1. CAN外设简介
前面我们已经详细介绍了CAN总线:
CAN总线_时光の尘的博客-CSDN博客
下面我们开始在STM32单片机上对其进行使用,在使用前我们先来了解一下STM32的一些CAN外设。
STM32内置bxCAN外设(CAN控制器),支持CAN2.0A和2.0B,可以自动发送CAN报文和按照过滤器自动接收指定CAN报文,程序只需处理报文数据而无需关注总线的电平细节。更详细的参数可以看参考手册,这里截了一点其主要特点:
1.1 CAN网拓扑结构
这里的每个CAN结点都挂载在CAN总线上,对于其中的一个CAN结点,他们是由控制芯片MCU、CAN控制器以及CAN收发器组成,一般情况下CAN控制器都集成在MCU里面,STM32就集成了CAN控制器,STM32引出了CAN_Rx和CAN_Tx引脚,与CAN收发器相连,CAN收发器通过引出的CAN_H和CAN_L与CAN总线相连:
1.2 引脚定义
这里我们使用的是STM32F103C8T6芯片,其上CAN资源为CAN1,我们查找数据手册,其相关引脚分别为:
1.3 CAN框图
下面是STM32的CAN外设的框图,其中上面部分是CAN1的,也就是主CAN,下面部分是CAN2的(只有互联型设备才有CAN2),也就是从CAN,我们使用的STM32F103C8T6只有CAN1的外设:
1.4 发送流程
对于CAN在STM32内部的工作,首先对于发送,当我们想要发送一个报文时,我们只需要将想要发送的报文的各个参数,通过CPU写入到邮箱中,然后给出请求发送的命令,然后发送和接收控制器就会等待总线空闲,然后自动把这个报文广播到总线上:
这里我们可以看出,其实一个邮箱就能完成上述功能,但是为什么要创建三个呢?内置三个邮箱邮箱的原因,实际上是为了防止总线繁忙造成发送拥堵,举个例子:
假如你写入了一个报文存储到邮箱0,让控制器将数据发出去,但是此时总线一直处于繁忙状态,控制器一直等不到总线空闲,数据无法发出会滞留在邮箱内,当CPU想发出下一个数据,就会发现邮箱满了没地方存储,这样CPU也会处于等待状态,这样就会造成拥堵:
STM32内置3个邮箱,当总线拥堵,邮箱0数据未发送出去,但是CPU想继续发送数据,这时就可以在邮箱1或者邮箱2中任意找一个邮箱进行存储数据,等待总线空闲进行发出:
这里可能会说万一邮箱全满了怎么办?一般情况下总线不会出现如此拥堵的情况,如果出现了只能要么CPU等待,要么CPU放弃本次数据存储。
当两个或者三个邮箱都有数据要发送,那么会先发哪一个呢?这里可以进行配置:现请求先发送或者按照ID号优先级发送。
下面是数据手册的一个基本流程,选择一个空置邮箱→写入报文 →请求发送:
大概查了一下意思:
- RQCP(Request completed)请求完成,=X表示任意值
- TXOK(Transmission OK)发送成功,=X表示任意值
- TME(Tranmsit mailbox empty)发送邮箱空,=1表示邮箱空置状态
- TXRQ(Tranmsit mailbox request)发送请求控制位,=1表示产生发送请求
- NART(No automatic retransmission)禁止自动重传,=0表示使用,=1表示禁止,CAN硬件在发送报文失败时会一直自动重传直到发送成功
- ABRQ(Abort request)终止发送
大概流程就是,首先邮箱处于空置状态,此时TXRQ=1,产生一个发送请求,进入挂号状态(在三个邮箱随便找一个进去):
此时判断邮箱是否处于最高优先级,若是最高优先级进入预定状态,若是未处于则继续处于挂号状态:
或者终止发送ABRQ=1,回到空置状态:
数据进入预定状态也可以ABRQ=1,回到空置状态:
或者等待总线空闲CAN总线=IDLE,进入发送状态:
数据发送成功,回到空置状态:
若是发送失败,若是NART=0,表示使用自动重传,回到预定状态:
若是NART=1,表示不使用自动重传,回到空置状态:
1.5 接收流程
对于数据的接收,当CAN总线出现任何一个数据帧或者遥控帧的报文波形时,控制器就会将报文收下,但是总线上的报文不一定就是自己需要的,需要通过接收过滤器进行筛选,接收过滤器可以根据ID号对报文进行过滤,如果ID不是我们想要的,就不会通过过滤器,数据就不会进行后续的传递,默认条件下不去配置过滤器,过滤器处于失能的状态,无法使用,想要什么ID就可以对过滤器进行配置。
过滤器过滤完的数据,会存储在FIFO中,在STM32总共配置了两个FIFO,每个FIFO配备3个邮箱,过滤器过滤完的数据可以指定进入那个FIFO :
假设配备FIFO 0接收过滤器0过滤的数据,假设过滤器通过了三个CAN总线发出的数据,将FIFO 0的三个邮箱全占满了:
这里可以配置FIFO锁定,假如配置了FIFO锁定,在FIFO满了以后,新的报文就会丢弃;
假如配置了FIFO不锁定,在FIFO满了以后,新报文会把邮箱2的数据踢出去:
此时若是CPU读取数据,邮箱0的数据会被读取,那么邮箱1的数据,就会往前进入到邮箱0中,依次类推:
至于为什么会设立两个FIFO,可以理解为医院就诊,患者就是数据,通过过滤器将急诊患者和普通患者区分开,急诊患者的队伍(基数较少,数据紧急的队伍),普通患者的队伍(基数较大,数据不紧急的队伍):
注意,这里FIFO 0和FIFO 1自己不区分优先级,默认优先级是相同,需要自己进行区分二者的优先级。
下面是他手册上的一个流程,接收到一个报文→匹配过滤器后进入FIFO 0或FIFO 1→CPU读取:
这里流程和上面介绍的差不多,就不做重复介绍了,不过需要注意的是,这里应该是二进制的01、10、11表示1、2、3,这里用的十六进制应该表示错了:
1.6 标识符过滤器
下面数据手册上的有关过滤器的配置,每个过滤器的核心由两个32位寄存器组成:R1[31:0]和R2[31:0]:
图中一些参数:
- FSCx:位宽设置 置0,16位;置1,32位
- FBMx:模式设置 置0,屏蔽模式;置1,列表模式
- FFAx:关联设置 置0,FIFO 0;置1,FIFO 1(相当于选择数据进入哪个FIFO)
- FACTx:激活设置 置0,禁用;置1,启用(相当于给过滤器加个开关,一般我们不可能把所有过滤器全用上,关掉不用的防止误操作)
其中x表示0~13那个过滤器里的,例如过滤器1里的FSC就表示FSC1,过滤器8里的FBM表示FBM8,依次类推。
由上图通过FSCx和FBMx二者的组合,可以出现四种模式:
FBMx | |||
1 | 0 | ||
FSCx | 1 | 2个32位过滤器-标识符列表 | 1个32位过滤器-标识符屏蔽 |
0 | 4个16位过滤器-标识符列表 | 2个16位过滤器-标识符屏蔽 |
1.6.1 2个32位过滤器-标识符列表
32位列表模式,这个是最简单的模式,该模式下,R1和R2两个寄存器,都写入的是目标ID,我们可以把想要的ID写入到R1和R2两个寄存器,总共可以写两个目标的ID,过滤器通过对比这两个ID,来让匹配同样的ID的数据通过:
那么要如何进行配置ID呢?首先,我们看下面这个映像,在其高11位需要存入的是标准格式的ID(STID)号:
然后后面需要存入的这18位,是扩展格式的ID(EXID)号(注意实际上扩展ID是29位,若是写成扩展ID,标准ID将作为扩展ID的高11位):
这里可以参考之前将帧格式的内容:
CAN总线数据帧格式详细介绍-CSDN博客
扩展格式的ID实际上是标准格式的11位ID号加上扩展格式的18位ID号组成,简单来说,如果你想要写入一个标准格式的ID号,只需要写入前11位的STID就行,后面18位EXID就用不了了,全部写0就行;如果想写一个扩展ID,需要将标准ID(STID)作为EXID的高11位,然后加上后面18位。
那么如何区分是标准ID还是扩展ID?这就需要通过IDE位来进行判定,若是IDE=0,那就表示是标准ID,若是IDE=1,那就表示是扩展ID,这里需要注意,若是你写的是扩展ID,但是你的IDE位确实写0,那么就会只取扩展ID的高11位作为标准ID来看。
后面的RTR位是看你想要过滤遥控帧还是数据帧,RTR=1就是过滤遥控帧,如果RTR=0就是过滤数据帧。
最后一位是保留位,没实际作用,默认0:
这里我们可以看出一点,我们过滤器可能既需要过滤扩展ID,也需要过滤标准ID,但也有很多情况我们的过滤器只需要过滤标准ID,不需要过滤扩展ID,那么我们就会发现我们此时在使用32位列表模式,就会发现,扩展ID的18位,处于闲置浪费的状态。
1.6.2 4个16位过滤器-标识符列表
为了解决闲置浪费,我们可以配置“4个16位过滤器-标识符列表”模式:
该模式下R1和R2都被拆开,拆成4个16位的寄存器,每个寄存器写入一个标准格式的目标ID,这样一个过滤器就可以写入4个目标ID,极大地利用资源。
该模式下的映像也是前11位写入标准ID:
后面的RTR位也是过滤遥控帧还是数据帧,RTR=1就是过滤遥控帧,如果RTR=0就是过滤数据帧:
因为没有扩展帧,因此IDE位默认写“0”:
多出来的三位,没啥用写“0”即可:
这里我们又需要思考,即便是一个过滤器能过滤4种标准ID,14个过滤器全部使用最高也只能过滤56个ID,面对我们平常使用不会出现问题,但是有些时候可能上百个传感器去监听进行数据传递,例如我们使用100个温度传感器,其ID是0x100~0x199和100个湿度传感器其ID是0x200~0x299,我们要如何才能将所有的数据都检测到呢?
这里我们就引入了屏蔽,如果我们屏蔽掉后两位,认为取0x1开头的ID是温度数据,以0x2开头的是湿度数据,不就有效解决这个问题了吗。
1.6.3 1个32位过滤器-标识符屏蔽
比如说R1写ID号,R2决定哪些ID是必须一样,哪些ID是任意的,这样就能完美满足需求:
这里R2的作用类似于掩码。
延续上面温湿度的例子:
假设我们要过滤出0x1开头的所有ID号,11位的二进制表示就是:
ID号:001 xxxx xxxx
首先我们需要把这个ID号,写入到R1寄存器里,EXID不需要全部写“0”,因为我们是标准ID,所有IDE位必须写“0”,RTR根据需求来:
如果现在是列表模式,那么我们就只能过滤掉一个ID(0x100),但现在是屏蔽模式,那么我们就可以在R2里标明,在过滤时哪些位是必须匹配的,哪些是无关的,这样我们就需要给R2的高3位给1,这样就表示我们ID高3位必须为001才能通过过滤器,除此之外,屏蔽位的IDE位必须给1,不然就表示标准格式和扩展格式都无所谓了,这样可能使某些扩展ID进来,就不符合需求了:
STID | EXID | IDE | RTR | 保留位 | |
ID | 001x xxxx xxx | 0 0000 0000 0000 0000 0 | 0 | X | 0 |
屏蔽 | 1110 0000 000 | 0 0000 0000 0000 0000 0 | 1 | X | 0 |
其中RTR位根据需求:
RTR | RTR | RTR | |
ID | 0 | 1 | 1/0 |
屏蔽 | 1 | 1 | 0 |
必须匹配数据帧 | 必须匹配遥控帧 | 数据帧/遥控帧都可以 |
1.6.4 2个16位过滤器-标识符屏蔽
如果只需要过滤标准ID的话,可以使用下面这个,可以过滤两组屏蔽位的ID,使用方法和上面一样:
综合上面的四种情况句几个例子:
总线上存在的ID | 想要接收的ID | 过滤器模式 | R1[31:0]配置值 | R2[31:0]配置值 |
0x123, 0x234, 0x345, 0x456, 0x567, 0x678 | 0x234, 0x345, 0x567 | 16位/列表 | ID: R1[15:0]=0x234<<5 ID: R1[31:16]=0x345<<5 | ID: R2[15:0]=0x567<<5 ID: R2[31:16]=0x000<<5 |
0x100~0x1FF, 0x200~0x2FF, 0x310~0x31F, 0x320~0x32F | 0x200~0x2FF, 0x320~0x32F | 16位/屏蔽 | ID: R1[15:0]=0x200<<5 Mask: R1[31:16]= (0x700<<5)|0x10|0x8 | ID: R2[15:0]=0x320<<5 Mask: R2[31:16]= (0x7F0<<5)|0x10|0x8 |
0x123, 0x234, 0x345, 0x456, 0x12345678, 0x0789ABCD | 0x123, 0x12345678 | 32位/列表 | ID: R1[31:0]= 0x123<<21 | ID: R2[31:0]= (0x12345678<<3)|0x4 |
0x12345600~ 0x123456FF, 0x0789AB00~ 0x0789ABFF | 0x12345600~ 0x123456FF | 32位/屏蔽 | ID: R1[31:0]= (0x12345600<<3)|0x4 | Mask: R2[31:0]= (0x1FFFFF00<<3)|0x4|0x2 |
任意ID | 只要遥控帧 | 32位/屏蔽 | ID: R1[31:0]=0x2 | Mask: R2[31:0]=0x2 |
任意ID | 所有帧 | 32位/屏蔽 | ID: R1[31:0]=随意 | Mask: R2[31:0]=0 |
1.7 测试模式
1.7.1 静默模式
静默模式:用于分析CAN总线的活动,不会对总线造成影响:
在静默模式下,bxCAN可以正常地接收数据帧和远程帧,但只能发出隐性位,而不能真正发送报文。如果bxCAN需要发出显性位(确认位、过载标志、主动错误标志),那么这样的显性位在内部被接回来从而可以被CAN内核检测到,同时CAN总线不会受到影响而仍然维持在隐性位状态。显性位(确认位、错误帧)不会真正发送到总线上。
1.7.2 环回模式
环回模式:用于自测试,同时发送的报文可以在CAN_TX引脚上检测到,在环回模式下,bxCAN把发送的报文当作接收的报文并保存(如果可以通过接收过滤)在接收邮箱里。
在环回模式下CAN内核忽略确认错误(在数据/远程帧的确认位时刻,不检测是否有显性位)。在环回模式下,bxCAN在内部把Tx输出回馈到Rx输入上,而完全忽略CANRX引脚的实际状态。
1.7.3 环回静默模式
环回静默模式:用于热自测试,自测的同时不会影响CAN总线。即可以像环回模式那样测试bxCAN,但却不会影响CANTX和CANRX所连接的整个CAN系统。在环回静默模式下,CANRX引脚与CAN总线断开,同时CANTX引脚被驱动到隐性位状态。
1.8 工作模式
bxCAN有3个主要的工作模式:初始化、正常和睡眠模式。在硬件复位后,bxCAN工作在睡眠模式以节省电能,同时CANTX引脚的内部上拉电阻被激活。软件通过对CAN_MCR寄存器的INRQ或SLEEP位置“1”,可以请求bxCAN进入初始化或睡眠模式。一旦进入了初始化或睡眠模式,bxCAN就对CAN_MSR寄存器的INAK或SLAK位置“1”来进行确认,同时内部上拉电阻被禁用。当INAK和SLAK位都为“0”时,bxCAN就处于正常模式。在进入正常模式前,bxCAN必须跟CAN总线取得同步;为取得同步,bxCAN要等待CAN总线达到空闲状态,即在CANRX引脚上监测到11个连续的隐性位。
SLAK(Sleep ack)睡眠确认状态位 ,=1表示硬件已经确认进入睡眠模式了
INAK(Init ack)初始化确认位,=0表示硬件没有确认进入初始化模式
1.8.1 初始化模式
初始化模式:用于配置CAN外设,禁止报文的接收和发送。
1.8.2 正常模式
正常模式:配置CAN外设后进入正常模式,以便正常接收和发送报文
1.8.3 睡眠模式
睡眠模式:低功耗,CAN外设时钟停止,可使用软件唤醒或者硬件自动唤醒,通过AWUM:置1,自动唤醒,一旦检测到CAN总线活动,硬件就自动清零SLEEP,唤醒CAN外设;置0,手动唤醒,软件清零SLEEP,唤醒CAN外设。
这三种模式详细想要了解怎么工作的,可以看数据手册,都是一些寄存器的配置,回头讲代码的时候,边用边讲,下面是他的一个手册相关部分截图:
1.9 位时间特性
位时间特性逻辑通过采样来监视串行的CAN总线,并且通过与帧起始位的边沿进行同步,及通过与后面的边沿进行重新同步,来调整其采样点。
这个在之前讲位同步是已经讲过:
CAN总线位同步的使用以及总线仲裁规则详解-CSDN博客
不过需要有一点注意的是:
可以看出在CAN协议的位同步中,SS和PBS1之间还有个PTS,但是STM32是没有PTS段的,这里可能是因为,PBS1和PTS二者之间没啥事要干,STM32设计者讲二者融合到一起了,还能少设计个参数。
上图公式可写为:
波特率 = APB1时钟频率 / 分频系数 / 一位的Tq数量
= 36MHz / (BRP[9:0]+1) / (1 + (TS1[3:0]+1) + (TS2[2:0]+1))
同步段(SYNC_SEG):通常期望位的变化发生在该时间段内。其值固定为1个时间单元(1 x tCAN)。
时间段1(BS1):定义采样点的位置。它包含CAN标准里的PROP_SEG和PHASE_SEG1。其值可以编程为1到16个时间单元,但也可以被自动延长,以补偿因为网络中不同节点的频率差异所造成的相位的正向漂移。
时间段2(BS2):定义发送点的位置。它代表CAN标准里的PHASE_SEG2。其值可以编程为1到8个时间单元,但也可以被自动缩短以补偿相位的负向漂移。
重新同步跳跃宽度(SJW)定义了,在每位中可以延长或缩短多少个时间单元的上限。其值可以编程为1到4个时间单元。
有效跳变被定义为,当bxCAN自己没有发送隐性位时,从显性位到隐性位的第1次转变。
下面是各种CAN帧:
1.10 中断
bxCAN占用4个专用的中断向量。通过设置CAN中断允许寄存器(CAN_IER),每个中断源都可以单独允许和禁用。
1.10.1 发送中断
数据手册上表述的是:
简单来说,就是发送中断就是发送邮箱空时产生的中断。
1.10.2 FIFO 0中断
数据手册上表述的是:
FIFO 0中断:收到一个报文/FIFO 0满/FIFO 0溢出时产生
1.10.3 FIFO 1中断
数据手册上表述的是:
FIFO 1中断:收到一个报文/FIFO 1满/FIFO 1溢出时产生
1.10.4 错误和状态变化中断
数据手册上表述的是:
状态改变错误中断:出错/唤醒/进入睡眠时产生
1.11 错误处理和离线恢复
CAN协议描述的出错管理,完全由硬件通过发送错误计数器(CAN_ESR寄存器里的TEC域),和接收错误计数器(CAN_ESR寄存器里的REC域)来实现,其值根据错误的情况而增加或减少。
当TEC大于255时,bxCAN就进入离线状态,同时CAN_ESR寄存器的BOFF位被置’1’。在离线状态下,bxCAN无法接收和发送报文。
根据CAN_MCR寄存器中ABOM位的设置,bxCAN可以自动或在软件的请求下,从离线状态恢复(变为错误主动状态)。在这两种情况下,bxCAN都必须等待一个CAN标准所描述的恢复过程(CAN RX引脚上检测到128次11个连续的隐性位)。
如果ABOM位为’1’,bxCAN进入离线状态后,就自动开启恢复过程。
如果ABOM位为’0’,软件必须先请求bxCAN进入然后再退出初始化模式,随后恢复过程才被开启。
注: 在初始化模式下,bxCAN不会监视CAN RX引脚的状态,这样就不能完成恢复过程。为了完成恢复过程,bxCAN必须工作在正常模式。
CAN总线_时光の尘的博客-CSDN博客