找回密码
 立即注册
查看: 183|回复: 6

请教各位老师,can收发器不接负载,怎么能正常发送多组报文

[复制链接]

1

主题

2

回帖

15

积分

新手上路

积分
15
发表于 2026-9-21 11:48:28 | 显示全部楼层 |阅读模式
STC32G12K128做了一个can发送电路板,
can1,接上负载能正常依次发送写好的多组ID和数据。
如果不接负载的情况下没有收到应答反馈,就会一直重发第一组数据不停,不知道怎么修改程序让它不接负载也能正常依次发出编辑好的多组报文。

现在只能通过硬件解决,把can2装一个收发器并在can1上,利用can2充当假负载。

回复

使用道具 举报 送花

已绑定手机

7

主题

445

回帖

6692

积分

论坛元老

积分
6692
发表于 2026-9-21 13:20:02 | 显示全部楼层
BEI中断里对CMR的AT置1,终止传输。
回复

使用道具 举报 送花

已绑定手机

872

主题

1万

回帖

2万

积分

管理员

积分
25432
发表于 2026-9-22 15:46:58 | 显示全部楼层
截图202609221546556633.jpg
回复

使用道具 举报 送花

已绑定手机

872

主题

1万

回帖

2万

积分

管理员

积分
25432
发表于 2026-9-22 15:47:15 | 显示全部楼层

我已经掌握了这个问题的完整背景和官方/社区的回复思路。下面给出正式解答。

问题解答:STC32G12K128 的 CAN 不接负载时为何一直重发第一帧,怎么改

一、现象原因分析

您遇到的现象,本质上是 CAN 协议本身的应答(ACK)机制 + 自动重传机制 导致的,不是芯片 bug:

  • CAN 总线上,发送节点发完一帧后,必须收到至少一个其他节点的应答位(ACK),这一帧才算发送成功。
  • 不接负载时,总线上没有任何节点应答,ACK 槽为隐性 → 芯片判定发生 ACK 错误,于是自动重传当前帧。
  • 重传一直不成功,就一直卡在第一帧,后面的报文自然发不出去。这也是为什么您看到的"一直重发第一组数据不停"。
  • 您用 can2 加收发器当"假负载"(其实是有节点应答),恰好提供了这个 ACK,所以能正常发。这是硬件解法,但治标不治本。

二、正确思路:主动检测错误并终止当前帧传输

不接负载(无应答)的场景,正确做法是在错误中断里主动中止当前帧的传输,让发送状态机退出重传、继续发下一帧。

手册中 CAN 命令寄存器 CMR 提供了这个能力:

符号 地址 B7 B6 B5 B4 B3 B2 B1 B0
CMR 0x01 - - - - - TR AT -
  • TR:CAN 模块发送请求,写 1 发起一次帧传输
  • AT:CAN 模块传输终止,写 1 终止当前的帧传输

这正是论坛回复 1(lcws***)给出的办法:"BEI 中断里对 CMR 的 AT 置 1,终止传输。"

三、BEI 中断与错误标志

总线错误会触发 BEI(总线错误中断),它位于 CAN 中断/应答寄存器 ISR/IACK:

符号 地址 B7 B6 B5 B4 B3 B2 B1 B0
ISR/IACK 0x03 - ALI EWI EPI RI TI BEI DOI
  • BEI:CAN 模块在接收或者发送过程中产生了总线错误
  • 这些中断标志都是 写 1 清零

具体是哪种错误,看 CAN 总线错误信息寄存器 ECC:

符号 地址 B7 B6 B5 B4 B3 B2 B1 B0
ECC 0x18 RXWRN TXWRN EDIR ACKER FRMER CRCER STFER BER
  • ACKER:ACK 错误(无节点应答时置位,正是您的情况)
  • 其余为帧格式、CRC、位填充、位错误等

四、处理流程图(文字描述)

进入 BEI 中断
   ↓
读 ECC 判断错误类型(可选,便于调试)
   ↓
清 BEI 中断标志(对 ISR/IACK 的 BEI 写 1)
   ↓
判断当前是否"无应答/发送失败"
   ↓
对 CMR 的 AT 写 1,终止当前帧传输
   ↓
