环回模式实验

Tutorial: 卓越工程师 Category: 嵌入式 Published: 2026-10-23 11:05:29 Views: 0 Likes: 0 Comments: 0

191.1.14 环回模式实验

hcan.Init.Mode = CAN_MODE_LOOPBACK;                         /* 将 bxCAN 配置为环回模式;这是本课相对上一工程的核心改动。 */

Can_Filter_Init();                                         /* 配置接收过滤器并启动 CAN;环回报文同样要先通过过滤器。 */
OLED_Init();                                               /* 初始化 OLED,供后面显示环回收到的温湿度数据。 */
OLED_Clear();                                              /* 清除 OLED 显存,避免显示上一次运行留下的内容。 */

while (1)                                                  /* 主循环同时执行“接收”和“发送”,构成一次完整的自发自收实验。 */
{
    uint32_t Recv_ID = 0;                                  /* 保存本次接收报文的标识符。 */
    uint32_t Recv_Len = Can_Recv_Msg(&Recv_ID, Rx_Data);    /* 从 FIFO0 取出一帧,并获得报文 ID 和数据长度。 */
    if(Recv_Len>0)                                         /* 返回长度大于 0,表示本次取到了带数据的报文。 */
    {
        printf("recv id 0x%x:\r\n", Recv_ID);             /* 通过 USART1 打印接收到的 CAN 标识符。 */
        printf("recv length %u:\r\n", Recv_Len);          /* 打印数据长度 DLC。 */
        printf("recv data:");                              /* 输出接收数据的提示文字。 */
        for(int i=0;i<Recv_Len;i++)                        /* 按接收长度逐字节遍历数据区。 */
            printf(" %u ",Rx_Data[i]);                    /* 以无符号十进制输出当前字节。 */
        printf("\r\n");                                   /* 当前报文打印结束后换行。 */

        OLED_ShowString(32, 0, "Can Test",16);             /* 在 OLED 第 0 行显示实验标题。 */
        sprintf(OLED_BUFF,"H:%u  T:%u",Rx_Data[0],Rx_Data[2]); /* 按课程数据布局提取湿度整数和温度整数。 */
        OLED_ShowString(0, 2, OLED_BUFF,16);                /* 在 OLED 第 2 行显示格式化后的温湿度。 */

        memset(Rx_Data, 0, 8);                             /* 清空接收缓冲区,避免旧数据干扰下一次观察。 */
    }else{                                                 /* 当前 FIFO 没有取到有效的非零长度报文。 */
        printf("recv eorro\r\n");                         /* 保留课程源码中的原始错误提示字符串。 */
    }

    DHT_Read();                                            /* 读取 DHT11;结果写入驱动提供的全局 Data 数组。 */
    HAL_Delay(100);                                        /* 按课程源码延时 100 ms 后再发送。 */
    uint32_t ret = Can_Send_Msg(0x12, SEND_LEN, Data);      /* 用标准 ID 0x12 发送 Data 的前 5 个字节。 */
    if(ret == 0)                                           /* 课程封装以 0 表示 HAL 层已接受本次发送请求。 */
    {
        printf("send data:");                             /* 输出发送数据的提示文字。 */
        for(int i=0;i<SEND_LEN;i++)                        /* 遍历本次实际发送的 5 个字节。 */
            printf(" %u ",Data[i]);                       /* 逐字节打印发送内容。 */
        printf("\r\n");                                   /* 发送数据打印结束后换行。 */
        printf("send ok\r\n");                            /* 打印发送接口返回成功的信息。 */
    }else{                                                 /* 发送接口返回非 0。 */
        printf("send eorro\r\n");                         /* 保留课程源码中的原始错误提示字符串。 */
    }
}

本课用 07Can_DHT11_OLED_Loop 工程验证 bxCAN 的测试模式。实验板没有连接 CAN 收发器,程序仍能把自己发送的报文收回来,由此证明数据回路发生在 MCU 内部的 CAN 控制器 中,而不是外部 CANH/CANL 总线上。

