请选择 进入手机版 | 继续访问电脑版

 找回密码
 立即注册
楼主: yanwe***

CAN总线上同时接收标准帧和扩展帧时会出现接收乱码

[复制链接]

该用户从未签到

2

主题

14

回帖

64

积分

注册会员

积分
64
 楼主| 发表于 2023-1-30 11:26:38 | 显示全部楼层
测试步骤 :MCU下载完程序后,打开CAN软件(PCAN-VIEW)设置为250Khz,将设置完的扩展帧和标准帧分别选择发送即可。 用PCAN模拟的整车CAN网络。
CAN.png

CAN测试250Khz.rar

58.94 KB, 下载次数: 62

  • TA的每日心情
    开心
    昨天 09:29
  • 签到天数: 99 天

    [LV.6]常住居民II

    25

    主题

    1153

    回帖

    3610

    积分

    论坛元老

    积分
    3610
    发表于 2023-1-30 15:34:23 | 显示全部楼层
    CAN模块数据接收时一帧数据占据4的整数倍缓冲区空间,不足补0。
    标准帧11个字节,硬件模块只会在最后补一个0,占用12字节空间。

    扩展帧13个字节,硬件模块会在最后补三个0,占用16字节空间。
    例程使用最简单的方式兼容两种帧的读取,统一读取16字节,总线上经过滤波后需要读取的帧间隔有1毫秒以上基本上也不会有问题。
    如果总线上需要读取的帧数据量很大并且间隔时间很短的话,标准帧可能会将后一个帧的前4个字节一起读出,导致下一帧数据错乱。
    针对这种情况我稍微修改了一下 CanReadFifo 读取CAN缓冲区数据函数的方法,你再测测看是否会好一些。

    CAN测试250Khz_修改接收函数.zip

    26.69 KB, 下载次数: 30

    该用户从未签到

    538

    主题

    8730

    回帖

    1万

    积分

    管理员

    积分
    14185
    发表于 2023-1-30 19:18:30 | 显示全部楼层
    这个贴很有价值, CAN的软件实战,提问的人有经验,有耐心,回答的人也是细致周到的在完善软件部分

    该用户从未签到

    2

    主题

    14

    回帖

    64

    积分

    注册会员

    积分
    64
     楼主| 发表于 2023-1-31 10:10:23 | 显示全部楼层
    测试后发现以下问题1、会出现丢帧。2、若随机选择发送内容时会出现 FIFO溢出,但是出现标准帧时不会,单独扩展帧时就会溢出加入标准帧就不会出现。你们用专业设备模拟下整车网络吧 加入些错误帧如随机ID和数据等。硬件结构不能通过软件让FIFO寄存器填满么?
    112.jpg

    CAN测试250Kh-1.31.rar

    58.81 KB, 下载次数: 18

  • TA的每日心情
    开心
    昨天 09:29
  • 签到天数: 99 天

    [LV.6]常住居民II

    25

    主题

    1153

    回帖

    3610

    积分

    论坛元老

    积分
    3610
    发表于 2023-1-31 15:15:52 | 显示全部楼层
    本帖最后由 乘风飞扬 于 2023-1-31 15:18 编辑

    CAN模块的接收缓存RXBUFn上限是64字节,一旦总线一次性产生的报文数据量超过缓存上限,丢帧是自然的。所以汽车上的报文大部分是周期性的,就算事件型的报文基本上也会连续发送多帧。

    我重新对 CanReadMsg 、CanReadFifo 这两个接收函数进行优化,将一次性接收的多帧报文数据一帧帧的从缓存里全部读出来,充分发挥缓存的作用。
    具体过程参考附件例程:
    CAN测试250Khz_修改接收函数-20230131.zip (21.39 KB, 下载次数: 24)

    该用户从未签到

    2

    主题

    14

    回帖

    64

    积分

    注册会员

    积分
    64
     楼主| 发表于 2023-1-31 15:24:14 | 显示全部楼层
    本帖最后由 yanwei0433 于 2023-2-1 10:22 编辑

    好的 我明天看下  我没有发消息的权限  电话没法发

    该用户从未签到

    538

    主题

    8730

    回帖

    1万

    积分

    管理员

    积分
    14185
    发表于 2023-1-31 15:41:31 | 显示全部楼层
    6361  王  , 您电话已发陈工 1.png

    该用户从未签到

    2

    主题

    14

    回帖

    64

    积分

    注册会员

    积分
    64
     楼主| 发表于 2023-2-1 09:28:39 | 显示全部楼层
    测试后没发现什么问题 基本解决了我的问题。下面为代码



    /*---------------------------------------------------------------------*/
    /* --- STC MCU Limited ------------------------------------------------*/
    /* --- STC 1T Series MCU Demo Programme -------------------------------*/
    /* --- Mobile: (86)13922805190 ----------------------------------------*/
    /* --- Fax: 86-0513-55012956,55012947,55012969 ------------------------*/
    /* --- Tel: 86-0513-55012928,55012929,55012966 ------------------------*/
    /* --- Web: www.STCMCU.com --------------------------------------------*/
    /* --- Web: www.STCMCUDATA.com  ---------------------------------------*/
    /* --- QQ:  800003751 -------------------------------------------------*/
    /* 如果要在程序中使用此代码,请在程序中注明使用了STC的资料及程序        */
    /*---------------------------------------------------------------------*/

    /*************  功能说明    **************

    本例程基于STC32G为主控芯片的实验箱进行编写测试。

    使用Keil C251编译器,Memory Model推荐设置XSmall模式,默认定义变量在edata,单时钟存取访问速度快。

    edata建议保留1K给堆栈使用,空间不够时可将大数组、不常用变量加xdata关键字定义到xdata空间。

    CAN总线扩展帧收发测试用例.

    DCAN是一个支持CAN2.0B协议的功能单元。

    收到一个扩展帧后, CAN ID加1, 数据原样发送出去.

    MCU每秒钟发送一帧固定数据.

    默认波特率250KHz, 用户可自行修改.

    下载时, 默认时钟 24MHz (用户可自行修改频率).

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

    #include "STC32G.h"
    #include "intrins.h"
    #include "STDIO.H"

    typedef         unsigned char        u8;
    typedef         unsigned int        u16;
    typedef         unsigned long        u32;

    #define MAIN_Fosc        24000000UL

    /****************************** 用户定义宏 ***********************************/
    //CAN总线波特率=Fclk/((1+(TSG1+1)+(TSG2+1))*(BRP+1)*2)
    #define TSG1    2                //0~15
    #define TSG2    1                //1~7 (TSG2 不能设置为0)
    #define BRP     7                //0~63
    //24000000/((1+3+2)*8*2)=250KHz

    #define SJW     1                //重新同步跳跃宽度

    //总线波特率100KHz以上设置为 0; 100KHz以下设置为 1
    #define SAM     0                //总线电平采样次数: 0:采样1次; 1:采样3次

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


    /*************  本地常量声明    **************/

    #define Timer0_Reload   (65536UL -(MAIN_Fosc / 1000))       //Timer 0 中断频率, 1000次/秒
    #define BRT_A (65536 - (MAIN_Fosc / 115200) / 4)       // 串口频率

    /*************  本地变量声明    **************/

    u32 CAN_ID;
    u8 RX_BUF[8];
    u8 TX_BUF[8];
    u8 buffer_a[16],FIFO;
    bit B_CanRead;      //CAN 收到数据标志
    bit B_1ms;          //1ms标志
    u16 msecond;

    typedef struct
    {
            u32        ID;             //CAN ID
            u8        DLC;            //数据长度
            u8        DataBuffer[8];  //数据缓存
    }CAN_DataDef;

    CAN_DataDef CAN_Rx[8];

    /*************  本地函数声明    **************/
    void CANInit();
    void CanSendMsg(u32 canid, u8 *pdat);
    u8 CanReadReg(u8 addr);
    u8 CanReadMsg(void);
    //u8 CanReadFifo(u8 *pdat);

    /********************* 主函数 *************************/
    void main(void)
    {
        u8 i,j,n;

        WTST = 0;  //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
        EAXFR = 1; //扩展寄存器(XFR)访问使能
        CKCON = 0; //提高访问XRAM速度

        P0M1 = 0x00;   P0M0 = 0x00;   //设置P0.4、P0.5为漏极开路(实验箱加了上拉电阻到3.3V)
        P1M1 = 0x00;   P1M0 = 0x00;   //设置P1.1、P1.4、P1.5为漏极开路(实验箱加了上拉电阻到3.3V), P1.1在PWM当DAC电路通过电阻串联到P2.3
        P2M1 = 0x00;   P2M0 = 0x00;   //设置P2.2~P2.5为漏极开路(实验箱加了上拉电阻到3.3V)
        P3M1 = 0x00;   P3M0 = 0x00;   //设置P3.4、P3.6为漏极开路(实验箱加了上拉电阻到3.3V)
        P4M1 = 0x00;   P4M0 = 0x00;   //设置P4.2~P4.5为漏极开路(实验箱加了上拉电阻到3.3V)
        P5M1 = 0x00;   P5M0 = 0x00;   //设置P5.2、P5.3为漏极开路(实验箱加了上拉电阻到3.3V)
        P6M1 = 0x00;   P6M0 = 0x00;   //设置为漏极开路(实验箱加了上拉电阻到3.3V)
        P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口

            AUXR = 0x80;    //Timer0 set as 1T, 16 bits timer auto-reload,
            TH0 = (u8)(Timer0_Reload / 256);
            TL0 = (u8)(Timer0_Reload % 256);
            ET0 = 1;    //Timer0 interrupt enable
            TR0 = 1;    //Tiner0 run

            TMOD = 0x00;      // 时器1为方式2 初值自动装入 产生波特率
            TH1 = BRT_A >> 8; // 定时器初始为0xFF0XF7,晶振为24MHz,本函数实际产生的波特率为115200bps
            TL1 = BRT_A;      // 同上  f70xF4
            SCON = 0x50;      // 串口设置为方式1,REN=1,允许接收
            TR1 = 1;          // 启动定时器1
            T1x12 = 1;
            S1BRT = 0;
            ES = 1; // 使能串口接收中断,



            CANInit();
           
            EA = 1;                 //打开总中断

            CAN_ID = 0x01234567;
            TX_BUF[0] = 0x11;
            TX_BUF[1] = 0x22;
            TX_BUF[2] = 0x33;
            TX_BUF[3] = 0x44;
            TX_BUF[4] = 0x55;
            TX_BUF[5] = 0x66;
            TX_BUF[6] = 0x77;
            TX_BUF[7] = 0x88;
           
            while(1)
            {
                    if(B_CanRead==1)
                    {
                           
                            B_CanRead = 0;
                            P20=~P20;

                n = CanReadMsg();
                if(n>0)
                {
                    for(i=0;i<n;i++)
                    {
                        printf("CAN%d ID=0x%08lX DLC=%d ",n,CAN_Rx.ID,CAN_Rx.DLC);
                        for(j=0;j<CAN_Rx.DLC;j++)
                        {
                            printf("0x%02X ",CAN_Rx.DataBuffer[j]);    //从串口输出收到的数据
                        }
                        printf("\r\n");
                    }
                }
                    }                               
            }
    }



    void UartPutc(unsigned char dat)
    {
            SBUF = dat;
            while(!TI);
            TI = 0;
    }

    char putchar(char c)
    {
            UartPutc(c);
            return c;
    }


    /********************** Timer0 1ms中断函数 ************************/
    void timer0 (void) interrupt 1
    {
        B_1ms = 1;      //1ms标志
    }

    //========================================================================
    // 函数: u8 ReadReg(u8 addr)
    // 描述: CAN功能寄存器读取函数。
    // 参数: CAN功能寄存器地址.
    // 返回: CAN功能寄存器数据.
    // 版本: VER1.0
    // 日期: 2020-11-16
    // 备注:
    //========================================================================
    u8 CanReadReg(u8 addr)
    {
            u8 dat;
            CANAR = addr;
            dat = CANDR;
            return dat;
    }

    //========================================================================
    // 函数: void WriteReg(u8 addr, u8 dat)
    // 描述: CAN功能寄存器配置函数。
    // 参数: CAN功能寄存器地址, CAN功能寄存器数据.
    // 返回: none.
    // 版本: VER1.0
    // 日期: 2020-11-16
    // 备注:
    //========================================================================
    void CanWriteReg(u8 addr, u8 dat)
    {
            CANAR = addr;
            CANDR = dat;
    }

    //========================================================================
    // 函数: void CanReadFifo(CAN_DataDef *CANx)
    // 描述: 读取CAN缓冲区数据函数。
    // 参数: *CANx: 存放CAN总线读取数据.
    // 返回: none.
    // 版本: VER2.0
    // 日期: 2023-01-31
    // 备注:
    //========================================================================
    void CanReadFifo(CAN_DataDef *CANx)
    {
            u8 i;
        u8 pdat[5];
        u8 RX_Index=0;

            pdat[0] = CanReadReg((u8)(RX_BUF0 + (RX_Index++&3)));
       
        if(pdat[0] & 0x80)  //判断是标准帧还是扩展帧
        {
            pdat[1] = CanReadReg((u8)(RX_BUF0 + (RX_Index++&3)));   //扩展帧ID占4个字节
            pdat[2] = CanReadReg((u8)(RX_BUF0 + (RX_Index++&3)));
            pdat[3] = CanReadReg((u8)(RX_BUF0 + (RX_Index++&3)));
            pdat[4] = CanReadReg((u8)(RX_BUF0 + (RX_Index++&3)));
            CANx->ID = (((u32)pdat[1] << 24) + ((u32)pdat[2] << 16) + ((u32)pdat[3] << 8) + pdat[4]) >> 3;
        }
        else
        {
            pdat[1] = CanReadReg((u8)(RX_BUF0 + (RX_Index++&3)));   //标准帧ID占2个字节
            pdat[2] = CanReadReg((u8)(RX_BUF0 + (RX_Index++&3)));
                CANx->ID = ((pdat[1] << 8) + pdat[2]) >> 5;
        }

        CANx->DLC = pdat[0] & 0x0f;   //数据长度

            for(i=0;((i<CANx->DLC) && (i<8));i++)        //读取数据长度为len,最多不超过8
            {
                    CANx->DataBuffer = CanReadReg((u8)(RX_BUF0 + (RX_Index++&3)));   //读取有效数据
            }
        while(RX_Index&3)   //判断已读数据长度是否4的整数倍
        {
            CanReadReg((u8)(RX_BUF0 + (RX_Index++&3)));  //读取填充数据,一帧数据占据4的整数倍缓冲区空间,不足补0
        }
    }

    //========================================================================
    // 函数: u8 CanReadMsg(void)
    // 描述: CAN接收数据函数。
    // 参数: none.
    // 返回: 帧个数.
    // 版本: VER2.0
    // 日期: 2023-01-31
    // 备注:
    //========================================================================
    u8 CanReadMsg(void)
    {
            u8 i;
            u8 n=0;

        do{
            CanReadFifo(&CAN_Rx[n++]);  //读取接收缓冲区数据
            i = CanReadReg(SR);
        }while(i&0x80);     //判断接收缓冲区里是否还有数据,有的话继续读取
            return n;   //返回帧个数
    }





    //========================================================================
    // 函数: void CanSendMsg(u32 canid, u8 *pdat)
    // 描述: CAN发送数据函数。
    // 参数: canid: CAN ID; *pdat: 发送数据缓冲区.
    // 返回: none.
    // 版本: VER1.0
    // 日期: 2020-11-19
    // 备注:
    //========================================================================
    void CanSendMsg(u32 canid, u8 *pdat)
    {
            u32 CanID;

            CanID = canid << 3;
            CanWriteReg(TX_BUF0,0x88);        //bit7: 标准帧(0)/扩展帧(1), bit6: 数据帧(0)/远程帧(1), bit3~bit0: 数据长度(DLC)
            CanWriteReg(TX_BUF1,(u8)(CanID>>24));
            CanWriteReg(TX_BUF2,(u8)(CanID>>16));
            CanWriteReg(TX_BUF3,(u8)(CanID>>8));

            CanWriteReg(TX_BUF0,(u8)CanID);
            CanWriteReg(TX_BUF1,pdat[0]);
            CanWriteReg(TX_BUF2,pdat[1]);
            CanWriteReg(TX_BUF3,pdat[2]);

            CanWriteReg(TX_BUF0,pdat[3]);
            CanWriteReg(TX_BUF1,pdat[4]);
            CanWriteReg(TX_BUF2,pdat[5]);
            CanWriteReg(TX_BUF3,pdat[6]);

            CanWriteReg(TX_BUF0,pdat[7]);
            CanWriteReg(TX_BUF1,0x00);
            CanWriteReg(TX_BUF2,0x00);
            CanWriteReg(TX_BUF3,0x00);

            CanWriteReg(CMR ,0x04);                //发起一次帧传输
    }

    //========================================================================
    // 函数: void CANSetBaudrate()
    // 描述: CAN总线波特率设置函数。
    // 参数: none.
    // 返回: none.
    // 版本: VER1.0
    // 日期: 2020-11-19
    // 备注:
    //========================================================================
    void CANSetBaudrate()
    {
            CanWriteReg(BTR0,(SJW << 6) + BRP);
            CanWriteReg(BTR1,(SAM << 7) + (TSG2 << 4) + TSG1);
    }

    //========================================================================
    // 函数: void CANInit()
    // 描述: CAN初始化函数。
    // 参数: none.
    // 返回: none.
    // 版本: VER1.0
    // 日期: 2020-11-19
    // 备注:
    //========================================================================
    void CANInit()
    {
            CANEN = 1;          //CAN1模块使能
            CanWriteReg(MR  ,0x05);  //使能 Reset Mode, 采用单滤波设置

            CANSetBaudrate();        //设置波特率
           
    // 设置过滤ID为:xxF8xxxx 的帧才接收
    //        CanWriteReg(ACR0,0x07);                //总线验收代码寄存器
    //        CanWriteReg(ACR1,0xc0);
    //        CanWriteReg(ACR2,0x00);
    //        CanWriteReg(ACR3,0x00);
    //        CanWriteReg(AMR0,0xF8);                //总线验收屏蔽寄存器
    //        CanWriteReg(AMR1,0x07);
    //        CanWriteReg(AMR2,0xFF);
    //        CanWriteReg(AMR3,0xFF);

            //取消过滤ID,所有帧都接收
            CanWriteReg(ACR0,0x00);                //总线验收代码寄存器
            CanWriteReg(ACR1,0x00);
            CanWriteReg(ACR2,0x00);
            CanWriteReg(ACR3,0x00);
            CanWriteReg(AMR0,0xFF);                //总线验收屏蔽寄存器
            CanWriteReg(AMR1,0xFF);
            CanWriteReg(AMR2,0xFF);
            CanWriteReg(AMR3,0xFF);

            CanWriteReg(IMR ,0xff);                //中断寄存器
            CanWriteReg(ISR ,0xff);                //清中断标志
            CanWriteReg(MR  ,0x01);                //退出 Reset Mode, 采用单滤波设置

            P_SW1 = (P_SW1 & ~(3<<4)) | (0<<4); //端口切换(CAN_Rx,CAN_Tx) 0x00:P0.0,P0.1  0x10:P5.0,P5.1  0x20:P4.2,P4.5  0x30:P7.0,P7.1
            CANICR = 0x02;                //CAN中断使能
    }

    //========================================================================
    // 函数: void CANBUS_Interrupt(void) interrupt CAN1_VECTOR
    // 描述: CAN总线中断函数。
    // 参数: none.
    // 返回: none.
    // 版本: VER1.0
    // 日期: 2020-11-19
    // 备注:
    //========================================================================
    void CANBUS_Interrupt(void) interrupt CAN1_VECTOR
    {
            u8 isr;
            u8 arTemp;
            arTemp = CANAR;     //CANAR现场保存,避免主循环里写完 CANAR 后产生中断,在中断里修改了 CANAR 内容
           
            isr = CanReadReg(ISR);
            if((isr & 0x04) == 0x04)
        {
                    CANAR = 0x03;
                    CANDR = 0x04;    //CLR FLAG
                   
        }       
            if((isr & 0x08) == 0x08)
        {
                    CANAR = 0x03;
                    CANDR = 0x08;    //CLR FLAG
           
                    B_CanRead = 1;
        }

            if((isr & 0x40) == 0x40)  //ALI
        {
                    CANAR = ISR;
                    CANDR = 0x40;    //CLR FLAG
        }       

            if((isr & 0x20) == 0x20)  //EWI
        {
                    CANAR = ISR;
                    CANDR = 0x20;    //CLR FLAG
        }       

            if((isr & 0x10) == 0x10)  //EPI
        {
                    CANAR = ISR;
                    CANDR = 0x10;    //CLR FLAG
        }       

            if((isr & 0x02) == 0x02)  //BEI
        {
                    CANAR = ISR;
                    CANDR = 0x02;    //CLR FLAG
        }       

            if((isr & 0x01) == 0x01)  //DOI
        {
                    CANAR = ISR;
                    CANDR = 0x01;    //CLR FLAG
        }       

            CANAR = arTemp;    //CANAR现场恢复
    }

    该用户从未签到

    2

    主题

    14

    回帖

    64

    积分

    注册会员

    积分
    64
     楼主| 发表于 2023-2-1 09:30:54 | 显示全部楼层
    测试后没发现什么问题,可以满足我的要求,谢谢。 下面代码为同时接收标准帧和扩展帧的



    /*---------------------------------------------------------------------*/
    /* --- STC MCU Limited ------------------------------------------------*/
    /* --- STC 1T Series MCU Demo Programme -------------------------------*/
    /* --- Mobile: (86)13922805190 ----------------------------------------*/
    /* --- Fax: 86-0513-55012956,55012947,55012969 ------------------------*/
    /* --- Tel: 86-0513-55012928,55012929,55012966 ------------------------*/
    /* --- Web: www.STCMCU.com --------------------------------------------*/
    /* --- Web: www.STCMCUDATA.com  ---------------------------------------*/
    /* --- QQ:  800003751 -------------------------------------------------*/
    /* 如果要在程序中使用此代码,请在程序中注明使用了STC的资料及程序        */
    /*---------------------------------------------------------------------*/

    /*************  功能说明    **************

    本例程基于STC32G为主控芯片的实验箱进行编写测试。

    使用Keil C251编译器,Memory Model推荐设置XSmall模式,默认定义变量在edata,单时钟存取访问速度快。

    edata建议保留1K给堆栈使用,空间不够时可将大数组、不常用变量加xdata关键字定义到xdata空间。

    CAN总线扩展帧收发测试用例.

    DCAN是一个支持CAN2.0B协议的功能单元。

    收到一个扩展帧后, CAN ID加1, 数据原样发送出去.

    MCU每秒钟发送一帧固定数据.

    默认波特率250KHz, 用户可自行修改.

    下载时, 默认时钟 24MHz (用户可自行修改频率).

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

    #include "STC32G.h"
    #include "intrins.h"
    #include "STDIO.H"

    typedef         unsigned char        u8;
    typedef         unsigned int        u16;
    typedef         unsigned long        u32;

    #define MAIN_Fosc        24000000UL

    /****************************** 用户定义宏 ***********************************/
    //CAN总线波特率=Fclk/((1+(TSG1+1)+(TSG2+1))*(BRP+1)*2)
    #define TSG1    2                //0~15
    #define TSG2    1                //1~7 (TSG2 不能设置为0)
    #define BRP     7                //0~63
    //24000000/((1+3+2)*8*2)=250KHz

    #define SJW     1                //重新同步跳跃宽度

    //总线波特率100KHz以上设置为 0; 100KHz以下设置为 1
    #define SAM     0                //总线电平采样次数: 0:采样1次; 1:采样3次

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


    /*************  本地常量声明    **************/

    #define Timer0_Reload   (65536UL -(MAIN_Fosc / 1000))       //Timer 0 中断频率, 1000次/秒
    #define BRT_A (65536 - (MAIN_Fosc / 115200) / 4)       // 串口频率

    /*************  本地变量声明    **************/

    u32 CAN_ID;
    u8 RX_BUF[8];
    u8 TX_BUF[8];
    u8 buffer_a[16],FIFO;
    bit B_CanRead;      //CAN 收到数据标志
    bit B_1ms;          //1ms标志
    u16 msecond;

    typedef struct
    {
            u32        ID;             //CAN ID
            u8        DLC;            //数据长度
            u8        DataBuffer[8];  //数据缓存
    }CAN_DataDef;

    CAN_DataDef CAN_Rx[8];

    /*************  本地函数声明    **************/
    void CANInit();
    void CanSendMsg(u32 canid, u8 *pdat);
    u8 CanReadReg(u8 addr);
    u8 CanReadMsg(void);
    //u8 CanReadFifo(u8 *pdat);

    /********************* 主函数 *************************/
    void main(void)
    {
        u8 i,j,n;

        WTST = 0;  //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
        EAXFR = 1; //扩展寄存器(XFR)访问使能
        CKCON = 0; //提高访问XRAM速度

        P0M1 = 0x00;   P0M0 = 0x00;   //设置P0.4、P0.5为漏极开路(实验箱加了上拉电阻到3.3V)
        P1M1 = 0x00;   P1M0 = 0x00;   //设置P1.1、P1.4、P1.5为漏极开路(实验箱加了上拉电阻到3.3V), P1.1在PWM当DAC电路通过电阻串联到P2.3
        P2M1 = 0x00;   P2M0 = 0x00;   //设置P2.2~P2.5为漏极开路(实验箱加了上拉电阻到3.3V)
        P3M1 = 0x00;   P3M0 = 0x00;   //设置P3.4、P3.6为漏极开路(实验箱加了上拉电阻到3.3V)
        P4M1 = 0x00;   P4M0 = 0x00;   //设置P4.2~P4.5为漏极开路(实验箱加了上拉电阻到3.3V)
        P5M1 = 0x00;   P5M0 = 0x00;   //设置P5.2、P5.3为漏极开路(实验箱加了上拉电阻到3.3V)
        P6M1 = 0x00;   P6M0 = 0x00;   //设置为漏极开路(实验箱加了上拉电阻到3.3V)
        P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口

            AUXR = 0x80;    //Timer0 set as 1T, 16 bits timer auto-reload,
            TH0 = (u8)(Timer0_Reload / 256);
            TL0 = (u8)(Timer0_Reload % 256);
            ET0 = 1;    //Timer0 interrupt enable
            TR0 = 1;    //Tiner0 run

            TMOD = 0x00;      // 时器1为方式2 初值自动装入 产生波特率
            TH1 = BRT_A >> 8; // 定时器初始为0xFF0XF7,晶振为24MHz,本函数实际产生的波特率为115200bps
            TL1 = BRT_A;      // 同上  f70xF4
            SCON = 0x50;      // 串口设置为方式1,REN=1,允许接收
            TR1 = 1;          // 启动定时器1
            T1x12 = 1;
            S1BRT = 0;
            ES = 1; // 使能串口接收中断,



            CANInit();
           
            EA = 1;                 //打开总中断

            CAN_ID = 0x01234567;
            TX_BUF[0] = 0x11;
            TX_BUF[1] = 0x22;
            TX_BUF[2] = 0x33;
            TX_BUF[3] = 0x44;
            TX_BUF[4] = 0x55;
            TX_BUF[5] = 0x66;
            TX_BUF[6] = 0x77;
            TX_BUF[7] = 0x88;
           
            while(1)
            {
                    if(B_CanRead==1)
                    {
                           
                            B_CanRead = 0;
                            P20=~P20;

                n = CanReadMsg();
                if(n>0)
                {
                    for(i=0;i<n;i++)
                    {
                        printf("CAN%d ID=0x%08lX DLC=%d ",n,CAN_Rx.ID,CAN_Rx.DLC);
                        for(j=0;j<CAN_Rx.DLC;j++)
                        {
                            printf("0x%02X ",CAN_Rx.DataBuffer[j]);    //从串口输出收到的数据
                        }
                        printf("\r\n");
                    }
                }
                    }                               
            }
    }



    void UartPutc(unsigned char dat)
    {
            SBUF = dat;
            while(!TI);
            TI = 0;
    }

    char putchar(char c)
    {
            UartPutc(c);
            return c;
    }


    /********************** Timer0 1ms中断函数 ************************/
    void timer0 (void) interrupt 1
    {
        B_1ms = 1;      //1ms标志
    }

    //========================================================================
    // 函数: u8 ReadReg(u8 addr)
    // 描述: CAN功能寄存器读取函数。
    // 参数: CAN功能寄存器地址.
    // 返回: CAN功能寄存器数据.
    // 版本: VER1.0
    // 日期: 2020-11-16
    // 备注:
    //========================================================================
    u8 CanReadReg(u8 addr)
    {
            u8 dat;
            CANAR = addr;
            dat = CANDR;
            return dat;
    }

    //========================================================================
    // 函数: void WriteReg(u8 addr, u8 dat)
    // 描述: CAN功能寄存器配置函数。
    // 参数: CAN功能寄存器地址, CAN功能寄存器数据.
    // 返回: none.
    // 版本: VER1.0
    // 日期: 2020-11-16
    // 备注:
    //========================================================================
    void CanWriteReg(u8 addr, u8 dat)
    {
            CANAR = addr;
            CANDR = dat;
    }

    //========================================================================
    // 函数: void CanReadFifo(CAN_DataDef *CANx)
    // 描述: 读取CAN缓冲区数据函数。
    // 参数: *CANx: 存放CAN总线读取数据.
    // 返回: none.
    // 版本: VER2.0
    // 日期: 2023-01-31
    // 备注:
    //========================================================================
    void CanReadFifo(CAN_DataDef *CANx)
    {
            u8 i;
        u8 pdat[5];
        u8 RX_Index=0;

            pdat[0] = CanReadReg((u8)(RX_BUF0 + (RX_Index++&3)));
       
        if(pdat[0] & 0x80)  //判断是标准帧还是扩展帧
        {
            pdat[1] = CanReadReg((u8)(RX_BUF0 + (RX_Index++&3)));   //扩展帧ID占4个字节
            pdat[2] = CanReadReg((u8)(RX_BUF0 + (RX_Index++&3)));
            pdat[3] = CanReadReg((u8)(RX_BUF0 + (RX_Index++&3)));
            pdat[4] = CanReadReg((u8)(RX_BUF0 + (RX_Index++&3)));
            CANx->ID = (((u32)pdat[1] << 24) + ((u32)pdat[2] << 16) + ((u32)pdat[3] << 8) + pdat[4]) >> 3;
        }
        else
        {
            pdat[1] = CanReadReg((u8)(RX_BUF0 + (RX_Index++&3)));   //标准帧ID占2个字节
            pdat[2] = CanReadReg((u8)(RX_BUF0 + (RX_Index++&3)));
                CANx->ID = ((pdat[1] << 8) + pdat[2]) >> 5;
        }

        CANx->DLC = pdat[0] & 0x0f;   //数据长度

            for(i=0;((i<CANx->DLC) && (i<8));i++)        //读取数据长度为len,最多不超过8
            {
                    CANx->DataBuffer = CanReadReg((u8)(RX_BUF0 + (RX_Index++&3)));   //读取有效数据
            }
        while(RX_Index&3)   //判断已读数据长度是否4的整数倍
        {
            CanReadReg((u8)(RX_BUF0 + (RX_Index++&3)));  //读取填充数据,一帧数据占据4的整数倍缓冲区空间,不足补0
        }
    }

    //========================================================================
    // 函数: u8 CanReadMsg(void)
    // 描述: CAN接收数据函数。
    // 参数: none.
    // 返回: 帧个数.
    // 版本: VER2.0
    // 日期: 2023-01-31
    // 备注:
    //========================================================================
    u8 CanReadMsg(void)
    {
            u8 i;
            u8 n=0;

        do{
            CanReadFifo(&CAN_Rx[n++]);  //读取接收缓冲区数据
            i = CanReadReg(SR);
        }while(i&0x80);     //判断接收缓冲区里是否还有数据,有的话继续读取
            return n;   //返回帧个数
    }





    //========================================================================
    // 函数: void CanSendMsg(u32 canid, u8 *pdat)
    // 描述: CAN发送数据函数。
    // 参数: canid: CAN ID; *pdat: 发送数据缓冲区.
    // 返回: none.
    // 版本: VER1.0
    // 日期: 2020-11-19
    // 备注:
    //========================================================================
    void CanSendMsg(u32 canid, u8 *pdat)
    {
            u32 CanID;

            CanID = canid << 3;
            CanWriteReg(TX_BUF0,0x88);        //bit7: 标准帧(0)/扩展帧(1), bit6: 数据帧(0)/远程帧(1), bit3~bit0: 数据长度(DLC)
            CanWriteReg(TX_BUF1,(u8)(CanID>>24));
            CanWriteReg(TX_BUF2,(u8)(CanID>>16));
            CanWriteReg(TX_BUF3,(u8)(CanID>>8));

            CanWriteReg(TX_BUF0,(u8)CanID);
            CanWriteReg(TX_BUF1,pdat[0]);
            CanWriteReg(TX_BUF2,pdat[1]);
            CanWriteReg(TX_BUF3,pdat[2]);

            CanWriteReg(TX_BUF0,pdat[3]);
            CanWriteReg(TX_BUF1,pdat[4]);
            CanWriteReg(TX_BUF2,pdat[5]);
            CanWriteReg(TX_BUF3,pdat[6]);

            CanWriteReg(TX_BUF0,pdat[7]);
            CanWriteReg(TX_BUF1,0x00);
            CanWriteReg(TX_BUF2,0x00);
            CanWriteReg(TX_BUF3,0x00);

            CanWriteReg(CMR ,0x04);                //发起一次帧传输
    }

    //========================================================================
    // 函数: void CANSetBaudrate()
    // 描述: CAN总线波特率设置函数。
    // 参数: none.
    // 返回: none.
    // 版本: VER1.0
    // 日期: 2020-11-19
    // 备注:
    //========================================================================
    void CANSetBaudrate()
    {
            CanWriteReg(BTR0,(SJW << 6) + BRP);
            CanWriteReg(BTR1,(SAM << 7) + (TSG2 << 4) + TSG1);
    }

    //========================================================================
    // 函数: void CANInit()
    // 描述: CAN初始化函数。
    // 参数: none.
    // 返回: none.
    // 版本: VER1.0
    // 日期: 2020-11-19
    // 备注:
    //========================================================================
    void CANInit()
    {
            CANEN = 1;          //CAN1模块使能
            CanWriteReg(MR  ,0x05);  //使能 Reset Mode, 采用单滤波设置

            CANSetBaudrate();        //设置波特率
           
    // 设置过滤ID为:xxF8xxxx 的帧才接收
    //        CanWriteReg(ACR0,0x07);                //总线验收代码寄存器
    //        CanWriteReg(ACR1,0xc0);
    //        CanWriteReg(ACR2,0x00);
    //        CanWriteReg(ACR3,0x00);
    //        CanWriteReg(AMR0,0xF8);                //总线验收屏蔽寄存器
    //        CanWriteReg(AMR1,0x07);
    //        CanWriteReg(AMR2,0xFF);
    //        CanWriteReg(AMR3,0xFF);

            //取消过滤ID,所有帧都接收
            CanWriteReg(ACR0,0x00);                //总线验收代码寄存器
            CanWriteReg(ACR1,0x00);
            CanWriteReg(ACR2,0x00);
            CanWriteReg(ACR3,0x00);
            CanWriteReg(AMR0,0xFF);                //总线验收屏蔽寄存器
            CanWriteReg(AMR1,0xFF);
            CanWriteReg(AMR2,0xFF);
            CanWriteReg(AMR3,0xFF);

            CanWriteReg(IMR ,0xff);                //中断寄存器
            CanWriteReg(ISR ,0xff);                //清中断标志
            CanWriteReg(MR  ,0x01);                //退出 Reset Mode, 采用单滤波设置

            P_SW1 = (P_SW1 & ~(3<<4)) | (0<<4); //端口切换(CAN_Rx,CAN_Tx) 0x00:P0.0,P0.1  0x10:P5.0,P5.1  0x20:P4.2,P4.5  0x30:P7.0,P7.1
            CANICR = 0x02;                //CAN中断使能
    }

    //========================================================================
    // 函数: void CANBUS_Interrupt(void) interrupt CAN1_VECTOR
    // 描述: CAN总线中断函数。
    // 参数: none.
    // 返回: none.
    // 版本: VER1.0
    // 日期: 2020-11-19
    // 备注:
    //========================================================================
    void CANBUS_Interrupt(void) interrupt CAN1_VECTOR
    {
            u8 isr;
            u8 arTemp;
            arTemp = CANAR;     //CANAR现场保存,避免主循环里写完 CANAR 后产生中断,在中断里修改了 CANAR 内容
           
            isr = CanReadReg(ISR);
            if((isr & 0x04) == 0x04)
        {
                    CANAR = 0x03;
                    CANDR = 0x04;    //CLR FLAG
                   
        }       
            if((isr & 0x08) == 0x08)
        {
                    CANAR = 0x03;
                    CANDR = 0x08;    //CLR FLAG
           
                    B_CanRead = 1;
        }

            if((isr & 0x40) == 0x40)  //ALI
        {
                    CANAR = ISR;
                    CANDR = 0x40;    //CLR FLAG
        }       

            if((isr & 0x20) == 0x20)  //EWI
        {
                    CANAR = ISR;
                    CANDR = 0x20;    //CLR FLAG
        }       

            if((isr & 0x10) == 0x10)  //EPI
        {
                    CANAR = ISR;
                    CANDR = 0x10;    //CLR FLAG
        }       

            if((isr & 0x02) == 0x02)  //BEI
        {
                    CANAR = ISR;
                    CANDR = 0x02;    //CLR FLAG
        }       

            if((isr & 0x01) == 0x01)  //DOI
        {
                    CANAR = ISR;
                    CANDR = 0x01;    //CLR FLAG
        }       

            CANAR = arTemp;    //CANAR现场恢复
    }

    该用户从未签到

    538

    主题

    8730

    回帖

    1万

    积分

    管理员

    积分
    14185
    发表于 2023-2-1 20:31:25 | 显示全部楼层
    开心,STC CAN 的实用程序,又多了分成功的积累
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

    GMT+8, 2024-3-29 10:01 , Processed in 0.077352 second(s), 65 queries .

    Powered by Discuz! X3.5

    © 2001-2024 Discuz! Team.

    快速回复 返回顶部 返回列表