1. 项目概述与核心价值
在电机驱动和功率电子领域,生成高质量的三相正弦波脉宽调制(PWM)信号是核心挑战之一。传统的微控制器直接生成PWM,常常受限于硬件定时器的比较匹配机制,导致占空比调节范围受限,尤其是在接近0%或100%的极端占空比时,容易出现最小脉宽(MPW)问题,影响控制精度和动态响应。今天要深入探讨的,是飞思卡尔(现恩智浦)在其TPU(定时器处理单元)上实现的一种精妙解决方案:3Sin3Xor函数集。这个方案的核心思想,是利用两个TPU通道配合一个外部异或(XOR)门,来生成一路PWM信号。听起来有点绕?简单来说,它把生成一个完美PWM波形的任务,拆解成两个互补的“半成品”信号,让它们经过一个简单的数字逻辑门“加工”后,合成为一个最终的、无最小脉宽限制的PWM波。
这种设计的直接好处是显而易见的:实现了真正的0%到100%全范围占空比调节。这意味着你可以对电机进行极其精细的控制,无论是需要极短的有效脉冲,还是接近常通的驱动信号,都能精确无误地产生,这对于高性能伺服驱动、精密变频器或任何要求高动态性能的场合至关重要。当然,天下没有免费的午餐,其代价是需要占用双倍的TPU通道资源。但对于许多对控制品质有严苛要求的应用而言,这种资源交换是完全值得的。接下来,我将带你从原理到配置,彻底拆解这个经典的“XOR版本”三相正弦波PWM生成方案,分享我在实际项目中配置和调试这类系统时积累的实战经验与避坑指南。
2. 核心原理:为何选择XOR方案?
在深入配置细节之前,我们必须先理解“为什么”。为什么一个简单的XOR门能解决最小脉宽问题?这需要从TPU生成PWM的常规方式说起。
2.1 传统单通道PWM的局限性
典型的微控制器或TPU生成PWM,通常采用一个定时器通道配合一个比较寄存器。定时器计数器循环计数,当计数值与比较寄存器A匹配时,输出翻转(例如从低变高);当与比较寄存器B匹配时,再次翻转(从高变低)。这样,通过设置A和B的值,就定义了脉冲的起始和结束位置,从而决定了占空比。
这里存在一个根本性限制:硬件比较匹配和输出动作需要时间。假设定时器时钟频率为20MHz,一个计数周期是50ns。如果要求产生一个宽度仅为100ns(2个计数周期)的高电平脉冲,理论上可以设置A和B的值相差2。但在实际中,从检测到匹配事件,到执行输出引脚的电平切换,硬件需要数个时钟周期的处理时间(即所谓的输出比较延迟或死区时间插入电路的固有延迟)。这可能导致一个严重问题:当软件设置的脉冲宽度小于这个硬件处理所需的最小时间时,输出要么无法产生这个窄脉冲,要么会产生一个畸变、宽度不确定的脉冲。这个“硬件无法可靠产生的最小脉冲宽度”就是MPW。同样,在需要接近100%占空比时(即低电平脉冲极窄),也会遇到对称的问题。
2.2 XOR方案的巧妙破局
3Sin3Xor方案的核心思路是“化整为零,合二为一”。它不再试图用一个通道直接生成最终的PWM信号,而是用两个通道(我们称之为R通道和T通道)分别生成两个信号,比如A1和A2。这两个信号本身并不是标准的PWM,但它们具有一个关键特性:最终需要的PWM信号的高电平时间,恰好等于A1和A2信号电平不同的时间。
这正是异或(XOR)逻辑门的特性:当两个输入不同时,输出为高;相同时,输出为低。因此,将A1和A2接入一个XOR门,其输出就是最终的PWM信号。
那么,TPU如何生成A1和A2呢?它通过精密的计算,控制A1和A2的上升沿和下降沿位置,使得:
- 在期望的PWM高电平区间内,
A1和A2保持相反电平(一个高一个低)。 - 在PWM低电平区间内,
A1和A2保持相同电平(同时为高或同时为低)。
通过精心安排这两个信号的边沿,可以确保无论所需的PWM高电平时间多短(甚至理论上趋近于0),A1和A2的边沿之间总有足够的时间间隔供TPU硬件响应,从而绕开了单个通道的MPW限制。本质上,它将生成一个极窄脉冲的时序压力,分解到了生成两个具有合理间隔的边沿上。
2.3 方案优势与代价总结
优势:
- 全占空比范围:彻底消除MPW限制,实现0%-100%的线性控制。
- 改善波形质量:在极端占空比下,输出波形依然干净、准确,减少了因脉冲畸变引起的电机转矩脉动和电流谐波。
- 提升控制精度:特别适用于空间矢量调制(SVPWM)等需要过调制或精确控制零矢量的高级算法。
代价:
- 资源占用翻倍:每相PWM需要2个TPU通道,三相系统共需6个核心通道,外加余弦表占用的4个通道,基础资源需求就是10个通道。
- 需要外部硬件:必须使用外部XOR门芯片(如74系列逻辑门)或FPGA/CPLD来实现逻辑运算,增加了电路复杂性和BOM成本。
- 软件配置更复杂:需要协调管理成对的R/T通道,初始化顺序和参数同步要求更严格。
理解了“为什么”,我们就能更好地驾驭“怎么做”。接下来,我们进入实战配置环节。
3. 系统架构与函数集详解
3Sin3Xor并非一个孤立的函数,而是一个由多个TPU函数协同工作的函数集。理解每个函数的角色和它们之间的协作关系,是正确配置的基石。
3.1 核心函数组成
整个函数集包含4类TPU函数,它们必须配合使用:
3Sin3Xor_R 与 3Sin3Xor_T:这是生成PWM的“心脏”。每相PWM需要一对R和T通道。R通道(“Reference”或“主通道”)负责复杂的计算,包括正弦波调制、角度累加和重载判断;T通道(“Toggle”或“从通道”)则根据R通道的计算结果,执行精确的定时和输出翻转。三相系统需要3对这样的R/T通道。
余弦表:这不是一个主动运行的“函数”,而是一块存储在连续4个TPU通道参数RAM中的静态数据区。它存储了32个点的第一象限余弦值,供R通道在计算三相调制波时进行快速查表和线性插值。官方文档指出其最大误差仅为0.021%,对于电机控制而言完全足够。
3Sin3Xor_sync:同步信号发生器。这是一个可选但极其有用的功能。它能产生一个与PWM中心点严格同步的可调脉冲信号。你可以设置这个脉冲相对于PWM中心点的偏移(
move参数)和脉冲宽度(pw参数)。这个信号常用于触发外部事件,例如同步ADC采样(在PWM波形的中心点采样电流,噪声最小),或者为其他需要与PWM周期锁相的电路提供时钟基准。3Sin3Xor_res:解析器参考信号发生器。同样是可选功能,用于生成一个与PWM同步的50%占空比方波。其周期可以是PWM周期的整数倍。在电机控制中,这个信号常用来模拟或驱动解析器(Resolver)的励磁信号。
3Sin3Xor_fault:故障输入函数。这是一个安全功能,监控一个指定的输入引脚。当该引脚发生高到低跳变时,它会立即将所有3Sin3Xor相关的PWM输出通道(包括同步和解析器参考信号)强制拉低,并取消所有已计划的输出动作。这是一种硬件级的快速保护机制,用于应对过流、过压等故障。
3.2 通道配置规则与实战示例
配置这些函数时,必须遵循严格的规则,否则系统无法正常工作。下表总结了配置选项:
| TPU 函数 | 必要性 | 通道数量 | 通道分配规则 |
|---|---|---|---|
| 3Sin3Xor_R | 必需 | 3 | 任意3个通道 |
| 3Sin3Xor_T | 必需 | 3 | 任意3个通道 |
| 余弦表 | 必需 | 4 | 任意4个连续的通道 |
| 3Sin3Xor_sync | 可选 | 1个或多个 | 任意通道 |
| 3Sin3Xor_res | 可选 | 1个或多个 | 任意通道 |
| 3Sin3Xor_fault | 可选 | 1 | 任意通道,强烈建议使用通道15 |
关键经验:故障通道的配置文档强烈建议将故障函数放在通道15,并设置DTPU位。这是因为许多微控制器(如MPC5xx系列)的TPU模块中,通道15有一个特殊的硬件功能:当该通道被配置为输入且检测到低电平时,可以硬件全局禁用所有TPU输出引脚。这比软件响应中断后再去关闭输出要快得多,对于保护功率器件至关重要。在实际项目中,只要使用了故障保护功能,务必遵循此建议。
一个典型的、包含所有可选功能的完整配置示例如下:
| 通道号 | TPU 函数 | 优先级 | 说明 |
|---|---|---|---|
| 0 | 3Sin3Xor_R (A相) | 中 | A相主通道 |
| 1 | 3Sin3Xor_T (A相) | 中 | A相从通道 |
| 2 | 3Sin3Xor_R (B相) | 中 | B相主通道 |
| 3 | 3Sin3Xor_T (B相) | 中 | B相从通道 |
| 4 | 3Sin3Xor_R (C相) | 中 | C相主通道 |
| 5 | 3Sin3Xor_T (C相) | 中 | C相从通道 |
| 10 | 3Sin3Xor_sync | 低 | 同步信号,例如用于触发ADC |
| 11 | 3Sin3Xor_res | 低 | 解析器参考信号 |
| 12 | 余弦表 1/4 | 无 | 余弦值存储区 |
| 13 | 余弦表 2/4 | 无 | 余弦值存储区 |
| 14 | 余弦表 3/4 | 无 | 余弦值存储区 |
| 15 | 3Sin3Xor_fault+ 余弦表 4/4 | 高 | 故障输入与余弦表共用 |
这个配置占用了通道0-5,10-15,共12个通道。注意通道15的复用:因为故障函数3Sin3Xor_fault本身没有参数,所以它可以和余弦表的最后一个存储单元共享同一个通道的参数RAM空间,这是官方允许且推荐的节省通道的做法。
4. 关键参数解析与计算过程
要让这套系统运转起来,CPU需要通过TPU的参数RAM与这些函数通信。理解每个参数的含义和计算方法是软件调试的关键。
4.1 核心控制参数(由CPU写入)
这些参数决定了PWM的基本行为和电机控制的状态。
Ampl(振幅):16位有符号分数格式。代表定子参考电压矢量的幅值。特别注意:此参数仅接受正值。其数值范围与PWM的调制比直接相关。例如,当
Ampl为最大值32767时,对应调制比为1(即正弦波峰值处占空比为100%或0%)。计算实际电压指令时,需要根据直流母线电压进行标幺化处理。Theta(角度):32位有符号分数格式。代表定子参考电压矢量的当前角度。范围
<-1, 1)对应于<-180°, 180°>。这是一个绝对角度值。通常,在启动或需要精确定位时,由CPU直接设置。在正常旋转运行时,更常用的是下面的dTheta。dTheta(角度增量):32位有符号分数格式。代表每个PWM周期角度增加的量,直接决定电机的电角速度。计算公式:
dTheta = (期望的电角速度 * PWM周期时间) / 180°例如,PWM频率为10kHz(周期T_pwm=100μs),希望电机以1000电弧度/秒旋转。则每个PWM周期转过的电角度为1000 rad/s * 100e-6 s = 0.1 rad ≈ 5.73°。由于参数范围是<-1, 1)对应<-180°, 180°>,所以单位是1对应180°。因此,dTheta = 5.73° / 180° ≈ 0.03183。在32位分数格式下,需要将其转换为定点数。如果采用Q31格式(1位符号,31位小数),则写入的参数值应为0.03183 * 2^31 ≈ 68356500(十六进制0x0412AA94)。T(周期):16位无符号整数。这是最重要的参数之一,直接决定PWM的开关频率。它表示一个完整的PWM周期所包含的TPU定时器(TCR1)时钟周期数。计算公式:
T = TPU时钟频率 / 期望的PWM频率例如,TPU的TCR1时钟为20MHz,需要生成10kHz的PWM,则T = 20,000,000 / 10,000 = 2000。文档中提到最大PWM频率可达45kHz(T=444),这是在仅运行3Sin3Xor函数且IMB时钟为40MHz的理想情况下。实际项目中必须考虑TPU的负载,如果还有其他函数运行,需要为它们预留服务时间,T值必须设置得更大。prescaler(预分频):16位无符号整数。它定义了“重载周期”——即每隔多少个PWM周期,TPU才会从参数RAM中重新读取
Ampl、dTheta、T等新值。设置较大的prescaler可以降低CPU更新参数的负担。例如,PWM频率为10kHz,设置prescaler=100,则CPU每秒只需更新10000/100=100次参数,更新频率为100Hz,这对多数CPU来说绰绰有余。
4.2 同步机制参数:LD_OK
这是一个由CPU和TPU共同读写的16位状态参数,是协调双方数据更新的握手信号。
- CPU写:当CPU准备好一组新的控制参数(
Ampl,dTheta,T,prescaler,有时包括Theta)后,将LD_OK写入$0001。如果希望同时更新绝对角度Theta,则写入$8001。 - TPU读/写:在下一个重载点(由
prescaler控制),TPU会读取这些新参数。一旦读取完成,TPU会自动将LD_OK清零为0。 - CPU读:CPU可以轮询或通过中断(由R通道产生)检测
LD_OK。当发现其被TPU清零后,就知道可以安全地写入下一组参数了。
这个机制至关重要,它确保了参数更新的原子性和同步性,避免了CPU在TPU读取过程中修改参数而导致的波形撕裂或计算错误。
4.3 同步与解析器信号参数
move:16位有符号整数。用于3Sin3Xor_sync和3Sin3Xor_res。它定义了同步脉冲的边沿或解析器信号的跳变沿,相对于PWM周期中心点的偏移量。单位为TCR1时钟周期。move < 0:事件发生在中心点之前。move > 0:事件发生在中心点之后。- 重要限制:
|move|必须小于T/4。这是为了保证功能在TPU的调度时序内能正确执行。
pw:16位无符号整数。仅用于3Sin3Xor_sync,定义同步脉冲的宽度(高电平时间),单位同样是TCR1时钟周期。sync_presc_addr:8位无符号整数。位于3Sin3Xor_R/T的参数区。用于实现PWM预分频器(prescaler)与同步信号预分频器的同步切换。当PWM的prescaler参数在运行时被修改,你希望同步信号的周期也同步改变时,需要将此参数设置为同步信号通道的presc_copy参数地址(格式为$X4,X为通道号)。这样,PWM在重载新参数时,会自动将新的prescaler值拷贝到同步信号的presc_copy中,实现无缝切换。如果设为0,则两个预分频器独立,需要手动管理,易造成信号不同步。
5. 初始化与运行流程详解
正确的初始化顺序是系统稳定运行的保证。以下步骤基于官方文档,并融入了实际调试中的注意事项。
5.1 初始化步骤分解
禁用通道:对所有计划使用的TPU通道,将其通道优先级位写为
00(禁用)。系统复位后,所有通道默认禁用,此步可省略,但显式执行是个好习惯。分配函数:向每个通道的“通道功能选择”寄存器写入对应的函数代码。这些代码在链接TPU函数库时确定。务必确保R/T通道配对正确,余弦表占用连续的4个通道。
参数预配置:在初始化TPU函数之前,必须提前写好所有必要的参数。这包括:
- 所有R/T通道的:
T,prescaler,Theta_H,Theta_L,sync_presc_addr。 - 余弦表的32个值(写入那4个连续通道的参数RAM特定位置)。
- 如果使用了同步或解析器通道,设置其
move、pw(仅同步)、prescaler等参数。 - 设置初始的
Ampl(通常从0开始)、dTheta、Theta。 - 将
LD_OK初始化为0。
- 所有R/T通道的:
发出主机服务请求(HSR)进行初始化: a. 向任意一个
3Sin3Xor_R通道发出HSR类型%10(二进制10)。注意:只需要对一个R通道操作,它会自动初始化所有3对R/T通道。b. 如果使用了同步、解析器或故障通道,分别向它们发出HSR类型%10。启用通道服务(最关键的一步): a. 为所有
3Sin3Xor_R和3Sin3Xor_T通道分配相同的优先级(中或高)。所有6个通道优先级必须一致,这是内部协同工作的要求。 b.等待R/T通道初始化完成:CPU需要等待步骤4a中R通道的HSR位被TPU自动清除(或等待该R通道产生初始化完成中断),这标志着R/T通道已就绪。 c.然后,再为同步、解析器通道分配优先级(通常设为低优先级)。这个顺序不能错,因为同步/解析器函数需要从已运行的R/T通道获取时间信息。
实战心得:启动时的“软启动”文档提到,PWM信号在初始化后约1.6ms(在20MHz TCR1时钟下)才开始运行,且初始输出为50%占空比。这是一个安全设计,给外部自举电容充电留出了时间。在软件上,我们通常也遵循“软启动”策略:初始化后,先将
Ampl设为0,然后缓慢增加dTheta(频率)和Ampl(电压),直到目标值。这可以避免电机启动时的电流冲击。
5.2 运行时控制
系统运行后,CPU的主要任务就是根据控制算法(如FOC),周期性地更新Ampl和dTheta(可能还有Theta)。
- 等待重载机会:通过中断或轮询检测
LD_OK是否为0。 - 写入新参数:计算新的
Ampl、dTheta等值,写入对应通道的参数RAM。 - 触发重载:将
LD_OK写为$0001(或$8001以更新Theta)。 - 循环:TPU会在下一个重载点读取新参数,并清零
LD_OK,CPU可以开始准备下一组参数。
6. 性能估算与资源规划
TPU是一个时间敏感型的协处理器,必须确保其有足够的能力在规定时间内完成所有计算和输出动作。
6.1 最大PWM频率计算
文档给出的45kHz上限(T=444 @ 20MHz TCR1)是一个理论峰值。实际最大频率取决于TPU的总负载。计算步骤如下:
- 列出所有并发运行的TPU函数及其状态:包括3Sin3Xor的各个状态(SFR, SFC等)、同步、解析器、故障函数,以及系统中可能存在的其他任何TPU函数(如输入捕捉、其他PWM等)。
- 获取每个状态的执行时间:查阅各函数的“状态统计表”(如文档中的Table 8, 9, 13, 17, 21),获取其最大IMB时钟周期数。
- 计算单PWM周期内总服务时间:
- 将一个PWM周期(T个TCR1周期)换算成IMB时钟周期数。例如,若TCR1时钟=20MHz,IMB时钟=40MHz,则换算系数为2(即1个TCR1周期=2个IMB周期)。
- 估算在一个PWM周期内,每个函数状态可能被触发的次数,乘以它的执行时间(IMB周期)。
- 对于3Sin3Xor,主要考虑
SFR和SFC状态,它们每个PWM周期每相都会执行。 - 不要忘记时间槽转换时间:TPU在不同通道间切换服务时,有固定的开销(TST=10或14个IMB周期)。每个被执行的状态前后都可能涉及此开销。
- 判断可行性:所有函数在一个PWM周期内所需的总IMB周期数,必须小于
T * (IMB时钟频率 / TCR1时钟频率)。必须留有足够的余量(建议>20%),以应对最坏情况下的时序。
简化估算示例:假设只有3Sin3Xor运行,根据文档,最耗时的状态是SFR(66 IMB周期)和SFC(最长96 IMB周期)。三相系统,每相每周期执行一次SFR和一次SFC,共6次服务。加上通道切换开销,粗略估算需要(66+96)*3 + 6*14 ≈ 558IMB周期。若IMB时钟40MHz,TCR1时钟20MHz,则一个PWM周期T对应的IMB周期数为2T。要满足2T > 558,即T > 279。对应的PWM频率为20MHz / 279 ≈ 71.7kHz。这远高于45kHz,说明45kHz的限制包含了更保守的调度余量和可能的状态组合。因此,在实际设计时,必须基于最坏情况详细核算,并以文档给出的45kHz(或根据你的时钟折算)作为安全上限的参考。
6.2 余弦表精度分析
函数使用32点第一象限余弦表结合线性插值。最大误差为7(在Q15格式的-32768到32767范围内),误差率为7 / 32767 ≈ 0.021%。这个误差对于电机控制产生的谐波影响微乎其微,完全在可接受范围内。它是在代码空间(存储表)和计算时间(线性插值)之间一个非常好的折中。
7. 常见问题排查与调试技巧
基于实际项目经验,以下是一些你很可能遇到的情况和解决方法。
7.1 问题速查表
| 现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 无PWM输出 | 1. 通道未正确初始化或启用。 2. 外部XOR门电路故障或未供电。 3. 故障引脚被意外拉低。 | 1. 检查TPU通道配置寄存器:函数号是否正确?优先级是否已设置(非00)? 2. 用示波器分别测量TPU引脚(如A1, A2)和XOR门输出。确认XOR门电源、接地正常。 3. 检查故障输入引脚电平,并读取 fault_pinstate参数确认状态。 |
| PWM输出混乱,非对称或频率不对 | 1. R/T通道配对错误或相位分配错误。 2. 参数 T计算错误。3. LD_OK机制使用错误,导致参数更新混乱。 | 1. 核对通道分配表,确保A相R/T、B相R/T、C相R/T正确配对。 2. 重新计算 T值,确认TCR1时钟频率配置正确。3. 确保CPU只在 LD_OK==0时写参数,并在写完后正确设置LD_OK=$0001。使用调试器观察LD_OK位和参数值的变化。 |
| 电机振动或噪音大 | 1. PWM频率不在电机合适的范围内(通常音频范围内会啸叫)。 2. 正弦波不光滑,余弦表数据错误或插值逻辑问题。 3. dTheta计算有误,导致频率波动。 | 1. 尝试提高PWM频率(减小T),通常建议在10kHz-20kHz以上,避开人耳敏感频段。 2. 检查写入余弦表的32个值是否正确(Q15格式的cos值)。 3. 检查 dTheta的计算公式和定点数转换,确保没有溢出或精度丢失。 |
| 同步信号或解析器信号不同步 | 1.sync_presc_addr或presc_addr参数设置错误。2. move参数绝对值超过T/4限制。3. 同步/解析器通道在R/T通道之前初始化。 | 1. 确认sync_presc_addr指向了正确的同步通道参数地址(格式$X4)。2. 计算并确保` |
| 无法达到高占空比(如>95%)或低占空比(如<5%) | 如果问题依旧,说明XOR方案未生效!可能原因: 1. XOR门逻辑错误(例如用了与门)。 2. R和T通道输出极性配置错误。 | 1. 确认使用的是真正的异或门(XOR),不是同或门(XNOR)或其他。 2. 检查TPU输出引脚的极性控制寄存器,确保R和T通道输出均为主动高电平(或根据XOR门逻辑调整)。理想的测试方法是,设置 Ampl=0,此时输出应为50%占空比方波。 |
| 故障保护功能不触发或误触发 | 1. 故障通道未配置在通道15,或DTPU位未设置。 2. 故障输入引脚有噪声。 3. 故障函数未正确初始化。 | 1.务必将故障函数配置在通道15,并确认TPU模块的DTPU控制位已置1,启用硬件快速关断。 2. 在故障输入引脚增加适当的RC滤波(时间常数要远小于PWM周期),并检查PCB布局避免噪声耦合。 3. 确保对故障通道也发出了HSR=%10进行初始化。 |
7.2 调试工具与技巧
- 示波器是关键:至少需要双通道示波器,最好四通道。同时观察一对R/T通道的输出(如A1和A2),以及它们经过XOR门后的最终PWM输出。这能直观验证XOR逻辑是否正确工作。
- 利用同步信号触发ADC:将
3Sin3Xor_sync的脉冲设置在PWM周期的中心点(move=0),用这个脉冲触发电流采样ADC。用示波器观察同步脉冲和PWM波形的相对位置,确保采样时刻准确落在PWM“平顶”区,这是获得准确电流反馈的基础。 - 软件仿真辅助:在编写底层驱动前,可以在MATLAB/Simulink或Python中建模整个系统,包括余弦表、插值算法、R/T信号生成和XOR逻辑。这能帮助你在硬件调试前验证参数计算和逻辑的正确性,尤其是
dTheta与转速的换算关系。 - 从静态开始:调试时,先让电机静止。设置
dTheta=0,然后缓慢改变Ampl,观察PWM占空比是否平滑变化。再固定一个小的Ampl,缓慢改变dTheta,观察PWM波形是否呈现三相对称且频率逐渐变化的正弦调制特性。 - 关注TPU负载:如果系统还运行其他复杂TPU任务,一定要如前所述进行严格的时间预算分析。可以使用TPU的调试功能或监控特定状态标志,来检查是否有服务请求因超时而被丢弃。
这套基于TPU的XOR版本三相正弦波PWM生成方案,虽然配置上比传统单通道方案复杂,但其带来的全占空比范围和无最小脉宽限制的优势,在高性能电机驱动领域是无可替代的。它体现了在资源受限的嵌入式系统中,通过软硬件协同设计解决复杂工程问题的经典思路。