一、工作模式

1.1 正常模式

bxCAN 的运行状态与测试模式不是同一层概念:

  • 睡眠模式:降低功耗,控制器不进行正常报文收发。复位后 bxCAN 默认处于睡眠状态。
  • 初始化模式:软件配置位时序、测试模式等参数的状态。SILM、LBKM 等位只能在初始化模式修改。
  • 正常模式:完成初始化并退出睡眠后,控制器开始参与总线通信。

静默、环回和环回静默是正常工作流程下的三种测试配置,由 CAN_BTR 的 SILM、LBKM 位组合得到。CubeMX 生成的 HAL_CAN_Init() 会负责进入初始化模式、写入这些配置,再退出初始化模式;业务代码还需要调用 HAL_CAN_Start()。本工程把启动动作封装在 Can_Filter_Init() 中。

1.2 静默模式

静默模式对应 CAN_MODE_SILENT:

  • CANRX 输入仍连接外部总线,因此控制器可以监听数据帧和远程帧。
  • 控制器的显性输出不会真正驱动到总线;CANTX 对外维持隐性状态。
  • 本节点不会向外发出 ACK、主动错误标志或过载标志,所以不会主动改变总线电平。
  • 适用于总线监听、协议分析以及在不干扰现有网络的前提下检查接收链路。

“静默”应理解为只监听、不影响总线,而不只是软件层面的“没有调用发送函数”。

1.3 环回模式

环回模式对应 CAN_MODE_LOOPBACK,本课实际使用的配置是:

hcan.Init.Mode = CAN_MODE_LOOPBACK; /* 配置 LBKM=1、SILM=0,启用普通环回测试。 */

数据路径可以概括为:

发送邮箱 → bxCAN 发送逻辑 ─┬→ CANTX 引脚
                           └→ 内部反馈 → 接收过滤器 → 接收 FIFO

外部 CANRX 引脚 ──×──→ bxCAN 接收逻辑(环回模式下忽略该引脚)

需要准确区分三个细节:

  1. 发送报文会被内部反馈成接收报文,但仍要通过已配置的接收过滤器,之后才会进入 FIFO。
  2. CANRX 引脚的实际电平会被忽略,外部总线报文不会混入本次自测。
  3. 普通环回模式下,发送波形仍可以在 CANTX 引脚上观察到;只是控制器为了完成单节点自测,会忽略 ACK 错误。

因此,普通环回适合验证 CAN 控制器的发送邮箱、位时序、过滤器、FIFO 和接收软件链路,但它不能证明外部收发器、终端电阻和 CANH/CANL 布线正确。

1.4 环回静默模式

环回静默模式对应 CAN_MODE_SILENT_LOOPBACK,即 LBKM=1、SILM=1:

  • 发送数据直接在控制器内部反馈给接收端;
  • CANRX 与外部总线断开;
  • CANTX 始终保持隐性,不把测试波形送到外部总线;
  • 适合设备已经接入真实网络时进行“热自测”,避免测试报文干扰其他节点。

四种模式可以这样比较:

模式接收外部总线CANTX 输出报文内部自收典型用途
正常是是否实际组网通信
静默是否否无干扰监听总线
环回否是是实验室自发自收、观察 TX 波形
环回静默否否是不影响外部网络的内部自测

上述行为可对照 ST 的 RM0008 参考手册 中“bxCAN 测试模式”章节。

二、环回实验

2.1 工程改动

课程从前面的 04Can_DHT11_OLED 工程复制出 07Can_DHT11_OLED_Loop,主要做两项改动:

  1. 将 CAN 模式从 CAN_MODE_NORMAL 改成 CAN_MODE_LOOPBACK。
  2. 注释原来用于二选一的 #if ZET6、#else、#endif,让接收代码和发送代码在同一个循环中都参与编译。

