单片微机原理与接口技术——基于STC15W5K32S4:串口双机通信实验(51汇编软件仿真)


在这里插入图片描述

main.asm

/*----------------------------------------------------------------------------------
;实验名称:	串口双机通信实验
;功    能:	1.上电初始化LCD1602,显示指针在第二行;

;			2.第一行是从外界接收到的字符显示,第二行是本机通过矩阵键盘输入的字符字符显示;

;			3.通过矩阵键盘的功能键,可以设置数字符号、大小写模式切换,并使的对应的指示灯点亮;
;			  例如:按下“数字符号切换”按键,“数字符号切换指示灯”点亮,此时输入的字符是按键对应的数字和字符;
;				    再按下“数字符号切换”按键,“数字符号切换指示灯”熄灭,退出数字字符模式。
;			  当“数字符号切换指示灯”和“大小写切换指示灯”熄灭时,此时输入的是小写的英文字符;
;			  当同时设置数字字符模式,和大小写模式,优先级:数字字符 > 大写 > 小写;

;			4.设置了删除,清屏,发送功能键;
;			(1)“删除”是删除一位字符;
;			(2)输入完成,按下“发送”按键,才串口发送给所连接的单片机;
;				发送的字符串以 0A 0D 作为结束标志符号,在“发送”的服务程序中有单独的代码,按下“发送”自动添加

;			5.设置了避免连续按“发送”按键误操作程序;

;			6.可以同一单片机多次发送字符串到另一单片机。

;算法说明:	1.所有的工作寄存器R0~R7作为局部变量,不存储静态变量或全局变量;
;			2.所有的静态变量和全局变量都通过自定义地址来存储,所有变量总览如下:
;				LCD中的变量		LCD_RS				BIT			P2.5	 		;1602数据命令选择端口
;								LCD_RW				BIT			P2.6	 		;1602读写选择端口
;								LCD_EN				BIT			P2.7	 		;1602使能端口
;								LCD_OverLineSize_Light	BIT		P3.4			;1602一行显示越界指示灯
;								LCD_DATA			EQU			P0		 		;1602数据端口
;								LCD_ALL_FLAG		DATA		20H		 		;标志位
;								LCD_FLAG			BIT			LCD_ALL_FLAG.0	;1602读忙标志位
;								LCD_Line_BIT		BIT			LCD_ALL_FLAG.1	;换行标志位
;								LCD_DAT				DATA		21H		 		;1602数据命令字	
;								LCD_DELAYED			DATA		22H		 		;延时字
;								LCD_LineSP			DATA		23H				;LCD堆栈指针(最大为一行16个字符)
;								LCD_StackAddress	DATA		0A0H			;LCD堆栈首地址
;								LCD_StackSize		EQU			20				;LCD堆栈大小
;				juzheng中的变量 	KEY     			DATA        24H				;用于存储按键的编号:0~F
;								JZ_ALL_FLAG			DATA		25H				;矩阵键盘的所有标志位
;								Shift_number_BIT	BIT			JZ_ALL_FLAG.0	;切换数字标志位
;								Shift_LH_BIT		BIT			JZ_ALL_FLAG.1	;切换大小写标志位
;								Shift_number_Light	BIT			P3.2			;切换数字指示灯
;								Shift_LH_Light		BIT			P3.3			;切换大小写指示灯	
;				usart中的变量	Usart_Buffer		DATA		27H		 		;串口缓存器
;								Usart_StackSP		DATA		28H				;串口堆栈指针
;								Usart_StackAddress	DATA		80H				;串口堆栈首地址
;								Usart_StackSize		EQU			20				;串口堆栈大小
;			3.不足之处:
;			(1)函数块冗杂,一些函数块重复出现,但是这些重复出现的函数块所输入的参数不同,
;				此时还需要再通过设置新的变量来获得输入的值,再调用函数块,这样的话,定义的变量会较多,
;				这是一个需要改进的地方。
;			(2)关于LCD,串口堆栈的设置和处理,感觉冗杂,需要简化处理,此时就需要重新更改堆栈的处理方式
;设计者:
;设计时间:2020.4.24
;设计版本:V1.0	 */
/**************头文件包含*********************/
$INCLUDE(LCD1602.INC)
$INCLUDE(JUZHENG.INC)
$INCLUDE(USART.INC)

ORG		0000H
LJMP	MAIN
ORG		0023H
LJMP	Usart_ISR
ORG		0100H
/******************主函数********************/
MAIN:
	MOV		SP,	#0C0H			;设堆栈指针 ,远离工作区
	CLR		Shift_number_BIT	;清除切换标志位
	CLR		Shift_LH_BIT
	CLR		Shift_number_Light	;熄灭所有指示灯
	CLR		Shift_LH_Light
	LCALL	LCD_INIT			;LCD初始化
	LCALL	Usart_Init			;串口初始化
MAINLOOP:
	LCALL KEYSCAN  				;扫描矩阵按键
    LJMP  MAINLOOP				;死循环
/*******************结束********************/
END
	

STC15_init

;以下是变量及函数的声明
PUBLIC STC15_INIT

	
;***************************************************
;定义程序区块,编译时动态分配程序地址,此段代码可以不要
	_STC15_INIT_ASM SEGMENT CODE
	RSEG	_STC15_INIT_ASM
