打印
[学习资料]

PIC32MZ0512EFF相关的CAN配置

[复制链接]
1912|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
观海|  楼主 | 2021-8-2 16:20 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
在使用芯片PIC32MZ0512EFF配置CAN初始化的过程中,我遇到了一个问题:这个芯片的CAN部分没有直接供我们使用的发送寄存器和接收寄存器。

下面会描述一下我的解决方法,并说说我的理解。

直接上干货:这里把对CAN2的初始化的全部过程都写出来。每个步骤在后文都会有详细的代码示例。

void CAN2_Init(void)
{
        CAN2_GPIOPPSConfig();
        //第一步:对CAN2进行引脚分配(PPS映射),以及设置相应的输入输出
        C2CONSET = _C2CON_ON_MASK;
        //第二步:启用can2模块,也可以写成C2CONbits.ON = 1;
        CAN2_ConfigurationMode();
        //第三步:顾名思义,就是进入CAN2的配置模式
        CAN2_BaudrateInit();
        //第四步:配置CAN2的波特率
        C2FIFOBA = 0x0000100;
        //第五步:设置FIFO起始地址
        CAN2_FIFOn();
        //第六步:设置FIFO,包括需要用到的FIFO数量以及每个FIFO相应的功能和容量大小
        CAN2_FilterMaskInit();
        //第七步:设置过滤器和屏蔽器
        CAN2_LoopMode();
        //第八步:进入CAN2的自检模式(环回模式),当然如果相应引脚与CAN收发器连接了,可以进入正常模式
}


注:后面按顺序分4个部分展示第一步,第三步和第八步,第四步,第五六七步的代码和我的理解

使用特权

评论回复
沙发
观海|  楼主 | 2021-8-2 16:21 | 只看该作者
#第一部分

void CAN2_GPIOPPSConfig(void)
{
        SYSKEY = 0x00000000;
    SYSKEY = 0xAA996655;
    SYSKEY = 0x556699AA;
    CFGCONbits.IOLOCK = 0;
    //解锁序列
    //在芯片手册里有说到:正常工作状态下,对PPS管脚映射功能配置失效。要想使用,需要执行一个解锁序列,然后把IOLOCK清零
    C2RXR = 4;
    //把RD4设为C2RX
    RPD5R = 15;
    //把RD5设为C2TX
    CFGCONbits.IOLOCK = 1;
    SYSKEY = 0x00000000;
    //设完PPS后把管脚映射锁置1,把序列清零
    TRISDSET = _PORTD_RD4_MASK;
    //可以写成TRISDbits.TRISD4 = 1;
    TRISDCLR = _PORTD_RD5_MASK;
    //可以写成TRISDbits.TRISD5 = 0;
}




注:对解锁序列的理解不深,这里就不发表看法了。但还是说一下我实际操作中的现象:

1、我在没有执行解锁序列的情况下也能使用管脚映射功能(这应该和我的配置位设置内容有关联)
2、如果之后要设置为环回模式,请确保所要使用的引脚悬空


使用特权

评论回复
板凳
观海|  楼主 | 2021-8-2 16:29 | 只看该作者
#第二部分

void CAN2_ConfigurationMode(void)
{
    C2CONbits.REQOP = 4;//配置为配置模式
    while(C2CONbits.OPMOD!=4);//等待配置模式配置完毕
}
void CAN2_LoopMode(void)
{
    C2CONbits.REQOP = 2;//配置为环回模式
    while(C2CONbits.OPMOD!=2);//等待环回模式配置完毕
}
void CAN2_NormalMode(void)
{
    C2CONbits.REQOP = 0;//配置为正常模式
    while(C2CONbits.OPMOD!=0);//等待正常模式配置完毕
}



注:环回模式可以在不接外部芯片(比如CAN收发芯片MCP2551)的情况下测试程序


使用特权

评论回复
地板
观海|  楼主 | 2021-8-2 16:30 | 只看该作者
#第三部分

void CAN2_BaudrateInit(void)
{
    //500Mbps
    C2CFGbits.BRP = 9;//波特率预分频
    C2CFGbits.SJW = 0;//同步跳转宽度
    C2CFGbits.PRSEG = 0;//传播时间段(Prop_Seg)
    C2CFGbits.SEG1PH = 3;//相位缓冲段1(Phase_Seg1)
    C2CFGbits.SAM = 0;//在采样点采样一次
    C2CFGbits.SEG2PHTS = 1;//表示相位缓冲段2可自由编程
    C2CFGbits.SEG2PH = 3;//相位缓冲段2(Phase_Seg2)
}

/*波特率计算公式:
*                                      PBCLK5
*              baud = ----------------------------------------------  
*                     (Prop_Seg+Phase_Seg1+Phase_Seg2+SJW)*2*(BRP+1)
*/
//我用的外部晶振,在进行倍频处理后为200MHz,PBCLK5为CAN的外设时钟,外设时钟默认分频2,所以PBCLK5 = 100MHz


使用特权

