ECAN远程帧通信程序解读

来源:本站
导读:目前正在解读《ECAN远程帧通信程序解读》的相关信息,《ECAN远程帧通信程序解读》是由用户自行发布的知识型内容!下面请观看由(电工技术网 - www.9ddd.net)用户发布《ECAN远程帧通信程序解读》的详细说明。
简介:主函数操作流程:
时钟设置
清中断标志
ECAN1初始化,DMA0,2初始化,使能中断
ECAN2初始化,DMA1初始化 ,使能中断
ECAN2写信息,ECAN1写信息,发送请求
主循环为空,等待中断

主函数操作流程:

时钟设置 清中断标志 ECAN1初始化,DMA0,2初始化,使能中断 ECAN2初始化,DMA1初始化 ,使能中断 ECAN2写信息,ECAN1写信息,发送请求 主循环为空,等待中断

ECAN1初始化

void ecan1Init(void){

C1CTRL1bits.REQOP=4;

while(C1CTRL1bits.OPMODE!=4); //进入配置模式

ecan1ClkInit(); //时钟初始化,配置TQ

C1FCTRLbits.FSA=0b10000;//设置FIFO从buffer16开始

C1FCTRLbits.DMABS=0b000;//DMA中开辟32个BUFFER

//FILTER设置

//下面FILTER的设置参数含义为:设置FILTER1,ID为0X1FFFFFFF,拓展模式,指向缓存1,MASK选择0

ecan1WriteRxAcptFilter(1,0x1FFEFFFF,1,1,0);

//MASK设置

//下面MASK的设置参数含义为:设置MASK1,所有的位都要比较,模式看后面,模式为拓展模式

ecan1WriteRxAcptMask(1,0x1FFFFFFF,1,1);

//设置完FILTER和MASK,回到常规模式

C1CTRL1bits.REQOP=0;

while(C1CTRL1bits.OPMODE!=0);

//定义BUFFER0和BUFFER1的方向,以及优先级

C1RXFUL1=C1RXFUL2=C1RXOVF1=C1RXOVF2=0x0000;

C1TR01CONbits.TXEN0=1; /* ECAN1, Buffer 0 is a Transmit Buffer */

C1TR01CONbits.TXEN1=0; /* ECAN1, Buffer 1 is a Receive Buffer */

C1TR01CONbits.TX0PRI=0b11; /* Message Buffer 0 Priority Level */

C1TR01CONbits.TX1PRI=0b11; /* Message Buffer 1 Priority Level */

}

ECAN2初始化

void ecan2Init(void){

C2CTRL1bits.REQOP=4; //进入配置模式

while(C2CTRL1bits.OPMODE!=4);

ecan2ClkInit(); //配置时钟TQ

C2FCTRLbits.FSA=0b10000;//FIFO首地址

C2FCTRLbits.DMABS=0b000; //缓存数量

//下面FILTER的设置参数含义为:设置FILTER1,ID为0X1FFFFFFF,拓展模式,指向缓存0,MASK选择0

ecan2WriteRxAcptFilter(1,0x1FFEFFFF,1,0,0);

//下面MASK的设置参数含义为:设置MASK1,所有的位都要比较,模式看后面,模式为拓展模式

ecan2WriteRxAcptMask(1,0x1FFFFFFF,1,1);

C2CTRL1bits.REQOP=0;

while(C2CTRL1bits.OPMODE!=0);//进入常规模式

C2RXFUL1=C2RXFUL2=C2RXOVF1=C2RXOVF2=0x0000;

C2TR01CONbits.TXEN0=1; //ECAN2发送

C2TR01CONbits.TX0PRI=0b11; //优先级设置

}

DMA初始化

void dma0init(void){

DMACS0=0; //冲突检测清零

DMA0CON=0x2020; //方向为发送,外设直接地址

DMA0PAD=0x0442; /发送地址

DMA0CNT=0x0007; //发送计数

DMA0REQ=0x0046; //写入请求好

DMA0STA= __builtin_dmaoffset(ecan1msgBuf); //地址指向

DMA0CONbits.CHEN=1; //使能通道0

}

void dma1init(void){

DMA1CON=0x2020; //方向为发送,外设直接地址

DMA1PAD=(int)&C2TXD; //C2TXD为发送数据寄存器

DMA1CNT=0x0007; //发送计数

DMA1REQ=0x0047; //请求号

DMA1STA= __builtin_dmaoffset(ecan2msgBuf); //DPRAM偏置,地址指向

DMA1CONbits.CHEN=1; //使能CH1

}