返回主循环,继续发送下一组报文

五、补充建议

  1. 必须先进入 BEI 中断并做 AE 应答:STM32/STC 这类 CAN 控制器,中断标志不清会反复进中断,务必对 ISR/IACK 对应位写 1 清零。
  2. AT 只是"终止当前帧",不会关闭 CAN 的错误计数机制。若一直无应答,错误计数持续累加,最终可能进入 BUS-OFF(手册 SR 寄存器 BS 位说明:错误次数超过 255 次会触发 BUS-OFF,此时总线忙,收不到也发不出),需要注意程序上对 BUS-OFF 的恢复处理。
  3. CAN 无应答重传是协议特性,不是错误。如果您的应用场景本来就是"单机发、不要求被接收",那更合理的做法是:保证总线上至少有一个能应答的节点(如同坛友用 can2 当负载),或明确接受"发送帧可能被重传/无法确认成功"。您描述的"不接负载也要依次发多组报文",本质是用 AT 强制中止,属于对协议特性的规避处理,请评估在实际总线环境下是否合适。

六、建议的验证方法

  • 先在 BEI 中断里加一个计数/置标志,观察无负载时是否确实进 BEI、ECC 的 ACKER 是否置位,验证判断逻辑。
  • 再在确认 ACKER 后对 CMR 的 AT 写 1 中止,观察是否继续发下一帧。
  • 接负载后确认功能恢复正常,避免 AT 误动作影响正常通信。

如果您方便的话,可以把您现在的发送函数代码 + CAN 中断服务函数贴出来,我帮您对照手册核对一下中断标志清除、TR/AT 的操作顺序是否正确,并给出可编译的修改版。

*信息来源:STC32G 系列手册「CAN总线,兼容CAN2.0A/B,1M~10Kbps;I2S,时钟」章节之 CAN命令寄存器(CMR)、CAN中断/应答寄存器(ISR/IACK)、CAN总线错误信息寄存器(ECC)、CAN状态寄存器(SR);论坛帖 tid=25612 与 tid=20099 的用户反馈。*


🔍 搜索 "STC 单片机 论坛 帖子"

⏱️ 查找用时 6.8s | 总回答用时 13.6s

回复

使用道具 举报 送花

1

主题

2

回帖

15

积分

新手上路

积分
15
 楼主| 发表于 2026-9-22 19:32:02 | 显示全部楼层
lcws*** 发表于 2026-9-21 13:20
BEI中断里对CMR的AT置1,终止传输。

谢谢老师,我再研究研究
回复

使用道具 举报 送花

1

主题

2

回帖

15

积分

新手上路

积分
15
 楼主| 发表于 2026-9-22 19:41:40 | 显示全部楼层
神*** 发表于 2026-9-22 15:47
我已经掌握了这个问题的完整背景和官方/社区的回复思路。下面给出正式解答。
问题解答:STC32G12K128 的 CA ...

你好老师,代码大概如下,按照例程里面的稍微修改了一下。

                               
                               
                                if(sr & 0x01)                //判断是否有 BS:BUS-OFF状态
                                {
                                        CANAR = MR;
                                        CANDR &= ~0x04;  //清除 Reset Mode, 从BUS-OFF状态退出
                                }
                                else
                                {
                                        CanSendMsg(CAN_ID,TX_BUF);
                                }
                        }
                }



        }
}


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


}


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

//========================================================================
// 函数: void CanWriteReg(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(u8 *pdat)
// 描述: 读取CAN缓冲区数据函数。
// 参数: *pdat: 存放CAN缓冲区数据.
// 返回: none.
// 版本: VER1.0
// 日期: 2020-11-16
// 备注:
//========================================================================
void CanReadFifo(u8 *pdat)
{
        pdat[0]  = CanReadReg(RX_BUF0);
        pdat[1]  = CanReadReg(RX_BUF1);
        pdat[2]  = CanReadReg(RX_BUF2);
        pdat[3]  = CanReadReg(RX_BUF3);

        pdat[4]  = CanReadReg(RX_BUF0);
        pdat[5]  = CanReadReg(RX_BUF1);
        pdat[6]  = CanReadReg(RX_BUF2);
        pdat[7]  = CanReadReg(RX_BUF3);

        pdat[8]  = CanReadReg(RX_BUF0);
        pdat[9]  = CanReadReg(RX_BUF1);
        pdat[10] = CanReadReg(RX_BUF2);
        pdat[11] = CanReadReg(RX_BUF3);

        pdat[12]  = CanReadReg(RX_BUF0);
        pdat[13]  = CanReadReg(RX_BUF1);
        pdat[14]  = CanReadReg(RX_BUF2);
        pdat[15]  = CanReadReg(RX_BUF3);
}

