环回模式实验
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 接收逻辑(环回模式下忽略该引脚)
需要准确区分三个细节:
- 发送报文会被内部反馈成接收报文,但仍要通过已配置的接收过滤器,之后才会进入 FIFO。
- CANRX 引脚的实际电平会被忽略,外部总线报文不会混入本次自测。
- 普通环回模式下,发送波形仍可以在 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,主要做两项改动:
- 将 CAN 模式从
CAN_MODE_NORMAL改成CAN_MODE_LOOPBACK。 - 注释原来用于二选一的
#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 程序执行顺序
当前循环的顺序是“先收、后发”:
- 第一次进入循环时,FIFO 可能还没有报文,因此可能先打印一次
recv eorro。 - 随后
DHT_Read()更新数据,Can_Send_Msg()把 5 字节报文送入发送邮箱。 - bxCAN 在内部把该帧反馈到接收路径,通过过滤器后进入 FIFO0。
- 下一轮循环执行
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、仲裁、错误恢复和抗干扰能力;
- 多节点在目标波特率下能否稳定通信。
三、学习检查
完成本课后,应能回答:
- 为什么环回模式不接 CAN 收发器也能收到自己的数据?
- 普通环回与环回静默在 CANTX 引脚上的行为有什么区别?
- 为什么环回收到的报文仍可能被过滤器挡住?
- 为什么应在 CubeMX 中保存模式,而不只修改生成的
can.c? - 环回成功为什么不能证明 CANH/CANL 物理网络一定正常?