评论回复
5
观海|  楼主 | 2021-8-2 16:31 | 只看该作者
#第四部分

void CAN2_FIFOn(void)
{
    C2FIFOCON0bits.FSIZE = 31;//FIFO0深度为32个缓冲区
    C2FIFOCON0bits.TXEN = 1;//FIFO0配置为发送区


    C2FIFOCON1bits.FSIZE = 0;//FIFO1深度为1个缓冲区
    C2FIFOCON1bits.DONLY = 0;
    C2FIFOCON1bits.TXEN = 0;//FIFO1配置为接收区


    C2FIFOCON2bits.FSIZE = 0;//FIFO2深度为1个缓冲区
    C2FIFOCON2bits.DONLY = 0;
    C2FIFOCON2bits.TXEN = 0;//FIFO2配置为接收区


    C2FIFOCON3bits.FSIZE = 0;//FIFO3深度为1个缓冲区
    C2FIFOCON3bits.DONLY = 0;
    C2FIFOCON3bits.TXEN = 0;//FIFO3配置为接收区


    C2FIFOCON4bits.FSIZE = 0;//FIFO4深度为1个缓冲区
    C2FIFOCON4bits.DONLY = 0;
    C2FIFOCON4bits.TXEN = 0;//FIFO4配置为接收区
}
//这里解释一下FIFO的机制:就我所使用的芯片,拥有2个CAN区域,每个CAN拥有32个FIFO,每个FIFO拥有1-32个可配置的数据缓冲区,每个数据缓冲区
//可容纳4*32个bit或是1个报文(包括ID和数据)。每个FIFO都可以配置为发送区或是接收区,且FIFO紧密排列。
//例如上面的程序:CAN2的FIFO排列如下
//FIFO0   发送区   可以容纳32个报文  物理地址0X00000100
//FIFO1   接收区   可以容纳1 个报文  物理地址0X00000300
//FIFO2   接收区   可以容纳1 个报文  物理地址0X00000310
//FIFO3   接收区   可以容纳1 个报文  物理地址0X00000320
//FIFO4   接收区   可以容纳1 个报文  物理地址0X00000330
//FIFO5   未分配   可以容纳1 个报文  物理地址0X00000340
//FIFO6   未分配   可以容纳1 个报文  物理地址0X00000350
//........
//FIFO31  未分配   可以容纳1 个报文  物理地址0X000004F0

void CAN2_FilterMaskInit(void)
{
    C2FLTCON0bits.FSEL0 = 1;//接收到的与过滤器0匹配的数据存储在FIFO1中
    C2FLTCON0bits.MSEL0 = 1;//选择接收屏蔽器1
    C2RXF0bits.EID = 0x0;//设置过滤器0
    C2RXF0bits.SID = 0xE;
    C2RXF0bits.EXID = 1;


    C2FLTCON0bits.FSEL1 = 2;//接收到的与过滤器1匹配的数据存储在FIFO2中
    C2FLTCON0bits.MSEL1 = 1;//选择接收屏蔽器1
    C2RXF1bits.EID = 0x0;//设置过滤器1
    C2RXF1bits.SID = 0xA;
    C2RXF1bits.EXID = 1;


    C2FLTCON0bits.FSEL2 = 3;//接收到的与过滤器2匹配的数据存储在FIFO3中
    C2FLTCON0bits.MSEL2 = 1;//选择接收屏蔽器1
    C2RXF2bits.EID = 0x0;//设置过滤器2
    C2RXF2bits.SID = 0xC;
    C2RXF2bits.EXID = 1;


    C2FLTCON0bits.FSEL3 = 4;//接收到的与过滤器3匹配的数据存储在FIFO4中
    C2FLTCON0bits.MSEL3 = 1;//选择接收屏蔽器1
    C2RXF3bits.EID = 0x0;//设置过滤器3
    C2RXF3bits.SID = 0x8;
    C2RXF3bits.EXID = 1;


    C2RXM1bits.EID = 0x0;//设置屏蔽器1
    C2RXM1bits.SID = 0xF;
    C2RXM1bits.MIDE = 1;

    C2FLTCON0bits.FLTEN0 = 1;//启用过滤器
    C2FLTCON0bits.FLTEN1 = 1;
    C2FLTCON0bits.FLTEN2 = 1;
    C2FLTCON0bits.FLTEN3 = 1;
}


使用特权

评论回复
6
观海|  楼主 | 2021-8-2 16:32 | 只看该作者
以下为接收发送时需要用到的宏,这几个宏在XC32编译器手册中被提到

typedef unsigned long _paddr_t; //物理地址
typedef unsigned long _vaddr_t; //虚拟地址
#define KVA_TO_PA(v)         ((_paddr_t)(v) & 0x1fffffff)//将内核地址转换成物理地址
#define PA_TO_KVA0(pa)        ((void *) ((pa) | 0x80000000))//将物理地址转换成KSEG0虚拟地址
#define PA_TO_KVA1(pa)        ((void *) ((pa) | 0xa0000000))//将物理地址转换成KSEG1虚拟地址