这里的“关掉预处理”不是关闭编译器功能,而是把条件编译指令注释掉。源文件中的普通 C 语句因此依次执行:先尝试接收,再读取 DHT11 并发送。

模式应优先在 CubeMX 的 CAN → Parameter Settings → Mode 中选择 Loopback,然后重新生成代码。若只手改 can.c,下次 CubeMX 生成代码时可能恢复为 .ioc 文件中的配置。本工程的 .ioc 已保存:

  • Mode = CAN_MODE_LOOPBACK
  • APB1/CAN 时钟为 36 MHz
  • 预分频为 4
  • BS1 为 9 TQ,BS2 为 8 TQ
  • 计算波特率为 500 kbit/s

2.2 硬件连接

本实验需要:

  • STM32F103ZET6 开发板;
  • DHT11,数据线连接 PA5;
  • I²C OLED,SCL/SDA 分别使用 PB6/PB7;
  • USART1 串口,用于观察日志;
  • ST-Link,用于下载和调试。

不需要外接 CAN 收发器。PA12 上可能出现 CANTX 波形,但 PA12 是控制器逻辑引脚,不能代替 CAN 收发器形成符合物理层规范的 CANH/CANL 差分总线。

2.3 程序执行顺序

当前循环的顺序是“先收、后发”:

  1. 第一次进入循环时,FIFO 可能还没有报文,因此可能先打印一次 recv eorro。
  2. 随后 DHT_Read() 更新数据,Can_Send_Msg() 把 5 字节报文送入发送邮箱。
  3. bxCAN 在内部把该帧反馈到接收路径,通过过滤器后进入 FIFO0。
  4. 下一轮循环执行 Can_Recv_Msg() 时取出上一轮发送的报文,串口和 OLED 显示相应数据。

所以“第一次没有收到”不等于环回失败,要结合后续循环、发送返回值和 FIFO 状态判断。

另一个与 CAN 环回无关、但实验时必须知道的限制是:课程代码每轮只延时 100 ms 就再次调用 DHT_Read(),快于 DHT11 常用的约 1 s 采样周期。环回链路仍可用缓存中的数据验证,但不要把每次循环打印都理解成传感器产生了一组全新的测量值。

2.4 预期现象

  • 串口打印 send data 与 recv data,两组有效载荷应一致。
  • 接收 ID 应为 0x12,长度应为 5。
  • OLED 使用 Rx_Data[0] 和 Rx_Data[2] 显示湿度、温度整数部分。
  • 即使没有 CAN 收发器,内部接收仍能成功。

课程随后把模式改回 CAN_MODE_NORMAL。没有外部收发器、第二个节点和 ACK 时,正常模式不能形成完整 CAN 通信,接收 FIFO 也不会出现内部反馈的报文,因此会看到接收错误或发送异常。这一对比正是环回实验的验证点。

2.5 本实验能验证什么、不能验证什么

能够验证:

  • CAN 外设已经初始化;
  • 发送邮箱接口可以装载报文;
  • 报文能经过内部位时序和环回路径;
  • 过滤器、FIFO 和接收函数能工作;
  • 应用层对 ID、长度和数据的处理基本正确。

不能验证:

  • CAN 收发器供电和使能脚是否正确;
  • CANH/CANL 是否接反、短路或存在共模问题;
  • 两端 120 Ω 终端电阻是否正确;
  • 实际总线上的 ACK、仲裁、错误恢复和抗干扰能力;
  • 多节点在目标波特率下能否稳定通信。

三、学习检查

完成本课后,应能回答:

  1. 为什么环回模式不接 CAN 收发器也能收到自己的数据?
  2. 普通环回与环回静默在 CANTX 引脚上的行为有什么区别?
  3. 为什么环回收到的报文仍可能被过滤器挡住?
  4. 为什么应在 CubeMX 中保存模式,而不只修改生成的 can.c?
  5. 环回成功为什么不能证明 CANH/CANL 物理网络一定正常?