STM32F103 这颗芯片在国内嵌入式圈子里的地位,懂的都懂。价格便宜、资料多、例程烂大街,随便一搜就是标准库、HAL 库、寄存器版三件套,再加上 FreeRTOS 这种开源 RTOS 加持,本该是新手入门、老手做小项目的梦幻开局。但很多朋友在实际打样、焊接、烧录之后,发现一个很诡异的现象:芯片上电之后一点反应都没有,调试器连不上,代码下载不进去,或者偶尔能跑但跑着跑着就死机。
如果你排查了供电、复位、晶振、BOOT 引脚、下载电路,全部没问题,那我很认真地提醒你一句:你可能买到“假芯片”了。这里的假芯片不是指那种肉眼可见的打磨片、翻新片,而是指市面上大量存在的、打着 STM32F103 型号但内部结构或工艺存在缺陷的兼容芯片、降级片、散新片。这篇文章我就结合自己踩过的一堆坑,把“STM32F103 + FreeRTOS 上电没反应”和“假芯片”之间那些纠缠不清的关系彻底讲清楚,顺带聊聊在 FreeRTOS 移植和调试过程中,怎么反推硬件是原装还是来路不明。
1. 内容整体设计与思路拆解
先别急着抓瞎换芯片,我建议你按照“硬件最小系统验证 -> 烧录链路验证 -> RTOS 运行验证”的顺序来排查。很多人一上来就怀疑 FreeRTOS 配置有问题,其实在芯片本身靠不靠谱这件事没确认之前,所有软件层面的分析都属于空中楼阁。
STM32F103 最小系统的核心就那几样:3.3V 供电、8MHz 晶振加两个负载电容、复位电路(NRST 上拉 10k 电阻加 100nF 电容)、BOOT0 和 BOOT1 引脚接地、SWD 下载口(SWDIO、SWCLK)。这些看着简单,但假芯片恰恰在这些最简单的地方翻车。
我当初那个项目用的是 STM32F103C8T6,板子是自己画的四层板,原理图参考的是网上流传最广的那个“F103 最小系统参考设计”。焊接完第一批 20 片板子后,有 5 片上电后完全无反应,测量 3.3V 正常,NRST 有 3.3V,8MHz 晶振两端有波形(用示波器能抓到正弦波),BOOT0 接地正常,但就是 SWD 扫描不到设备。换了一颗芯片重焊,立刻就能烧录。这时候我才警觉起来,把另外几颗疑似“假芯片”的料拿去仔细检查,发现丝印和正品有明显差异,来源是某宝上单价 4.5 元的“全新原装”,而正规渠道同型号芯片价格普遍在 8 元以上。
这种经历不是个例,在国内嵌入式开发圈里,“F103 假芯片”早就不是什么新鲜话题了。所以这篇文章的整体思路,就是先帮你判断芯片是真是假,再告诉你假芯片在 FreeRTOS 项目里会造成哪些千奇百怪的故障,最后给出移植 FreeRTOS 时必须做的几个底层的验证步骤。
2. 核心细节解析与实操要点
2.1 假芯片的常见来源与识别方法
先说说什么叫“假芯片”。严格意义上讲,假芯片分为几类:
- 打磨片:把原本印着别的型号的芯片表面磨掉,重新打上 STM32F103 的丝印,这类芯片最坑,因为你完全不知道它内部到底是什么内核、多少 Flash、多少 RAM。
- 翻新片:从废旧板子上拆下来,重新整形、去氧化、重新打标。这类芯片工作状态不稳定,内部 Flash 可能有坏块。
- 散新片:来路不明、无正规渠道记录的新片,可能是生产厂家淘汰的次品,也可能是被人为筛选下来的不合格批次。
- 兼容片:国产 GD32F103、APM32F103 等直接标成 STM32F103 卖,虽然大部分情况下能兼容,但在某些外设行为、电源时序、ADC 精度上有差异,最典型的就是 GD32 的 USB 和 CAN 外设在低版本固件上有坑,还有主频跑 72MHz 时个别批次会不稳定。
识别方法其实不难,但需要借助一点工具:
- 看丝印:正品 STM32F103C8T6 的丝印字体是激光刻字,线条清晰,笔画边缘锐利。打磨片重新打标后,字体一般偏粗,有轻微晕染感,甚至能看到旧丝印的残留痕迹。
- 看底部:正品芯片底部有一圈金属散热焊盘(exposed pad),翻新片这个焊盘可能有氧化痕迹或划痕。
- 拿万用表测电源引脚对地阻抗:正品 F103 的 VDD 对 GND 阻抗一般在几百欧姆到几千欧姆之间,具体值和万用表型号有关,但如果同一批料中某几颗的阻抗明显异于其他颗,就要高度警惕。
- 用 STM32CubeProgrammer 或 STM32 ST-LINK Utility 读取芯片的 UID 和 Flash 大小,然后和正品参数对比。
用 STM32CubeProgrammer 读取芯片信息是最权威的验证方式。正常 STM32F103C8T6 应该显示:
- Device ID:0x410(STM32F1xx 中容量型号)
- Flash 大小:64KB
- RAM 大小:20KB
如果你读出 Flash 是 128KB,或者 Device ID 显示 0x411(那是 F103RE 之类的引脚兼容芯片),又或者读出的 UID 全 0xFF,那基本可以断定芯片有问题。
2.2 假芯片对 FreeRTOS 移植的“雪崩效应”
很多人觉得,芯片是真是假,顶多影响能不能烧录,只要程序能烧进去,跑起来应该差不多。这个想法在裸机 demo 上可能还成立,但在 FreeRTOS 这种依赖精确时钟节拍、内存管理和任务切换的系统里,假芯片的缺陷会被急剧放大。
我遇到过的几个典型案例:
案例一:系统滴答(SysTick)不准。FreeRTOS 的时基默认用 SysTick 实现,假设你配置了 1ms 一个 tick,但某颗翻新片的内部振荡器参数漂移严重,或者外部晶振起振不稳,实际 tick 可能是 0.8ms 也可能是 1.5ms,导致任务调度完全混乱。最典型的症状就是:LED 闪烁频率明显不对,串口输出乱序,任务优先级高的好像不执行,其实不是不执行,而是时间基准全乱套。
案例二:堆栈溢出检测触发。FreeRTOS 里有堆栈溢出检测(configCHECK_FOR_STACK_OVERFLOW),当任务栈写越界时会触发钩子函数。假芯片的内存 SRAM 可能存在坏块,或者存储单元访问时序不稳定,导致任务栈在运行过程中被随机改写,然后莫名其妙触发堆栈溢出。真实现场就是这个钩子函数被触发后,串口打印一行 “Stack overflow!” 然后系统卡死。
案例三:FreeRTOS 临界区失效。临界区的核心是暂停调度器或者关中断,但有些兼容芯片在中断嵌套(NVIC)的实现上和正品有细微差异,导致进入临界区后中断并没有被真正屏蔽,出现竞态条件,信号量被错误释放,队列数据被覆盖。这种问题跑裸机永远发现不了,一上 FreeRTOS 立刻暴露。
2.3 FreeRTOS 最小系统验证三板斧
不管你是新移植物件,还是拿到一块新板子,我建议你先把 FreeRTOS 精简到一个最原始的“点灯”工程,只做三件事,排除掉大部分芯片硬件问题。
第一件事:跑一个独立于 FreeRTOS 的裸机 LED 闪烁程序(使用 SysTick 延时),确认基础硬件没问题。我一般用 GPIOA 的 PA1 引脚,推挽输出,500ms 翻转一次。如果这个程序在几块板子上都能稳定跑几个小时,说明供电、晶振、复位、GPIO 这些外设是好的。
第二件事:建一个最简单的 FreeRTOS 工程,只创建一个任务,任务里又是 LED 闪烁,但这次用 vTaskDelay(500) 来实现。注意打开 configUSE_TIMERS、configUSE_TICK_HOOK,在 tick 钩子里累计一个计数器,通过调试器读取这个计数器,验证 tick 频率是否准确。正确情况下,计数器每秒递增 1000(对应 1ms tick)。
第三件事:用 xTaskCreate 创建两个任务,一个每秒打印一个字符 A,另一个每秒打印字符 B,通过串口观察输出是否严格交替且互不阻塞。如果 A 和 B 能稳定交替输出,说明 FreeRTOS 的任务调度、信号量、串口中断嵌套都没问题。
这三板斧看着简单,但能筛掉 90% 的“假芯片 + FreeRTOS”组合故障。我踩坑后发现,很多所谓“FreeRTOS 跑不起来”的问题,追根溯源根本就是芯片电源稳定度差、复位时序异常或 SRAM 坏块。
3. 实操过程与核心环节实现
3.1 最小系统硬件检查清单
在烧录任何代码之前,先用万用表和示波器过一遍这张检查表。
| 检查项 | 测试方法 | 正常结果 | 异常可能原因 |
|---|---|---|---|
| VDD(3.3V) | 万用表直流档测 C1 两端 | 3.28~3.32V | 电源芯片损坏、滤波电容极性反 |
| NRST | 万用表测电压 | 3.3V | 上拉电阻没焊或短路 |
| BOOT0 | 万用表测电压 | 0V | 没接地,或引脚悬空 |
| 8MHz 晶振 | 示波器测量 OSC_IN 引脚 | 有明显的正弦波 | 晶振虚焊、负载电容不匹配 |
| SWDIO | 万用表测对地二极管压降 | 0.4~0.7V | SWDIO 被占死或芯片损坏 |
| SWCLK | 万用表测对地二极管压降 | 0.4~0.7V | SWCLK 被占死或芯片损坏 |
| NRST 复位波形 | 示波器观察上电瞬间 | 从 0V 快速拉高到 3.3V | RC 复位电路参数不对 |
这张表里的任何一项异常,都可能导致芯片“看起来没反应”。注意,很多人认为“芯片有电流消耗,说明芯片在工作”,这不一定。我之前遇到过一颗兼容片,上电后整板电流高达 200mA,远高于正常 F103 的 50~80mA(不带外设时),结果芯片内部已经发生了 latch-up(闩锁效应),程序根本跑不起来,原因是芯片内部保护电路设计不完善,上电过程中 3.3V 上升速率过快触发了闩锁。
所以,上电瞬间最好用示波器电流探头或串联采样电阻看浪涌电流大小和持续时间,这一步对翻新片尤为重要。
3.2 烧录链路验证与“三短一长”排查法
如果你确认最小系统硬件没问题,但 Keil 或 STM32CubeProgrammer 仍然提示 no target connected,可以按下面的“三短一长”思路排查:
- 短按一下:检查 ST-LINK 或 J-Link 和板子的 SWD 接线是否可靠。最常见的问题是 SWDIO、SWCLK、GND 三条线排序接反,或者杜邦线接触不良。这种情况的典型表现是时连时断。
- 短按两下:检查目标芯片供电。SWD 调试器本身可以从引脚偷电,但电流能力有限,如果芯片 VDD 没有单独的 3.3V 供电,调试器可能能扫到芯片但写 Flash 时报错。
- 短按三下:检查复位引脚。有些调试器在连接目标时会拉低 NRST,如果 NRST 外围电容过大(比如超过 1uF),可能导致复位时间过长,连接不上。
- 长按一下:如果以上全部正常,把 BOOT0 拉高到 3.3V,芯片从系统存储器启动(也就是出厂 Bootloader),再尝试连接。如果 BOOT0 拉高后能连上,说明用户 Flash 区域可能被写坏或者程序里配置了不合适的引脚状态(比如把 SWD 引脚重映射成了普通 GPIO),此时可以先全片擦除,再把 BOOT0 拉回低电平。
这套方法的核心逻辑就是逐级排除“链路问题、供电问题、复位问题、Flash 内容问题”。在很多案例里,假芯片恰恰是在最后一步暴露出来的:长期老化的翻新片,其 BootROM 已经损坏,BOOT0 拉高后也无法进入系统 Bootloader,SWD 就完全连不上。这种情况几乎可以断定是芯片本身的问题,不用纠结软件了。
3.3 在 Keil 工程中验证芯片真伪的补充手段
除了外接工具,Keil 本身也给了我们一些验证手段。在 Debug 设置里选好 ST-Link 或 J-Link 后,进入 Flash Download 页面,可以看到自动检测到的芯片型号。正品 C8T6 一般显示 STM32F103C8,Flash 大小 64KB。某些假芯片因为内部 Flash 实际是 128KB(比如用 C8T6 重新打磨成 C6T6,或者用 C8T6 冒充 C6T6),Keil 检测后可能提示 “Mismatch”。
还有一个非常实用的方法:读 UID。正品 F103 的 UID 在地址 0x1FFFF7E8 处,共 12 字节。你在 Keil 的 Command 窗口输入指令或直接在调试状态下打开 Memory 窗口查看这个地址的数值。如果读出来全是 0x00 或全是 0xFF,几乎可以断言不是正品。
另外,在程序里也可以把 UID 读取出来,通过串口打印到上位机,和芯片丝印上的批次信息做交叉验证。虽然 UID 不具备绝对的证明力(有些打磨片连 UID 都伪造成 0xFFFFFFFF),但至少能作为排查的辅助手段。
3.4 FreeRTOS 移植时的栈与堆配置技巧
回到 FreeRTOS 本身,即使芯片是正品,移植时如果堆栈配置不合理,也会出现类似于“假芯片”的死机现象。我给一个比较稳妥的初始配置参考:
#define configMINIMAL_STACK_SIZE 128 // 最小任务栈(单位:字,即 512 字节) #define configTOTAL_HEAP_SIZE ( 20 * 1024 ) // C8T6 有 20KB RAM,堆全给 FreeRTOS #define configUSE_TIMERS 1 #define configTIMER_TASK_STACK_DEPTH 256 #define configMAX_TASK_NAME_LEN 16configTOTAL_HEAP_SIZE设置为 20KB 且没有额外分配大数组时,FreeRTOS 会使用内部的内存管理(heap_4.c)来分配任务栈和内核对象。注意,20KB 是 C8T6 的 RAM 上限,如果你还要用串口接收大缓冲或 DMA,就必须缩小 heap。硬核的做法是留出 2~4KB 给用户别的大数组,也就是configTOTAL_HEAP_SIZE设成 16000 左右。
假芯片场景下,如果你买的“F103C8T6”实际是 F103C6T6(Flash 32KB、RAM 10KB),你用这么大的 heap 去创建任务,大概率在xTaskCreate时直接返回pdFAIL(错误码为 -1)。更隐蔽的情况是,芯片内部 SRAM 有坏块,导致 heap 内存里分配出来的内存块读写出错,任务创建看似成功,但任务一跑就 HardFault。
所以在 FreeRTOS 移植阶段,强烈建议你先在main函数的vTaskStartScheduler()之前打印一下当前剩余堆栈空间和堆大小:
printf("Free heap init: %u\n", (unsigned int)xPortGetFreeHeapSize());然后在创建每个任务后再次打印,确认堆内存分配是否递减正常。这一步能帮你把“内存不够”和“芯片是假的”两个问题区分开。
4. 常见问题与排查技巧实录
4.1 上电后 LED 都不亮,但芯片有电流
这种情况我排查过很多次,最高频的原因有三种:一是 3.3V 电源纹波过大,翻新片的复位电路对电源噪声极其敏感,哪怕正常启动也会被瞬间拉回复位态;二是外部晶振起振时间过长,程序卡在启动文件的SystemInit等待晶振稳定,而某些兼容片内部 RTC 或时钟安全系统(CSS)行为异常;三是芯片的 BOOT1 被意外拉高,导致从 RAM 启动,但 RAM 里没有有效代码,程序指针飞掉。
你可以在SystemInit之后、main函数最开始的位置加一个 GPIO 翻转操作(比如把 PA1 拉高),用示波器测 PA1 电平,如果能看到一个短暂的高电平脉冲,说明程序已经跑起来了,不是芯片假,而是后面某个外设初始化卡死。如果 PA1 从始至终没有电平变化,就需要回到上一步检查最小系统。
4.2 烧录成功但 vTaskDelay 不工作
还有个典型的假芯片症状:程序能烧进去,裸机点灯正常,但加了 FreeRTOS 后,任务里循环调用vTaskDelay(1000),LED 却一直不闪烁。用调试器打断点看,程序卡在vTaskDelay内部的portYIELD_WITHIN_API()或xTaskIncrementTick相关代码里。
这类问题的根子多半在 SysTick 中断没触发。FreeRTOS 的vPortSetupTimerInterrupt函数会配置 SysTick,但启动文件里如果没有正确使能 SysTick 中断,任务调度器就永远无法推进时基。在假芯片上,SysTick 控制寄存器(地址 0xE000E010)的访问可能出现异常,导致定时器重装载值和当前值不同步。你在调试器里可以直接查看SysTick->CTRL和SysTick->VAL,如果VAL一直为 0 或者不递减,说明 SysTick 根本没跑起来。
另一个容易被忽略的点是中断优先级分组。FreeRTOS 要求使用NVIC_PriorityGroup_4(即全部 4 位都用于优先级抢占),如果你在main里设置了其他分组(比如NVIC_PriorityGroup_2),FreeRTOS 的临界区实现会失效,SysTick 中断可能在错误的时间被屏蔽,导致调度器挂死。
4.3 串口输出乱码或首个字符丢失
这个和假芯片相关的概率低一些,但也不排除。F103 的 USART 波特率由外设时钟(PCLK)决定,如果芯片内部 PLL 不稳定,倍频出来的 72MHz 和标准值偏差较大,按 115200 的配置实际波特率可能是 114300 或 116000,大概率能通信但偶尔会乱码。用示波器看 TX 引脚的波形,测量单 bit 时间宽度,正常 115200 波特率单 bit 约 8.68us,如果差得超过 5%,就需要注意了。
还有一种情况是翻新片在焊接过程中受过高温,内部晶振基准漂移,对 USART 这样的异步通信影响尤为明显。解决办法是:把串口波特率降到 9600,看是否稳定。如果 9600 下通信稳定,说明芯片内部的时钟源确实有偏差,此时可以考虑改用内部 HSI 振荡器跑 64MHz(注意外设时钟分频),而不是外部 HSE。
4.4 FreeRTOS 任务跑飞或 HardFault 的排查顺序表
如果你已经确认芯片来源没问题,那任务跑飞大概率是移植配置问题。但如果你用了“散新片”或者价格过于便宜的“全新原装”,排查顺序就要调整。
| 症状 | 优先级 1 排查 | 优先级 2 排查 | 优先级 3 排查 |
|---|---|---|---|
| 任务不调度 | 配置问题(时基源、优先级分组) | 芯片 SysTick 异常 | 外部晶振频率偏差 |
| 随机 HardFault | 任务栈溢出 | 内存堆耗尽 | 翻新片内部 SRAM 不稳定 |
| 信号量无效 | 临界区配置 | 中断嵌套设置 | 内核兼容性 |
| 外设中断频繁死机 | 中断优先级分配 | 中断服务函数运行过长 | 芯片中断控制器缺陷 |
我在实际项目里的习惯是:先把configASSERT打开,xTaskCreate返回值检查打出来,再打开configCHECK_FOR_STACK_OVERFLOW设为 2,加入栈溢出钩子函数。这样如果任务栈越界,能第一时间看到提示。对于 FreeRTOS 的初学用户,我不建议一上来就追求高配置,而是先把基础跑通再加任务,每加一个任务做一次稳定性测试,逐步积累信心。
4.5 巧用 FreeRTOS 的 TraceALYZER 工具辅助定位
当你的 FreeRTOS 项目出现诡异问题时,手动打日志的效率其实很低。我个人的经验是用SEGGER SystemView或者FreeRTOS+Trace,通过 J-Link 的 RTT 通道实时查看任务调度、堆栈使用、中断触发情况。这个工具能看到每个任务的执行状态、切换频率、时间线,排查“某个任务好像没在跑”的问题特别高效。
不过要注意,SystemView 依赖调试器实时读取目标内存,这要求 SWD 链路非常稳定。如果你买到的芯片 SWD 引脚内部上拉电阻有问题,导致链路时通时断,SystemView 采集到的数据就不完整。这种情况下,先用一个最简单的裸机程序测 SWD 稳定性,比如每隔 100ms 翻转一次 GPIO,用逻辑分析仪看 SWDIO 上是否有规律的调试包,如果包不连续,不要急着怪软件,得回到硬件层面找原因。
这里我特别提示一点:打开 Debug 选项里 “Download to Flash” 时,如果 Keil 提示擦除失败或者校验失败,先不要重复尝试,停一下,用 STM32CubeProgrammer 的 “Full chip erase” 做一次整体擦除,然后重新连接。如果整体擦除都失败,芯片内部 Flash 基本已经不可靠了,这也是假芯片的重灾区之一。
5. 从假芯片到 FreeRTOS 的验证策略总结
如果把这一路的经验浓缩成几句可以“抄作业”的建议,我会这样说:
第一,不要图便宜。STM32F103 的正规渠道价格摆在那里,同样标称 C8T6,5 块钱和 8 块钱的料,后面省下的采购成本会在调试成本上加倍还回来。尤其是批量生产,一旦混入假芯片,质量事故和企业信誉的损失根本不是那点物料差价能比的。
第二,新板子到手先烧一个“全引脚扫描”的测试程序。所谓全引脚扫描程序,就是将所有 GPIO 配置成推挽输出,轮流翻转,然后通过逻辑分析仪还是示波器或者连 LED 去验证每一个引脚的电气特性是否正常。这个测试能发现很多芯片引脚内部开路、短路的问题,而且比直接跑应用更省时间。
第三,换芯片时要“连换连测”,不要一次性把所有板子都焊接完成。我先焊一两块,跑通 FreeRTOS 基础例程,再焊下一批。这样即使买到同一批次的假芯片,损失也被控制在小范围里。
第四,工程上建议用 STM32CubeMX 初始化工程,哪怕是标准库的老手,也可以把 CubeMX 生成的外设初始化代码作为参照,至少保证时钟树和引脚复用是和芯片实际资源匹配的。FreeRTOS 部分,CubeMX 的 Middleware 集成相对规范化,可以减少低级配置错误。
第五,当外设、任务、中断足够多时,建议把 FreeRTOS 的内核配置整理成一个独立头文件(比如my_freertos_conf.h),集中管理configTOTAL_HEAP_SIZE、configMAX_PRIORITIES、configUSE_MUTEXES等项目级参数。换芯片型号时(比如从 C8T6 升级到 R8T6、CBT6),只需要修改 RAM 相关的配置就行了。
6. 个人实操体会与后续扩展建议
最后再分享一些我在这个项目里积累的零散心得。验证芯片真伪这件事,除了上面提到的各种方法,还有一个很朴素的技巧:拿几颗从正规代理商申请到的样片作为“基准片”,在新采购的批次里随机抽出三五颗,分别测上电电流、SWD 扫描、UID 读取和内部 Flash 擦写速度,再跑一遍 FreeRTOS 稳定性测试,和基准片做横向对比。这个方法不能 100% 鉴别所有假芯片,但能筛出大多数行为异常批次。
关于 FreeRTOS 的外设使用,如果你是在 F103 上做 Modbus RTU 或者使用 RS232 进行工业通信(这也是很多人的实际项目需求),那我额外提醒:Modbus RTU 的 3.5 字符时间间隔通常依赖一个精确的定时器,建议用 TIM2 或 TIM3 做超时定时,不要完全依赖 SysTick,因为 FreeRTOS 里 SysTick 的优先级可能被设置为最低(取决于你的configLIBRARY_LOWEST_INTERRUPT_PRIORITY设定),一旦忙于调度或有高优先级中断抢占,SysTick 周期的抖动会导致 3.5T 判定不准。这也是我跑 Modbus 从站时踩过的一个大坑,和芯片真假无关,但容易让人误以为芯片不靠谱。
如果你的项目涉及掉电保存数据(也就是网友常搜的“stm32f103 掉电保存数据”),建议把数据写到芯片内部 Flash 的独立扇区里,并在写入前做 Flash 擦写次数统计。假芯片的 Flash 寿命往往不达标,原来标称 10 万次擦写的 Flash,实际可能几千次就出坏块。一个可以落地的保护方案是:把关键数据冗余存储两份,写入时先写备份副本,掉电重启后比较校验和,哪个有效用哪个。
另外,如果你打算后续把 FreeRTOS 项目往 STM32H7 或其他更高性能的芯片上迁移,F103 阶段的移植经验依然有价值,只是要注意 H7 的 Cache 和 MPU 相关配置,以及 FreeRTOS 的configENABLE_MPU等选项的是否启用。这个方向可以作为你逐步深入 RTOS 的下一个里程碑。
现在回想起来,那批让我怀疑人生的“假芯片”,其实是逼着我从“照着例程点灯”的舒适区走出来,开始认真对待硬件最小系统、时钟树、Flash 参数、中断优先级和内存布局这些底层细节。技术问题带来的挫败感,往往会转化为对系统更深的理解,这也是这个项目对我来说最有价值的部分。希望这篇总结,能帮你少走几段冤枉路。