指令集与编程要点
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