这一篇文章来记录一下AWR2944的MCAN。
列出MCAN的一些基本特性:
首先看一些MCAN模块与外部常见的信号有:
再来看看MCAN模块的一些组成部分:
MCAN具有多种工作模式,如下:
MCAN对其空间提供1bit和2bit检测机制,当MCAN检测到ECC错误时,会报告错误,可触发中断,并记录错误地址。
MCAN可配置TDC对第二采样点(SSP)进行补偿。关于对CAN TDC和SSP不太了解的朋友可参考这篇文章:《TDC传输延迟补偿和SSP第二采样点》。
之前就提过Messgae RAM是MCAN内部的一段1600Words的RAM空间,在不同的应用中对CAN的收发邮箱的数量要求不尽相同,MCAN为了灵活应对各种应用场合干脆就直接整出一点RAM出来,让用户根据自己的应用来配置。配置方法也很简单:
其原理就是将各个部分的首地址偏移告诉CAN Core就行了,因为每个element的大小都是固定的,这样CAN Core和用户软件就能通过首地址和index来访问想要的element。从上面的图中就可以看出,MCAN同时最大支持128个标准帧硬件Filter,64个扩展帧硬件Filter,2个深度64的RX_FIFO,64个RX_Buffer,32个TX_Buffer。还有32深度的Tx_Event_FIFO,这个其实是用来记录发送信息的,当MACN成功发送一帧报文后,可以自动将相关发送信息记录在Tx_Event_FIFO中。
在这里有一个非常重要的点就是,根据上面的描述,MCAN可同时支持32个TX buffer,128深度的RX FIFO以及64个RX Buffer,但是这里需要注意的是MCAN的RAM Size只有1600个Words,在涉及到CANFD的应用场景来说,随着DLC的增加,每一个RX_TX_Buffer所占的空间就会变大,所以在这个时候,MCAN就无法同时支持这么多的buffer存在了,所在在设计MCAN的Buffer数量时,需要注意这个点。Buffer DLC与其所占的RAM大小关系如下图所示:
以上各种element在Message Buffer中的数据结构如下:
每个element的每一位代表什么意思在RM手册都有详细的描写,在这里就不多做赘述。
上面说到MCAN最大同时支持2个深度64的RX_FIFO,64个RX_Buffer,所以MCAN理论上同时可最大存储192条报文,但实际上也根本用不到这么多,64个基本上都满足大部分应用了。MCAN的接收机制比较简单,即MCAN接收到满足硬件ID Filter的报文就会将其存储在RX_FIFO或者RX_Buffer中。每一个RX_Buffer可以指定一个硬件ID Filter,而RX_FIFO则可以同时指定多个硬件Filter。MCAN接收到报文后优先匹配FIFO的Filter再匹配RX_Buffer的Filter。
MCAN的ID Filter支持三种模式分别是:
除了ID Filter,还有一个全局Filter的概念,全局Filter所限制的则是MCAN是否接收远程帧。如果总线上有远程帧,则直接拒绝接收报文。另外还有一个扩展帧的全局MASK,也就是说如果接收的帧类型是扩展帧,那么会先将该扩展帧的ID与这个全局MASK作与操作,然后接下来再与ID Filter进行匹配。
另外RX_FIFO有两种工作模式,分别是Blocking Mode和Overwrite Mode,它所决定的就是当FIFO满了之后,当新的一条报文来了之后模式选择拒收还是覆盖原有报文。
注意:RX_FIFO有一个非要重要的事情就是FIFO Acknowledge Handling。MCAN的FIFO其实是一个简化版的软件FIFO,它是不支持自己进行数据移位,是需要通过一个硬件层面上的put index和get index进行数据读取和写入的,在写入的时候,RX_FIFO会自动更新put index,但是在读取的时候,需要软件对FIFO进行read ack,然后CAN Core进行FIFO状态更新,所以软件必须确保FIFO Acknowledge的操作被正确执行。RX_FIFO状态更新示意图如下:
另外FIFO对于CPU来说只是一段内存,CPU是可以随意访问FIFO的,所以可以直接去FIFO里面读,不按FIFO的顺序读,这种方式下当然就不需要维护FIFO的get index了,但是不推荐这种方法。
对于Tx的处理机制就比较复杂了,MCAN最大支持32个Tx Buffer,然后又可将部分或者全部邮箱配置成FIFO模式(或者Queue模式,,二者只能同时存在一个,唯一的区别就是两个模式下发送报文的优先级不同,FIFO模式为邮箱号小的优先级高,而Queue模式则是根据ID仲裁优先级。而普通的Tx Buffer和配置成FIFO,Queue模式的区别就是Tx Buffer的发送流程完全是通过软件来实现的,而FIFO,Queue模式会有硬件的干预,具体有什么不同我也不知道,总之不推荐混合用就行了,个人觉得怎么简单怎么来,要不就全部配置成Queue模式,要不全部配置成FIFO模式。如果非要混合用,可以参考RM手册中对Tx Handling的具体描述。
Tx Handling这一块还有一个有趣的功能叫做Tx Event Handling,如果使能了这个功能后,在成功发送一帧报文后,那么MCAN会自动将发送成功的报文信息存储到Tx Event FIFO中,具体信息如上文中Tx Event FIFO Element 的结构所示。
对于SDK的应用就比较简单了,参考一下TI官网的SDK里面的demo,很容易就能把代码整出来,在这里提一下需要注意的两点就是波特率的计算以及CAN ID的处理。
对于波特率的计算,TI的SDK设计的初始化结构体如下:
typedef struct
{
uint32_t nomRatePrescalar;
/**< Nominal Baud Rate Pre-scaler
* Range:[0x0-0x1FF]
*/
uint32_t nomTimeSeg1;
/**< Nominal Time segment before sample point
* Range:[0x0-0xFF]
*/
uint32_t nomTimeSeg2;
/**< Nominal Time segment after sample point
* Range:[0x0-0x7F]
*/
uint32_t nomSynchJumpWidth;
/**< Nominal (Re)Synchronization Jump Width
* Range:[0x0-0x7F]
*/
uint32_t dataRatePrescalar;
/**< Data Baud Rate Pre-scaler
* Range:[0x0-0x1F]
*/
uint32_t dataTimeSeg1;
/**< Data Time segment before sample point
* Range:[0x0-0x1F]
*/
uint32_t dataTimeSeg2;
/**< Data Time segment after sample point
* Range:[0x0-0xF]
*/
uint32_t dataSynchJumpWidth;
/**< Data (Re)Synchronization Jump Width
* Range:[0x0-0xF]
*/
}MCAN_BitTimingParams;
仲裁段和数据段的计算方式一致,计算波特率的公式如下:
C a n B u a d = S o u r c e C l k / ( R a t e P r e s c a l a r + 1 ) / ( T i m e S e g 1 + T i m e S e g 2 + 3 ) CanBuad = SourceClk / (RatePrescalar + 1) / (TimeSeg1 + TimeSeg2 + 3) CanBuad=SourceClk/(RatePrescalar+1)/(TimeSeg1+TimeSeg2+3)
首先SourceClk就是Can模块的时钟源,这个值在SDK中是通过syscfg工具自动生成的,可以通过查看时钟配置结构体去获取,如下:
这个时候我们再去适当的配置一下RatePrescalar,TimeSeg1和TimeSeg2的值就可以得到自己想要的波特率。采样点的计算就非常简单了,计算方式如下:
S a m p l e P o i n t = 1 − ( T i m e S e g 2 + 1 ) / ( T i m e S e g 1 + T i m e S e g 2 + 3 ) SamplePoint = 1 - (TimeSeg2 + 1) / (TimeSeg1 + TimeSeg2 + 3) SamplePoint=1−(TimeSeg2+1)/(TimeSeg1+TimeSeg2+3)
还有一点需要注意的地方就是标准帧ID的处理,因为在MCAN的RAM空间映射中,标准帧ID和扩展帧ID存储的位置有一点区别,详情请看RM手册中这个描述,如下:
对于标准帧ID的存储位置是在ID区域的高11bit,所以我们在对报文配置结构体MCAN_TxBufElement进行赋值的时候,如果是标准帧ID,则需要将ID左移18bit后再复制给结构体(对于读取报文时也是一样,需要对ID进行右移),操作如下:
if(txMsg->xtd == TRUE)
{
txMsg->id = canId;
}
else
{
/* Standard message identifier 11 bit, stored into ID[28-18] */
txMsg->id = canId << 18;
}
对于MCAL的配置就非常非常简单了,TI的mcal并没有把MCAN的功能全部发挥出来,所以它所提供的配置接口非常的简单,对于FIFO以及Filter等很多复杂的功能都直接在代码中定死了,所以对MCAL的配置也是非常非常的简单,在这里就不多说什么了,参照TI给的mcal例子就可以随便搞出来了。