1. 项目概述与核心价值
如果你曾经在嵌入式开发中,面对一块“裸板”束手无策,不知道如何把第一行代码灌进去,或者苦于无法在真实的硬件上设置断点、单步跟踪,那么MC68SZ328的引导加载程序(Bootstrap Mode)和在线仿真(ICE)模块,就是为你量身定制的“瑞士军刀”。这两个功能,一个负责“从零到一”的启动和程序注入,另一个则负责“庖丁解牛”般的运行时调试,共同构成了早期嵌入式系统开发中不可或缺的基石。
MC68SZ328,这颗来自摩托罗拉(后飞思卡尔)的经典龙珠(DragonBall)系列微处理器,曾是众多PDA、工业控制器的核心。它的引导加载程序并非一个复杂的软件,而是一段固化在芯片内部的微码(Microcode)。当芯片以特定模式启动时,这段微码会接管CPU,将UART(通用异步收发器)初始化为一个简单的命令行接口,等待主机通过串口发送特定格式的命令和数据。这个过程,我们称之为“Bootstrap”,即自举。它的核心价值在于,即使目标板上的Flash是空的、RAM也未初始化,你依然可以通过一根串口线,将最初的启动代码、内存初始化脚本乃至完整的应用程序,一点一点地“喂”给芯片,让系统“活”起来。
而在线仿真(ICE)模块,则是另一个维度的利器。它通过在芯片内部集成地址比较器、控制信号比较器和指令插入单元,让开发者能够以极低的硬件成本,实现类似高级调试器的功能——设置硬件断点。你可以在指定的内存地址(无论是读取数据还是执行指令时)让CPU暂停,并将控制权交给一个外部的调试监控程序(通常运行在由EMUCS信号选通的专用内存区域)。这对于排查那些“时隐时现”、与时序紧密相关的硬件交互Bug至关重要,因为它不依赖于软件插桩,对代码执行流的影响最小。
简单来说,引导加载程序解决了“如何把程序放进去”的问题,而在线仿真模块解决了“如何看清程序是怎么跑的”问题。两者结合,为MC68SZ328的嵌入式开发提供了从烧写到深度调试的完整闭环解决方案。接下来,我将带你深入这两个模块的细节,从硬件配置到软件协议,从操作流程到避坑指南,让你彻底掌握这套经典的开发工具链。
2. 引导加载程序(Bootstrap Mode)深度解析
2.1 核心工作原理与硬件进入条件
MC68SZ328的引导加载程序,本质上是一个极度精简的、通过串口交互的监控程序。它的设计哲学是“最小化依赖”:不依赖外部存储器,不依赖复杂的协议栈,只依靠芯片内最基本的UART模块和一小块指令缓冲区。
要让芯片进入这个模式,硬件上需要满足一个特定的引脚电平组合,并在该组合下进行复位。这是通过BST2、BST1、BST0这三个模式选择引脚实现的:
- BST2=0, BST1=1, BST0=0:这个组合(二进制010)即代表引导加载模式。它是三种操作模式(正常、仿真、引导)中优先级最高的。也就是说,只要复位时是这个电平,芯片就会无视其他配置,强制进入Bootstrap模式。
这里有一个非常关键的硬件设计细节:这三个引脚通常在复位后被锁存(Latched)。这意味着,你需要在系统上电复位(Power-On Reset)的整个过程中,或者在手动按下复位键的整个低电平期间,确保这三个引脚的电平稳定在010。任何毛刺或不稳定都可能导致模式识别错误,进而无法进入Bootstrap。在实际的板卡设计中,我们通常会用上拉或下拉电阻将这三个引脚固定在确定电平,而不是让它们悬空。
进入模式后,芯片内部会生成一套固定的复位向量,将栈指针(SP)和程序计数器(PC)指向内部预设地址,从而跳转到内置的引导加载微码开始执行。此时,UART1和UART2控制器会被自动初始化为:19200波特率、8位数据位、无校验位、1位停止位(8N1)。这个初始波特率是固定的,无法更改,因为微码还没有运行到你能够配置寄存器的阶段。
2.2 通信协议核心:B-Record格式详解
引导加载程序与主机(通常是PC)通信的全部内容,都基于一种称为“Bootstrap Record”或b-record的文本格式。理解这种格式,是玩转引导加载程序的关键。
一个完整的b-record看起来像这样:0000400010428142423C30200032C6548154420C42。它由三个固定字段组成,所有字符均为大写十六进制(A-F):
- 地址字段(Address Field):4字节(8个十六进制字符)。它指明了本条记录中数据将要写入的起始内存地址。这个地址可以是系统RAM的地址,也可以是MC68SZ328内部任何一个可寻址的寄存器地址。例如
00004000表示从地址0x4000开始存放数据。 - 计数字段(Count Field):1字节(2个十六进制字符)。它指明了紧随其后的“数据字段”中包含的字节数。注意,是字节数,不是字符数。因为每两个十六进制字符表示一个字节,所以数据字段的字符数应该是
计数*2。例如10表示后面有16个字节的数据(32个字符)。 - 数据字段(Data Field):长度可变,为
计数字段指定的字节数。即实际要写入目标地址的二进制数据,以十六进制ASCII码形式表示。
每条b-record必须以一个回车符(CR,\r,ASCII 0x0D)结束。主机发送时,通常以\r\n(回车换行)结尾,但加载程序只认回车符作为记录结束标志。
b-record分为两种类型,通过计数字段的值来区分:
- 数据记录(Data B-Record):
计数> 0。这是最常见的形式,用于向指定地址写入数据。例如初始化内存控制器、设置PLL、加载程序代码等。 - 执行记录(Execution B-Record):
计数= 0。此记录不携带数据,其作用是命令CPU从地址字段指定的位置开始执行程序。例如,在下载完一个应用程序到RAM后,发送0000400000\r就会让CPU跳转到0x4000去执行。
2.3 完整操作流程与实战步骤
掌握了原理和格式,我们来走一遍完整的引导加载流程。假设你手头有一块MC68SZ328开发板、一根USB转串口线、一个终端软件(如Tera Term、PuTTY或简单的screen命令)。
步骤1:硬件连接与终端配置
- 将开发板的UART1(或UART2)的TXD、RXD、GND与USB转串口线对应连接。
- 确保
BST2/1/0引脚被正确设置为0/1/0(引导模式)。 - 打开终端软件,创建串口连接。关键参数必须设置为:波特率19200,数据位8,校验位None,停止位1,流控制None。
- 一个经常被忽略的设置是本地回显(Local Echo)。建议在初始连接阶段将其关闭。因为引导加载程序本身会回显它收到的每一个字符(ASCII码>=0x30的),如果终端再回显一次,你会看到每个字符出现两次,造成干扰。
步骤2:建立通信链路
- 给目标板上电或按下复位键。
- 在终端里,随意输入一个字符(比如字母
A)。这个字符的作用是让引导加载程序“探测”是哪个UART端口收到了数据,从而确定通信链路。它不会被当作b-record的一部分。 - 如果链路建立成功,目标板会做两件事:
- 将你发送的那个字符回显回来(所以你会在终端看到你输入的字符)。
- 发送一个单独的
@字符(ASCII 0x40)作为确认(ACK)信号。
- 看到
@,恭喜你,引导加载程序已经准备就绪,可以接收b-record了。
注���:对于UART2,有一个特例。其发送引脚
TXD2默认是禁用的(被复用为其他功能)。因此,在通过UART2进行引导时,你将看不到回显的字符和@。为了启用它,你需要在建立连接后,发送第一条b-record来配置端口J的选择寄存器:FFFFF43B01CF\r。这条记录的意思是,向地址0xFFFFF43B(Port J Select Register)写入一个字节的数据0xCF(其中bit 5被清零,以启用TXD2)。发送后,通信回显功能才会正常。
步骤3:系统初始化与程序下载在下载你的应用程序之前,通常需要先初始化目标板的基本环境,比如配置系统时钟(PLL)、初始化SDRAM控制器、设置片选信号等。这些操作都是通过向对应的寄存器地址写入特定的值来实现的,也就是发送一系列的数据b-record。
例如,假设我们需要配置内存控制寄存器MBAR(地址假设为0xFFFFF100)为0x40000001,那么对应的b-record就是:FFFFF1000140000001\r。分解来看:
FFFFF100: 地址字段,目标寄存器地址。01: 计数字段,表示后面有1个字节的数据?等等,这里有个大坑!MC68SZ328是32位处理器,其寄存器通常是32位(4字节)或16位(2字节)。MBAR很可能是一个32位寄存器。所以,正确的计数应该是04,数据字段应该是40000001(4字节)。但原文示例中计数为01,这暗示了引导加载程序可能只支持按字节(Byte)写入。对于32位寄存器,我们需要连续发送4条b-record,分别写入4个字节。或者,更常见的做法是,初始化脚本(一个.bld文件)会由工具自动生成,它已经处理好了这种按字节的写入序列。这一点务必与你所用的具体芯片手册和初始化脚本核对清楚。
初始化完成后,就可以发送包含你应用程序机器码的b-record了。这些记录通常由一个转换工具(如STOB.EXE)从链接器生成的S-Record或Intel Hex格式文件转换而来。
步骤4:执行程序与返回引导程序下载到RAM(例如0x4000)后,发送一条执行b-record:0000400000\r。CPU就会跳转到0x4000开始执行你的程序。
如果你的程序运行完毕后,你还希望继续使用引导加载程序(比如下载另一个程序),你需要在程序的最后安排一条指令,跳转回引导加载程序的入口。根据文档,这个入口地址是$FFFFFF6C。在68000汇编中,就是JMP $FFFFFF6C。这样,CPU又会回到等待接收b-record的状态。
2.4 波特率切换与高级技巧
19200的初始波特率对于传输大量代码来说可能较慢。引导加载程序允许在连接建立后动态切换波特率。原理就是通过b-record去修改UART的波特率控制寄存器(BAUD Control Register)。
例如,系统使用32.768kHz外部晶振,系统时钟为16.58MHz时,19200波特率对应的控制寄存器值可能是0x0126。要切换到38400波特率,需要将该寄存器值改为0x0026。
- 首先,在19200波特率下,发送修改寄存器的b-record:
FFFFF9020100\r(假设UART1的波特率寄存器地址是0xFFFFF902,写入一个字节0x00)。注意,由于是按字节操作,可能需要两条记录来覆盖整个16位寄存器。 - 发送完该记录的最后一个字符(回车)后,目标板的波特率立即改变了。此时,你必须以最快速度将终端软件的波特率也改为38400,否则后续通信将因失步而乱码。
- 同理,可以继续切换到115200等更高波特率。关键点在于,每次修改波特率的b-record,其本身必须在上一个波特率下发送;而发送完成后,要立即与目标板同步切换主机的波特率。
3. 在线仿真(ICE)模块原理与应用
3.1 ICE模块架构与进入方式
如果说引导加载程序是“注入灵魂”,那么在线仿真模块就是“灵魂出窍观察术”。它允许开发者在程序运行时进行非侵入式的观察和中断。
ICE模块的硬件核心主要包括:
- 地址比较器与掩码寄存器:可以设置一个32位的地址断点,并可通过掩码设置一个地址范围。
- 控制信号比较器:可以匹配当前总线周期是“读”还是“写”,是“取指令”还是“访问数据”。
- A-Line指令插入单元:这是实现执行断点的关键。当CPU取指地址与断点地址匹配时,该单元会“偷梁换柱”,将数据总线上的实际指令操作码替换为
0xA000(68000 CPU的“A-Line异常”指令)。 - 专用中断与片选:提供一个专用的Level 7中断向量和
EMUCS片选信号,用于将控制权切换到外部的调试监控程序。
进入仿真模式同样依靠BST2~0引脚,组合为001。复位后,CPU的复位向量被硬编码为PC=0xFFFC0020,SSP=0xFFFCFFFC。这意味着,你必须将你的仿真监控程序(Emulator Monitor)烧录到物理地址0xFFFC0000开始的内存中,并且该内存区域必须由EMUCS信号选通(通常是一个独立的ROM或Flash)。EMUCS信号会自动在访问0xFFFC0000~0xFFFDFFFF这个128KB空间时有效。
3.2 断点机制详解:执行断点 vs. 总线断点
ICE模块支持两种断点,理解它们的区别至关重要:
1. 执行断点(Program Breakpoint)
- 目的:在CPU取指某个特定地址的指令时中断。
- 实现原理:
- 在ICE模块中设置好目标地址(写入
ICEMACR)。 - 使能程序断点(设置
ICEMCR中的PBEN=1)。 - 当CPU发起对该地址的取指总线周期时,ICE模块的地址比较器匹配成功。
- A-Line插入单元立即动作,将本应返回给CPU的指令码替换为
0xA000。 - CPU执行这条
0xA000指令,这会触发一个“A-Line异常”。 - A-Line异常的向量地址是固定的。在仿真模式下且
HMDIS=0时,该向量被硬编码为0xFFFC0010(即Level 7中断向量)。 - CPU跳转到
0xFFFC0010执行,这个地址位于EMUCS选通的监控程序空间,从而将控制权交给外部调试器。
- 在ICE模块中设置好目标地址(写入
- 特点:这是最常用的断点,用于暂停程序执行。它需要修改目标地址的指令,但由于是在总线周期中动态替换,并不实际改变内存中的内容。
2. 总线断点(Bus Breakpoint)
- 目的:在CPU访问(读或写)某个特定地址的数据时中断。
- 实现原理:
- 在ICE模块中设置好目标地址和访问类型(读/写,通过
ICEMCCR设置)。 - 使能总线断点(设置
ICEMCR中的PBEN=0,BBIEN=1)。 - 当CPU发起对该地址的数据总线周期(读或写),且类型匹配时,ICE模块直接触发一个Level 7中断。
- CPU跳转到Level 7中断向量(同样是
0xFFFC0010),控制权移交。
- 在ICE模块中设置好目标地址和访问类型(读/写,通过
- 特点:用于监控对特定内存地址或寄存器的访问,常用于调试内存数据损坏、外设寄存器误写等问题。它不涉及指令替换。
单点与多点模式:
- 单点模式(SB=1):
EMUBRK引脚作为输出。ICE模块内部完成所有比较,当断点命中时,从此引脚输出一个有效信号(通常为低电平),可以用于触发外部逻辑分析仪等设备。 - 多点模式(SB=0):
EMUBRK引脚作为输入。此时,内部地址比较器的高位地址与掩码参与比较,低位地址的比较则由外部硬件(如FPGA或CPLD)完成,两者结���相“与”后产生断点信号。这允许通过外部电路扩展更多的断点条件(如数据值比较、复杂逻辑组合)。
3.3 ICE寄存器配置指南
ICE模块的功能完全通过一组内存映射寄存器(MMR)来控制,地址在0xFFFFFD00附近。配置流程需要��谨的顺序:
配置地址与掩码(ICEMACR/ICEMAMR):
ICEMACR:写入你希望设置断点的32位地址。ICEMAMR:地址掩码寄存器。某位设为1,则表示对应地址位在比较时被忽略(“不关心”)。这用于设置地址范围断点。例如,若ICEMAMR = 0xFFFF0000,则断点将在ICEMACR的高16位指定的64KB范围内任何地址命中。
配置控制比较与掩码(ICEMCCR/ICEMCMR)(仅总线断点需要):
ICEMCCR:RW位决定匹配读周期还是写周期;PD位决定匹配指令周期还是数据周期(对于总线断点,通常设为数据周期)。ICEMCMR:对应位的掩码。如果对读/写或指令/数据周期不关心,可将对应掩码位置1。
配置控制寄存器(ICEMCR):这是总开关。
CEN:比较使能。必须置1,比较逻辑才工作。建议最后设置此位。PBEN:程序断点使能。1为执行断点,0为总线断点。BBIEN:总线断点中断使能。设为1,总线断点命中时才会触发中断。SB:单点模式选择。1为单点(EMUBRK输出),0为多点(EMUBRK输入)。HMDIS:硬映射禁用。通常保持为0,使得在仿真模式下,中断向量等关键地址被映射到内部硬编码值,指向监控程序。SWEN:软件使能。在正常模式下,通过写此位来启用ICE功能。
检查状态寄存器(ICEMSR):当Level 7中断发生时,监控程序应读取此寄存器,通过
BRKIRQ(程序断点)或BBIRQ(总线断点)位来判断中断来源,并在处理完毕后向对应位写1以清除中断标志。
3.4 典型仿真器设计与实战考量
文档图24-2展示了一个典型的低成本仿真器设计框图,其核心思想是“最小化附加硬件”:
- MC68SZ328目标插座:通过一个插槽或探针连接到目标板。
- 电平转换缓冲器:因为早期MC68SZ328是3.3V器件,而当时很多调试主机是5V逻辑,所以需要电平转换。
- 监控程序存储器:一块被
EMUCS选通的ROM或Flash,存放调试监控程序。 - 可选地址比较器:用于实现多点断点模式下的外部地址比较。
- 可选映射FPGA:用于实现更复杂的地址重映射或数据捕获功能。
- 主机接口:通常是RS-232串口或并口,用于与PC上的调试软件通信。
实战中的关键点:
- 内存冲突:仿真模式下,地址
0xFFFC0000~0xFFFDFFFF被保留给监控程序。你的目标板设计绝不能将任何物理内存(如SDRAM、Flash)映射到这个区间,否则会发生访问冲突。 - 中断向量:确保你的监控程序在
0xFFFC0010(Level 7中断)和0xFFFC0020(复位向量)处有有效的代码入口。 - 退出仿真:监控程序在完成调试后,如何让系统回归正常模式?通常需要软件修改某个寄存器(或触发硬件复位)来改变操作模式。单纯跳转到用户程序可能无法完全恢复所有ICE相关的硬件状态。
- 性能影响:使能ICE模块,特别是地址比较逻辑,会引入微小的时序延迟。在对时序极其敏感的应用中(例如高速串行通信),需要评估其影响。
4. 常见问题、调试技巧与避坑指南
基于多年的嵌入式调试经验,以下是一些在使用MC68SZ328引导加载和ICE功能时极易踩坑的地方和解决思路。
4.1 引导加载程序连接失败排查表
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 发送字符后无任何回响 | 1. 硬件连接错误(TX/RX接反、地线未接)。 2. 波特率等串口参数设置错误。 3. 芯片未进入Bootstrap模式(BST引脚电平错误)。 4. 目标板电源或复位电路故障。 | 1. 用万用表或示波器检查TX/RX线序与电压。 2.确认波特率是19200,8N1,无流控。 3. 用示波器在复位期间测量BST2/1/0引脚,确保为010。 4. 检查电源电压和复位信号波形。 |
有回显字符但无@ | 1. 终端软件打开了“本地回显”,看到的只是本地回显,并非目标板回显。 2. 目标板供电不足或时钟不稳定。 | 1.关闭终端软件的本地回显(Local Echo)。 2. 发送字符后,用示波器测量目标板UART的TXD引脚,看是否有 @字符(0x40的波形)发出。 |
| 使用UART2时无任何回响 | UART2的TXD2引脚默认被禁用。 | 在发送首个探测字符前或后,先发送使能TXD2的b-record:FFFFF43B01CF\r。 |
| 发送b-record后无反应 | 1. b-record格式错误(字母小写、缺少回车、字符数不对)。 2. 目标地址不可写(如访问了未初始化的内存或只读区域)。 3. 终端软件发送了额外的不可见字符(如Windows换行符 \r\n在某些配置下可能多发送一个\n)。 | 1. 使用STOB.EXE等工具生成b-record,确保格式正确。手动输入时注意大写和回车。2. 确保先初始化内存控制器,再向RAM下载代码。向寄存器写值时确认地址正确。 3. 尝试在纯文本编辑器里编写b-record文件,然后用“发送文件”功能传输,避免终端输入差异。 |
| 能下载但程序不运行 | 1. 执行记录的地址错误。 2. 下载的程序代码本身有误或链接地址不对。 3. 初始化不完整(如未正确配置时钟,CPU跑在错误频率下)。 | 1. 确认执行b-record中的地址与程序链接的入口地址一致。 2. 检查转换前的S-Record文件,确认代码段地址正确。可以用反汇编工具简单查看。 3. 逐步检查初始化b-record序列,确保关键寄存器(如PLLCR、SCR)配置正确。 |
4.2 ICE功能异常排查指南
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 无法进入仿真模式 | 1. BST引脚电平不是001。 2. EMUIRQ引脚在上电复位时为高电平(这会禁用ICE模块)。3. 监控程序存储器的 EMUCS片选信号未正确连接或存储器内容为空。 | 1. 复位时测量BST引脚确认为001。 2. 检查 EMUIRQ引脚的上拉/下拉配置,确保复位时为低。3. 用逻辑分析仪或示波器检查复位后访问0xFFFC0000区域时, EMUCS信号是否有效,以及数据总线上是否有正确的监控程序指令码。 |
| 断点无法触发 | 1. ICE寄存器配置错误(CEN未使能、地址设置错)。 2. 断点类型设置错误(在数据访问地址设置了执行断点)。 3. 监控程序的Level 7中断向量不正确。 4. 在多点模式下,外部比较器未给出正确的 EMUBRK信号。 | 1. 单步调试监控程序,检查ICE相关寄存器(ICEMCR, ICEMACR等)的值是否与预期一致。 2. 区分“执行地址”和“数据地址”。对变量地址应设总线断点(读/写),对函数入口设执行断点。 3. 确认 HMDIS=0,并且监控程序在0xFFFC0010处有有效的跳转指令。4. 用示波器测量 EMUBRK引脚在断点地址被访问时的电平变化。 |
| 断点触发后程序跑飞 | 1. 监控程序的中断处理程序保存/恢复现场不完整。 2. Level 7中断服务程序未正确清除中断标志(ICEMSR)。 3. A-Line异常处理有误。 | 1. 检查监控程序的ISR,是否保存了所有必要的寄存器(D0-D7, A0-A6, SR)。 2. 在ISR结束前,务必向 ICEMSR中的BRKIRQ或BBIRQ位写1以清除中断。3. 确保监控程序能正确处理 0xA000指令引发的异常流程。 |
| 使能ICE后系统不稳定 | 1. ICE模块的地址比较范围与正常程序内存区域重叠,导致误触发。 2. 电平转换缓冲器驱动能力不足或时序问题。 3. ICE功能本身消耗电流,影响电源完整性。 | 1. 检查ICEMAMR(地址掩码)设置,确保断点范围精确。调试结束后禁用ICE(CEN=0)。2. 检查缓冲器的OE(输出使能)时序,确保在非仿真模式下输出为高阻态,不影响目标系统。 3. 在电源入口处增加去耦电容,确保ICE模块供电稳定。 |
4.3 实操心得与高级技巧
制作“万能”初始化脚本:为你的特定板卡制作一个标准的.bld初始化文件。这个文件应该按顺序包含:关闭看门狗、配置系统时钟(PLL)、初始化内存控制器(SDRAM时序参数)、配置必要的GPIO、初始化用于调试的UART。每次开发新项目,先通过引导加载程序运行这个脚本,确保硬件基础环境一致。
利用指令缓冲区进行“微手术”:引导加载程序的32字节指令缓冲区(位于0xFFFFD2)是一个强大的底层调试工具。你可以通过b-record向这个缓冲区写入几条68000指令(比如读取某个关键寄存器的值,并通过UART发送出来),然后用执行记录运行它。这相当于在不破坏内存原有代码的情况下,执行了一段临时的诊断程序。
仿真模式下的“软复位”:在监控程序中,可以通过软件写入系统控制寄存器来触发一个软复位。但要注意,复位后BST引脚的状态需要保持,否则会退出仿真模式。一种做法是在监控程序存储器中放置一个非易失性标志位,复位后检查该标志,决定是跳转到用户程序还是留在监控程序。
波特率切换的自动化:在PC端的调试工具脚本中,可以自动化波特率切换流程。例如,工具在19200下发送修改波特率寄存器的b-record后,立即将本机串口波特率切换到新速率,然后发送一个预定义的同步字符(如
@),并等待目标板在新速率下的回显,以此确认切换成功。ICE与引导加载程序联调:最强大的工作流是结合两者。首先通过引导加载程序,将你的调试监控程序和用户程序都下载到RAM中。然后,利用ICE功能在用户程序中设置断点。当断点命中,控制权交给监控程序后,监控程序可以通过UART与PC主机通信,实现查看/修改内存、寄存器、单步执行等高级调试功能。这实现了一个完全基于硬件断点的低成本仿真调试环境。
MC68SZ328的这套引导加载和在线仿真机制,虽然诞生于嵌入式开发的“上古时代”,但其设计思想——通过最精简的硬件支持实现最核心的调试功能——至今仍闪烁着智慧的光芒。在资源受限、没有JTAG和SWD的系统中,理解并掌握这些“古老”的技术,往往能让你在解决棘手问题时多一种思路,多一件武器。