题目:
编写一个按键计数程序。如图所示,对“独立按键”的按键次数进行计数。8个独立按键的计数值采用16进制分别显示在“2*4位共阴数码管”中。每位计数溢出时,控制“蜂鸣器”报警。
接线:
8位独立按键接单片机P1
数码管8位段选接单片机P0
蜂鸣器接P2.7
数码管的位选由38译码器来控制,译码器的ABC分别接单片机的P2.0,P2.1,P2.2,
注:这里的数码管位选(动态扫描)是由单片机的三个引脚按照38译码器的输出改变单片机引脚电平来实现的,反应到单片机的低三位来说就是循环输出0-7,这里不需要移位什么的,因为程序中直接使用的十进制数,单片机内部是按照二进制来处理,因此无需任何转换。
经过测试,完美运行,能够实现题目要求。
以下是运行图片:
以下为全部代码,若需完整工程文件请移步至这里进行下载工程代码:
//宏定义KEYCOUNTER部分请自行加上,
//这部分是定义计数器这个变量代表的某个地址
//伪指令
ORG 0000H
AJMP START
//按键程序
ORG 0100H
START: MOV SP, #60H ;堆栈指针
MOV P1, #0FFH ;按键初始化
MOV P0, #3FH ;段选初始化
MOV R2, #0H ;位选初始化
MOV R4, #08H ;8位数码管
//8个计数器,分别存按键的次数
MOV KEYCOUNTER1, #0H ;计数器初始化为0
MOV KEYCOUNTER2, #0H
MOV KEYCOUNTER3, #0H
MOV KEYCOUNTER4, #0H
MOV KEYCOUNTER5, #0H
MOV KEYCOUNTER6, #0H
MOV KEYCOUNTER7, #0H
MOV KEYCOUNTER8, #0H
//************按键判别***************//
KEY: MOV P1,#0FFH ;P1口按键全写1(高电平)
MOV A,P1 ;将P1口状态读到A
CPL A ;取反
JZ DIS ;若A为0 ,无按键按下,跳转KEY重新扫描
LCALL D10ms ;消抖
MOV A,P1 ;再次检测
CPL A ;再取反
JZ DIS ;若A为0 ,无按键按下,跳转KEY重新扫描
JB ACC.0,PS0 ;直接寻址位ACC.0为1则转移 PS0
JB ACC.1,PS1 ;以下同理
JB ACC.2,PS2
JB ACC.3,PS3
JB ACC.4,PS4
JB ACC.5,PS5
JB ACC.6,PS61
JB ACC.7,PS71
PS61: LJMP PS6 ;超出范围,因此用此方式进行跳转
PS71: LJMP PS7
DIS: LCALL DISP ;跳转到数码管显示程序
LJMP KEY
//***********按键处理函数****************//
PS0: JNB P1.0, PS0 ;判断P1.0的状态,为0则转移(说明按键未松开,原地等待)
MOV R0, KEYCOUNTER1 ;计数值存到寄存器
INC R0 ;计数器加1
MOV KEYCOUNTER1, R0 ;返回给计数器
CJNE R0,#16,DIS ;R0不等于16则转移到显示程序
MOV KEYCOUNTER1, #0 ;等于16就直接清零
LCALL SOUND ;蜂鸣器报警
LJMP DIS ;跳转到显示程序
PS1: JNB P1.1, PS1 ;和PS0按键处理函数一样
MOV R0, KEYCOUNTER2
INC R0
MOV KEYCOUNTER2, R0
CJNE R0,#16,DIS
MOV KEYCOUNTER2, #0
LCALL SOUND
LJMP DIS
PS2: JNB P1.2, PS2
MOV R0, KEYCOUNTER3
INC R0
MOV KEYCOUNTER3, R0
CJNE R0,#16,RE
MOV KEYCOUNTER3, #0
LCALL SOUND
LJMP DIS
PS3: JNB P1.3, PS3
MOV R0, KEYCOUNTER4
INC R0
MOV KEYCOUNTER4, R0
CJNE R0,#16,RE
MOV KEYCOUNTER4, #0
LCALL SOUND
LJMP DIS
PS4: JNB P1.4, PS4
MOV R0, KEYCOUNTER5
INC R0
MOV KEYCOUNTER5, R0
CJNE R0,#16,RE
MOV KEYCOUNTER5, #0
LCALL SOUND
LJMP DIS
PS5: JNB P1.5, PS5
MOV R0, KEYCOUNTER6
INC R0
MOV KEYCOUNTER6, R0
CJNE R0,#16,RE
MOV KEYCOUNTER6, #0
LCALL SOUND
LJMP DIS
PS6: JNB P1.6, PS6
MOV R0, KEYCOUNTER7
INC R0
MOV KEYCOUNTER7, R0
CJNE R0,#16,RE
MOV KEYCOUNTER7, #0
LCALL SOUND
LJMP DIS
PS7: JNB P1.7, PS7
MOV R0, KEYCOUNTER8
INC R0
MOV KEYCOUNTER8, R0
CJNE R0,#16,RE
MOV KEYCOUNTER8, #0
LCALL SOUND
RE: LJMP DIS
D10ms: MOV R7,#25 ;延时子程序,用于按键消抖
D1: MOV R6,#200
DJNZ R6,$ ;R6减1非0,原地跳转
DJNZ R7,D1
RET
//*************显示子程序**************//
//P2口低3位为位选线 ----------R2
//P0口为段选线 ----------R1
ORG 0500H
DISP: MOV A,R2 ;读取位选
MOV P2,A ;输出到端口
H1: CJNE R2,#0,H2 ;R2不等于0则转移
MOV R1,KEYCOUNTER1 ;将计数器的值传给R1寄存器(R1保存的段选数据)
LJMP NEXT ;收到计数器的值以后直接跳转到下一步,不再执行中间程序
H2: CJNE R2,#1,H3 ;R2不等于1则转移
MOV R1,KEYCOUNTER2
LJMP NEXT
H3: CJNE R2,#2,H4 ;R2不等于2则转移
MOV R1,KEYCOUNTER3
LJMP NEXT
H4: CJNE R2,#3,H5 ;R2不等于3则转移
MOV R1,KEYCOUNTER4
LJMP NEXT
H5: CJNE R2,#4,H6 ;R2不等于4则转移
MOV R1,KEYCOUNTER5
LJMP NEXT
H6: CJNE R2,#5,H7 ;R2不等于5则转移
MOV R1,KEYCOUNTER6
LJMP NEXT
H7: CJNE R2,#6,H8 ;R2不等于6则转移
MOV R1,KEYCOUNTER7
LJMP NEXT
H8: CJNE R2,#7,NEXT ;R2不等于7则转移
MOV R1,KEYCOUNTER8
LJMP NEXT
NEXT: MOV A,R1 ;将段选数据送到A
MOV DPTR,#TAB
MOVC A,@A+DPTR ;查段码表
MOV P0,A ;将段选数据送到端口
INC R2 ;循环显示位选
CJNE R2,#8,NEXT1 ;R2最大为8,也就是只控制低3位(138译码器的输入端)
MOV R2,#0 ;清零
NEXT1: LCALL DELAY
DJNZ R4,DISP ;R4减1不为0则跳转到DISP,重新循环点亮其他几位数码管 RET
//********延时子程序********//
DELAY: MOV R5,#10
DEL0: MOV R6,#1
DEL1: MOV R7,#20
DEL2: DJNZ R7,DEL2
DJNZ R6,DEL1
DJNZ R5,DEL0
RET
//********共阴极数码管码表***********//
TAB: DB 3FH,06H,5BH,4FH,66H,6DH,7DH,07H,7FH,6FH,77H,7CH,39H,5EH,79H,71H
//*****蜂鸣器报警,IO口输出高电平*******//
ORG 0800H
SOUND: SETB P2.7
MOV R4,#64H ;延时100ms
LOOP: MOV R3,#0F9H
LOOP1: DJNZ R3,LOOP1
DJNZ R4,LOOP
CLR P2.7
RET
END