;***************************************************
P0M1	DATA	0x93	; P0M1.n,P0M0.n 	=00--->Standard,	01--->push-pull
P0M0	DATA	0x94	; 					=10--->pure input,	11--->open drain
P1M1	DATA	0x91	; P1M1.n,P1M0.n 	=00--->Standard,	01--->push-pull
P1M0	DATA	0x92	; 					=10--->pure input,	11--->open drain
P2M1	DATA	0x95	; P2M1.n,P2M0.n 	=00--->Standard,	01--->push-pull
P2M0	DATA	0x96	; 					=10--->pure input,	11--->open drain
P3M1	DATA	0xB1	; P3M1.n,P3M0.n 	=00--->Standard,	01--->push-pull
P3M0	DATA	0xB2	; 					=10--->pure input,	11--->open drain
P4M1	DATA	0xB3	; P4M1.n,P4M0.n 	=00--->Standard,	01--->push-pull
P4M0	DATA	0xB4	; 					=10--->pure input,	11--->open drain
P5M1	DATA	0xC9	; P5M1.n,P5M0.n 	=00--->Standard,	01--->push-pull
P5M0	DATA	0xCA	; 					=10--->pure input,	11--->open drain
P6M1	DATA	0xCB	; P6M1.n,P6M0.n 	=00--->Standard,	01--->push-pull
P6M0	DATA	0xCC	; 					=10--->pure input,	11--->open drain
P7M1	DATA	0xE1	;
P7M0	DATA	0xE2	;
P4      DATA    0C0H
P5      DATA    0C8H
	
STC15_INIT:
	// 端口初始化
	CLR		A
	MOV		P0M1,	A 					;设置为准双向口
 	MOV		P0M0,	A
	MOV		P1M1,	A 					;设置为准双向口
 	MOV		P1M0,	A
	MOV		P2M1,	A 					;设置为准双向口
 	MOV		P2M0,	A
	MOV		P3M1,	A 					;设置为准双向口
 	MOV		P3M0,	A
	MOV		P4M1,	A 					;设置为准双向口
 	MOV		P4M0,	A
	MOV		P5M1,	A 					;设置为准双向口
 	MOV		P5M0,	A
	MOV		P6M1,	A 					;设置为准双向口
 	MOV		P6M0,	A
	MOV		P7M1,	A 					;设置为准双向口
 	MOV		P7M0,	A
	RET
;*****************结束*******************	
	END

LCD1602

;程序名称:	LCD1602.asm
;程序说明:	1602汇编程序,51单片机汇编程序,仅需修改引脚定义即可。晶振大小12M,
;			程序测试完全正确。内部包含写数据、写命令(包括读忙和不读忙)、初始化等子函数。
;			调用时先给LCD_DAT赋值,给出需要写入的数据或命令,然后调用。
;			eg:	写数据:	MOV		LCD_DAT,	#'A'	;30H为LCD_DAT
;						LCALL	LCD_W_DATA
;				写命令:	MOV		LCD_DAT,	#38H	;30H为LCD_DAT
;						LCALL	LCD_W_CMD			;(或者LCD_CMD)
;***********************命令字******************************************************************
;RS=0、RW=0——表示向LCM写入指令
;RS=0、RW=1——表示读取Busy标志
;RS=1、RW=0——表示向LCM写入数据
;RS=1、RW=1——表示从LCM读取数据

;01H:清除DDRAM的所有单元,光标被移动到屏幕左上角。
;02H:DDRAM所有单元的内容不变,光标移至左上角。
;04H:写入DDRAM后,地址指针减一,比如第一个字符写入8FH,则下一个字符会写入8EH;屏幕上的内容不滚动。
;05H:写入DDRAM后,地址指针减一,同上一种情况;每一个字符写入以后,屏幕上的内容向右滚动一个字符位。
;06H:写入DDRAM后,地址指针加一,比如第一个字符写入80H,则下一个字符会写入81H;屏幕上的内容也是不滚动。这应该是最常用的一种显示方式。
;07H:写入DDRAM后,地址指针加一,同上一种情况;每一个字符写入以后,屏幕上的内容向左滚动一个字符位。
;08H、09H、0AH、0BH:关闭显示屏,对DDRAM的操作还是在进行的,接着执行下面的某条指令,就能看到刚才屏幕关闭期间,对DDRAM操作的效果了。
;0cH:打开显示屏,不显示光标,光标所在位置的字符不闪烁。
;0dH:打开显示屏,不显示光标,光标所在位置的字符闪烁。
;0eH:打开显示屏,显示光标,光标所在位置的字符不闪烁。
;0fH:打开显示屏,显示光标,光标所在位置的字符闪烁。
;	关于光标的位置:光标所在的位置指示了下一个被写入的字符所处的位置,
;	加入在写入下一个字符前没有通过指令设置DDRAM的地址,那么这个字符就应该显示在光标指定的地方。
;10H:每输入一次该指令,AC就减一,对应了光标向左移动一格。整体的画面不滚动。
;14H:每输入一次该指令,AC就加一,对应了光标向右移动一格。整体的画面不滚动。
;18H:每输入一次该指令,整体的画面就向左滚动一个字符位。
;1CH:每输入一次该指令,整体的画面就向右滚动一个字符位。
;		画面在滚动的时候,每行的首尾是连在一起的,也就是每行的第一个字符,若左移25次,就会显示在该行的最后一格。
;		在画面滚动的过程中,AC的值也是变化的。
;20H:4位总线,单行显示,显示5×7的点阵字符。
;24H:4位总线,单行显示,显示5×10的点阵字符。
;28H:4位总线,双行显示,显示5×7的点阵字符。
;2CH:4位总线,双行显示,显示5×10的点阵字符。
;30H:8位总线,单行显示,显示5×7的点阵字符。
;34H:8位总线,单行显示,显示5×10的点阵字符。
;38H:8位总线,双行显示,显示5×7的点阵字符。这是最常用的一种模式。3CH:8位总线,双行显示,显示5×10的点阵字符。
;80H:第一行
;0C0H:第二行
;*********************************************************************************************************
/*********以下是变量及函数的声明**************/
PUBLIC LCD_R_DATA
PUBLIC LCD_W_DATA
PUBLIC LCD_W_CMD
PUBLIC LCD_CMD
PUBLIC LCD_INIT
PUBLIC LCD_DAT
PUBLIC LCD_FLAG
PUBLIC LCD_Line_BIT
PUBLIC LCD_LineSP
PUBLIC LCD_StackAddress
PUBLIC LCD_Save2Stack
PUBLIC LCD_OverLineSize_Light
;***************************************************
;定义程序区块,编译时动态分配程序地址,此段代码可以不要
	_LCD1602_ASM SEGMENT CODE
	RSEG	_LCD1602_ASM
