MC68HC05指令周期时序测量:从原理到示波器实战
1. 项目概述:为什么我们需要深究指令周期时序?
在嵌入式开发的日常里,我们常常把微控制器(MCU)当作一个“黑盒”:写代码、编译、烧录、运行,只要功能正确,似乎就万事大吉。但当你需要处理与模拟信号、高速事件或精确时间控制相关的任务时,这个“黑盒”的内部运作节奏——也就是指令周期时序——就成了决定成败的关键。这就像指挥一个交响乐团,如果你不知道每个乐手(CPU、外设、总线)在何时、以何种速度演奏,最终的音乐(系统行为)就可能杂乱无章。
MC68HC05系列,特别是JJ/JP型号,就是一个典型的例子。它内部集成了一个异步的模拟接口(比如电压比较器),这个接口的工作节奏与CPU的数字时钟并不同步。当你用软件去读取比较器的输出状态(CMP1或CMP2位)时,你读到的是哪个时刻的值?这个“时刻”取决于总线速度和当前正在执行的那条指令。如果你正在用MCU的模拟功能做模数转换(比如模式0或模式1),需要精确测量外部电容的充电时间,那么不理解指令何时真正“采样”了外部信号,你的测量结果就可能存在几个时钟周期的误差,这对于精度要求高的应用是致命的。
官方数据手册通常只告诉你每条指令需要几个时钟周期,但不会告诉你在这个周期内的哪个“节拍”上发生了读或写操作。这份由摩托罗拉(后飞思卡尔)工程师Mark L. Shaw撰写的应用笔记AN1738,正是为了填补这个空白。它不仅仅是一份理论说明,更提供了一套可实操的测量方法,让你能用示波器亲眼“看到”指令执行的内部时序。这对于调试时序敏感型驱动、优化中断响应、甚至逆向理解没有完整文档的老旧芯片行为,都极具价值。
2. 核心概念解析:指令周期、PH2时钟与关键信号
在深入测量方法之前,我们必须先建立几个核心概念。这些概念是理解后续所有时序图和数据的基础。
2.1 指令周期与总线周期
对于MC68HC05这类经典8位MCU,CPU的工作是由一个主振荡器(OSC1)驱动的。这个时钟经过内部分频,产生驱动内部总线的时钟,通常称为PH2(Phase 2)时钟。一条指令的执行由一个或多个“总线周期”组成,而一个总线周期通常对应两个PH2时钟边沿(一个上升沿和一个下降沿)。
例如,一条LDA(加载累加器)指令,在直接寻址模式下需要3个总线周期。但关键问题是:在这3个周期里,CPU到底在哪个精确的“点”上从目标地址读取了数据?是在周期开始、中间还是末尾?这个点,就是我们需要测量的“读时序”。
2.2 读时序与写时序的本质区别
这是本应用笔记的核心洞见之一,也是很多开发者容易混淆的地方。
读操作的关键时刻:对于所有读取数据的指令(如LDA,BIT,BRCLR),数据被锁存进CPU的时刻发生在该读周期的最后一个PH2时钟的上升沿。即使目标地址(比如某个I/O端口)的数据在周期结束前就已经稳定有效,CPU也只会在那个特定的上升沿进行采样。你可以把它想象成一场考试的交卷时间,不管你多早写完,监考老师只在打铃收卷的那一刻才来收你的卷子(读取数据)。
写操作的关键时刻:对于写入数据的指令(如STA,BSET),情况更复杂一些。数据被写入目标寄存器的时刻发生在写周期内,但不同目标寄存器的写入时刻有差异:
- 对于端口B(PORTB)和大多数内部寄存器,数据大约在写周期的中间时刻开始写入。
- 对于端口A(PORTA)和端口C(PORTC),数据在写周期的一开始就开始写入。
这意味着,如果你用STA指令向PORTA和PORTB写同一个值,PORTB上的电平变化会比PORTA晚大约半个总线周期。这个差异在需要严格同步多个输出的场景下必须被考虑。
2.3 “虚读”周期与读-修改-写指令
另一个重要的细节是“虚读”(Dummy Read)周期。有些指令,比如STA(存储),在执行过程中会先产生一个“读”周期,但这个周期并不真正捕获数据用于运算,它可能只是为了获取操作数地址。在时序图上,这个周期看起来像一个读操作,但它对数据总线上的内容不敏感。
此外,像BSET(位置位)、BCLR(位清零)这类“读-修改-写”指令,它们的操作更为复杂:先读取整个字节,在CPU内部修改特定位,然后再将整个字节写回去。因此,它们的时序包含了读和写两个阶段,且读阶段发生在指令周期序列的较早位置,以便为后续的修改和写入留出时间。
3. 典型指令时序图深度解读
应用笔记中提供了两张关键的时序图,分别是LDA(读)和STA(写)在直接寻址模式下的情况。我们结合图表和文字描述,将其拆解为更易理解的步骤。
3.1 LDA指令(直接寻址)时序分解
假设我们执行指令LDA $10,意为从地址$10加载数据到累加器A。
周期1(取指操作码):
- 地址总线:输出程序计数器(PC)当前的值,指向
LDA指令的操作码所在地址(假设为$0800)。 - R/W信号:为高电平,表示读操作。
- 数据总线:在周期末尾,CPU从
$0800读取LDA指令的操作码(例如$B6)。 - 关键事件:在PH2的上升沿,操作码被锁存到指令寄存器(LIR信号有效)。这个上升沿标志着一条新指令的开始,是我们测量所有时序的基准零点。
- 地址总线:输出程序计数器(PC)当前的值,指向
周期2(取操作数地址):
- 地址总线:PC递增,指向下一个字节(
$0801),这里存放的是操作数的地址低字节($10)。 - R/W信号:保持高电平(读)。
- 数据总线:CPU读取地址
$10。 - 关键事件:这仍然是一个读周期,但读取的是地址,而非最终数据。
- 地址总线:PC递增,指向下一个字节(
周期3(读取实际数据):
- 地址总线:输出操作数地址
$10。 - R/W信号:保持高电平(读)。
- 数据总线:位于
$10的存储单元或寄存器将其数据放到总线上。 - 最关键事件:在这个周期的末尾,PH2时钟的上升沿,数据总线上的值被最终锁存到CPU的累加器A中。这就是“读”操作完成的精确时刻。图表中标注“DATA CAPTURES ON THIS EDGE”。
- 地址总线:输出操作数地址
注意事项:图表显示,对于PORTA、PORTC和比较器输出(CMP1/CMP2),数据在周期结束前(PH2上升沿前50ns甚至100ns)就可能已出现在内部总线上,但CPU只在那个指定的上升沿进行采样。因此,外部信号必须在这个上升沿之前足够长时间保持稳定,以满足芯片的“数据建立时间”要求。
3.2 STA指令(直接寻址)时序分解
假设执行指令STA $20,意为将累加器A的值存储到地址$20。
- 周期1与周期2:与
LDA类似,用于取指操作码和操作数地址$20。 - 周期3(虚读周期):
- 地址总线:输出目标地址
$20。 - R/W信号:为高电平(读)。
- 关键事件:这是一个“虚读”周期。CPU访问地址
$20,但忽略读回的数据。这个周期可能是为了准备写入路径。
- 地址总线:输出目标地址
- 周期4(执行写入):
- 地址总线:继续输出目标地址
$20。 - R/W信号:变为低电平,表示写操作。
- 数据总线:CPU将累加器A的值放到数据总线上。
- 最关键事件(差异点):
- 对于PORTA和PORTC:数据在周期4一开始(PH2上升沿后约50ns)就开始驱动到端口引脚上。
- 对于PORTB和内部寄存器:数据在周期4中间时刻(PH2上升沿后约50ns到下降沿之间)才开始写入。
- 数据在整个写周期内都保持有效,直到周期结束。
- 地址总线:继续输出目标地址
理解这两张图,你就掌握了MC68HC05 JJ/JP系列最核心的读写行为模型。应用笔记中的表1和表2则是对所有相关指令的时序总结,是编程时速查的宝贵资料。
4. 实战测量:用MMDS05和示波器捕捉时序
理论很美好,但硬件世界充满变数。芯片制造公差、电源噪声、负载差异都可能导致时序与手册有微小偏差。因此,拥有一套自己的测量方法至关重要。AN1738提供的方案基于当时的经典开发工具MMDS05(Motorola Modular Development System)。
4.1 测量系统搭建详解
测量核心思想是:利用CPU内部产生的、与指令执行严格同步的信号(LIR)作为触发基准,去观察外部I/O引脚(如PA5)上的变化,并以内部总线时钟(PH2)作为时间标尺。
所需设备清单:
- MMDS05开发系统:包含主机和对应的JJ/JP系列仿真器板(Personality Board)。仿真器板通过一个插头座(Pod)连接或替代目标MCU。
- 四通道示波器:带宽建议100MHz以上,用于同时观察多个信号。
- 脉冲发生器:要求能被外部触发,并产生可调宽度和延迟的脉冲。
- 74HC00芯片(四路与非门)x1:用于信号缓冲和整形。因为LIR信号驱动能力有限,而脉冲发生器的触发输入通常需要特定的阻抗匹配。
- 连接线、探头、电源若干。
硬件连接步骤(对照图4):
提取基准信号:
- 在MMDS05主板(通常是U6芯片的第2脚)找到
LIR(Load Instruction Register)信号。这个信号在每条指令开始时(操作码被锁存的PH2上升沿)会产生一个脉冲。 - 在MMDS05主板(通常是U7芯片的第9脚)找到
PH2信号。这是内部总线时钟,是测量所有时间间隔的参考。 - 使用
LIR信号作为整个测量的“起点”触发器。
- 在MMDS05主板(通常是U6芯片的第2脚)找到
构建触发链:
- 将
LIR信号(来自U6-2)连接到第一片74HC00中的一个与非门输入端(两个输入端短接作为反相器)。输出连接到第二个与非门(同样接成反相器)。两级反相器主要起缓冲和整形作用,提高驱动能力,并隔离后级电路对前级敏感信号的影响。 - 将第二级反相器的输出连接到脉冲发生器的“外部触发”输入端口。这样,每开始执行一条新指令,就会触发脉冲发生器一次。
- 将
生成测量脉冲:
- 设置脉冲发生器工作模式为“外部触发”模式。
- 调整脉冲发生器的输出脉冲宽度。这个宽度是整个测量的关键调节参数。我们的目标是产生一个非常窄的脉冲,其宽度刚好覆盖我们想要测量的那个读或写周期。
- 将脉冲发生器的输出,再经过另外两个74HC00与非门(接成反相器)进行缓冲,然后连接到示波器的一个通道(例如通道1)。这个缓冲同样是防止示波器探头负载影响脉冲发生器输出。
连接观测点:
- 将
PH2信号(来自U7-9)连接到示波器的另一个通道(例如通道2)。这是我们所有时间测量的基准时钟。 - 将目标MCU的某个I/O引脚(例如
PA5)连接到示波器的第三个通道(例如通道3)。我们将在软件中控制这个引脚,使其状态变化与我们感兴趣的读/写操作严格同步。 - (可选)将脉冲发生器的触发输入信号(即缓冲后的LIR)也接到示波器第四通道,用于监控触发是否正常。
- 将
软件准备: 应用笔记末尾附带了完整的timing.asm汇编源代码。它的核心逻辑是构造一个无限循环,在循环中执行你想要测量时序的特定指令(比如LDA ACR或STA PORTA),并在指令执行前后立即翻转PA5引脚的电平。PA5引脚上的这个跳变边沿,就标记了软件执行到该指令的时刻。
4.2 测量流程与技巧
测量读时序(例如,测量LDA ACR指令读取ACR寄存器的时刻):
- 编译并下载
timing.asm中的CHECK1或CHECK3例程到MMDS05的仿真内存中。 - 配置脉冲发生器:将输出脉冲宽度设置为略大于一个总线周期(例如,在2.1MHz总线频率下,周期为476ns,可先设为500-600ns)。
- 触发与同步:运行程序。示波器应使用通道1(脉冲发生器输出)或通道4(LIR触发信号)作为触发源。你应该能看到稳定的、周期性的波形。
- 定位关键窗口:观察通道2(PH2)和通道1(测量脉冲)。调整脉冲发生器的延迟和宽度,使产生的窄脉冲刚好覆盖你认为的“读周期”(根据表1,对于
LDA direct,读发生在第3个周期)。 - 观测数据锁存点:此时观察通道3(PA5)。软件会在执行
LDA ACR后立即改变PA5状态。你需要在那个窄脉冲窗口内,仔细观察PA5的跳变沿与PH2上升沿的关系。 - 微调与捕获:细微调整脉冲宽度和延迟,你会发现当脉冲窗口刚好覆盖数据被锁存的那个PH2上升沿时,PA5的输出会在窗口内出现随机性的跳变(因为采样时刻的微小抖动)。这个跳变点,就是精确的读时刻。记录下此时PA5跳变沿与最近的PH2上升沿之间的时间差(
Δt)。
测量写时序(例如,测量STA PORTA指令写入端口的时刻): 流程类似,但软件例程使用CHECK2,它循环地将PORTA全部置高再置低。你需要在示波器上观察PORTA某个引脚(如PA0)的电平变化。调整脉冲窗口覆盖写周期,观察PA0电平是在窗口的起始处变化(对应PORTA的早写特性)还是在窗口中间变化(对应PORTB的晚写特性)。
实操心得与避坑指南:
- 信号完整性是生命线:使用短而优质的探头接地线(弹簧接地针最好),避免引入振铃和噪声。74HC00的缓冲至关重要,不要省略。
- 理解“随机跳变”:在测量读时序时,PA5的跳变之所以在正确窗口内看起来“随机”,是因为我们试图用一个固定宽度的脉冲去捕捉一个发生在固定相位但绝对时间有微小抖动的边缘。当脉冲窗口刚好覆盖这个边缘时,由于触发抖动、噪声等因素,边缘落在窗口内的位置每次触发都可能略有不同,在屏幕上就表现为波形不稳定或“模糊”。这正是你找到精确时刻的标志。
- PH2信号是关键:所有时间测量都必须以PH2的边沿为基准。应用笔记表3中的“Start vs. PH2 rise (ns)”等数据,正是这样测量出来的。
- 适配其他MCU:这套方法的精髓是找到目标MCU在仿真器板上的
LIR和PH2测试点。你需要查阅对应仿真器板的原理图来定位这些信号。
5. 测量结果解读与工程应用
通过上述方法,应用笔记得到了表3的精确数据。我们来解读并转化这些数据为工程知识。
表3数据解读示例:
- 读PORTA时序:“End vs. next PH2 fall (ns)”为0,“Equiv. portion of PH2 cycle”为1.0。这意味着,读PORTA的操作,其数据锁存时刻发生在当前读周期的末尾,精确对齐下一个PH2下降沿(或等效于当前PH2周期的1.0处)。而“Start vs. PH2 fall (ns)”为-50ns,表示数据在PH2下降沿前50ns就开始有效了(建立时间)。
- 写PORTA时序:“Start vs. PH2 rise (ns)”为+50ns,“Equiv. portion of PH2 cycle”为0.5。这意味着,写PORTA的操作在写周期开始后、PH2上升沿后约50ns启动,大约相当于半个PH2周期的时间点。
- 写PORTB时序:“Start vs. PH2 rise (ns)”为+50ns,但“Equiv. portion of PH2 cycle”为0.0。这印证了之前的结论:PORTB的写入发生在写周期的中间点附近,而这个点更接近于以PH2上升沿为起点计算的0.0位置(即本周期内),而不是0.5。
这些数据如何指导编程?
- 精确的模数转换(ADC)采样:在JJ/JP系列使用模拟比较器进行斜率ADC时,你需要知道
LDA ASR(读取比较器状态)指令在哪个精确时刻锁定了比较器的输出。根据表3,读CMP1/CMP2位在PH2下降沿前100ns数据就已开始有效,并在下降沿被锁存。因此,你需要确保在锁存时刻之前,比较器的输出已经稳定。如果比较器响应慢,你可能需要在读取前插入NOP指令来增加等待时间。 - 严格的输出同步:如果你需要PORTA和PORTB的某个位同时翻转,直接使用
STA指令是不行的。因为写入PORTA比PORTB早约半个周期。解决方案可以是:先计算好要写入的值,分别存入临时变量,然后通过连续两条STA指令写入。虽然不能做到绝对同时,但可以将时间差控制在已知的一个指令周期内,这比未知的半个周期差异更可控。或者,如果硬件允许,可以考虑使用同一个端口。 - 中断响应时间校准:在计算最坏情况中断响应时间时,你需要考虑当前正在执行的指令可能最长需要多少个周期才能完成(到达一个可安全中断的边界)。理解每条指令的周期构成,能帮助你更精确地计算这个时间。
- 软件延时循环的微调:在编写需要微秒级精度的软件延时时,单纯循环NOP指令可能不够。你需要知道循环体本身(比如判断、跳转指令)的准确周期。结合指令时序表,你可以设计出周期数极其精确的延时例程。
6. 常见问题与排查思路
在实际测量和基于时序编程的过程中,你可能会遇到以下问题:
问题1:示波器上完全抓不到稳定的PA5跳变信号,或者波形极其混乱。
- 排查思路:
- 检查软件:确认下载的程序是否正确,并且确实在执行你期望测量的那段循环代码。可以通过在循环中加入更明显的标志(比如让一个LED闪烁)来验证程序基本运行正常。
- 检查硬件连接:确认
PA5引脚是否确实连接到了示波器通道,并且没有接触不良。确认MCU的电源和地稳定。 - 检查触发:确保示波器触发源设置正确(通常是连接LIR或脉冲输出的通道),触发电平设置在信号幅度的中间值,触发模式为“正常”或“自动”。
- 检查脉冲发生器设置:确认脉冲发生器处于“外部触发”模式,触发极性正确(通常是上升沿触发)。输出的脉冲宽度和幅度要设置合适(宽度先调大,如1μs,幅度与MCU逻辑电平匹配,如5V或3.3V)。
- 检查缓冲电路:确认74HC00芯片已正确供电(Vcc和GND),输入输出连接正确。可以用示波器单独测量每一级反相器前后的波形,看信号是否正常传递和整形。
问题2:能抓到波形,但PA5的跳变边沿非常“宽”或“模糊”,无法精确定位。
- 排查思路:
- 探头带宽与接地:使用更高带宽的示波器探头,并务必使用最短的接地路径(弹簧接地针)。长接地线会引入电感,严重劣化高速边沿。
- 负载效应:确保PA5引脚除了连接示波器探头,没有连接其他重负载电路。示波器探头本身通常是高阻(如10MΩ),但电容可能仍有10-15pF。如果驱动能力不足,可以考虑在软件中让PA5驱动一个由74HC00构成的缓冲器后再测量。
- 系统噪声:检查MCU和实验板的电源是否干净。可以在电源引脚附近并联一个0.1μF和一个10μF的电容进行退耦。
问题3:测量出的时间与表3中的数据有较大偏差(超过20-30ns)。
- 排查思路:
- 示波器校准:检查示波器的时间基准是否已校准。可以用一个已知频率的精确信号源(如函数发生器)进行验证。
- 探头延迟:不同探头和通道可能有细微的传输延迟。对于需要精确测量时间差的场合,应使用同一型号的探头,并在测量前将各通道的探头补偿调节到最佳状态,或者使用示波器的“ Deskew”功能校准通道间延迟。
- 信号路径差异:
PH2和PA5信号经过的路径不同(一个来自仿真器板测试点,一个来自MCU引脚),路径上的寄生电容电感会导致传播延迟不同。这种系统误差在纳秒级测量中难以完全避免,但可以通过测量一个已知时序关系的信号来标定这个误差。对于大多数嵌入式应用,理解相对时序(如PORTA比PORTB早写半个周期)比绝对纳秒值更重要。
问题4:如何将这种方法应用到没有MMDS05的其他MC68HC05型号或更现代的MCU上?
- 核心思路迁移:方法的核心是找到与指令流严格同步的内部信号作为触发基准。在现代开发中:
- 利用SWD/JTAG调试器:许多现代ARM Cortex-M MCU的调试接口支持“指令跟踪”或“数据观察点”功能。你可以设置一个观察点,当特定地址被访问时,让调试器输出一个同步脉冲(或触发一个GPIO),然后用示波器捕捉这个脉冲和外部IO的变化。
- 利用芯片内置的跟踪功能:一些高端MCU有嵌入式跟踪宏单元(ETM),可以流式输出执行信息,配合专业工具可以重构指令流,但这通常成本较高。
- 软件模拟触发点:如果硬件上没有这样的测试点,最后的办法是在代码中,在目标指令前后插入对某个“调试专用”GPIO引脚的操作(置高/置低),用这个GPIO的边沿作为示波器触发源。但这会引入几条额外指令的延迟,你需要精确知道这些额外指令的周期数,并在最终结果中扣除。这种方法精度较低,但适用于粗略验证。
- 查找替代测试点:即使对于老旧的MCU,在其评估板或仿真器上,
PH2(或类似的内部时钟)和LIR(或指令取指信号)常常会引到测试点或连接器上,用于工厂测试。需要仔细研究硬件原理图。
掌握指令周期时序的测量与分析,是从嵌入式“程序员”迈向“系统工程师”的关键一步。它让你不再仅仅满足于代码的功能正确,而是开始关注代码在时间维度上的精确行为,从而设计出更可靠、性能更优的嵌入式系统。这份针对MC68HC05 JJ/JP系列的应用笔记,其价值不仅在于给出了具体的数据和方法,更在于提供了一种严谨的、可迁移的硬件时序分析思维框架。
