| 锁存地址用的ALE信号,也会在MOVX指令执行时,由30脚的ALE/PRO管脚输出,这个时候,请楼主仔细看懂MOVX指令的读写周期时序图了。单片机的指令,一定要结合单片机的特点来学习。毕竟有些指令是和单片机的硬件结构有关的,不是单纯的寄存器之间的数据转移。 |
|
楼主提供的图片,看不完全。虽然使用的是2864,但是如果2864第八位地址以外的引脚结成固定地址,MPU只想访问低八位地址对应的RAM空间的时候,完全可以使用MOVX A , @Ri和MOVX @Ri, A来读写2864。比如说: 1、2864的A8到A12引脚都接地了,那么MPU通过MOVX A , @Ri和MOVX @Ri, A来读写2864的空间地址就是0x0000~0x00FF; 2、2864的A8到A12引脚都接VCC了,那么MPU通过MOVX A , @Ri和MOVX @Ri, A来读写2864的空间地址就是0x1F00~0x1FFF; 通常访问外部ROM或者RAM时,由于P0口被分时使用,所以在硬件上会加一个地质锁存功能的芯片,比如74HC373之类的,在P0口输出地址的时候,把地址锁存在74HC373中。 |
|
楼主需要搞清楚MOVX @Ri, A(i=0/1)这条指令执行时,对单片机而言。会有什么动作。单纯地看只是个传送数据的指令而已。实际上,当执行MOVX @R0, A这条指令时,R0中保存的8位数据,是用来访问外部RAM的低八位地址,A中保存的是要输出的8位数据。执行指令时,低八位地址会从P0口送出,寻址范围是256个单元,同时,P3.6管脚会出现一个的低电平信号,用来启动目标RAM的WR写允许。按照时序,P0口输出地址后,会接着输出数据。所以P0是被分时使用的。此时,P2口不是寻址空间高八位,仍然可以作为普通IO口使用。 如果希望访问的对象空间大于256.可以使用MOVX @DPTR,A这条指令。同样,数据保存在A中,对象RAM的地址保存在DPTR中,这个时候,P2口是地址高八位的输出口,P0仍然分时复用,先输出第八位地址,然后是输出8位数据。 MOVX @Ri, A和MOVX A, @Ri的不同之处在于,指令执行后,产生低电平的引脚是不一样的,一个是P3.6,用作写RAM;一个是P3.7,用于读RAM。P3.6和P3.7都是端口复用的引脚。同时,数据流向也不一样,前者是输出数据给RAM,P0口输出万地址后,会接着输出数据。而MOVX A, @R指令是在P0口输出玩第八位地址后,接着等待输入外部RAM传给MPU的8位数据。 |
Y_G_G 发表于 2019-6-29 11:54 哥,我也是增加点基础知识,了解单片机最基础的东西。我知道这个已经过时了。 |
|
你拼了命的去研究霸王龙的生活习性,然后回家种香蕉,有多大意义呢? 1.这个程序非常不严谨,实际中用不了,DPTR,R0的初始值没有确定,程序有问题 2.猛的搞那么一段代码出来,绝大多数人不会知道它在实际电路中能有什么用 3.所用器件已经过时了,现在没有人用这玩意,也就没有人知道怎么真正的写完一个完整的程序出来 4.这破烂一样的程序没头没尾的,给谁都不会知道R0从哪里来,DPTR的值是多少,作者也不想写,就像是走走过场,应付一下的把这代码交代出来而已 以下才是汇编真正完整样子,可以烧录到单片机中正确运行,有晶振频率,端口指定,电路编号,备注 ;===============基于STC8的4*USB充电电流电压显示器===============================================; ;名称:基于STC8的4*USB充电电流电压显示器 ; ;作者:老玉 ; ;日期:2019/03/14 ; ;功能:可以显示4个USB充电端口的充电电流 ; ;单片机型号:STC8A4K32S2A12 ; ;电路与PCB编号:YGG_190314.0_DA ; ;晶振:使用片上的24.0MHZ振荡电路 ; ;功能: ; ;最后更新日期:20190320 ; ;备注说明; 程序已经测试OK ; ;如果电流数据不对,就把电路中四个运放放大电路的增益改成50倍,如果是对的,就不用改动 ; ; ;================================================================================================ ;端口指定======================================================================================== P4 EQU 0C0H ;P4 端口的地址 P5 EQU 0C8H ;P5端口的地址 OUT EQU P0 ;P0作为输出端口 ;==================================================================================================== ;12864显示屏的端口指定=============================================================================== RS BIT P1.2 ; RW BIT P1.1 ; E BIT P1.0 ; PSB BIT P3.6 ; RST_12864 BIT P3.5 ; ;==================================================================================================== ;标志位指定========================================================================================== MODE BIT 20H ;设定模式标示位 MIAO BIT 21H ;秒闪标志位 I_FU BIT 24H ;完全显示标志位 ;==================================================================================================== ;STC8的端口IO模式控制寄存器指定====================================================================== P0M0 EQU 094H ; P0M1 EQU 093H ;控制P0端口输出模式的寄存器 P1M0 EQU 092H ; P1M1 EQU 091H ;控制P1端口输出模式的寄存器 P2M0 EQU 096H ; P2M1 EQU 095H ;控制P2端口输出模式的寄存器 P3M0 EQU 0B2H ; P3M1 EQU 0B1H ;控制P3端口输出模式的寄存器 P4M0 EQU 0B4H ; P4M1 EQU 0B3H ;控制P4端口输出模式的寄存器 P5M0 EQU 0CAH ; P5M1 EQU 0C9H ;控制P5端口输出模式的寄存器 ;==================================================================================================== ;STC8的ADC相关寄存器指定============================================================================= ADCCFG DATA 0DEH ;ADC配置寄存器 ADC_CONTR DATA 0BCH ;ADC控制寄存器 ADC_RES DATA 0BDH ;ADC缓冲器高4位 ADC_RESL DATA 0BEH ;ADC缓冲器低8位 ADC_P13 EQU 083H ;P13ADC端口 ADC_P14 EQU 084H ;P14ADC端口 ADC_P15 EQU 085H ;P15ADC端口 ADC_P16 EQU 086H ;P16ADC端口 ADC_P17 EQU 087H ;P17ADC端口 ;==================================================================================================== WDT_CONTR EQU 0C1H ;看门狗地址 COUNT DATA 030H ;存放计数用的 COUNT_1 DATA 031H ;计数用的 DATA_BUF DATA 032H ;运算过程中用到的数据缓存器 COUNT_2 DATA 033H ;计数用的 TWIN DATA 035H ; LED_1 DATA 036H ;个位保存器 LED_10 DATA 037H ;十位保存器 LED_100 DATA 038H ;百位保存器 LED_1000 DATA 039H ;千位保存器 DIV_L DATA 03AH ;除法高位用的缓存器 DIV_H DATA 03BH ;除法低位用的缓存器 DATA_BUF_L DATA 03CH ;数据暂存器的低位 DATA_BUF_H DATA 03DH ;数据暂存器的高位 ADC_SUM_H DATA 03FH ;ADC累加计算的高位存储器 ADC_SUM_L DATA 040H ;ADC累加计算的低位存储器 ADDR_12864 DATA 041H ;指定12864的显示地址 DEL_1 DATA 042H ;延时用的 DEL_2 DATA 04FH ;误差值存放用的寄存 D_P14 DATA 050H ;通道0误差值 D_P15 DATA 051H ;通道1误差值 D_P16 DATA 052H ;通道2误差值 D_P17 DATA 053H ;通道3误差值 ADC_ADD_H DATA 054H ;ADC总电流计算的高位存储器 ADC_ADD_L DATA 055H ;ADC总电流计算的低位存储器 I_OUT DATA 056H ;用于提示有没有充电电流的寄存器 I_ALL_L DATA 057H ;用于保存总电流的低位数 I_ALL_H DATA 058H ;用于保存总电流的高位数 ;程序开始============================================================================================== ORG 0000H JMP STAR ORG 0100H STAR: MOV ADCCFG,#02FH ;设置ADC的时钟和转换结果存放格式 MOV 0BAH,#80H ;地址位于XDATA区,要访问之前,必须先把0BAH地址中的EAXFR位置位才能访问,只有用MOVX @DPTR,A和MOVX A,@DPTR读写 MOV A,#00000111B ;选择P1.0,P1.1,P1.2内部上拉电阻 MOV DPTR,#0FE11H ; MOVX @DPTR,A ; MOV A,#0FFH ;选择P2内部上拉电阻 MOV DPTR,#0FE12H ; MOVX @DPTR,A ; MOV A,#11111111B ;选择P4内部上拉电阻 MOV DPTR,#0FE14H ; MOVX @DPTR,A ; MOV 0BAH,#00 ; MOV P0M1,#00000000B ; MOV P0M0,#00000000B ;P0测定成默认的传统8051端口 MOV P1M1,#11111000B ;P1.0 P1.1 P1.2作为传统端口,P1.3 P1.4 P1.5 P1.6 P1.7作为ADC端口 MOV P1M0,#00000000B ; MOV P2M1,#00000000B ; MOV P2M0,#00000000B ; MOV P3M1,#00000000B ; MOV P3M0,#00000000B ; MOV P4M1,#00000000B ;P4端口设定 MOV P4M0,#00000000B ; MOV P5M1,#00000000B ;P5端口设定 MOV P5M0,#00000000B ; CALL RESET_12864 ;12864初始化 CALL MUTE_12864 ; MOV COUNT_1,#5 ; MOV COUNT_2,#100 ; MOV SP,#080H ; MOV WDT_CONTR,#24H ;设定看门狗 ;====================================================================================================== MAIN: CALL DISPLAY_VIN CALL DISPLAY_AD1 CALL DISPLAY_AD0 CALL DISPLAY_AD2 CALL DISPLAY_AD3 CALL DISP_I_ALL CPL MIAO CALL MUTE_12864 ; MAIN_END: ORL WDT_CONTR,#10H ;看门狗清除 CALL DELAY ;延时一次 DJNZ COUNT_2,MAIN_END ;延长显示和ADC的时间,使显示看起来更加稳定 MOV COUNT_2,#250 ; JMP MAIN ;================================================================================================================ ;显示通道P1.3 显示输入电压 显示地址:095H,最后显示的单位是"V"=86 DISPLAY_VIN: SETB I_FU ; MOV A,#ADC_P13 ;选择通道 CALL ADC_P1 ;选择该通道进行ADC MOV ADDR_12864,#085H;选择显示的地址 MOV R1,#86 ;<<<====最后显示的单位是"V"=86 CALL DISPLAY_ADC ;显示 CLR I_FU ; MOV I_ALL_L,#0 ; MOV I_ALL_H,#0 ; RET ;================================================================================================================ ;================================================================================================================ ;显示通道P1.4 显示充电电流 显示地址:08CH,最后显示的单位是"A"=65 DISPLAY_AD0: MOV A,#ADC_P14 ;选择通道 CALL ADC_P1 ;选择该通道进行ADC MOV ADDR_12864,#08CH;选择显示的地址 MOV R1,#65 ;<<<====最后显示的单位是"A"=65 CALL DISPLAY_ADC ;显示 MOV A,#09CH ;<<<===选择要显示的地址 CALL WRITE12864_INS ;写入地址 JNB MIAO,DISPLAY_AD0_1; MOV I_OUT,#32 ;秒到了就显示空白 DISPLAY_AD0_1: MOV A,I_OUT ; CALL WRITE12864_DATA ; RET ;================================================================================================================ ;================================================================================================================ ;显示通道P1.7 显示充电电流 显示地址:08EH,最后显示的单位是"A"=65 DISPLAY_AD1: MOV A,#ADC_P17 ;选择通道 CALL ADC_P1 ;选择该通道进行ADC MOV ADDR_12864,#08AH;选择显示的地址 MOV R1,#65 ;<<<====最后显示的单位是"A"=65 CALL DISPLAY_ADC ;显示 MOV A,#09AH ;<<<===选择要显示的地址 CALL WRITE12864_INS ;写入地址 JNB MIAO,DISPLAY_AD1_1; MOV I_OUT,#32 ;秒到了就显示空白 DISPLAY_AD1_1: MOV A,I_OUT ; CALL WRITE12864_DATA ; RET ;================================================================================================================ ;================================================================================================================ ;显示通道P1.5 显示充电电流 显示地址:088H,最后显示的单位是"A"=65 DISPLAY_AD2: MOV A,#ADC_P15 ;选择通道 CALL ADC_P1 ;选择该通道进行ADC MOV ADDR_12864,#08EH;选择显示的地址 MOV R1,#65 ;<<<====最后显示的单位是"A"=65 CALL DISPLAY_ADC ;显示 MOV A,#09EH ;<<<===选择要显示的地址 CALL WRITE12864_INS ;写入地址 JNB MIAO,DISPLAY_AD2_1; MOV I_OUT,#32 ;秒到了就显示空白 DISPLAY_AD2_1: MOV A,I_OUT ; CALL WRITE12864_DATA ; RET ;================================================================================================================ ;================================================================================================================ ;显示通道P1.6 显示充电电流 显示地址:08AH,最后显示的单位是"A"=65 DISPLAY_AD3: MOV A,#ADC_P16 ;选择通道 CALL ADC_P1 ;选择该通道进行ADC MOV ADDR_12864,#088H;选择显示的地址 MOV R1,#65 ;<<<====最后显示的单位是"A"=65 CALL DISPLAY_ADC ;显示 MOV A,#098H ;<<<===选择要显示的地址 CALL WRITE12864_INS ;写入地址 JNB MIAO,DISPLAY_AD3_1; MOV I_OUT,#32 ;秒到了就显示空白 DISPLAY_AD3_1: MOV A,I_OUT ; CALL WRITE12864_DATA ; RET ;================================================================================================================ DISP_I_ALL: SETB I_FU ; MOV A,I_ALL_L ; SUBB A,#30 ;总电流小于30.不显示 MOV A,I_ALL_H ; SUBB A,#0 ; JNC DISP_I_ALL_1 ; MOV I_ALL_L,#0 ; MOV I_ALL_H,#0 ; DISP_I_ALL_1: MOV A,I_ALL_H ; MOV B,I_ALL_L ; CALL DATA_DISP ;处理总电流数据 MOV ADDR_12864,#094H;选择显示的地址 MOV R1,#65 ;<<<====最后显示的单位是"A"=65 CALL DISPLAY_ADC ;显示 MOV I_ALL_H,#0 ; MOV I_ALL_L,#0 ;显示完一次总电流就要归零一次 CLR I_FU ; RET ;===================================================================================== ;ADC_P1 使用到的共用寄存器: A B R0 COUNT DEL_1 DEL_2 ;调用此程序之前,要先把通道选择放入A,把误差什放入R0 ;转换完成之后,ADC结果的高位存放在:ADC_SUM_H,低位存放在:ADC_SUM_L ;功能:进行ADC转换,并把转换结果进行平均,此子程序一共进行10次ADC,再平均得到最后的数据 ADC_P1: MOV ADC_CONTR,A ;选择ADC通道A,A要在进入ADC之前先指定好 MOV COUNT,#8 ;ADC一共在进行16次,然后再平均 MOV ADC_RES,#0 ; MOV ADC_RESL,#0 ; MOV ADC_SUM_L,#0 ; MOV ADC_SUM_H,#0 ;ADC的两个累加存储先清除 ADC_P1_1: CLR C ; MOV A,#01000000B ; ORL ADC_CONTR,A ;使用"或"启动ADC ADC_P1_2: MOV A,#00100000B ; ANL A,ADC_CONTR ; JZ ADC_P1_2 ; ANL ADC_CONTR,#11011111B;清除ADC中断标志位 CLR C MOV A,ADC_RESL ;高位值传送到缓存器 ADD A,ADC_SUM_L ;ADC运算结果的低位相加 MOV ADC_SUM_L,A ;把运算结果保存到累加存储器 MOV A,ADC_RES ; ADDC A,ADC_SUM_H ;ADC运算结果的高位相加 MOV ADC_SUM_H,A ;把运算结果保存到累加存储器 DJNZ COUNT,ADC_P1_1 ;一共要进行10次的ADC MOV COUNT,#2 ; ADC_LOOP: CLR C MOV A,ADC_SUM_H RRC A MOV ADC_SUM_H,A MOV A,ADC_SUM_L RRC A MOV ADC_SUM_L,A DJNZ COUNT,ADC_LOOP ;以上进行的是ADC之后累加值的平均 ;===========================================; MOV R0,#03 ;默认显示为爱心 CLR C ; MOV A,ADC_SUM_L ; SUBB A,#20 ;R0为误差值存放寄存器 MOV A,ADC_SUM_H ; SUBB A,#0 ; JNC ADC_END_1 ;C为0就表示ADC的结果是大于20的 MOV R0,#32 ;32为空格,就是不显示,空白 ADC_END_1: CLR C MOV I_OUT,R0 ;送充电指示标志 MOV A,ADC_SUM_L SUBB A,#04 ;04是固有的误差 MOV A,ADC_SUM_H ; SUBB A,#0 ; JNC ADC_END_2 ; MOV ADC_SUM_L,#0 ; MOV ADC_SUM_H,#0 ; ADC_END_2: MOV A,ADC_SUM_H ;高位ADC的结果存放到A MOV B,ADC_SUM_L ;低位ADC的结果存放到B ;===============计算总电流================================================ CLR C MOV A,I_ALL_L ; ADD A,ADC_SUM_L ; MOV I_ALL_L,A ; MOV A,I_ALL_H ; ADDC A,ADC_SUM_H ; MOV I_ALL_H,A ; ;======================================================================= MOV A,ADC_SUM_H ;高位ADC的结果存放到A MOV B,ADC_SUM_L ;低位ADC的结果存放到B CALL DATA_DISP ;对ADC转换结果的高低位进行拆分,拆分成4个数分别存放在4个地址 RET ;=================================================================================== ;DATA_DISP==============数据拆分,把一个数拆分成四个数,用于显示====================== ;把16进制的高低两个字节分别放到A和B,处理之后,个十百千位会分别放到4个地址中,使用减法运算来达到除法的效果 ;进入之前要先把高位数据存放到A,低位数据存放到B DATA_DISP: MOV LED_1,#0 MOV LED_10,#0 MOV LED_100,#0 MOV LED_1000,#0 ;先把四个清除 MOV DATA_BUF_H,A ; MOV DATA_BUF_L,B ;把数据传送到缓冲器中 MOV R0,#0E8H ; MOV R1,#03H ;1000的16进制为03E8H MOV R2,#100 MOV R3,#10 MOV R4,#0 SUB_1000: CLR C MOV A,DATA_BUF_L SUBB A,R0 MOV DIV_L,A; MOV A,DATA_BUF_H; SUBB A,R1 MOV DIV_H,A JC SUB_100 MOV DATA_BUF_L,DIV_L MOV DATA_BUF_H,DIV_H INC LED_1000 JMP SUB_1000 SUB_100: CLR C MOV A,DATA_BUF_L SUBB A,R2 MOV DIV_L,A MOV A,DATA_BUF_H; SUBB A,R4 MOV DIV_H,A JC SUB_10 MOV DATA_BUF_L,DIV_L MOV DATA_BUF_H,DIV_H INC LED_100 JMP SUB_100 SUB_10: MOV A,DATA_BUF_L MOV B,R3 DIV AB MOV LED_10,A MOV LED_1,B RET ;============================================================================ ;这一段是把拆分之后的四个数字的数据分别关去查表,并用于12864的显示 ;调用程序之前: 要先把准备显示的地址放到ADDR_12864 要显示的单位放到 R1 ;四个显示的数值要先分解好放到四个地址中 DISPLAY_ADC: MOV A,LED_1 ; MOV DPTR,#TAB2 ; MOVC A,@A+DPTR ; MOV LED_1,A ; MOV A,LED_10 ; MOV DPTR,#TAB2 ; MOVC A,@A+DPTR ; MOV LED_10,A ; MOV A,LED_100 ; MOV DPTR,#TAB2 ; MOVC A,@A+DPTR ; MOV LED_100,A ; MOV A,LED_1000 ; MOV DPTR,#TAB2 ; MOVC A,@A+DPTR ; MOV LED_1000,A ; MOV A,ADDR_12864 ;<<<<=====写入要显示的地址 LCALL WRITE12864_INS ; MOV A,LED_1000 ; CALL WRITE12864_DATA ;显示千位 MOV A,#46 ;显示点"." CALL WRITE12864_DATA ;显示点 MOV A,LED_100 ; CALL WRITE12864_DATA ;显示百位 JNB I_FU,D_A_2 ;如果不是在显示总电流就直接不显示后面的,只要1.0 这种形式 D_A_1: MOV A,LED_10 ; CALL WRITE12864_DATA ;显示十位 MOV A,LED_1 ; CALL WRITE12864_DATA ;显示个位 MOV A,R1 ;<<<<=====写入在最后显示的单位 CALL WRITE12864_DATA ;显示最后的单位 D_A_2: ; CALL DELAY ; RET ;=============================================================================== ;===================延时子程序================================================== DELAY: MOV DEL_2,#50 MS5DISP1: MOV DEL_1,#255 DJNZ DEL_1,$ DJNZ DEL_2,MS5DISP1 RET ;===================向12864写入数据============================================= WRITE12864_DATA: CALL ASK_BF ;查BF是否忙 SETB RS CLR RW MOV OUT,A SETB E CLR E CALL DELAY RET ;===================向12864写入指令============================================= WRITE12864_INS: CALL ASK_BF ;查BF是否忙 CLR RS CLR RW MOV OUT,A SETB E CLR E CALL DELAY RET ;===================查BF是否忙================================================== ASK_BF: MOV OUT,#0FFH ;释放P0端口 CLR RS SETB RW SETB E JB OUT.7,ASK_BF CLR E CALL DELAY RET ;================================================================================ ;================================================================================================================ ;菜单显示 MUTE_12864: MOV A,#080H ;把要显示的地址传送到显示的缓冲器上 LCALL WRITE12864_INS ;写入要显示的地址 MOV DPTR,#TAB3 ; MUTE_1: CLR A ; MOVC A,@A+DPTR ;查表 JZ MUTE_2 ;A的值为0就表示已经读到了最后一个内容了 CALL WRITE12864_DATA ;调用写入数据子程序 INC DPTR ;地址加1,显示下一个地址的数据 JMP MUTE_1 ;再去查表显示 MUTE_2: MOV A,#090H ;把要显示的地址传送到显示的缓冲器上 LCALL WRITE12864_INS ;写入要显示的地址 MOV DPTR,#TAB4 ; MUTE_2_1: CLR A ; MOVC A,@A+DPTR ;查表 JZ MUTE_END ;A的值为0就表示已经读到了最后一个内容了 CALL WRITE12864_DATA ;调用写入数据子程序 INC DPTR ;地址加1,显示下一个地址的数据 JMP MUTE_2_1 ;再去查表显示 MUTE_END: RET ;============================================================================================== ;================================================================================================================ ;12864初始化 RESET_12864: CLR RST_12864 ;12864复位 RESET_1: CALL DELAY ;延时 DJNZ R0,RESET_1 ; SETB RST_12864 ;关闭12864复位 SETB PSB ;8位数据模式 MOV A,#06H ;设定整体显示 LCALL WRITE12864_INS ;写入指令 MOV A,#030H ;设定使用基本指令 LCALL WRITE12864_INS ;写入指令 MOV A,#0CH ;设定显示状态开关 LCALL WRITE12864_INS ;写入指令 MOV A,#01H ;清除屏幕 LCALL WRITE12864_INS ;写入指令 RET ;================================================================================================================ ORG 1000H TAB2: DB '0123456789' ;显示数字的0-9 TAB3: DB '电源电压',':',0H ;以0H作为结果标志 TAB4: DB '总电流',':',0H ; END |
lwh999995 发表于 2019-6-28 01:30 还是不太明白,我现在把程序一条一条的读给你听 wr2 MOVX A,@DPTR 外部地址的数据给累加器 MOV R2,A 累加器数值给寄存器。它用的是MOV指令,应该是给单片机内部的寄存器 MOVX @R0,A 累加器数值给2864A的R0寄存器,问题是R0是哪里来的,没有说明 INC DPTR 地址指针加一 INC R0 目的地址指针加一 cjne R0,#00H,NEXT 地址指针未满,转NEXT INC R2 注释上说是高位指针加一,,但是题目上R2是低位地址啊?P2是2864A的高位地址 NEXT DJNZ R1,WR2 页面未装满转移 DEC R0 恢复最后写的数据地址 LOOP: MOVX A,@R0 读2864A数据给累加器 XRL A,R2 与写入的最后一个数据相异或,,这个为什么写这个指令啊,不太明白 R2应该是最开始存入的数据。 JB ACC.7, LOOP 最高位不等,LOOP 什么作用 RET |
| P2是指P2IO口吧 |