跳转至

指令集与编程要点

8051 指令集包含 111 条指令,分为五大类。虽然现在主要用 C51 语言编程,但理解常用指令有助于阅读反汇编代码、调试和优化关键代码段。

指令分类

类别 指令数 说明
数据传送 28 MOV、MOVX、MOVC、PUSH、POP 等
算术运算 24 ADD、SUBB、INC、DEC、MUL、DIV 等
逻辑运算 25 ANL、ORL、XRL、CLR、CPL、RL、RR 等
控制转移 17 LJMP、SJMP、JZ、CJNE、DJNZ、LCALL、RET 等
位操作 17 SETB、CLR、CPL、JB、JNB、JBC、ANL C、ORL C 等

必须掌握的常用指令

数据传送指令

MOV A, #0x55      ; 立即数送累加器
MOV R0, A         ; A 送 R0
MOV DPTR, #0x1234 ; 16 位立即数送数据指针
MOVX @DPTR, A     ; A 送外部 RAM(DPTR 指向的地址)
MOVC A, @A+DPTR   ; 从程序存储器读常数表
PUSH ACC          ; 入栈
POP ACC           ; 出栈

算术运算指令

ADD A, R0         ; A = A + R0
ADDC A, R1        ; A = A + R1 + CY(带进位加)
SUBB A, R2        ; A = A - R2 - CY(带借位减)
INC A             ; A = A + 1
DEC R3            ; R3 = R3 - 1
MUL AB            ; B:A = A × B(16 位结果)
DIV AB            ; A = A ÷ B 的商,B = 余数
DA A              ; 十进制调整(用于 BCD 码运算)

逻辑运算指令

ANL A, R0         ; A = A & R0(与)
ORL A, R1         ; A = A | R1(或)
XRL A, #0xFF      ; A = A ^ 0xFF(异或,用作取反)
CLR A             ; A = 0
CPL A             ; A = ~A(取反)
RL A              ; A 左环移 1 位
RR A              ; A 右环移 1 位
SWAP A            ; A 的高 4 位与低 4 位交换

控制转移指令

LJMP 0x1000       ; 长跳转(64K 范围)
SJMP LOOP         ; 短跳转(-128~+127 字节)
JZ LABEL          ; A=0 则跳转
JNZ LABEL         ; A≠0 则跳转
CJNE A, #0x10, NEQ; A≠0x10 则跳转到 NEQ
DJNZ R0, LOOP     ; R0 减 1,非 0 则跳转(循环控制)
LCALL SUBROUTINE  ; 长调用
RET               ; 子程序返回
RETI              ; 中断返回
NOP               ; 空操作(精确延时用)

位操作指令(8051 特色)

SETB P1.0         ; P1.0 置 1
CLR P1.0          ; P1.0 置 0
CPL P1.0          ; P1.0 取反
JB P1.0, LABEL    ; P1.0=1 则跳转
JNB P1.0, LABEL   ; P1.0=0 则跳转
JBC P1.0, LABEL   ; P1.0=1 则跳转并自动清零
MOV C, P1.0       ; P1.0 送进位位 CY
ANL C, /P1.1      ; CY = CY & (NOT P1.1)

C51 编程要点

存储类型关键字

关键字 存储区域 访问速度 使用建议
data 内部 RAM(00H~7FH) ★★★★★ 频繁使用的变量、中断变量
idata 内部 RAM(00H~FFH) ★★★★ 一般变量
xdata 外部/扩展 RAM ★★★ 大量数据缓冲区
code 程序 Flash ★★ 常量表、字符串
#define uchar unsigned char
#define uint  unsigned int

uchar data count;          // 高速计数变量
uchar code seg_table[] = { // 放在 Flash 中,不占 RAM
    0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07
};

中断函数声明

void Timer0_ISR() interrupt 1    // 定时器 0
void UART_ISR()   interrupt 4    // 串口(需判断 RI/TI)
void INT0_ISR()   interrupt 0 using 1  // 使用寄存器组 1

SFR 位定义

// Keil C51 内置的位定义
// sbit P1_0 = P1^0;  // 已预定义

// 自定义位定义
sbit LED    = P1^0;
sbit KEY    = P3^2;
sbit BUZZER = P1^7;

精确延时

// 利用 NOP 实现微秒级延时(12MHz,12T)
void delay_us(unsigned int us) {
    while (us--) {
        _nop_();   // 1μs(一个机器周期)
        _nop_();
    }
}

// 利用定时器实现毫秒级延时
void delay_ms(unsigned int ms) {
    unsigned int i, j;
    for (i = 0; i < ms; i++)
        for (j = 0; j < 123; j++);  // 约 1ms(12MHz)
}

常用位运算操作

// 置位某位
P1 |= (1 << 3);      // P1.3 = 1

// 清零某位
P1 &= ~(1 << 3);     // P1.3 = 0

// 取反某位
P1 ^= (1 << 3);      // P1.3 = ~P1.3

// 读取某位
if (P1 & (1 << 3)) { // 如果 P1.3 = 1

// 操作 SFR 特定位
TMOD &= 0xF0;        // 清除低 4 位(不影响 T1 设置)
TMOD |= 0x01;        // 设置 T0 为模式 1

宏定义技巧

// 引脚定义
#define LED_RED     P1^0
#define LED_GREEN   P1^1
#define KEY_START   P3^2

// 位操作宏
#define SET_BIT(reg, bit)   (reg |= (1 << bit))
#define CLR_BIT(reg, bit)   (reg &= ~(1 << bit))
#define GET_BIT(reg, bit)   ((reg >> bit) & 1)

// 延时宏
#define delay_1ms() delay_us(1000)

// 状态宏
#define BUZZER_ON   BUZZER = 0
#define BUZZER_OFF  BUZZER = 1