//========================================================================
// 函数: u16 CanReadMsg(u8 *pdat)
// 描述: CAN接收数据函数。
// 参数: *pdat: 接收数据缓冲区.
// 返回: CAN ID.
// 版本: VER1.0
// 日期: 2020-11-19
// 备注:
//========================================================================
u16 CanReadMsg(u8 *pdat)
{
        u8 i;
        u16 CanID;
        u8 buffer[16];

        CanReadFifo(buffer);
        CanID = ((buffer[1] << 8) + buffer[2]) >> 5;
        for(i=0;i<8;i++)
        {
                pdat = buffer[i+3];
        }
        return CanID;
}

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

        CanID = canid << 5;
//        CanWriteReg(TX_BUF0,0x08);        //bit7: 标准帧(0)/扩展帧(1), bit6: 数据帧(0)/远程帧(1), bit3~bit0: 数据长度(DLC)
        CanWriteReg(TX_BUF0,changdu);
        CanWriteReg(TX_BUF1,(u8)(CanID>>8));
        CanWriteReg(TX_BUF2,(u8)CanID);
        CanWriteReg(TX_BUF3,pdat[0]);

        CanWriteReg(TX_BUF0,pdat[1]);
        CanWriteReg(TX_BUF1,pdat[2]);
        CanWriteReg(TX_BUF2,pdat[3]);
        CanWriteReg(TX_BUF3,pdat[4]);

        CanWriteReg(TX_BUF0,pdat[5]);
        CanWriteReg(TX_BUF1,pdat[6]);
        CanWriteReg(TX_BUF2,pdat[7]);
       
        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模块使能
        CANSEL = 0;         //选择CAN1模块
        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

//        CAN2EN = 1;         //CAN2模块使能
//        CANSEL = 1;         //选择CAN2模块
//        P_SW3 = (P_SW3 & ~(3)) | (0);       //端口切换(CAN_Rx,CAN_Tx) 0x00:P0.2,P0.3  0x01:P5.2,P5.3  0x02:P4.6,P4.7  0x03:P7.2,P7.3

        CanWriteReg(MR  ,0x04);                //使能 Reset Mode
        CANSetBaudrate();        //设置波特率
       
    //使用单滤波过滤器,只接收 ID=0x0067 的报文  0000 1100 1110
/*        CanWriteReg(ACR0,0x0C);                //总线验收代码寄存器
        CanWriteReg(ACR1,0xE0);
        CanWriteReg(ACR2,0x00);
        CanWriteReg(ACR3,0x00);
        CanWriteReg(AMR0,0x00);                //总线验收屏蔽寄存器
        CanWriteReg(AMR1,0x0F);
        CanWriteReg(AMR2,0xFF);
        CanWriteReg(AMR3,0xFF);
*/
        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, 采用单滤波设置(设置过滤器后注意选择滤波模式)

        CANICR = 0x02;                //CAN中断使能
}

//========================================================================
// 函数: void CANBUS_Interrupt(void) interrupt CAN_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)  //TI
        {
                CANAR = ISR;
                CANDR |= 0x04;    //CLR FLAG
        }
        if((isr & 0x08) == 0x08)  //RI
        {
                CANAR = ISR;
                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现场恢复
}
回复

使用道具 举报 送花

已绑定手机

872

主题

1万

回帖

2万

积分

管理员

积分
25432
发表于 2026-9-22 20:05:33 | 显示全部楼层
直接反复和  STC AI助手讨论 https://help.stcaimcu.com/
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

GMT+8, 2026-11-8 02:49 , Processed in 0.073241 second(s), 71 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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