资源描述
3-3 访问外部数据存储器和程序存储器可以用哪些指令来实现?举例说明。 答:访问外部数据存储器可以用以下指令实现:
解:MOVX A, @Ri ;((Ri))→A,寄存器Ri指向的片外RAM地址中的内容送到累加器A中
MOVX @Ri, A ;A→((Ri)),累加器中内容送到寄存器Ri指向的片外RAM地址中
MOVX A, @DPTR ;((DPTR))→A,数据指针指向的片外RAM地址中的内容送到累加器A中
MOVX @DPTR, A ;A→((DPTR)),累加器中内容送到数据指针指向的片外RAM地 址中
访问程序存储器可以用以下指令实现:
MOVC A, @A+DPTR ;((A+DPTR))→A,表格地址单元中的内容送到累加器A中
MOVC A, @A+PC ;PC+1→PC,((A+PC))→A,表格地址单元中的内容送到累加器A中
3-4 试用下列3种寻址方式编程,将立即数0FH送入内部RAM的30H单元中。 (1) 立即寻址方式;(2) 寄存器寻址方式;(3) 寄存器间接寻址方式
答(1)立即寻址方式: MOV 30H, #0FH
(2)寄存器寻址方式: MOV R5, #0FH
MOV 30H, R5
(3)寄存器间接寻址方式:MOV @R1, #0FH
3-7 写出实现下列要求的指令或程序片段。 (1) 将R0的内容传送到R1;
(2) 内部RAM的20H单元内容传送到寄存器R1;
(3) 内部RAM的20H单元的D7和D3位清零,其它位保持不变;
(4) 外部RAM的1000H单元内容传送到内部RAM的60H单元中;
(5) 外部RAM的1000H单元内容传送到寄存器R2;
(6) 清除累加器高4位。
答:(1)MOV A, R0
MOV R1, A
(2)MOV R1, 20H
(3)MOV A, 20H
ANL A, #77H
(4)MOV DPTR, #1000H
MOVX A, @DPTR
MOV 60H, A
(5)MOV DPTR, #1000H
MOVX A, @DPTR
MOV R2, A
(6)ANL A, #0FH
3-8 试编写一段程序,将内部数据存储器30H、3lH单元内容传送到外部数据存储器1000H、1001H单元中。 答: MOV A, 30H
MOV DPTR, #1000H
MOV @DPTR, A
MOV A, 31H
MOV DPTR, #1000H
MOV @DPTR, A
3-9 试编写一段程序,将外部数据存储器40H单元中的内容传送到0l00H单元中。
答: MOV DPTR, #0040H
MOV A, @DPTR
MOV 0100H, A
3-10 试编写—段程序,将R3中的数乘4(用移位指令)。
答: MOV A, R3
MOV 23H, #02H
L0: DJNZ 23H, L1
L1: RL A
SJMP L0
3-11 试编写—段程序,将R2中的各位倒序排列后送入R3中。
答: MOV A, R2
MOV 23H, #08H
L0: DJNZ 23H, L1
L1: RL A
SJMP L0
MOV R3, A
3-12 试编写—段程序,将P1口的高5位置位,低3位不变。
答: MOV A, P1
ORL A, #F8H
MOV P1, A
4-4 编写程序,将片内30H~39H单元中的内容送到以2000H为首的外部存储器。
答: ORG 0000H
START: MOV R0, #30H
MOV DPTR, #2000H
MOV R1, #10
CLR A
LOOP: MOV A, @R0
MOVX @DPTR, A
DEC R1
DJNZ R1, LOOP
END
4-5 编写程序,采用算术平均值滤波法求采样平均值,设8次采样值依次放在20H~27H的连续单元中,结果保留在A中。
答:FILT: CLR A
MOV R2, A
MOV R3, A
MOV R0, #20H
MOV R7, #08H
;初始化
FILT1: MOV A, @R0
ADD A, R3
MOV R3, A
CLR A
ADDC A, R2
MOV R2, A
INC R0
DJNZ R7, FILT1
;累加采样值到R3,累加进位到R2=00000xxxB
FILT2: SWAP A
RL A ;R2/8,节省一个指令周期
XCH A, R3
SWAP A
RL A ;R3/8,节省一个指令周期
ADD A, #80H ;四舍五入
ANL A, #1FH ;屏蔽移位进入的前三位
ADDC A, R3 ;结果相加
END
;取平均值
4-6 编写程序,将存放在内部RAM起始地址为20H和30H的两个3字节无符号相减,结果存放在内部RAM单元70H、71H、72H中(低位对应低字节)。
答: ORG 0000H
START: MOV R0, #20H
MOV R1, #30H
MOV R2, #03H
CLR C
S0: LCALL XU
MOV 70H, A
LCALL XU
MOV 70H, A
LCALL XU
MOV 70H, A
S1: MOV A, @R0
SUBB A, @R1
INC R0
INC R1
RET
END
4-7 编写程序,实现两个双字节无符号数的乘法运算,乘数存放在R2和R3中(R2存放高字节,R3存放低字节,以下类同),被乘数存放在R6和R7中,积存放在R4、R5、R6和R7中。
答: ORG 0000H
DMUL: MOV A, R3
MOV B, R7
MUL AB
MOV R0, A
MOV R1, B
;低8位*低8位
MOV A, R2
MOV B, R7
MUL AB
ADD A, R1
MOV R1, A
MOV A, B
ADDC A, #00H
MOV R5, A
;高8位*低8位
MOV A, R3
MOV B, R6
MUL AB
ADD A, R1
MOV R1, A
MOV A, R5
ADDC A, B
MOV R5, A
MOV A, #00H
ADDC A, #00H
MOV R4, A
;低8位*高8位
MOV A, R3
MOV B, R6
MUL AB
ADD A, R5
MOV R5, A
MOV A, R4
ADDC A, B
MOV R4, A
;高8位*高8位
MOV A, R0
MOV R7, A
MOV A, R1
MOV R6, A
;R0、R1给R7、R6
END
4-8 假设在R0指向的片内RAM区,存有20个16进制数的ASCII字串。将ASCII码转换为16进制数,然后两两合成一个字节,从低地址单元到高地址单元依次组合。
答: ORG 0000H
START: MOV R7, #20
S0: MOV A, R0
MOV R1, A ;R1为中间量
MOV A, @R0
CLR C
SUBB A, #30H
MOV @R1, A
JC S01
MOV A, @R0
SUBB A, #07H
MOV @R1, A
S01: INC R0
MOV A, @R0
CLR C
SUBB A, #30H
SWAP A
ORL A, @R1
MOV @R1, A
JC S02
MOV A, @R0
SUBB A, #07H
SWAP A
OR A, @R1
MOV @R1, A
S02: INC R0
DJNZ R7, S0
END
4-9 结合例4-24和图4-5编写线性标度变换程序。
答: 将256近似255,256比255做浮点运算简单很多
PUSH ACC
PUSH PSW
MOV A, #Am
MOV 20H, #A0
CLR C
SUBB A, 20H ;将256近似255,则A为小数点后的位
MOV 21H, #Nx
MOV B, 21H
MUL AB ;B为整数位,A为小数点后的位
ADD A, #80H ;四舍五入
MOV A, B
ADDC A, #A0
MOV 38H, A
POP PSW
PUSH ACC
RET
5-4 试编写一段程序,将内部数据存储器30H、3lH单元内容传送到外部数据存储器1000H、1001H单元中去。
答:#include <reg52.h>
#include <absacc.h>
void main(void)
{
unsigned int temp;
temp = DWORD[0x0030];
XWORD[0x1000] = temp;
while(1);
}
5-5 试编写一段程序,将外部数据存储器40H单元中的内容传送到50H单元。
答:方法一:
#include <reg52.h>
#include <absacc.h>
void main(void)
{
XBYTE[0x0050] = XBYTE[0x0040];
while(1);
}
方法二:
#include <reg52.h>
#include <absacc.h>
void main(void)
{
unsigned char xdata *xp;
unsigned char data temp;
xp = 0x0040;
temp = *xp;
xp = 0x0050;
*xp =temp;
while(1);
}
5-6 试编写—段程序,将R3中的数乘以4。
答:#include <reg52.h>
void main(void)
{
unsigned int data *a;
#pragma asm
MOV 30H, R3
#pragma endasm
a = 0x30;
*a = *a*4;
while(1);
}
5-7 试编写—段程序,将R2中的各位倒序排列后送入R3中。
答:考虑C中没有循环移位,对于这种很低级的运算,采用嵌入汇编的方式完成。
#include <reg52.h>
void main(void)
{
#pragma asm
MOV R7, #08H
MOV R3, #00H
DEL: CLR CY
MOV A, R2
RLC A
MOV R2, A
MOV A, R3
RRC A
MOV R3, A
DJNZ R7, DEL
#pragma endasm
while(1);
}
5-8 试编写—段程序,将P1口的高5位置位,低3位不变。
答:#include <reg52.h>
void main(void)
{
P1 |= 0xf8;
while(1);
}
5-9 设8次采样值依次存放在20H~27H的连续单元中,采用算术平均值滤波法求采样平均值,结果保留在30H单元中。试编写程序。
答:#pragma small
#include <reg52.h>
void main(void)
{
unsigned char *dp = 0x20,i;
float temp = 0;
for(i=0;i<8;i++)
{
temp += *dp;
dp++;
}
temp /= 8;
dp = 0x30;
*dp = temp;
while(1);
}
5-10 从20H单元开始有一无符号数据块,其长度在20H单元中。编写程序找出数据块中最小值,并存入21H单元。
答:#pragma small
#include <reg52.h>
void main(void)
{
unsigned char *dp, num, min, i;
dp = 0x20;
num = *dp;
dp++;
for(i=1;i<num;i++)
{
min = *dp;
dp++;
if(min>*dp)
min = *dp;
}
dp = 0x21;
*dp = min;
while(1);
}
6-10 某系统有三个外部中断源1、2、3,当某一中断源变低电平时便要求CPU处理,它们的优先处理次序由高到低为3、2、1,处理程序的入口地址分别为2000H、2100H、2200H。试编写主程序及中断服务程序(转至相应的 入口即可)。
答:对系统的三个外中断源,可利用MCS-51的2个外中断源INT0和INTI,再将定时/计 数器T1 作为扩展的外部中断使用,INT0接外中断源3,INTI接外中断源2,定时/计数器T1接外中断源1,3个中断源设置为同级中断,外中断源1、2、3依次接到P1.0、P1.1、P1.2上。
汇编语言程序代码如下:
ORG 0000H ;复位入口地址
AJMP MAIN ;转主程序
ORG 0003H
AJMP INT1
ORG 0100H
MAIN: MOV TMOD, #60H ;T1方式2
MOV TH1, #0FFH
MOV TL1, #0FFH ;置初值
SETB TR1 ;启动计数器T1
SETB EA ;CPU中断开放
SETB ET1 ;允许T1中断
SETB IT0 ;允许外中断0产生中断
SETB IT1
SETB PX0 ;外中断0为高级中断
SETB PX1
SETB PT1
SETB IT0 ;外中断0为跳沿触发方式
SETB IT1
LOOP1: SJMP LOOP1 ;等待中断
;中断服务程序
ORG 1000H
INT1: PUSH PSW ;保护现场
PUSH ACC
JB P1.0,IR1 ;P1.0高,外中断1有请求
JB P1.1,IR2 ;P1.1高,外中断2有请求
JB P1.2,IR3 ;P1.2高,外中断3有请求
INTIR: POP ACC ;恢复现场
POP PSW
RETI ;中断返回
ORG 2000H
IR1: … … ;外中断1的中断处理程序
AJMP INTIR
ORG 2100H
IR2: … … ;外中断2的中断处理程序
AJMP INTIR
ORG 2200H
IR3: … … ;外中断3的中断处理程序
AJMP INTIR
END
7-5 采用定时/计数器T0对外部脉冲进行计数,每计数100个脉冲后,T0转为定时工作方式。定时1ms后,又转为计数方式,如此循环不止。假定MCS-51单片机的晶体振荡器频率为6MHz,请使用方式1实现,要求编写出程序。
答: ORG 0000H
START: CLR TR0
MOV TMOD, #05H
MOV TH0, #0FFH
MOV TL0, #9CH
;计数器初始化
S0: JBC TF0, NEXT
SJMP S0
NEXT: CLR TR0
MOV TMOD, #01H
MOV TH0, #0F8H
MOV TL0, #30H
;定时器初始化
SETB TR0
S1: JBC TF0, START
SJMP S1
END
7-7 编写程序,要求使用T0,采用方式2定时,在P1.0输出周期为400μs,占空比为10:1的矩形脉冲。 答:本题采用晶体振荡器频率为12MHz,使用定时器配合计数器,设计成一个40μs定时将P1.0置0和一个400μs定时将P1.0置1。
ORG 0000H ;中断入口地址
AJMP START
ORG 000BH ;定时器0的中断向量地址
AJMP TIME0
ORG 0030H
START: MOV SP, #5FH
MOV 30H, #00H ;软件计数器清零
MOV TMOD, #02H ;T0工作在方式1
MOV TH0, #216
MOV TL0, #216
SETB EA
SETB ET0
SETB TR0
;任意程序段
TIME0: PUSH ACC ;中断处理子程序
PUSH PSW
CLR P1.0
INC 30H
MOV A, 30H
CJNE A, #10, T0_4
SETB P1.0
MOV 30H, #00H
T0_4: POP PSW
POP ACC
RETI
END
7-9 利用定时/计数器T0产生定时时钟,由P1口控制8个指示灯。编一个程序,使8个指示灯依次一个一个闪动,闪动频率为20次/秒(8个灯依次亮一遍为一个周期)。
答:本题采用晶体振荡器频率为12MHz,每个灯的闪烁周期是:50ms,采用工作方式1。
ORG 0000H
AJMP START
ORG 000BH
AJMP TIME0
ORG 0030H
START: MOV SP, #5FH
MOV R7, #0FEH
MOV TMOD, #01H ;T0在工作方式1
MOV TH0, #3CH
MOV TL0, #0B0H
SETB EA
SETB ET0
SETB TR0
S0: AJMP S0 ;此处放任意程序段
TIME0: PUSH ACC
PUSH PSW
MOV P1, R7
MOV A, R7
RL A
MOV R7, A
MOV TH0, #3CH
MOV TL0, #0B0H
POP PSW
POP ACC
RETI
END
7-11 编写一段程序,功能要求为:当P1.0引脚的电平正跳变时,对P1.1的输入脉冲进行计数;当P1.2引脚的电平负跳变时,停止计数,并将计数值写入R0、R1(高位存R1,低位存R0)。
答:将P1.1的输入脉冲接入T0,即使用T0计数器完成对P1.1口的脉冲计数。R2中记T0计满数的次数。
程序代码如下:
ORG 0000H
LJMP MAIN
ORG 000BH
LJMP IT0
MAIN: JNB P1.0, MAIN
MOV TMOD, #05H ;定时/计数器T0为计数方式1
SETB TR0 ;启动T0,开始计数
SETB ET0 ;允许T0中断
SETB EA ;CPU开中断
WAIT: JB P1.2, WAIT
CLR EA
CLR TR0
MOV R1, TH0
MOV R0, TL0
AJMP $
IT0: INC R2
RETI
7-18 为什么定时/计数器T1用做串行口波特率发生器时,应采用方式2?若已知时钟频率、通信波特率,如何计算其初值?
答:因为方式2是有自动重装载计数值的功能,从而可以产生精确的波特率。串行工作方式0和方式2波特率是固定的,所以不用设初值;串行工作方式1和方式3时:
波特率:BR = (2SMOD×Td)/32
溢出一次的时间:1/Td = (256-TH1)*12/fosc
溢出率:Td = fosc/[12×(256-TH1)]
初值:TH1 = 256- fosc/(12* Td)
7-19 若晶体振荡器为11.059MHz,串行口工作于方式1,波特率为4800b/s,写出用T1作为波特率发生器的方式控制字和计数初值。
答: MOV TMOD, #20H
MOV TH1, #0FAH
MOV TL1, #0FAH
SETB TR1
MOV SCON, #50H
7-20 利用单片机串行口扩展24个发光二极管和8个按键,要求画出电路图并编写程序使24个发光二极管按照不同的顺序发光(发光的时间间隔为1s)。
答: ORG 1000H
START: MOV SCON, #00H ;串行口工作方式0
MOV R0, #00H
MOV R7, #03H
MOV R6, #24H
CLR P1.0 ;关闭并行输出
LOOP: MOV A, @R0
MOV DPTR, #TAB ;查表取数,送出
MOVC A, @A+DPTR
MOV SBUF, A
OUT0: JNB TI, OUT0
INC R0
CLR TI
DJNZ R7, LOOP
SETB P1.0 ;开启并行输出
ACALL DELAY
DJNZ R6, START
AJMP LOOP
DELAY: MOV R2, #250
D1: MOV R3, #100
D2: MOV R4, #20
D3: DJNZ R4, D3
DJNZ R3, D2
DJNZ R2, D1
RET
TAB: DB 00000000B, 00000000B, 00000001B, 00000000B
DB 00000000B, 00000010B, 00000000B, 00000000B
DB 00000100B, 00000000B, 00000000B, 00001000B
DB 00000000B, 00000000B, 00010000B, 00000000B
DB 00000000B, 00100000B, 00000000B, 00000000B
DB 01000000B, 00000000B, 00000000B, 10000000B
DB 00000000B, 00000001B, 00000000B, 00000000B
DB 00000010B, 00000000B, 00000000B, 00000100B
DB 00000000B, 00000000B, 00001000B, 00000000B
DB 00000000B, 00010000B, 00000000B, 00000000B
DB 00100000B, 00000000B, 00000000B, 01000000B
DB 00000000B, 00000000B, 10000000B, 00000000B
DB 00000001B, 00000000B, 00000000B, 00000010B
DB 00000000B, 00000000B, 00000100B, 00000000B
DB 00000000B, 00001000B, 00000000B, 00000000B
DB 00010000B, 00000000B, 00000000B, 00100000B
DB 00000000B, 00000000B, 01000000B, 00000000B
DB 00000000B, 10000000B, 00000000B, 00000000B
END
11-4 试设计一个顺序开关灯控制器,要求当按钮K第一次按下时,灯A立刻亮,灯B在延时11秒钟后亮,在灯B亮后15秒,灯C亮;当按钮K第2次按下时,灯C立刻灭,延时17秒后灯B灭,灯B灭后12秒,灯A灭。
#include<reg52.h>
#define uint unsigned int
unsigned char sum=0;
void delay(char);
sbit asight=P1^0; /*A灯*/
sbit bsight=P1^1; /*B灯*/
sbit csight=P1^2; /*C灯*/
void key1()
{
asight=1; /*A灯亮*/
delay(11);
bsight=1; /*B灯亮*/
delay(15);
csight=1; /*C灯亮*/
}
void key2()
{
csight=0; /*C灯灭*/
delay(17);
bsight=0; /*B灯灭*/
delay(12);
asight=0; /*A灯灭*/
}
void in_0() interrupt 0 using 0
{
switch (sum)
{
case 0:
{
key1();
sum++;
EX0=1; /*由于在执行中断之后EX和EA会硬件自动置0*/
EA=1;
break;
}
case 1: key2(); break;
}
}
void delay_1_s() /*延时1s*/
{
char wt=10;
uint j;
while(wt--)
{
j=9050;
while(j--);
}
}
void delay(char s) /*延时N秒,参数s为需要延时的秒数*/
{
while(s--)
{
delay_1_s();
}
}
void main()
{
P1=0x00;
EX0=1;
EA=1;
while(1)
{;}
}
展开阅读全文