;***************************************************
/***************变量声明区****************/
LCD_RS				BIT		P2.5	 			;1602数据命令选择端口
LCD_RW				BIT		P2.6	 			;1602读写选择端口
LCD_EN				BIT		P2.7	 			;1602使能端口
LCD_OverLineSize_Light	BIT		P3.4			;1602一行显示越界指示灯
LCD_DATA			EQU		P0		 			;1602数据端口

LCD_ALL_FLAG		DATA		20H		 		;标志位
LCD_FLAG			BIT			LCD_ALL_FLAG.0	;1602读忙标志位
LCD_Line_BIT		BIT			LCD_ALL_FLAG.1	;换行标志位
LCD_DAT				DATA		21H		 		;1602数据命令字	
LCD_DELAYED			DATA		22H		 		;延时字
LCD_LineSP			DATA		23H				;LCD堆栈指针(最大为一行16个字符)
LCD_StackAddress	DATA		0A0H			;LCD堆栈首地址
LCD_StackSize		EQU			20				;LCD堆栈大小
/***************************************************
函数名称:LCD_R_DATA
函数功能:1602读命令函数
输    入:LCD_DATA(P0口)
输    出:高位存至LCD_LAG中
***************************************************/
LCD_R_DATA:
		MOV		LCD_DATA,#0FFH
	LCD_BUSY:	CLR		LCD_RS
		SETB	LCD_RW
		NOP
		SETB	LCD_EN
		NOP
		MOV		Acc,LCD_DATA
		MOV		C,Acc.7
		MOV		LCD_FLAG,C
		CLR		LCD_EN
		NOP
		JB		LCD_FLAG,LCD_BUSY	
		RET			
/***************************************************
函数名称:LCD_W_DATA
函数功能:1602写数据函数
输    入:数据存在LCD_DAT
输    出:LCD_DATA(P0口)
***************************************************/
LCD_W_DATA:
		LCALL	LCD_R_DATA
		SETB	LCD_RS
		CLR		LCD_RW
		NOP			
		MOV		LCD_DATA,LCD_DAT
		SETB	LCD_EN
		NOP
		CLR		LCD_EN
		RET
/***************************************************
函数名称:LCD_W_CMD
函数功能:1602写命令函数,检测忙信号
输    入:命令存在LCD_DAT
输    出:LCD_DATA(P0口)
***************************************************/
LCD_W_CMD:
		LCALL	LCD_R_DATA
		CLR		LCD_RS
		CLR		LCD_RW
		NOP		
		MOV		LCD_DATA,LCD_DAT
		SETB	LCD_EN
		NOP
		CLR		LCD_EN
		RET	
/***************************************************
函数名称:LCD_CMD
函数功能:1602写命令函数,不检测忙信号
输    入:命令存在LCD_DAT
输    出:LCD_DATA(P0口)
***************************************************/
LCD_CMD:
		CLR		LCD_RS
		CLR		LCD_RW
		NOP		
		MOV		LCD_DATA,LCD_DAT
		SETB	LCD_EN
		NOP
		CLR		LCD_EN
		RET
/***************************************************
函数名称:LCD_INIT
函数功能:1602初始化函数
输    入:无
输    出:无
***************************************************/
LCD_INIT:
		MOV		LCD_LineSP,		#00H	;LCD存储区指针归零
		CLR		LCD_OverLineSize_Light
		
		SETB	LCD_Line_BIT			;第二行
		MOV		LCD_DELAYED,	#30
		LCALL	LCD_DELAY_MS
		MOV		LCD_DAT,		#38H	//设置显示模式
		LCALL	LCD_CMD
		MOV		LCD_DELAYED,	#10
		LCALL	LCD_DELAY_MS		    
		MOV		LCD_DAT,		#38H
		LCALL	LCD_CMD
		MOV		LCD_DELAYED,	#10
		LCALL	LCD_DELAY_MS
		MOV		LCD_DAT,		#38H
		LCALL	LCD_CMD
		MOV		LCD_DELAYED,	#10
		LCALL	LCD_DELAY_MS
		MOV		LCD_DAT,		#038H	//设置显示模式
		LCALL	LCD_W_CMD
		MOV		LCD_DAT,		#08H	//显示开
		LCALL	LCD_W_CMD
		MOV		LCD_DAT,		#01H	//清屏
		LCALL	LCD_W_CMD
		MOV		LCD_DAT,		#06H	//写第一个指针+1
		LCALL	LCD_W_CMD
		MOV		LCD_DAT,		#0CH	//开显示不显示光标,光标不闪烁
		LCALL	LCD_W_CMD
		MOV		LCD_DAT,		#0C0H	//第二行
		LCALL	LCD_W_CMD
	RET
/***************************************************
函数名称:LCD_Save2Stack
函数功能:存储LCD_DAT的内容到堆栈
输    入:LCD_StackAddress,LCD_LineSP
输    出:无
***************************************************/
LCD_Save2Stack:
	MOV		A,				#LCD_StackAddress
	ADD		A,				LCD_LineSP
	MOV		R0,				A
	MOV		@R0,			LCD_DAT
	INC		LCD_LineSP
	RET

/*****************************************
延时函数,延时时间为LCD_DELAYED*0.5毫秒 0~100毫秒的延时
*****************************************/			
LCD_DELAY_MS:	
	MOV		R7,		LCD_DELAYED
D1:	MOV		R6,		#0F8H
D2:	DJNZ	R6,		D2	
	DJNZ	R7,		D1
	
	RET