void dma2init(void){

DMACS0=0; //冲突检测清零

DMA2CON=0x0020; //接收,外设直接地址

DMA2PAD=0x0440; //接收地址

DMA2CNT=0x0007; //接收计数

DMA2REQ=0x0022; //请求号

DMA2STA= __builtin_dmaoffset(ecan1msgBuf); //地址指向

DMA2CONbits.CHEN=1; //使能通道2

}

void dma3init(void){

DMACS0=0;

DMA3CON=0x0020;

DMA3PAD=(int)&C2RXD; /* ECAN 2 (C2RXD) */

DMA3CNT=0x0007;

DMA3REQ=0x0037; /* ECAN 2 Receive */

DMA3STA=__builtin_dmaoffset(ecan2msgBuf);

DMA3CONbits.CHEN=1;

}

ECAN1写消息

void ecan1WriteMessage(void){

ecan1WriteTxMsgBufId(0,0x1FFEFFFF,1,1);

ecan1WriteTxMsgBufData(0,8,0,0,0,0);

}

//第一个函数:第一个参数,BUFFER号,第二个参数ID号,第三个,拓展ID标志,第四个,是否是远程帧

//上面的调用,写到缓存0,ID号是0X1FFFFFFF,是拓展帧,是远程帧

void ecan1WriteTxMsgBufId(unsigned int buf, long txIdentifier, unsigned int ide, unsigned int remoteTransmit){

unsigned long word0=0, word1=0, word2=0;

unsigned long sid10_0=0, eid5_0=0, eid17_6=0,a;

eid5_0 = (txIdentifier & 0x3F); //取得ID号

eid17_6 = (txIdentifier>>6) & 0xFFF;

sid10_0 = (txIdentifier>>18) & 0x7FF;

word1 = eid17_6;

if(remoteTransmit==1) { // 如果是远程帧,将远程帧的标志写入到WORD0和WORD2中

word0 = ((sid10_0 << 2) | ide | 0x2);

word2 = ((eid5_0 << 10)| 0x0200);}

else {

word0 = ((sid10_0 << 2) | ide);

word2 = (eid5_0 << 10);

}

ecan1msgBuf[buf][0] = word0;//将合成的WORD写入到BUFFER中

ecan1msgBuf[buf][1] = word1;

ecan1msgBuf[buf][2] = word2;

}

如果是数据帧,则继续写入数据,如果是远程帧,也可以写入一些无效数据,比如:

ecan1WriteTxMsgBufId(0,0x1FFEFFFF,1,1);

ecan1WriteTxMsgBufData(0,8,0,0,0,0);:

void ecan1WriteTxMsgBufData(unsigned int buf, unsigned int dataLength, unsigned int data1, unsigned int data2, unsigned int data3, unsigned int data4){

ecan1msgBuf[buf][2] = ((ecan1msgBuf[buf][2] & 0xFFF0) + dataLength) ;

ecan1msgBuf[buf][3] = data1;

ecan1msgBuf[buf][4] = data2;

ecan1msgBuf[buf][5] = data3;

ecan1msgBuf[buf][6] = data4;

}

中断函数的处理

上面的程序中,中断有6个,两个为ECAN1和ECAN2的中断,另外四个为DMA0~DMA3的四个中断

ECAN1和ECAN2的中断处理是一样的,具体接收子程序不同。

void __attribute__((interrupt, no_auto_psv))_C1Interrupt(void)

{

IFS2bits.C1IF = 0; //清零ECAN1中断标志

if(C1INTFbits.TBIF)

{

C1INTFbits.TBIF = 0; //发送中断?清空就行

}

if(C1INTFbits.RBIF)

{

if(C1RXFUL1bits.RXFUL1==1) //接收?则需要接收程序

{

rx_ecan1message.buffer=1; //设置一个缓存中有数据的标志

C1RXFUL1bits.RXFUL1=0; //清零接收中断标志

}

rxECAN1(&rx_ecan1message); //缓存有数据,则开始接收,然后清零标志

C1INTFbits.RBIF = 0;

}

}

ECAN1的接收程序:

void rxECAN1(mID *message)

