目录

一、 实验要求

二、 实验设计

2.1 整体思路

2.2 流程图

2.3 主要模块设计思路及分析

三、 实现效果

3.1 存入数据

3.2拷贝数据

四、源代码

五、总结和思考


一、 实验要求

(1)向起始地址为SRC的片内存储中,顺序写入NUM个单字节数(数值不限),并以$字符(ASCII码)结尾。

(2)从SRC中,向起始地址为DEST的片外存储中,逆序拷贝之前存储的NUM个单字节数,并以$字符结尾。

(3)将上述两个操作写成两个子程序。

二、 实验设计

2.1 整体思路

(1)向起始地址为SRC的片内存储中,顺序写入NUM个单字节数(数值不限),并以$字符(ASCII码)结尾。

        设定SRC的地址和NUM的地址,设定NUM的值(循环的次数)写入NUM地址中。设定A的初始值为1H。

        首先我们需要判断NUM的值是否为0,如果为0,直接存入‘$’符号,如果不为0,则进入循环。

        在循环中,将A中的值存入到SRC地址中,每循环一次,SRC的地址加一,A的值加一(可以替换为其他逻辑),循环次数减一。直到NUM循环次数为0时,表示存入完成。此时我们在当前的地址中存入‘$’符号,结束子程序。

(2)从SRC中,向起始地址为DEST的片外存储中,逆序拷贝之前存储的NUM个单字节数,并以$字符结尾。

        因为需要逆序拷贝,所以我们需要寻找上面存入的最后一个数据,即地址值为(@SRC+NUM-1),将它赋值给以起始地址为DEST的片外存储中,每循环一次,(@SRC+NUM-1)地址值减一,以起始地址为DEST的片外存储地址值加一,循环次数减一。当循环次数为0时,在当前的地址中存入‘$’符号,结束拷贝子程序。

(3)将上述两个操作写成两个子程序。

        将存入数据和拷贝数据写成两个子程序,在主程序中调用每一个子程序。每个子程序要注意现场保护(开头要将Acc和PSW进栈,结尾要把他们出栈)。子程序完成后要用RET返回。

2.2 流程图

2.3 主要模块设计思路及分析

(1)存入数据模块

        使用寄存器R0作为指向片内存储地址的指针,初始化为SRC地址。

        使用寄存器R1来控制循环次数,初始化为NUM,代表要写入的字节数。

        将累加器A初始化为 1,作为第一个要写入的值。

首先我们需要判断NUM的值是否为0,如果为0,直接存入‘$’符号,如果不为0,则进入循环。

进入循环,每次循环将A的值写入R0指向的地址,然后更新地址指针R0和累加器A的值(+1),并减少循环次数R1(-1)。通过DJNZ R1, COPY_LOOP判断循环次数是否为 0,如果不为 0 则继续循环。

        循环结束后,将‘$’字符写入当前地址,作为结尾标志。

(2)拷贝数据模块

        首先设置源地址指针和目的地址指针以及循环控制变量。

        将SRC地址加载到R0,作为源地址指针。

        将NUM值加载到R1,作为循环控制变量,表示要拷贝的字节数。

        通过计算NUM加上SRC地址再减一,得到源地址的末尾地址,并将其重新赋值给R0,这样R0就指向了源数据的最后一个字节。

        将DEST地址加载到数据指针DPTR,作为目的地址指针。

        进入循环进行数据拷贝。从源地址R0读取一个字节到累加器A。使用MOVX指令将A中的内容写入到目的地址DPTR。目的地址指针DPTR加一,源地址指针R0减一,实现逆序读取源数据,并减少循环次数R1(-1)。

        通过DJNZ R1, COPY_LOOP判断循环次数是否为 0,如果不为 0 则继续循环。

        循环结束后,将$字符写入目的地址的末尾。

(3)子程序模块

        将存入数据和拷贝数据写成两个子程序,在主程序中调用每一个子程序。每个子程序要注意现场保护(开头要将Acc和PSW进栈,结尾要把他们出栈)。子程序完成后要用RET返回。

三、 实现效果

3.1 存入数据

(1)设置NUM的值:

如图可以看到:

将NUM的值设为10(0AH)存入NUM的地址20H中。

(2)循环存入数据:

如图可以看到:

