时钟设置
清中断标志
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数据不对,则不会将数据读进来,这个是经过硬件处理的,不需要人工干预。