{

unsigned int ide=0;

unsigned int srr=0;

unsigned long id=0,d;

ide=ecan1msgBuf[message->buffer][0] & 0x0001; //将收到的帧的IDE和SRR读出来

srr=ecan1msgBuf[message->buffer][0] & 0x0002; //

if(ide==0)

{

message->id=(ecan1msgBuf[message->buffer][0] & 0x1FFC) >> 2; //如果是标准帧,则将ID读出来,并且标记为标准帧

message->frame_type=CAN_FRAME_STD;

}

else //如果是扩展帧,将ID也读出来,放到BUFFER 0/1/2中

{

id=ecan1msgBuf[message->buffer][0] & 0x1FFC;

message->id=id << 16;

id=ecan1msgBuf[message->buffer][1] & 0x0FFF;

message->id=message->id+(id << 6);

id=(ecan1msgBuf[message->buffer][2] & 0xFC00) >> 10;

message->id=message->id+id;

message->frame_type=CAN_FRAME_EXT;

}

//判断是不是远程帧,如果是远程帧,则将远程帧标志置位

if(srr==1)

{

message->message_type=CAN_MSG_RTR;

}

//如果为常规数据帧,则先标志帧类型为数据帧,然后将数据读入DMA的缓存,并且将数据长度也读入到BUFFER2中。

else

{

message->message_type=CAN_MSG_DATA;

message->data[0]=(unsigned char)ecan1msgBuf[message->buffer][3];

message->data[1]=(unsigned char)((ecan1msgBuf[message->buffer][3] & 0xFF00) >> 8);

message->data[2]=(unsigned char)ecan1msgBuf[message->buffer][4];

message->data[3]=(unsigned char)((ecan1msgBuf[message->buffer][4] & 0xFF00) >> 8);

message->data[4]=(unsigned char)ecan1msgBuf[message->buffer][5];

message->data[5]=(unsigned char)((ecan1msgBuf[message->buffer][5] & 0xFF00) >> 8);

message->data[6]=(unsigned char)ecan1msgBuf[message->buffer][6];

message->data[7]=(unsigned char)((ecan1msgBuf[message->buffer][6] & 0xFF00) >> 8);

message->data_length=(unsigned char)(ecan1msgBuf[message->buffer][2] & 0x000F);

}

}

四个DMA的中断程序都很简单,就是单纯的清标志。

发送ECAN数据:

void sendECAN(mID *message)

{

unsigned long word0=0; //合成帧的前三个字

unsigned long word1=0;

unsigned long word2=0;

//判断帧类型,是扩展帧,则前三个字定义不同,本例中为扩展帧格式

if(message->frame_type==CAN_FRAME_EXT)

{

word0=(message->id & 0x1FFC0000) >> 16; //取帧的最高11位,作为第一个字,右移16位,放到word1

word0=word0+0x0003; //这里是拓展帧,SRR和IDE都设置为1

word1=(message->id & 0x0003FFC0) >> 6; //取得6~17位一共12位

word2=(message->id & 0x0000003F) << 10; //取最后6位,放到字头

}

else

{

word0=((message->id & 0x000007FF) << 2); //这里是标准,SRR和IDE都设置为0

}

if(message->message_type==CAN_MSG_RTR)

{

if(message->frame_type==CAN_FRAME_EXT) //根据帧类型,将远程帧的位置设置为1,表示发送的是远程帧

word2=word2 | 0x0200;

else

word0=word0 | 0x0002;

//远程帧不需要跟数据,所以只需要发送头三个字就行了。FILTER<4:0>这5个位寄存器自动填充。

ecan1msgBuf[message->buffer][0]=word0;

ecan1msgBuf[message->buffer][1]=word1;

ecan1msgBuf[message->buffer][2]=word2;

}

else

{

word2=word2+(message->data_length & 0x0F); //如果是数据帧,先将数据长度在WORD2中写入。

ecan1msgBuf[message->buffer][0]=word0;

ecan1msgBuf[message->buffer][1]=word1;

ecan1msgBuf[message->buffer][2]=word2;

ecan1msgBuf[message->buffer][3]=((message->data[1] << 8) + message->data[0]); //继续填入数据域

ecan1msgBuf[message->buffer][4]=((message->data[3] << 8) + message->data[2]);

ecan1msgBuf[message->buffer][5]=((message->data[5] << 8) + message->data[4]);

ecan1msgBuf[message->buffer][6]=((message->data[7] << 8) + message->data[6]);

}

C1TR01CONbits.TXREQ0=1; //设置该位,请求发送一个帧。

}

在主循环中查找,如果状态为缓存满了,则开始读取。

读取的函数如下:

接收ECAN数据:

/******************************************************************************

*

* Function: rxECAN

* Description: moves the message from the DMA memory to RAM

*

* Arguments: *message: a pointer to the message structure in RAM

* that will store the message.

* Author: Jatinder Gharoo

*

*

******************************************************************************/

void rxECAN(mID *message)