将A的值循环存入以SRC为起始地址的源地址中。

(3)循环结束存入‘$’:

如图可以看到:

循环结束,将‘$’(24H)存入最后的地址中。

3.2拷贝数据

(1)逆序拷贝数据:

如图可以看到:

程序将以SRC为起始地址的数据逆序拷贝到以DEST为起始地址的存储区内。

(2)循环结束存入‘$’:

如图可以看到:

循环结束,将‘$’(24H)存入最后的地址中。

四、源代码

//1.向起始地址为SRC的片内存储中,顺序写入NUM个单字节数(数值不限),并以$字符(ASCII码)结尾。
//2.从SRC中,向起始地址为 DEST的片外存储中,逆序拷贝之前存储的NUM个单字节数,并以$字符结尾。
//3.将上述两个操作写成两个子程序。
    ORG 0000H  
    LJMP MAIN  
  
    ORG 2000H    
MAIN: 
 	SRC DATA 30H  
	DEST DATA 2000H
    NUM DATA 20H

	MOV A, #0AH       ; 将立即数0AH加载到累加器A中  ,为需要循环的次数   
    MOV R0, #NUM      ; 将NUM(实际上是20H)加载到寄存器R0中  
    MOV @R0, A    
  
    ACALL WRITE_TO_SRC    ; 调用写入子程序  
    ACALL COPY_TO_DEST  ; 调用复制子程序  
  
	SJMP	$
  
// 子程序:向SRC写入NUM个字节并以'$'字符结尾  
WRITE_TO_SRC: 
	PUSH PSW
	PUSH Acc
 
    MOV R0, #SRC        ; 将SRC地址加载到R0寄存器  
    MOV R1, NUM         ; 将NUM值加载到R1寄存器 ,为需要循环的次数 ,10次 
    MOV A, #01H 		;数值为1开始写入
  
WRITE_LOOP:  
    JZ WRITE_FINISH       ; 如果NUM为0,则跳转到WRITE_FINISH  

	MOV @R0, A			  ;将A的值存入SRC地址中
	INC R0				  ;地址加一
    INC A                 ; 示例:每次循环增加A的值(可以替换为其他逻辑)  
    DJNZ R1, WRITE_LOOP   ; 减1并判断R1(循环次数)是否为0,不为0则继续循环 
  
WRITE_FINISH:  
	MOV A, #'$'
    MOV @R0, A          ; 将'$'字符存储在最后 

	POP Acc
	POP PSW
	 
    RET  
  
// 子程序:将SRC中的数据逆序复制到DEST并以'$'字符结尾  
COPY_TO_DEST:
	PUSH PSW
	PUSH Acc
  
    MOV R0, #SRC         ; 将SRC地址加载到R0寄存器
	MOV R1, NUM  		 ; 将NUM值加载到R1寄存器
    MOV A, NUM         
	ADD A, R0
	ADDC A, #-1
	MOV R0, A  
    MOV DPTR, #DEST     ; 将DEST地址加载到DPTR(用于片外数据存储器访问)  
  
COPY_LOOP:  
	MOV A, @R0  
    MOVX @DPTR, A       ; 将A的内容写入到DEST地址
    INC DPTR            ; DEST地址加1  
    DEC R0              ; SRC地址减1(逆序)  
    DJNZ R1, COPY_LOOP  ; 减1并判断R1是否为0,不为0则继续循环  
  
    ; 写入'$'字符到DEST的末尾 
COPY_FINISH: 
	MOV A, #'$' 
	MOVX @DPTR, A
  
    POP Acc
	POP PSW  
     
    END  

五、总结和思考

本次实验掌握了51单片机片内/片外存储器的数据存取操作。

(1)通过实践,学会了在Keil中查看片外RAM(X:2000H)的方法,以及使用MOVX指令读写片外存储区的技巧。

(2)在编程方面,理解了子程序的现场保护机制(PUSH/POP Acc和PSW)和RET返回指令的用法,同时认识到SJMP $指令的作用是让程序原地循环,常用于调试或维持系统状态。

(3)实验还巩固了逆序拷贝等数据处理方法,加深了对指针操作(R0、DPTR)和循环控制(DJNZ)的理解。

这些知识为后续嵌入式开发奠定了重要基础。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