/*****************************************
延时函数,延时时间为LCD_DELAYED*2微秒	 0~500微秒的延时
*****************************************/			
LED_DELAY_US:
	MOV		R7,		A	
DU1:		
	DJNZ	R7,		DU1
	RET
END

juzheng

;程序名称:	juzheng.asm
;程序说明:	矩阵按键4行*8列,占用P1口和P2的低四位
;按键表示符号含义:
;	  ;Y     P1.0      P1.1     P1.2     P1.3	  P1.4	   P1.5	    P1.6	P1.7
			  ;|        |        |        |		   |        |        |        |   
	;X         |        |        |        |		   |        |        |        |  
   ;P2.0 ---- A/1 ---- B/2 ---- C/3 ---- D/4 ---- E/5 ---- F/6 ---- G/7 ---- 换行 ----
			  ;|        |        |        |		   |        |        |        |  
   ;P2.1 ---- H/8 ---- I/9 ---- J/0 ---- K/" ---- L/= ---- M/@ ---- N/# ---- 清除 ----
			  ;|        |        |        |		   |        |        |        |  
   ;P2.2 ---- O/$ ---- P/% ---- Q/& ---- R/, ---- S/. ---- T/( ---- U/) ---- 确定 ----
			  ;|        |        |        |		   |        |        |        |  
   ;P2.3 ---- V/+ ---- W/- ---- X/* ---- Y/ /---- Z/? ----空格/!----数字----大小写切换 ----
			  ;|        |        |        |		   |		|	  符号切换	  |
;按键数字标号:	  
		;Y     P1.0      P1.1     P1.2     P1.3	  P1.4	   P1.5	    P1.6	P1.7
			  ;|        |        |        |		   |        |        |        |   
	;X         |        |        |        |		   |        |        |        |  
	;P2.0 ---- 0 ------ 1 ------ 2 ------ 3 ------ 4 ------ 5 ------ 6 ------ 7 ----
			  ;|        |        |        |		   |        |        |        |  
	;P2.1 ---- 8 ------ 9 ------ 10 ----- 11 ----- 12 ----- 13 ----- 14 ----- 15 ----
			  ;|        |        |        |		   |        |        |        |  
	;P2.2 ---- 16 ----- 17 ----- 18 ----- 19 ----- 20 ----- 21 ----- 22 ----- 23 ----
			  ;|        |        |        |		   |        |        |        |  
	;P2.3 ---- 24 ----- 25 ----- 26 ----- 27 ----- 28 ----- 29 ----- 30 ----- 31 ----
			  ;|        |        |        |		   |		|	     |	      |
;以下是变量及函数的声明
PUBLIC KEYSCAN
PUBLIC KEY
PUBLIC Shift_number_BIT, Shift_LH_BIT, Shift_number_Light, Shift_LH_Light

;***************************************************
;定义程序区块,编译时动态分配程序地址,此段代码可以不要
	_JUZHENG_ASM SEGMENT CODE
	RSEG	_JUZHENG_ASM
;***************************************************
;****************头文件包含****************
$INCLUDE(DELAY.INC)
$INCLUDE(LCD1602.INC)
$INCLUDE(USART.INC)
;*****************自定义*********************************************
KEY     			DATA        24H				;用于存储按键的编号:0~F
JZ_ALL_FLAG			DATA		25H				;矩阵键盘的所有标志位
Shift_number_BIT	BIT			JZ_ALL_FLAG.0	;切换数字标志位
Shift_LH_BIT		BIT			JZ_ALL_FLAG.1	;切换大小写标志位
Shift_number_Light	BIT			P3.2			;切换数字指示灯
Shift_LH_Light		BIT			P3.3			;切换大小写指示灯	
/******************************************************************
函数名称:KEYSCAN
函数说明:按键扫描子程序:反转法识别按键,并将按键编码压入显示缓冲区
		;行列键扫描程序
		;使用XY查找4x8键的方法,只能单键,速度快
;****************************************************************/
KEYSCAN:
	ANL		P2,		#0F0H				
	MOV		P1,		#0FFH				;置列(P1)为输入模式,行(P2低四位)为输出模式
	MOV		A,		P1					;读入P1口状态,判断是否有键按下
	CJNE	A,		#0FFH,		KEYCODE ;不相等说明有键按下,转按下键的处理 
	MOV		KEY,	#0xFF
	RETURN:		
		RET							    ;相等说明没有键按下,返回

	KEYCODE:
		LCALL	DELAY4ms				;延时消除抖动
		MOV		A,		P1				;
		CJNE	A,		#0FFH,	KEYID	;延时后读入P1口状态,不相等说明确实有键按下,转入键识别处理过程
		LJMP	RETURN					; 相等则说明是干扰引起,不予处理

			KEYID:
				MOV		R4,		A 		; 此时的P1口状态已经读入R4中,对应按下的行为0,将行状态咱存于R4
				ORL		P2,		#0FH
				MOV		P1,		#00H    ;行列反转置列为输入模式,列为输出模式全为0
					NOP
					NOP					; 适当短延时,待端口电平稳定
				MOV		A,		P2      ;读入P2口状态,有键按下的话,则对应的行会被拉低,将列状态存入A中,
				ANL		A,		#0FH	;去除高四位,保留低四位
				MOV		R3,		A      	;按键扫描码暂存于R3中

				//下面等待键释放
				KEYUP:
					MOV 	A,		P2				;读入P2口状态
					ANL		A,		#0FH
					CJNE	A,		#0FH,	KEYUP  	; 不相等说明按键没有松开,继续等待
					LCALL	DELAY4ms				;相等说明按键松开,延时消除抖动
					MOV 	A,		P2				;读入P2口状态
					ANL		A,		#0FH
					CJNE	A,		#0FH,	KEYUP  	; 不相等说明按键没有真正松开,干扰造成,继续等待
                                             		; 相等说明按键真正松开了,开始根据存储于R3,R4中的按键扫描码做按键编码处理
			   KEYCODE1:MOV		DPTR,	#JBKEY	;DPTR指向显示码存储区
						MOV		R2,		#20H	;32个按键,循环32次
						MOV		R5,		#00H	;顺序读取按键的数字标号

						KEYCODELOOP:
							MOV		A,		R5
							ADD		A,		R5
							MOVC	A,		@A+DPTR	;
							SUBB	A,		R3		;
							JNZ		KEYCODEi
							MOV		A,		R5
							ADD		A,		R5							
							INC		A
							MOVC	A,		@A+DPTR	;
							SUBB	A,		R4
							JNZ		KEYCODEi		;
							MOV		DPTR,	#SEGCODE	;DPTR指向显示码存储区
							
							MOV		A,		R5
							MOV		KEY,	A		;保存到KEY
							
							MOV		A,		R5
							CLR		C
							SUBB	A,		#7
							JZ		JMP_KEY_7			;跳转到下方JMP_KEY_7
														
							MOV		A,		R5
							CLR		C
							SUBB	A,		#15
							JZ		JMP_KEY_15			;跳转到下方JMP_KEY_15
									
							MOV		A,		R5
							CLR		C
							SUBB	A,		#23
							JZ		JMP_KEY_23			;跳转到下方JMP_KEY_23
									
							MOV		A,		R5
							CLR		C
							SUBB	A,		#30
							JZ		JMP_KEY_30			;跳转到下方JMP_KEY_30
							
							MOV		A,		R5
							CLR		C
							SUBB	A,		#31
							JZ		JMP_KEY_31			;跳转到下方JMP_KEY_31
							
							MOV		A,	LCD_LineSP
							JNZ     Character_Show_Normal	;判断LCD_LineSP是否为0,否,正常显示
							MOV		LCD_DAT,	#01H		;是,则进行LCD清屏
							LCALL	LCD_W_CMD
							//显示串口堆栈里的内容
							;MOV		R0,		#Usart_StackAddress
							;MOV		R1,		#16				;LCD一行显示16个字符,不采取滚动模式
				;Show_UsartStask:
							;MOV		LCD_DAT,	@R0			;30H为LCD_DAT
							;LCALL	LCD_W_DATA				;显示串口堆栈中的内容到LCD显示
							;INC		R0
							;DJNZ	R1,		Show_UsartStask	;显示串口堆栈中的前16位
							MOV		R0,		#Usart_StackAddress		;获取堆栈首地址
							MOV		R1,		#16						;获取堆栈指针
							LCALL	ShowStask
							
							MOV		LCD_DAT,	#0C0H		;切换到LCD的第二行
							LCALL	LCD_W_CMD
				Character_Show_Normal:			
							MOV		A,	LCD_LineSP
							CJNE	A	,#16, Character_Show_Normali	;判断输入是否超过16个字符越界,越界则不保存
							SETB	LCD_OverLineSize_Light				;越界指示灯点亮
							LJMP RETURN           ; 返回
				Character_Show_Normali:	
							CLR		LCD_OverLineSize_Light				;没有越界,越界指示灯熄灭
							LCALL	Character_Show						;正常显示
							LJMP RETURN          						;退出本次按键扫描

				KEYCODEi:
					INC		R5				;
					DJNZ	R2,		KEYCODELOOP
					RET
				JMP_KEY_7:	
					LJMP KEY_7			;删除(删除一位字符)
					RET
				JMP_KEY_15:	
					LJMP KEY_15			;清屏
					RET
				JMP_KEY_23:	
					LJMP KEY_23			;确定发送
					RET
				JMP_KEY_30:	
					LJMP KEY_30			;切换成数字
					RET
				JMP_KEY_31:	
					LJMP KEY_31			;切换大小写
					RET	
					
