乘风飞扬 发表于 2023-1-30 15:34:23

CAN模块数据接收时一帧数据占据4的整数倍缓冲区空间,不足补0。
标准帧11个字节,硬件模块只会在最后补一个0,占用12字节空间。

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

神农鼎 发表于 2023-1-30 19:18:30

这个贴很有价值, CAN的软件实战,提问的人有经验,有耐心,回答的人也是细致周到的在完善软件部分

yanwei0433 发表于 2023-1-31 10:10:23

测试后发现以下问题1、会出现丢帧。2、若随机选择发送内容时会出现 FIFO溢出,但是出现标准帧时不会,单独扩展帧时就会溢出加入标准帧就不会出现。你们用专业设备模拟下整车网络吧 加入些错误帧如随机ID和数据等。硬件结构不能通过软件让FIFO寄存器填满么?

乘风飞扬 发表于 2023-1-31 15:15:52

本帖最后由 乘风飞扬 于 2023-1-31 15:18 编辑

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

我重新对 CanReadMsg 、CanReadFifo 这两个接收函数进行优化,将一次性接收的多帧报文数据一帧帧的从缓存里全部读出来,充分发挥缓存的作用。
具体过程参考附件例程:

yanwei0433 发表于 2023-1-31 15:24:14

本帖最后由 yanwei0433 于 2023-2-1 10:22 编辑

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

神农鼎 发表于 2023-1-31 15:41:31

6361王, 您电话已发陈工

yanwei0433 发表于 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;
u8 TX_BUF;
u8 buffer_a,FIFO;
bit B_CanRead;      //CAN 收到数据标志
bit B_1ms;          //1ms标志
u16 msecond;

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

CAN_DataDef CAN_Rx;

/*************本地函数声明    **************/
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 = 0x11;
        TX_BUF = 0x22;
        TX_BUF = 0x33;
        TX_BUF = 0x44;
        TX_BUF = 0x55;
        TX_BUF = 0x66;
        TX_BUF = 0x77;
        TX_BUF = 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);    //从串口输出收到的数据
                  }
                  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;
    u8 RX_Index=0;

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

    CANx->DLC = pdat & 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);//读取接收缓冲区数据
      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);
        CanWriteReg(TX_BUF2,pdat);
        CanWriteReg(TX_BUF3,pdat);

        CanWriteReg(TX_BUF0,pdat);
        CanWriteReg(TX_BUF1,pdat);
        CanWriteReg(TX_BUF2,pdat);
        CanWriteReg(TX_BUF3,pdat);

        CanWriteReg(TX_BUF0,pdat);
        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.10x10:P5.0,P5.10x20:P4.2,P4.50x30: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现场恢复
}

yanwei0433 发表于 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;
u8 TX_BUF;
u8 buffer_a,FIFO;
bit B_CanRead;      //CAN 收到数据标志
bit B_1ms;          //1ms标志
u16 msecond;

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

CAN_DataDef CAN_Rx;

/*************本地函数声明    **************/
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 = 0x11;
        TX_BUF = 0x22;
        TX_BUF = 0x33;
        TX_BUF = 0x44;
        TX_BUF = 0x55;
        TX_BUF = 0x66;
        TX_BUF = 0x77;
        TX_BUF = 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);    //从串口输出收到的数据
                  }
                  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;
    u8 RX_Index=0;

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

    CANx->DLC = pdat & 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);//读取接收缓冲区数据
      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);
        CanWriteReg(TX_BUF2,pdat);
        CanWriteReg(TX_BUF3,pdat);

        CanWriteReg(TX_BUF0,pdat);
        CanWriteReg(TX_BUF1,pdat);
        CanWriteReg(TX_BUF2,pdat);
        CanWriteReg(TX_BUF3,pdat);

        CanWriteReg(TX_BUF0,pdat);
        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.10x10:P5.0,P5.10x20:P4.2,P4.50x30: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现场恢复
}

神农鼎 发表于 2023-2-1 20:31:25

开心,STC CAN 的实用程序,又多了分成功的积累

乘风飞扬 发表于 2023-2-2 11:50:46

我更新了一下CAN总线库函数的例程,同时修改了CAN总线收发函数,兼容标准帧与扩展帧的接收与发送。
使用时只要将“STC32G_CAN.c”、“STC32G_CAN_Isr.c”、“STC32G_CAN.h”三个文件加载到自己的项目里,然后参考main.c文件里面的方法设置调用就行。

页: 1 [2] 3
查看完整版本: CAN总线上同时接收标准帧和扩展帧时会出现接收乱码