先上图:



使用特权

评论回复
7
观海|  楼主 | 2021-8-2 16:34 | 只看该作者
在PIC32中CPU所使用的是左边的虚拟地址,而外设使用的是右边的物理地址。也即使说软件操作写入外设寄存器(左边),CPU会对其转换为右边的物理地址。手册中指出C2FIFOBA、C2FIFOUAn为物理地址。C2FIFOUAn为FIFOn的起始地址,在需要从FIFO中读取或写入数据时,需要将C2FIFOUAn转换为左边的虚拟地址后通过软件读取或写入。

typedef union
{
    struct
    {
        unsigned TXCMSGSID_SID:11;
        unsigned :21;
        unsigned TXCMSGEID_DLC:4;
        unsigned TXCMSGEID_RB0:1;
        unsigned :3;
        unsigned TXCMSGEID_RB1:1;
        unsigned TXCMSGEID_RTR:1;
        unsigned TXCMSGEID_EID:18;
        unsigned TXCMSGEID_IDE:1;
        unsigned TXCMSGEID_SRR:1;
        unsigned :2;
        unsigned TXCMSGDATA0_Byte0:8;
        unsigned TXCMSGDATA0_Byte1:8;
        unsigned TXCMSGDATA0_Byte2:8;
        unsigned TXCMSGDATA0_Byte3:8;
        unsigned TXCMSGDATA1_Byte4:8;
        unsigned TXCMSGDATA1_Byte5:8;
        unsigned TXCMSGDATA1_Byte6:8;
        unsigned TXCMSGDATA1_Byte7:8;
    };
    uint32_t TXMessage[4];
}CANTXBuffer;

typedef union
{
    struct
    {
        unsigned RXCMSGSID_SID:11;
        unsigned RXCMSGSID_FILHIT:5;
        unsigned RXCMSGSID_CMSGTS:16;
        unsigned RXCMSGEID_DLC:4;
        unsigned RXCMSGEID_RB0:1;
        unsigned :3;
        unsigned RXCMSGEID_RB1:1;
        unsigned RXCMSGEID_RTR:1;
        unsigned RXCMSGEID_EID:18;
        unsigned RXCMSGEID_IDE:1;
        unsigned RXCMSGEID_SRR:1;
        unsigned :2;
        unsigned RXCMSGDATA0_Byte0:8;
        unsigned RXCMSGDATA0_Byte1:8;
        unsigned RXCMSGDATA0_Byte2:8;
        unsigned RXCMSGDATA0_Byte3:8;
        unsigned RXCMSGDATA1_Byte4:8;
        unsigned RXCMSGDATA1_Byte5:8;
        unsigned RXCMSGDATA1_Byte6:8;
        unsigned RXCMSGDATA1_Byte7:8;
    };
    uint32_t RXMessage[4];
}CANRXBuffer;

CANTXBuffer *CAN2TXBuffer;
CAN2TXBuffer = (CANTXBuffer*)(PA_TO_KVA1(C2FIFOUA0));
//相应内容存储于以下
//CAN2TXBuffer->TXMessage[0]
//CAN2TXBuffer->TXMessage[1]
//CAN2TXBuffer->TXMessage[2]
//CAN2TXBuffer->TXMessage[3]
CANRXBuffer *CAN2RXBuffer1,*CAN2RXBuffer2,*CAN2RXBuffer3,*CAN2RXBuffer4;
CAN2RXBuffer1 = (CANRXBuffer*)(PA_TO_KVA1(C2FIFOUA1));
//相应内容存储于以下
//CAN2RXBuffer1->RXMessage[0]
//CAN2RXBuffer1->RXMessage[1]
//CAN2RXBuffer1->RXMessage[2]
//CAN2RXBuffer1->RXMessage[3]
CAN2RXBuffer2 = (CANRXBuffer*)(PA_TO_KVA1(C2FIFOUA2));
//相应内容存储于以下
//CAN2RXBuffer2->RXMessage[0]
//CAN2RXBuffer2->RXMessage[1]
//CAN2RXBuffer2->RXMessage[2]
//CAN2RXBuffer2->RXMessage[3]
CAN2RXBuffer3 = (CANRXBuffer*)(PA_TO_KVA1(C2FIFOUA3));
//相应内容存储于以下
//CAN2RXBuffer3->RXMessage[0]
//CAN2RXBuffer3->RXMessage[1]
//CAN2RXBuffer3->RXMessage[2]
//CAN2RXBuffer3->RXMessage[3]
CAN2RXBuffer4 = (CANRXBuffer*)(PA_TO_KVA1(C2FIFOUA4));
//相应内容存储于以下
//CAN2RXBuffer4->RXMessage[0]
//CAN2RXBuffer4->RXMessage[1]
//CAN2RXBuffer4->RXMessage[2]
//CAN2RXBuffer4->RXMessage[3]


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

99

主题

3962

帖子

1

粉丝