//键7:删除(删除一位字符)
KEY_7:
	MOV		LCD_DAT,	#01H				;LCD清屏
	LCALL	LCD_W_CMD
	MOV		R0,		#Usart_StackAddress		;获取堆栈首地址
	MOV		R1,		#16						;获取堆栈指针
	LCALL	ShowStask
	MOV		LCD_DAT,	#0C0H				;切换到LCD第二行
	LCALL	LCD_W_CMD
	DEC		LCD_LineSP
	MOV		R0,		#LCD_StackAddress		;获取堆栈首地址
	MOV		R1,		LCD_LineSP				;获取堆栈指针
	LCALL	ShowStask
	
	LJMP 	RETURN					;退出本次按键扫描
	RET
	
//键15:清屏
KEY_15:
	MOV		LCD_LineSP,	#00H		;LCD堆栈指针归零
	CLR		LCD_OverLineSize_Light	;越界指示灯熄灭
	MOV		LCD_DAT,	#01H		;LCD清屏
	LCALL	LCD_W_CMD
	MOV		LCD_DAT,	#0C0H		;切换到LCD第二行
	LCALL	LCD_W_CMD
	LJMP 	RETURN					;退出本次按键扫描
	RET

//键23:确定发送
KEY_23:
	LCALL	SendNByte				;发送LCD堆栈中的字符
	MOV		Usart_Buffer,	#0AH	;结束标志字符 0A 0D
	LCALL	Send0neByte
	MOV		Usart_Buffer,	#0DH
	LCALL	Send0neByte
	//如果想在发送后本机LCD上显示刚刚发送的字符串,只有把下面的代码注释即可
  ;*******************************************************************
		MOV		LCD_DAT,	#01H				;LCD清屏
		LCALL	LCD_W_CMD
		MOV		R0,		#Usart_StackAddress		;获取堆栈首地址
		MOV		R1,		#16						;获取堆栈指针
		LCALL	ShowStask
		MOV		LCD_DAT,	#0C0H				;切换到LCD第二行
		LCALL	LCD_W_CMD
  ;*********************************************************************
	MOV		LCD_LineSP,	#00H		;LCD堆栈指针归零
	CLR		LCD_OverLineSize_Light	;越界指示灯熄灭
	LJMP 	RETURN					;退出本次按键扫描
	RET

