汇编编程

STC单片机汇编编程指南

从语法基础到工程实战,系统掌握 STC89/90 系列单片机汇编程序设计、常用模块驱动与调试技巧

一、汇编语言基础

面向硬件的底层程序设计语言

汇编语言是面向机器的低级程序设计语言,用助记符代替机器指令的二进制代码。对于 STC89C52RC 等 8051 内核单片机,掌握汇编语言有助于深入理解 CPU 执行过程、寄存器操作、存储器访问和中断响应机制,是单片机教学与工程调试的重要基础。

助记符

用英文缩写表示操作,如 MOV、ADD、SJMP、CALL,直观对应机器指令。

标号

代表指令或数据的地址,如 MAIN、LOOP、DELAY,用于跳转和调用定位。

操作数

指令处理的对象,可以是寄存器、立即数、直接地址或间接地址。

二、汇编程序基本结构

标准程序框架是规范与调试的前提

一个完整的 8051 汇编程序通常由文件头注释、伪指令声明、中断入口、主程序、子程序和中断服务程序组成。结构清晰不仅方便阅读,也便于 Keil 编译、Proteus 仿真和硬件调试。

;====================================================
; 程序名称:LED 闪烁实验
; 硬件平台:STC89C52RC 自研实训板
; 晶振频率:12MHz
; 作者:王
; 日期:2026-06-23
;====================================================

ORG 0000H          ; 复位入口
    LJMP MAIN       ; 跳转到主程序

ORG 0003H          ; 外部中断 0 入口
    LJMP INT0_ISR

ORG 0030H          ; 主程序起始地址
MAIN:
    MOV SP, #60H   ; 初始化堆栈指针
    MOV P1, #0FFH  ; 初始化 P1 口
LOOP:
    CPL P1.0        ; P1.0 取反
    LCALL DELAY     ; 调用延时子程序
    SJMP LOOP       ; 循环执行

; 延时子程序
DELAY:
    MOV R6, #250
D1:
    MOV R7, #250
D2:
    DJNZ R7, D2
    DJNZ R6, D1
    RET

; 外部中断 0 服务程序
INT0_ISR:
    PUSH ACC
    PUSH PSW
    ; 中断处理逻辑
    POP PSW
    POP ACC
    RETI

END                ; 程序结束

结构要点

ORG:指定程序或数据的起始地址,必须正确使用以保证中断入口地址固定。

主程序 MAIN:从 0030H 开始,完成初始化后进入主循环。

子程序:以 RET 返回,调用前注意保护现场。

中断程序:以 RETI 返回,入口地址固定,尽量简短。

END:汇编结束标志,必须放在程序末尾。

三、常用伪指令

指导汇编器完成编译的辅助命令

伪指令不是 CPU 执行的机器指令,而是告诉汇编器如何分配存储器、定义数据、定位程序等。熟练使用伪指令是编写规范汇编程序的基础。

伪指令功能示例
ORG指定起始地址ORG 0030H
END程序结束END
DB定义字节数据TAB: DB 0C0H, 0F9H
DW定义字数据(2 字节)DW 1234H
DS预留存储空间DS 10
EQU赋值,不可重定义LED_PORT EQU P1
DATA赋值,可重定义COUNT DATA 30H
BIT位地址赋值LED BIT P1.0
CODE / DATA / XDATA段声明SEGMENT CODE
; 伪指令使用示例
LED     BIT     P1.0
COUNT   EQU     20
BUFFER  DATA    30H

ORG 0100H
DISP_TAB:
    DB 0C0H, 0F9H, 0A4H, 0B0H, 099H
    DB 092H, 082H, 0F8H, 080H, 090H

四、数据定义与变量

合理分配片内 RAM 资源

STC89C52RC 片内 RAM 为 256 字节(部分兼容型号 128 字节)。变量应优先使用 30H~7FH 的通用 RAM 区,位变量使用 20H~2FH 的位寻址区,避免占用工作寄存器和 SFR。

字节变量

COUNT   DATA    30H
TEMP    DATA    31H

MOV COUNT, #10
INC COUNT

位变量

FLAG    BIT     00H
LED     BIT     P1.0

SETB FLAG
CLR LED

变量分配建议

  • 堆栈底 SP 初值建议设为 60H 以上,避免与工作寄存器、位寻址区冲突。
  • 连续变量尽量连续存放,便于间接寻址和批量处理。
  • 全局标志位统一使用位寻址区低 128 位,命名清晰。

五、程序流程控制

分支、循环与跳转

汇编程序通过跳转指令实现分支和循环。根据跳转距离和条件,可选择 SJMP、AJMP、LJMP、JZ/JNZ、CJNE、DJNZ 等指令。

无条件跳转

SJMP — 短跳转,范围 -128~+127

AJMP — 绝对跳转,同一 2KB 页内

LJMP — 长跳转,64KB 全空间

JMP @A+DPTR — 间接跳转,用于散转表

条件跳转

JZ / JNZ — ACC 为 0 / 非 0 跳转

CJNE — 比较不相等跳转,可产生 CY

DJNZ — 减 1 不为 0 跳转,常用作循环

JB / JNB / JBC — 位状态跳转

; 循环示例:R5 从 10 减到 0
    MOV R5, #10
LOOP:
    LCALL DO_SOMETHING
    DJNZ R5, LOOP

