单片机原理(2):程序设计
指令系统是计算机硬件的语言系统,也叫机器语言,它是软件和硬件的主要界面,从系统结构的角度看,它是系统程序员看到的计算机的主要属性。指令系统表征了计算机的基本功能,决定了机器所要求的能力,也决定了指令的格式和机器的结构。
51系列单片机一般使用汇编语言(Assembly Language)直接编程,其指令系统中,有进行数据传送、算术运算、逻辑运算、位操作、控制传递等功能的111条基本指令。此外也可以采用C语言进行程序设计。
寻址方式
寻址方式是CPU寻找操作数或操作数地址的方法,存放在不同位置的数据具有需要采用不同的方式进行寻址,不同类型计算机的寻址方式也不同,它是计算机重要的性能指标之一。MSC-51单片机有7种寻址方式。
立即寻址
指令中直接给出参与操作的数据,称立即数,用data表示。在汇编语言中,为标明立即数,为data加前缀”#”。立即数可以是8位和16位二进制数,分别用#data和#data16表示。
汇编指令格式:MOV A, #data
如:
1 | MOV A, #30H ;8位立即数30H放入累加器ACC中 |
直接寻址
指令中直接给出参与操作的数据的地址,直接地址一般用direct表示。
汇编指令格式:MOV A, direct
如:
1 | MOV A, 80H ;将80H单元,即P0口的内容放入累加器ACC中 |
寄存器寻址
参与操作的数据存放在寄存器中,汇编指令中直接以寄存器名来表示参与操作的数据地址,寄存器包括工作寄存器R0~R7、累加器ACC、寄存器B、数据指针DPTR。
汇编语言格式:MOV A, Rn ;n=0~7
如:
1 | MOV A, R1 ;将R1中的内容放入累加器ACC中 |
寄存器间接寻址
二次寻址,寻址中寄存器的内容为操作数所存放的地址。第一次寻址得到寄存器的内容为(R0)、(R1)或(DPTR),第二次寻址是将第一次寻址得到的寄存器内容作为地址,在其中存、取参与操作的数据。汇编语言中,寄存器前缀@是寄存器间接寻址的标志,有@R0、@R1、@DPTR等。
汇编语言格式:MOV A, @R0/R1/DPTR
如:
1 | MOV A, @DPTR ;将DPTR所指示的地址单元中的内容放入累加器ACC中 |
变址寻址
由两个寄存器提供地址。若由ACC、PC提供,在汇编语言指令中寻址地址表示为@A+PC;若由ACC和DPTR提供,在汇编语言指令中寻址地址为@A+DPTR。其中,PC或DPTR被称为基址寄存器,A被称为变址寄存器,基址与变址相加为16位无符号加法。
若变址寄存器ACC中的内容加基址寄存器DPTR(或PC)中内容时,低8位有进位,则该进位直接加到高位,不影响进位标志。因变址寻址指令多用于查表,故常称为查表指令。
汇编语言格式:MOV A, @A+DPTP
如:
1 | MOV A, @A+DPTR ;将A+DPTR所指示的地址单元中的内容放入累加器ACC中 |
相对寻址
以相对寻址指令的下一条指令的程序计数器PC的内容为基值,加上指令机器代码中的“相对地址”,形成新的PC值(要转移的指令地址)。指令机器代码中“相对地址”指的是用一个带符号的8位二进制补码表示的偏移字节数,其取值范围为-128~+127,负数表示向后转移,正数表示向前转移。rel代表一个8位带符号的偏移量,要转移的指令地址=(PC)+相对寻址指令字节数+rel,“( )”代表存储单元的内容。
汇编语言格式:SJMP rel
如:
1 | SJMP 08H ;指令代码为双字节,该指令将转到地址为(PC) + 02H + 08H |
位寻址
参与操作的数据为“位”,而不是字节,是对片内RAM中的位寻址区20H~2FH、SFR中11个可位寻址单元的位进行操作。bit代表内数据存储器RAM或SFR的直接寻址位。
汇编语言格式:MOV C, bit
如:
1 | MOV C, 00H ;或者写成 |
位寻址的位地址与直接寻址的字节地址形式完全一致,主要由操作码进行区分。
所有寻址方式的寻址范围总结如下表:
寻址方式 | 寻址存储器范围 |
---|---|
立即寻址 | 程序存储器ROM |
直接寻址 | 片内RAM低128KB,特殊功能寄存器SFR |
寄存器寻址 | 工作寄存器R0~R7,A,C,DPTR,AB |
寄存器间接寻址 | 片内RAM低128KB,片外RAM |
变址寻址 | 程序存储器ROM (@A+DPTR,@A+PC) |
相对寻址 | 程序存储器ROM(相对寻址指令的下一指令PC值加 -128~+127) |
位寻址 | 片内RAM的20H到2FH字节地址的所有位,可位寻址SFR |
指令系统
数据传送指令
指令 | 功能 |
---|---|
MOV (Move) | 传送内部RAM和SFR的数据 |
MOVX (Move External RAM) | 传送外部RAM的数据(外部RAM只能和累加器ACC之间进行传送数据) |
MOVC (Move Code) | 读取并传送ROM数据表格中的数据 |
PUSH (Push onto Stack) | 入栈 |
POP (Pop from Stack) | 出栈 |
XCH (Exchange) | 全字节交换 |
XCHD (Exchange low-order Digit) | 低半字节交换 |
SWAP | 高、低字节互换 |
如:
1 | MOV DPTR, #2000H ;外部RAM单元地址2000H传入DPTR |
算数运算指令
指令 | 功能 |
---|---|
ADD (Addition) | 加法 |
ADDC (Add with Carry) | 带进位加法 |
SUBB (Subtract with Borrow) | 带借位减法 |
INC (Increment) | 加1 |
DEC (Decrement) | 减1 |
MUL (Multiply) | 乘法 |
DIV (Divide) | 除法 |
DA (Decimal Adjust) | 十进制调整 |
逻辑运算指令
指令 | 功能 |
---|---|
ANL (AND Logic) | 逻辑与 |
ORL (OR Logic) | 逻辑或 |
XRL (Exclusive-OR Logic) | 逻辑异或 |
CLR (Clear) | 清零 |
CPL (Complement) | 取反 |
RL (Rotate Left) | 循环左移 |
RLC (Rotate Left throught the Carry flag) | 带进位循环左移 |
RR (Rotate Right) | 循环右移 |
RRC (Rotate Right throught the Carry flag) | 带进位循环右移 |
控制转移指令
指令 | 功能 |
---|---|
SJMP (Short Jump) | 短转移,后接偏移量rel |
AJMP (Absolute Jump) | 绝对转移,最大转移范围为2KB |
LJMP (Long Jump) | 长转移,转移目的地址在0~64KB空间范围 |
ACALL (Absolute subroutine Call) | 子程序绝对调用,最大调用范围为2KB |
LCALL (Long subroutine Call) | 子程序长调用,调用64KB空间范围内的子程序 |
RET (Return from subroutine) | 子程序返回 |
RETI (Return from Interruptio) | 中断返回 |
CJNE (Compare Jump if Not Equal) | 比较不相等则转移 |
DJNZ (Decrement Jump if Not Zero) | 减1后不为0则转移 |
JZ (Jump if Zero) | 结果为0则转移 |
JNZ (Jump if Not Zero) | 结果不为0则转移 |
JC (Jump if the Carry flag is set) | 有进位则转移 |
JNC (Jump if Not Carry) | 无进位则转移 |
JB (Jump if the Bit is set) | 位为1则转移 |
JNB (Jump if the Bit is Not set) | 位为0则转移 |
JBC(Jump if the Bit is set and Clear the bit) | 位为1则转移,并清除该位 |
NOP (No Operation) | 空操作 |
位操作指令
指令 | 功能 |
---|---|
SETB (Set Bit) | 将某个位置1 |
伪指令
伪指令在汇编时不产生机器码,不影响程序执行,仅指明在汇编时执行一些特殊的操作。
ORG 16位地址:起始指令,用在原程序或数据块的开始,指明此语句后面目标程序或数据块存放的起始地址。
如:
1 | ORG 1000H ;后面目标程序或数据块存放的起始地址为1000H |
[标号:]DB 字节数据项表:字节定义,将项表中的字节数据存放到从标号开始的连续字节单元中。
如:
1 | SEG: DB 32, 'A', 25H ;SEG~SEG+2地址单元依此存放20H、41H、25H |
[标号:]DW 双字节数据项表:字定义,定义16位地址表,16地址按低位地址存低位字。
如:
1 | TAB: DW 1234H, 25H ;TAB~TAB+3地址单元依此存放12H、34H、00H、25H |
[标号:]DS 数值表达式:保留字节,指示在程序存储器中保留与标号为起始地址的若干字节单元,单元个数由数值表达式指定。
如:
1 | L1: DS 32 ;L1地址开始保留32个存储单元 |
名字 EQU 表达式:等值指令,用与给一个表达式赋值或给字符串起名字。之后名字可用做程序地址,数据地址或立即数地址使用。名字必须是一字母开头的字母数字串。
如:
1 | SPACE EQU 10H ;程序中出现SPACE的地方都用10H代替 |
名字 DATA 直接字节地址:给8位内部RAM单元起个名字,名字必须是一字母开头的字母数字串,同一单元可起多个名字。
如:
1 | ERROR DATA 80H ;内部RAM 80H单元名为ERROR |
名字 XDATA 直接字节地址:给8位外部RAM起个名字,名字规定同DATA伪指令。
如:
1 | IO_PORT XDATA 0CF04H ;外部RAM 0CF04H单元名为IO_PORT |
名字 BIT 位指令:给一可位寻址的位单元起名,规定同DATA伪指令。
如:
1 | SWT BIT 30H ; 30H单元名为SWT |
[标号:] END:结束指令,指出源程序到此结束。
C语言编程
51单片机采用C语言编程后,通过C51编译器进行编译后产生目标代码,下载到单片机内部即可运行。编程时,不同存储器的寻址和数据类型等细节问题都由编译器解决,无需像使用汇编语言那样都必须考虑到,而且其中还有丰富的子程序库可以直接调用,大大减小编程工作量。此外,编程过程中还可以将C语言和汇编语言交叉使用,提高开发效率。
数据类型
C51中定义的数据类型比C语言多几种扩展数据类型:
bit用于定义为变量的名字,编译器会对其分配地址,位变量分配在内部RAM的20H~2FH单元相应的位区域,位地址范围是00~7FH,共128个。
sbit用于定义位变量的名字和地址,地址是确定的且不用编译器分配。它是SFR中的可以进行位寻址的确定位,也可是内部RAM的20H~2FH单元中确定的位。
sfr用于访问一个内存单元,利用它可以访问51单片机内部的所有SFR,一般用来声明SFR。
存储器类型
C51是面向8051单片机的程序语言,应用程序中使用的任何变量或常量必须以一定的存储器类型定位于单片机的相应的存储区域中,因此在定义变量类型时,还需要定义它的存储器类型:
对于单片机来说,访问片内RAM比访问片外RAM的速度要快得多,所以对于经常使用的变量应该置于片内RAM中,要用bdata、data、idata来定义;对于不经常使用的变量或规模较大的变量应该置于片外RAM中,要用pdata、xdata来定义。
中断函数
使用C51编写中断程序时,需要遵循如下格式:
1 | void 函数名(void) interrupt 中断号 [using 工作寄存器组号] { |
其中,51单片机中的每个中断的中断号定义如下表:
中断号 | 中断源 | 中断入口地址 |
---|---|---|
0 | 外部中断0 | 0003H |
1 | 定时器0 | 000BH |
2 | 外部中断1 | 0013H |
3 | 定时器1 | 0018H |
4 | 串口中断 | 0023H |
后面用using声明RAM中的工作寄存器组号为0~4,用于暂存当前工作状态,也可以不予声明,使用默认组。
中断函数不带有任何参数,不能有返回值,不能被其他程序调用,同时还要做到尽量精简。
插入汇编
想要在C程序里插入汇编代码,只要在汇编代码段前声明#pragma asm,段后声明#pragma endasm即可,格式如下:
1 | #pragma asm |
更新历史:
- 2017.11.23 完成初稿
单片机原理(2):程序设计