//键30:切换成数字
KEY_30:
	CPL		Shift_number_BIT		;翻转数字符号切换标志位
	CPL		Shift_number_Light		;翻转数字符号切换指示灯
	LJMP 	RETURN					;退出本次按键扫描
	RET

//键31:切换大小写
KEY_31:
	CPL		Shift_LH_BIT		;翻转切换大小写标志位
	CPL		Shift_LH_Light		;切换大小写标志位指示灯
	LJMP 	RETURN				;退出本次按键扫描
	RET

Character_Show:									;优先级:数字 > 大写 > 小写
	JB		Shift_number_BIT,	Shift_number	;数字的优先级高
	JB		Shift_LH_BIT,	Shift_upper			;其次是大写
	LCALL	Shift_lower							;最后是小写
	RET
	Shift_number:
		MOV		DPTR,	#SEGCODE		;设置DPTR到标准字库
		MOV		A,		KEY				;获取按键数字标号
		ADD		A,		#64				;数字符号区
		MOVC	A,		@A+DPTR	;
		MOV		LCD_DAT,	A			;
		LCALL	LCD_Save2Stack			;保存到LCD堆栈
		LCALL	LCD_W_DATA				;LCD显示字符
		LJMP 	RETURN					;退出本次按键扫描
		RET
	Shift_upper:
		MOV		DPTR,	#SEGCODE		;设置DPTR到标准字库
		MOV		A,		KEY				;获取按键数字标号
		ADD		A,		#32				;大写英文区
		MOVC	A,		@A+DPTR	;
		MOV		LCD_DAT,	A
		LCALL	LCD_Save2Stack			;保存到LCD堆栈
		LCALL	LCD_W_DATA				;LCD显示字符
		LJMP	RETURN					;退出本次按键扫描
		RET
	Shift_lower:
		MOV		DPTR,	#SEGCODE		;设置DPTR到标准字库
		MOV		A,		KEY				;获取按键数字标号
		MOVC	A,		@A+DPTR	;		;小写英文区
		MOV		LCD_DAT,	A
		LCALL	LCD_Save2Stack			;保存到LCD堆栈
		LCALL	LCD_W_DATA				;LCD显示字符
		LJMP 	RETURN					;退出本次按键扫描
	RET

/***************************************************
函数名称:SendNByte
函数功能:发送N个字节
输    入:Usart_Buffer
输    出:无
***************************************************/	
SendNByte:
	MOV		R0,		#LCD_StackAddress		;获取LCD堆栈首地址
	MOV		R1,		LCD_LineSP				;获取LCD堆栈指针
	CJNE	R1,		#00H,	SendNByteLOOP	;R1不为0,串口发送LCD_LineSP个字符
	RET										;R1为0,返回
SendNByteLOOP:
	MOV		Usart_Buffer,		@R0
	LCALL	Send0neByte						;
	INC		R0
	DJNZ	R1,		SendNByteLOOP			;串口发送LCD_LineSP个字符
	RET
JBKEY:							;判断按键对应的标号
;	 	0			1			2			3			4			5			6			7
DB	0EH,0FEH,	0EH,0FDH,	0EH,0FBH,	0EH,0F7H,	0EH,0EFH,	0EH,0DFH,	0EH,0BFH,	0EH,07FH
;	 	8			9			10			11			12			13			14			15
DB	0DH,0FEH,	0DH,0FDH,	0DH,0FBH,	0DH,0F7H,	0DH,0EFH,	0DH,0DFH,	0DH,0BFH,	0DH,07FH
;	 	16			17			18			19			20			21			22			23
DB	0BH,0FEH,	0BH,0FDH,	0BH,0FBH,	0BH,0F7H,	0BH,0EFH,	0BH,0DFH,	0BH,0BFH,	0BH,07FH
;	 	24			25			26			27			28			29			30			31
DB	07H,0FEH,	07H,0FDH,	07H,0FBH,	07H,0F7H,	07H,0EFH,	07H,0DFH,	07H,0BFH,	07H,07FH

SEGCODE:						;标准字库
DB	'a','b','c','d','e','f','g',' '
DB	'h','i','j','k','l','m','n',' '
DB	'o','p','q','r','s','t','u',' '
DB	'v','w','x','y','z',' ',' ',' '
	
DB	'A','B','C','D','E','F','G',' '
DB	'H','I','J','K','L','M','N',' '
DB	'O','P','Q','R','S','T','U',' '
DB	'V','W','X','Y','Z',' ',' ',' '
	
DB	'1','2','3','4','5','6','7',' '
DB	'8','9','0','"','=','@','#',' '
DB	'$','%','&',',','.','(',')',' '
DB	'+','-','*','/','?','!'

;*****************结束*******************
	END               ;

delay

;以下是变量及函数的声明
PUBLIC DELAY400MS
PUBLIC DELAY1ms	
PUBLIC DELAY4ms	
	
;***************************************************
;定义程序区块,编译时动态分配程序地址,此段代码可以不要
	_DELAY_ASM SEGMENT CODE
	RSEG	_DELAY_ASM
;***************************************************
//延时400ms子程序,用于数码管闪烁显示报警
//延时400ms子程序,用于数码管闪烁显示报警
DELAY400MS:								;@12.000MHz
	NOP
	NOP
	NOP
	PUSH	60H
	PUSH	61H
	PUSH	62H
	MOV		60H,	#5			;#15
	MOV		61H,	#152		;#152
	MOV		62H,	#2			;#82
NEXT0:
	DJNZ	62H,	NEXT0
	DJNZ	61H,	NEXT0
	DJNZ	60H,	NEXT0
	POP		62H
	POP		61H
	POP		60H
	RET
