4. 使用断点调试

任务说明

本案例面向单片机软件调试场景,目标是在调试器中设置断点,通过观察变量变化来理解程序执行流程。我们将使用一个简单的按键计数程序作为调试对象,在Keil MDK调试环境中完成断点设置、单步执行和变量监视操作。完成本案例后,你应能独立在调试器中设置断点、运行到断点处暂停、单步执行并观察变量实时变化,从而建立“代码行—变量值—执行顺序”之间的对应关系。

调试对象程序

以下程序实现功能:每次外部按键按下(P3.2引脚电平由高变低),全局变量press_count加1,当计数达到3次时,flag_reach_3被置1。该程序包含两个关键变量和明确的分支逻辑,适合作为断点调试的观察对象。


#include <reg51.h>

sbit KEY = P3^2;          // 按键输入引脚
unsigned char press_count = 0;   // 按键按下次数计数
bit flag_reach_3 = 0;            // 达到3次标志

void delay_ms(unsigned int ms) {
    unsigned int i, j;
    for (i = 0; i < ms; i++)
        for (j = 0; j < 120; j++);
}

void main(void) {
    unsigned char key_state = 1;   // 当前按键状态,1为未按下
    unsigned char last_key_state = 1; // 上一次按键状态

    while (1) {
        key_state = KEY;           // 读取当前按键电平

        // 检测下降沿:上一次为高,当前为低,表示一次按下
        if ((last_key_state == 1) && (key_state == 0)) {
            press_count++;         // 断点位置A:观察press_count变化
            if (press_count >= 3) {
                flag_reach_3 = 1;  // 断点位置B:观察flag_reach_3被置位
            }
        }

        last_key_state = key_state; // 更新上一次状态
        delay_ms(10);              // 简单消抖延时
    }
}

断点设置与调试步骤

本案例使用Keil MDK(以C51编译器为例)配合Proteus仿真或硬件开发板进行调试。请按以下步骤操作:

  1. 工程准备:在Keil中新建工程,选择Atmel AT89C51或兼容芯片,将上述代码添加至工程并编译,确保无错误。
  2. 进入调试模式:点击Debug菜单下的Start/Stop Debug Session(或直接按Ctrl+F5)进入调试界面。
  3. 打开监视窗口:在View菜单中打开Watch Window,在Watch #1页签中添加变量press_count和flag_reach_3。
  4. 设置断点:在代码编辑区找到press_count++;所在行(断点位置A),点击行号左侧灰色区域或按F9设置断点(红色高亮)。同样在flag_reach_3 = 1;所在行(断点位置B)设置第二个断点。
  5. 运行到断点:按F5(全速运行),程序将运行至第一个断点位置A处暂停。此时观察Watch窗口中press_count的值,应比初始值0增加了1(即变为1)。
  6. 单步执行:按F10(单步执行)逐行运行,观察程序从断点位置A继续执行到if (press_count >= 3)判断、再到last_key_state = key_state;的完整路径,注意flag_reach_3此时仍为0。
  7. 继续运行观察:再次按F5,程序会继续运行直到再次检测到按键下降沿,在断点位置A再次暂停。重复上述过程,当press_count增加到3时,继续按F5,程序将在断点位置B暂停,此时Watch窗口中flag_reach_3的值变为1。

执行流程与变量变化对照

下表展示了在调试过程中,随着断点命中,两个关键变量的预期变化顺序(假设初始press_count=0,flag_reach_3=0):

断点命中次序命中位置press_count值flag_reach_3值说明
第1次位置A10第一次按键,计数加1
第2次位置A20第二次按键,计数加1
第3次位置A30第三次按键,计数加1,但尚未执行到位置B
第4次位置B31执行if判断后进入分支,标志置1

注意:第3次命中位置A后,若继续按F5,程序会先执行if (press_count >= 3)判断(条件为真),然后进入分支执行flag_reach_3 = 1;,因此下一次断点将命中位置B。这一过程直观展示了断点暂停→变量检查→继续运行→下一断点的调试节奏,以及变量值随程序流程推进而变化的因果关系。

验证方法与判定标准

验证操作:在Keil调试环境中,通过逻辑分析仪(View菜单→Logic Analyzer)添加press_count和flag_reach_3变量,或直接使用Watch窗口观察。模拟按键输入时,可在Proteus中连接一个按钮到P3.2引脚,或使用调试器自带的信号函数(Debug菜单→Function Editor)编写简单的方波信号模拟按键动作。

判定标准:满足以下条件即可判定本案例调试目标达成:

  1. 能在断点位置A和B准确暂停,且暂停时程序计数器指向对应代码行;
  2. Watch窗口中press_count的值在每次命中位置A时递增1,且与按键动作次数一致;
  3. 当press_count达到3后,继续运行能命中位置B,且此时flag_reach_3的值由0变为1;
  4. 通过单步执行(F10)能清晰观察到程序从位置A到位置B之间经过的每一行代码,理解if分支的判定过程。

调试观测点示意

断点调试观测点示意 while (1) { key_state = KEY; if ((last_key_state==1)&&(key_state==0)) { 1 press_count++; ← 断点A if (press_count >= 3) { 2 flag_reach_3 = 1; ← 断点B } } last_key_state = key_state; delay_ms(10); } Watch 窗口 press_count 0→1→2→3 flag_reach_3 0→1 last_key_state 1→0→1 调试操作提示 F5: 运行到断点 F10: 单步执行 F9: 设置/取消断点 观察变量值随断点变化

上图左侧为代码中两个断点的位置,右侧为Watch窗口中关键变量在调试过程中的预期变化趋势。实际调试时,你将在断点暂停处看到变量值停留在当前状态,通过继续运行触发下一次断点来观察变量如何随执行流程推进而更新。