; 分支示例:判断 P1.0 状态
    JB P1.0, LED_ON
    SETB P1.0
    SJMP DONE
LED_ON:
    CLR P1.0
DONE:
    SJMP $

六、子程序设计

模块化编程的核心

子程序是完成特定功能的独立代码段,通过 ACALL/LCALL 调用,RET 返回。合理划分子程序可以提高代码复用性、可读性和可维护性。

设计原则

1

单一职责:每个子程序只完成一个功能,如延时、显示、按键扫描。

2

入口出口清晰:调用前设置入口参数,返回后读取出口参数。

3

现场保护:若子程序修改了调用者使用的寄存器,应 PUSH/POP 保护。

4

避免过深嵌套:8051 堆栈有限,子程序嵌套层数不宜过多。

; 带参数传递的延时子程序
; 入口:R6、R7 为延时计数初值
DELAY_MS:
    PUSH ACC         ; 保护 ACC
DLY1:
    MOV A, R7
DLY2:
    DEC A
    JNZ DLY2
    DJNZ R6, DLY1
    POP ACC          ; 恢复 ACC
    RET

七、中断服务程序

高效响应外部事件

中断是 8051 实时处理外部事件和定时事件的重要机制。中断服务程序(ISR)必须短小精悍,复杂处理交给主循环,通过标志位传递信息。

; 定时器 0 中断服务程序示例
ORG 000BH
    LJMP T0_ISR

T0_ISR:
    PUSH ACC        ; 保护累加器
    PUSH PSW        ; 保护程序状态字

    SETB T0_FLAG     ; 设置 1ms 标志位

    MOV TH0, #0FCH   ; 重装初值(1ms @12MHz)
    MOV TL0, #018H

    POP PSW         ; 恢复程序状态字
    POP ACC         ; 恢复累加器
    RETI

中断编程要点

初始化顺序:设置工作方式 → 赋初值 → 开中断源 → 开总中断 → 启动定时器。

RETI 结尾:中断返回必须使用 RETI,它会清除优先级状态。

保护现场:ISR 中使用的 ACC、PSW、DPTR 等应 PUSH/POP 保护。

标志位通信:ISR 只设置标志位,主循环查询标志后执行耗时操作。

八、常用模块汇编实例

可直接复用的典型驱动代码

1. 数码管动态扫描

DISP:
    MOV P0, #0FFH      ; 消隐
    MOV P2, DIGIT     ; 位选
    MOV A, BUF
    MOVC A, @A+DPTR   ; 查段码表
    MOV P0, A
    LCALL DELAY_1MS
    RET

2. 按键扫描(含消抖)

KEY_SCAN:
    MOV P1, #0FFH
    JNB P1.0, KEY_PRESS
    CLR KEY_FLAG
    RET
KEY_PRESS:
    JB KEY_FLAG, KEY_DONE
    LCALL DELAY_10MS
    JNB P1.0, KEY_VALID
    RET
KEY_VALID:
    SETB KEY_FLAG
    SETB KEY_EVENT
KEY_DONE:
    RET

3. 串口发送一个字符

; 入口:A 存放待发送字符
UART_SEND:
    CLR TI
    MOV SBUF, A
WAIT_TX:
    JNB TI, WAIT_TX
    CLR TI
    RET

九、Keil 汇编工程建立

从新建工程到生成 HEX 文件

1

新建工程

Project → New μVision Project,选择保存路径,单片机型号选择 Atmel → AT89C52 或 STC 对应型号。

2

添加源文件

新建 .asm 文件并保存到工程目录,右键 Source Group 1 → Add Existing Files 添加该文件。

3

配置输出

点击魔术棒 → Output,勾选 Create HEX File,便于 STC-ISP 下载。

4

编译与下载

按 F7 编译,确认 0 Error(s)、0 Warning(s)。使用 STC-ISP 选择串口、打开 HEX 文件并下载。

十、调试技巧与常见问题

提高排错效率的实践经验

善用单步与断点

在 Keil 中使用单步执行(F11)观察寄存器和内存变化,在关键跳转处设置断点,快速定位逻辑错误。

查看特殊功能寄存器

调试时重点关注 ACC、PSW、SP、TCON、SCON 等寄存器,确认配置是否符合预期。

检查中断入口

中断程序必须以 RETI 返回,入口地址必须正确,否则程序会跑飞。

堆栈溢出排查

子程序和中断嵌套过多会导致堆栈溢出,表现为随机复位或变量被改写。合理设置 SP 初值并控制嵌套深度。

常见现象可能原因排查方法
编译报错 "undefined symbol"标号未定义或拼写错误检查标号命名与调用是否一致
上电无反应复位电路、晶振未起振、EA 引脚电平测量 RST、XTAL 波形,确认 EA=VCC
程序跑飞堆栈溢出、RET/RETI 用错、中断入口错误检查 SP、中断向量、子程序返回指令
仿真正常硬件异常时序不满足、I/O 驱动能力不足增加延时、检查上拉电阻与电流负载

学习路径

从会写到写得规范

掌握 STC 单片机汇编编程,需要理解 8051 内核结构、熟悉指令系统、规范程序结构,并通过大量实验加深对定时器、中断、串口、I/O 等外设的理解。建议从点亮 LED 开始,逐步完成流水灯、按键、数码管、定时器中断、串口通信等实验,最终能够独立编写和调试中等规模的汇编程序。