//延时1ms子程序:修改R7可以改变延时时间的长短,控制扫描速度,12MHz时钟,延时约1ms
DELAY1ms:
	MOV		R7,		#2
	DEL2:
		MOV		R6,		#248
		NOP
	DEL3:
		DJNZ	R6,		DEL3
		DJNZ	R7,		DEL2
	RET									; 子程序返回
//延时4ms子程序:用于按键消除抖动,实际应用时可以根据按键灵敏度修改
DELAY4ms:								;@12.000MHz
	MOV		70H,	#38					;#38
	MOV		71H,	#5					;#85
	NEXT:
		DJNZ	71H,	NEXT
		DJNZ	70H,	NEXT
		RET
;*****************结束*******************		
		END	

usart(应该是uart,下面的文件写错了,另行修改)

;以下是变量及函数的声明
PUBLIC Usart_Init
PUBLIC Usart_ISR	
PUBLIC Send0neByte	
PUBLIC Usart_Buffer
PUBLIC Usart_StackSP
PUBLIC Usart_StackAddress
PUBLIC ShowStask
;***************************************************
;定义程序区块,编译时动态分配程序地址,此段代码可以不要
	_USART_ASM SEGMENT CODE
	RSEG	_USART_ASM
;***************************************************
$INCLUDE(LCD1602.INC)

Usart_Buffer			DATA		27H		 		;串口缓存器
Usart_StackSP			DATA		28H				;串口堆栈指针
Usart_StackAddress		DATA		80H				;串口堆栈首地址
Usart_StackSize			EQU			20				;串口堆栈大小
;AT89C52定时器有4种工作方式,与STC15的定时器工作方式不同
;有四种工作方式:
;方式0,13位定时/计数方式。
;方式1,16位的定时/计数方式。
;方式2,自动重装载8位工作方式
;方式3,定时/计数器0被拆成2个独立的定时/计数器来用。
;其中,TL0可以构成8位的定时器或计数器的工作方式,而TH0则只能作为定时器来用。
/***************************************************
函数名称:Usart_Init
函数功能:串口初始化
输    入:无
输    出:无
***************************************************/
Usart_Init:
	MOV		TMOD,	#20H	;定时器T1,模式1
	MOV		SCON,	#50H	;设置波特率重装值(256-12000000/12/32/2400)=243=F3
	MOV		TH1,	#0F3H
	MOV		TL1,	#0F3H
	MOV		PCON,	#00H
	SETB	ES
	SETB	EA
	SETB	TR1
	MOV		Usart_Buffer,	#00H		;清空Buffer
	MOV		Usart_StackSP,	#00H
	RET
/***************************************************
函数名称:Usart_ISR
函数功能:串口中断服务程序
输    入:无
输    出:Usart_Buffer
***************************************************/
Usart_ISR:
	PUSH	ACC
	JB		RI,		Usart_Receive	;判断是否在接收数据
Usart_Transmit:
	CLR		TI					;清除发送标志位
	RETI
Usart_Receive:
	CLR		RI					;清除接收标志位
	MOV		A,		SBUF
	CJNE	A,	#0AH,	JReceiveEND1	;判断结束标志字符 0A,不是,则跳转
	MOV		Usart_Buffer,	SBUF		;是 0A ,存放到串口缓存器
	JMP		Usart_Receive_RET			;退出串口中断服务函数
JReceiveEND1:
	MOV		A,		Usart_Buffer		
	CJNE	A,		#0AH,	Usart_Receive_Normal;判断串口缓存器中是否是 0A,不是,则跳转
	MOV		A,		SBUF						;是 0A,将SBUF中的内容放到累加器A
	CJNE	A,		#0DH,	JReceiveEND2		;判断A中是否是结束标志字符 0D,不是,则跳转
												;是 0D,串口接收字符串结束
	MOV		A,		Usart_StackSP				;				 
	JZ		JReceiveEND1i						;Usart_StackSP为0,则跳转,归零串口缓存器,和串口指针
												;避免了避免连续按“发送”按键误操作
	//接收完成后进行数据处理
		//此次接收,堆栈没有存内容的地址里的内容清除,即 Usart_StackSP ~ Usart_StackSize的内容清除
		MOV		A,		#Usart_StackAddress		;获取堆栈首地址
		ADD		A,		Usart_StackSP			;获取堆栈指针
		MOV		R0,		A
		MOV		A,		#Usart_StackSize
		SUBB	A,		Usart_StackSP
		MOV		R1,		A
		CLR_LOOP:
		MOV		@R0,	#00H			;30H为LCD_DAT
		INC		R0
		DJNZ	R1,		CLR_LOOP		;循环
		
		//接收到的字符串显示到LCD
		MOV		LCD_DAT,	#01H					;LCD清屏
		LCALL	LCD_W_CMD
		MOV		R0,		#Usart_StackAddress		;获取堆栈首地址
		MOV		R1,		Usart_StackSP			;获取堆栈指针
		LCALL	ShowStask
		
	MOV		LCD_DAT,	#0C0H			;切换到LCD第二行输入
	LCALL	LCD_W_CMD
JReceiveEND1i:	
	MOV		Usart_Buffer,	#00H		;串口缓存器清零
	MOV		Usart_StackSP,	#00H		;串口指针归零
	JMP		Usart_Receive_RET
JReceiveEND2:	
		MOV		Usart_Buffer,	#0AH		;获取接收到的数据到累加器A  
		MOV		A,				#Usart_StackAddress
		ADD		A,				Usart_StackSP
		MOV		R0,				A
		MOV		@R0,			Usart_Buffer
		INC		Usart_StackSP