{

unsigned int ide=0;

unsigned int rtr=0;

unsigned long id=0;

//先判断帧是标准的还是扩展帧,次位都定义在word0的最后一位的位置

ide=ecan1msgBuf[message->buffer][0] & 0x0001;

if(ide==0) //如果是标准帧,判断是远程帧还是数据帧

{

message->id=(ecan1msgBuf[message->buffer][0] & 0x1FFC) >> 2; //将ID读出来

message->frame_type=CAN_FRAME_STD; //为标准帧,将帧类型设置

rtr=ecan1msgBuf[message->buffer][0] & 0x0002; //将远程帧还是数据帧的标志读出来。

}

else

{

id=ecan1msgBuf[message->buffer][0] & 0x1FFC; //将11位读出来

message->id=id << 16; //放到ID号的头。

id=ecan1msgBuf[message->buffer][1] & 0x0FFF;//读WORD1,

message->id=message->id+(id << 6); //和刚刚读出来的组在一起

id=(ecan1msgBuf[message->buffer][2] & 0xFC00) >> 10; //继续读WORD2中的ID

message->id=message->id+id; //获取到完整的ID

message->frame_type=CAN_FRAME_EXT; //设置读取到的帧为拓展帧

rtr=ecan1msgBuf[message->buffer][2] & 0x0200;//将远程的标志设置好

}

if(rtr==1) //如果是远程帧,设置结构体消息类型为远程帧类型。

{

message->message_type=CAN_MSG_RTR;

}

else//如果是数据帧,则开始读数据

{

message->message_type=CAN_MSG_DATA; //类型设置为数据帧

message->data[0]=(unsigned char)ecan1msgBuf[message->buffer][3];//开始读数据,从BYTE0开始

message->data[1]=(unsigned char)((ecan1msgBuf[message->buffer][3] & 0xFF00) >> 8); //读高8位,作为BYTE1,保存到结构体数据存储1位置。

message->data[2]=(unsigned char)ecan1msgBuf[message->buffer][4];

message->data[3]=(unsigned char)((ecan1msgBuf[message->buffer][4] & 0xFF00) >> 8);

message->data[4]=(unsigned char)ecan1msgBuf[message->buffer][5];

message->data[5]=(unsigned char)((ecan1msgBuf[message->buffer][5] & 0xFF00) >> 8);

message->data[6]=(unsigned char)ecan1msgBuf[message->buffer][6];

message->data[7]=(unsigned char)((ecan1msgBuf[message->buffer][6] & 0xFF00) >> 8);

message->data_length=(unsigned char)(ecan1msgBuf[message->buffer][2] & 0x000F); //获取数据长度,继续保存到结构体中

}

clearRxFlags(message->buffer); //按照BUFFER号,以及在接收时硬件设置的BUFFER满标志读取,如果是BUFFER1,则把1清空。这里一共用到三个BUFFER,进行三次判断。

}

然后将收到的数据在液晶上显示出来。

ECAN的中断服务程序

void __attribute__((interrupt,no_auto_psv))_C1Interrupt(void)

{

if(C1INTFbits.RBIF) //中断来自已经收到一个存到BUFFER中的数据。

{

if(C1RXFUL1bits.RXFUL1) //如果传送来的数据保存在BUFFER1中

{

canRxMessage.buffer_status=CAN_BUF_FULL; //将这个状态设置给结构体,主程序中将通过这个标志来读BUFFER到RAM中。

canRxMessage.buffer=1; //告诉主程序BUFFER满还不行,还要告诉主程序读取的路径,为BUFFER1

}

else if(C1RXFUL1bits.RXFUL2)

{

canRxMessage.buffer_status=CAN_BUF_FULL;

canRxMessage.buffer=2;

}

else if(C1RXFUL1bits.RXFUL3)

{

canRxMessage.buffer_status=CAN_BUF_FULL;

canRxMessage.buffer=3;

}

else;

C1INTFbits.RBIF = 0; //接收标志要清空

}

else if(C1INTFbits.TBIF) //如果是发送数据产生的中断

{

puts_ecanTx(&canTxMessage); //将发送的数据在液晶上显示出来

C1INTFbits.TBIF = 0; //发送标志清空

}

else;

IFS2bits.C1IF=0; //同时要将ECAN中断标志情况。

}

问题点:

用到了DMA么?这里可以看到,在中断服务程序中只设置了标志,帧是读入到了DMA中,通知主程序在一次大循环后来读出DMA的值。

收到的数据没有MASK和FILTER?:在ECAN初始化函数中已经设置了MASK和FILTER,本例子中,标准模式,每位都需要比较,FILTER的ID是0x123,ID数据不对,则不会将数据读进来,这个是经过硬件处理的,不需要人工干预。

提醒:《ECAN远程帧通信程序解读》最后刷新时间 2024-03-14 01:10:29,本站为公益型个人网站,仅供个人学习和记录信息,不进行任何商业性质的盈利。如果内容、图片资源失效或内容涉及侵权,请反馈至,我们会及时处理。本站只保证内容的可读性,无法保证真实性,《ECAN远程帧通信程序解读》该内容的真实性请自行鉴别。