1、CAN模块结构
5744CAN模块主要由CHI(Control Host Interface)、PE(Protocol Engine)和MBs三个部分组成,其中CHI负责发送仲裁和接收匹配,PE负责时钟等的配置,MBs进行消息的存储。结构如下图所示:
2、MB(Message Buffer)结构
5744含有多达64个MB(Mailbox)进行数据的收发,通过配置对应MB的C/S寄存器来进行数据发送或接收。其结构如下:
-
CODE 缓存器码,是进行缓存器匹配和仲裁过程的一部分,编码如下所示:
0b0000 MB用于接收且未激活
0b0100 MB用于发送、激活并且为空,准备接收数据
0b0010 MB用于接收且为满
0b0110 MB用于接收且为OVERRUN,并且被overwrite
0b1010 MB接收到一个远程帧数据并发送一帧数据作为回应
0bxxx1 MB用于接收且正在更新MB的内容,此时CPU禁止范围MB。
0b1000 MB为用于发送且未激活
0b1001 MB用于发送且被中止
0b1100 MB用于发送,当RTR为0时内部消息为数据帧,当 RTR为1时,内部消息为远程帧
0b1110 MB用于发送,为进入的远程请求帧发送一帧数据作为回应 -
SRR 替代远程请求 固定为隐形,该位发送时必须被设置为1,若接收到时不为1,则仲裁丢失
-
IDE ID标识位 1:MB内为扩展帧 0:MB内为标准帧
-
RTR 远程传输请求 1 :当前MB有一个远程请求帧要传输 0:当前MB有一个数据帧要传输
-
DLC 当前MB内数据的字节长度
-
TIMESTAMP 自由运行时间戳
-
PRIO 本地优先级,使能情况下,对于发送信箱有意义,参与发送仲裁过程
-
ID 帧ID
-
DATA BYTE0-7 数据域
3、模块配置
1、波特率设置
CAN时钟可选择外设时钟或晶振时钟作为时钟源,再时钟源选定以后可进行波特率的配置。公式如下:
其中Time Quanta的组成如下:
则可通过配置PRESDIV、PSEG1、PSEG2、PROPSEG的值配置不同的波特率,综合上述两个式子波特率计算公式为:
Bit Rate=
fcanclk/([1 + (PSEG1 + 1) + (PSEG2 + 1) + (PROPSEG + 1)] x (PRESDIV + 1))
其中各个值的范围和彼此的限制关系如下:
由于CAN模块具有较多的MB,在进行发送仲裁的时候需要耗费一定的时间,为了给发送仲裁留充足的时间,外设时钟(控制CHI的时钟)和比特率需要保证一定的比率,也就是在处理每CAN bit的外设时钟数要足够大,计算公式如下:
fsys为系统时钟,即是外设桥时钟,NCCP就是每CAN bit 耗费的外设时钟数,随着采用MB的数目的增加应调整NCCP的值,对应关系如下:
2、CAN模块配置过程
1)多路复用功能选择
2)运行模式选择
3)进行时钟源的选择(需要先失能CAN模块然后再打开);
4)使能Frzee模式并进入Frzee模式以进行波特率配置;
5)进行波特率的配置
6)设置发送MB
7)设置接收MB(使能对应接收中断并设置中断优先级)
8)退出Frzee模式并确认
3、示例代码
/******************************************
* 名称 CAN_CAN0_Init
* 说明 进行CAN0的初始化
* 输入参数 无
* 返回值 无
* 示例 CAN_CAN0_Init();//进行CAN0的初始化
******************************************
*/
void CAN_CAN0_Init()
{
uint8_t i=0;
/*管脚复用设置*/
SIUL2.MSCR[16].B.SSS=1; //PB0复用为CAN0_TX
SIUL2.MSCR[16].B.OBE=1;
SIUL2.MSCR[16].B.SRC=3;
SIUL2.MSCR[17].B.IBE=1; //PB1复用为CAN0_RX
SIUL2.IMCR[32].B.SSS=2;
MC_ME.PCTL79.B.RUN_CFG=0; //选择运行模式0
CAN_0.MCR.B.MDIS=1; //关闭CAN模块
CAN_0.CTRL1.B.CLKSRC=0; //选择晶振作为CAN时钟源
CAN_0.MCR.B.MDIS=0; //开启CAN模块
CAN_0.MCR.B.FRZ=1; //允许进入Frzee 模式
CAN_0.MCR.B.HALT=1; //请求进入Frzee 模式
while(!CAN_0.MCR.B.FRZACK);//确认进入frzee 模式
CAN_0.CTRL1.B.PRESDIV=9; //bit rate=fcanclk/([1+(PSEG1+1)+(PSEG2+1)+(PROPSEG+1)]x(PRESDIV+1))
CAN_0.CTRL1.B.PSEG1=3; //=40MHz/((1+4+4+7)*10)=1/4MHz=250kHz
CAN_0.CTRL1.B.PSEG2=3;
CAN_0.CTRL1.B.PROPSEG=6;
CAN_0.CTRL1.B.RJW=3; //配置 Resync Jump Width为3+1=4
for(i=0;i<64;i++) //复位MBs为失活状态
{
CAN_0.MB[0].CS.B.CODE=0;
}
CAN_0.MB[0].CS.B.CODE=8; //配置MB0位发送激活状态
CAN_0.MB[5].CS.B.IDE=1; //配置MB5接收扩展帧
CAN_0.MB[5].ID.R=0x18FF9400;//配置要接收的CAN帧IID,需要根据要接收的帧ID确定
CAN_0.MB[5].CS.B.CODE=4; //配置MB5为接收激活状态
CAN_0.IMASK1.R = 0x00000020;//使能MB5接收和发送中断
INTC_0.PSR[523].R=0x800B; //配置MB5中断优先级
CAN_0.MCR.B.MAXMB=63; //选择最大可以数据缓存为默认,默认为63
CAN_0.MCR.B.HALT=0; //退出 Frzee模式
while(CAN_0.MCR.B.FRZACK & CAN_0.MCR.B.NOTRDY); //等待退出Frzee模式
}
4、传输过程
1、发送过程
1)检测对应标志位是否被置位并清除;
2)如果要使用的MB为激活状态,则写入中止码(0b1001)中止传输过程(或直接写入失活码(0b1000)失活对应MB);
3)写入ID、数据字节、数据长度,设置SRR、RTR、IDE等;
4)写入激活码激活MB参与发送仲裁进行传输。
2、示例代码
/*******************************************************
* 名称 CAN_CAN0_Send
* 说明 采用CAN0发送指定数据
* 输入参数
* can_msg
* 要发送的CAN信息
* 返回值 无
* 示例 CAN_CAN0_Send(can_msg);//发送CAN信息can_msg
*******************************************************
*/
void CAN_CAN0_Send(const CAN_Msg_Struct can_msg)
{
uint8_t i=0;
static uint32_t counter=0;
CAN_0.MB[0].CS.B.CODE=0b1000; //失活MB0
CAN_0.MB[0].ID.R=can_msg.can_msg_id; //设置ID
for(i=0;i<8;i++)
CAN_0.MB[0].DATA.B[i]=can_msg.can_msg_data[i];//设置数据
CAN_0.MB[0].CS.B.DLC=can_msg.can_msg_len; //设置数据长度
CAN_0.MB[0].CS.B.SRR=1; //设置SRR 强制为1
CAN_0.MB[0].CS.B.IDE=1; //发送帧为扩展帧
CAN_0.MB[0].CS.B.RTR=0; //非请求帧
CAN_0.MB[0].CS.B.CODE=0b1100; //设置MB0为发送激活 参与发送仲裁进行发送
}
3、接收过程
接收过程一般采用中断,本文只介绍CAN接收过程,具体中断配置参见PIT模块的介绍。
1)为保证数据完整新,先读取MB的C/S寄存器上锁MB;
2)读取ID、数据长度、数据等;
3)读取自由运行定时器解锁MB;
4)清零对应标志位;
4、示例代码
/***************************************
* 函数名 CAN_CAN0_Rx4_7_Isr
* 功能 CAN_0 MB4-7中断接收函数
* 输入参数 无
* 返回值 无
***************************************
*/
void CAN_CAN0_Rx4_7_Isr()
{
uint8_t i=0;
uint32_t dummy=0;
dummy=CAN_0.MB[5].CS.R; //上锁MB5
GL_Can_Msg.can_msg_id=CAN_0.MB[5].ID.R; //读取ID
GL_Can_Msg.can_msg_len=CAN_0.MB[5].CS.B.DLC; //读取数据长度
for(i=0;i<GL_Can_Msg.can_msg_len;i++) //读取数据
{
GL_Can_Msg.can_msg_data[i]=CAN_0.MB[5].DATA.B[i];
}
dummy=CAN_0.TIMER.R; //解锁MB5
//GPIO_ToggleBit(GPIO_PORTC,GPIO_PIN11);
CAN_0.IFLAG1.B.BUF5I= 1;
}