Usart_Receive_Normal:
		MOV		Usart_Buffer,	SBUF		;获取接收到的数据到累加器A  
		MOV		A,				#Usart_StackAddress
		ADD		A,				Usart_StackSP
		MOV		R0,				A
		MOV		@R0,			Usart_Buffer
		INC		Usart_StackSP

Usart_Receive_RET:
	POP		ACC
	RETI
/***************************************************
函数名称:ShowStask
函数功能:显示堆栈里的内容
输    入:R0(堆栈首地址),R1(获取堆栈指针)
输    出:无
调用举例:例如:
			MOV		R0,		#Usart_StackAddress		;获取堆栈首地址
			MOV		R1,		Usart_StackSP			;获取堆栈指针
			LCALL	ShowStask
***************************************************/		
ShowStask:	
		MOV		LCD_DAT,	@R0			;30H为LCD_DAT
		LCALL	LCD_W_DATA				;显示堆栈里的内容
		INC		R0
		DJNZ	R1,		ShowStask		;循环
	RET
/***************************************************
函数名称:Send0neByte
函数功能:发送一个字节
输    入:Usart_Buffer
输    出:无
***************************************************/	
Send0neByte:
	CLR		ES					;关闭串口中断
	CLR		TI					;清除发送标志位
	MOV		SBUF,	Usart_Buffer;将数据Buffer里的数据发送出去
SendFinish:
	JNB		TI,		SendFinish	;判断是否发送完毕
	CLR		TI					;是,则清除发送标志位
	SETB	ES					;开启串口中断
	RET

END
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 基于STC15W4K32S4的两路抢答器是一种用于教育培训等活动中的设备。它通过使用STC15W4K32S4作为控制器,实现了两个抢答按钮的功能。 该抢答器系统包括两个抢答按钮和一台主控。每个抢答按钮连接到STC15W4K32S4控制器上的GPIO口,用于控制信号的输入。主控通过串口STC15W4K32S4进行通信,控制抢答的过程。 在抢答开始前,主控会发送一个开始信号给STC15W4K32S4,启动抢答器。当抢答按钮被按下时,按钮的信号会被STC15W4K32S4检测到,并通过串口传输给主控。主控收到信号后,会记录抢答按钮按下的时间,并根据设定的规则进行得分计算。比如,先按下按钮的人可以获得更高的分数。 抢答过程中,主控还可以实时显示抢答的情况。它可以显示两个抢答按钮的状态,包括按下的次数、响应速度等信息。这样,主持人或老师可以根据实时数据来评判参与者的抢答情况,提供及时的反馈。 此外,该抢答器系统还可以具备一些额外的功能,比如计时器、音效等。计时器可以用来限制参与者的抢答时间,增加游戏的挑战性。音效可以用来提醒参与者抢答的开始和结束。 总之,基于STC15W4K32S4的两路抢答器是一种简、实用的设备。它可以促进参与者的积极性、培养快速反应能力,并提供实时数据供评判者参考。它适用于各种教育培训活动,为参与者和观众带来更多乐趣和互动。 ### 回答2: 基于STC15W4K32S4的两路抢答器是一种用于组织抢答比赛的装置。该芯片具备高性能和强大的功能,适用于嵌入式系统设计。 两路抢答器由主控制器、两个按键、液晶显示屏以及相关的电子元件组成。主控制器采用STC15W4K32S4芯片,具备高速计数器和定时器,可以实现灵活的抢答计时和显示功能。两个按键用于参与者抢答,按下后会触发主控制器进行计时和判断。液晶显示屏用于显示抢答的结果和相关信息。 在使用过程中,主控制器会通过定时器进行倒计时,并且在液晶显示屏上显示剩余时间。当参与者按下按键后,主控制器会停止计时并记录抢答的先后顺序。抢答的结果会在液晶显示屏上显示,并且可以通过串口输出给其他外部设备。此外,抢答器还可以设置不同的抢答模式,如人抢答、双人抢答或者团体抢答等。 基于STC15W4K32S4芯片的两路抢答器具有稳定的性能和高度可靠性。该芯片可以支持多种外设接口,如UART、I2C和SPI等,从而方便与其他设备进行通信和控制。此外,STC15W4K32S4还具备丰富的外设资源,例如PWM输出、ADC输入以及IO口控制等功能,可以满足不同的应用需求。 总而言之,基于STC15W4K32S4的两路抢答器在抢答比赛中具备高性能、灵活性和可靠性。它是一种理想的选择,可以应用于学校、培训构或者考试场合,为抢答比赛提供准确和公正的结果。 ### 回答3: 基于STC15W4K32S4的两路抢答器可以用于教育培训构、竞赛活动或者团队游戏中,以促进学习和竞争的氛围。 抢答器的设计需要考虑到使用者的操作方便性和可靠性。首先,通过STC15W4K32S4的高性能和丰富的接口功能,我们可以设计一个外观简洁、结构紧凑、方便携带的抢答器,可以支持两路抢答的功能。 抢答器主要由两部分组成:抢答和显示屏。抢答包含两个按键,用来表示两个不同的参与者,当按下按键时,抢答将通过串口通信将抢答信号发送给显示屏。显示屏接收到抢答信号后,会显示先按下按键的参与者,并发出相应的音效来确认抢答成功。 使用STC15W4K32S4的优势在于其丰富的外设资源和强大的处理能力。我们可以利用定时器/计数器模块来控制抢答时间,例如将抢答时间设置为10秒。当抢答时间到达时,显示屏会自动停止接收抢答信号,以确保抢答的公正性和时效性。 此外,我们还可以通过使用外部存储器(如EEPROM)来保存参与者的得分信息。每次抢答成功后,抢答将得分信息发送给显示屏,并更新得分。通过设置显示屏上的按钮功能,参与者可以查看自己的得分和排名情况。 综上所述,基于STC15W4K32S4的两路抢答器具有简易用、功能灵活、操作稳定等特点,可以满足教育、竞赛和游戏等各种场景的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值