设计目的:
1.熟悉PS/2协议的原理
2.学习单片机的使用
设计内容:
依照PS/2协议,在51芯片上开发汇编程序,使得按下开发板上的按键时,能够在PC机上输出a、b、c。
实验环境:
开发板、STC12C2052AD芯片、PS/2线、PC机、Keil软件、STC-ISP程序下载器
相关参数:
单片机振荡频率6MHz(具体情况请看此型号单片机用户手册)
指令周期0.5微秒(51型单片机的指令周期是其振荡频率的12分之一)
PS/2连接器引脚定义(见参考文献《PS/2技术参考》)
能够接受的键盘时钟信号频率10-20KHz(最大33KHz,推荐设为15KHz)
设计思想:
使用开发板上的按钮产生中断信号,使单片机进入相应的中断处理程序,从而向PC机发送相应的扫描码。PC机主板中有一个解码芯片,此芯片在收到扫描码之后,引发PC机操作系统的中断(I/O的中断),转到操作系统中的中断处理程序,从面在系统中输出相应字符。
原理:
实际的通用键盘上的每一个键都对应一个唯一个扫描码,这些键会在极短的时间内被扫描一遍,以确认它们有没有被按下,如果某个键被按下了,就发送其相应的扫描码。而在开发板上,51芯片有三个按键,对应芯片上的两个中断口和一个定时/计数器输入口。中断接口可以直接使用,当按下键时就触发中断,而定时/计数器需要设置为初始值为FFH,一旦按下键便会溢出而产生中断的计数器。
主机与键盘有四根线相连,时钟信号数、数据线、接地线、电源线。
时钟信号在任何时刻都是由键盘产生的,PC机在时种的下降沿接收数据线上的信号,要是PC机向键盘发送数据,则键盘在时钟的上升沿接收数据线上的数据。虽是由键盘产生时钟信号,但是PC机对总线有着绝对的控制权,只要其将时钟线拉低(高低电平信号有这样的特性:只有一端的信号为低电平,则整条线的信号都为低电平。这就是为什么要向单片机I/O口输出一个信号,要先将外部输入单片机这个I/O口的信号释放(拉高)。否则输出的永远都是低电平),就要以抵制键盘的输出。
总线上有这些几种状态:
1、PC等待键盘输出状态(键盘可以输出状态):CLK=1,DATA=1;
2、键盘等待PC输出状态(PC可以输出状态):CLK=1,DATA=0(这个状态之前还有一些状态要生产生的,详情请见参考文献《PS/2技术参考》)
3、正在输出状态:此时的时钟线与数据线上的信号没有定值,依据所要发送的数据不同而不同,CLK=X,DATA=X
键盘从外部存储器中读取相应键位的扫描码,当要改变某个键所要发送的扫描码时,只需改变外部存储器中对应位置的扫描码,这样就可以方便地改变某键所要发送的扫描码。在存放扫描码时,只需将扫描码按顺序存放,如E0,70按存储地址从小到大顺序地排列。因为通码(make code)最多只有两个十六进制数,且如有两个则第一个必是E0H,而断码(break code)最多只有三个,且如果有三个,则第一个必是E0H,如果有二个,则第一个必是F0H。所以,当发送时,如果发现第一个是E0H或F0H的话,则再发送其后的一个扫描码。
注意事项:
1、
发送8个数据位时,是低位先发送(least significant bit first),如果是用高位先发送,主机自然是无法识别会发送的数据,会报警;
2、
如果想要精确计算时间,那么像子程序调用语句会占用两个指令周期也应该考虑到;
3、
连接线应该连在指示灯靠近51芯片的一端,否则无法接收到主机发来的信号;PS/2
4、
汇编程序前的org 语句必须按其指示的地址大小排列,小的在前,大的在后;
程序:
;
=======================================
;
program
;
check clk and data before every 8 bit data sending
;
a function is created for send all 11bit data
;
a function is created for parity calculating
;
CAPSLOCK :BIG LETTER P1.3=0,SMALL LETTER P1.3=1
;
=======================================
;
=======================================
;
VARS USED LOCALY
;
R0 IS USED LOCALY AS A COUNTER
;
20H.1 20H.2 20H.3 20H.4 IS USED IN FUNCTION AS TEMP VAR
;
R3,R4,R5 USED LOCALLY IN DELAY()
;
===================================
;
Define Samples
;
====================================
MCLK EQU P1
.2
;
VALUE OF CLOCK LINE
MDATA EQU P1
.1
;
VALUE OF DATA LINE
SCAN EQU R1
;
VALUE OF THE SCAN CODE
RECV EQU R2
;
VALUE RECEIVED BY KEYBOARD
RECV2 EQU R6
;
THE VALUE RECEIVED SECONDLY
NEXTBIT EQU CY
;
VALUE OF THE BIT WILL SENT NEXT
PARITY EQU 20H
.1
;
VALUE OF PARITY
TEMP EQU 20H
.2
;
THIS BIT IS FOR TEMPORITARY USE
TEMP1 EQU 20H
.3
;
THIS BIT IS FOR TEMP USE
TEMP8BIT EQU 21HLOOPCOUNTER EQU 30H
;
COUNTER SPECIAL FOR SENDALLBIT
SCROLLLOCK EQU P1
.5
CAPSLOCK EQU P1
.6
NUMLOCK EQU P1
.7
ERROR_LIGHT EQU P1
.4
;
PARITY ERROR
ERROR_LIGHT_STOPBIT EQU P1
.3
;
STOP BIT RECEIVE ERROR
;
===================================
;
===================================
;
ORG
;
===================================
ORG 00HJMP MAINORG 0003HLJMP K1DOWNORG 000BHLJMP K3DOWNORG 0013HLJMP K2DOWN
;
============================================================
;
MAIN
;
============================================================
MAIN:
;
==============================
;
SET INTERRUPTER AND TIMER
mov ie
,
#10000101B
;
中断使能
mov ip
,
#00H
;
中断优先
mov tcon
,
#00000101b
;
中断为电平触发
;
TIME0 AS A BUTTON
MOV TMOD
,
#06H MOV TH0
,
#0FFH MOV TL0
,
#0FFH SETB ET0 SETB TR0
;
==============================
;
==============================
;
SET CLK AND DATA LINE TO NORMAL STATUS
SETB MCLK SETB MDATA
;
==============================
SETB CAPSLOCK
;
CAPSLOCK OFF(1)
SETB NUMLOCK
;
NUMLOCK OFF(1)
SETB SCROLLLOCK
;
SCROLLLOCK OFF(1)
CHKSTATUS: JNB MCLK
,
CHKSTATUS
;
IF MCLK==0 THEN GOTO CHKSTATUS
JB MDATA
,
CHKSTATUS
;
IF MDATA==1 THEN GOTO CHKSTAUS
;
NOW MCLK=1 AND MDATA =0 ,READY TO RECEIVE DATA FROM PC
LCALL RECV_CHK_SEND
;
RECEIVE DATA FROM PC
LJMP CHKSTATUS
;
WAIT FOR BREAK
;
============================================================
;
///MAIN
;
============================================================
;
===============================================================================================
;
AREA FOR FUNCTION
;
===============================================================================================
LJMP ENDOFFILE
;
====================================
;
IN THIS FUNCTION: RECEIVE ALL 10BIT DATA BY RECEIVEALLBIT() AND CHK WHETHER IT'S CORRECT THEN SEND CORRESPONDING SCANCODE TO PC
;
====================================
RECV_CHK_SEND: CLR P1
.0
;
INDICATE START RECEIVEING
LCALL RECEIVEALLBIT
;
RECV PARITY TEMP1
MOV A
,
RECV CJNE A
,
#0EDH
,
RECV_CHK_SEND_END
;
=====================
;
RECV==EQ NOW SET LED
;
CHKSTATUS_2:
;
JNB MCLK,CHKSTATUS_2 ;IF MCLK==0 THEN GOTO CHKSTATUS
;
JB MDATA,CHKSTATUS_2 ;IF MDATA==1 THEN GOTO CHKSTAUS
LCALL DELAY50 LCALL DELAY50 LCALL RECEIVEALLBIT
;
RECV PARITY TEMP1
;
========================
;
THE CODE IS RIGHT AFTER THIS ,TESTED BY MOV "RECV,#00000110"
MOV A
,
RECV RRC A
;
NOW C=LEAST BIT OF A
CPL C
;
PC: C=1 ON ;C=0 OFF NOW IN KEYBOARD C=1 OFF(LIGHT OFF) C=0 ON(LIGHT ON)
MOV SCROLLLOCK
,
C RRC A
;
NOW C=LEAST BIT OF A
CPL C
;
PC: C=1 ON ;C=0 OFF NOW IN KEYBOARD C=1 OFF(LIGHT OFF) C=0 ON(LIGHT ON)
MOV NUMLOCK
,
C RRC A
;
NOW C=LEAST BIT OF A
CPL C
;
PC: C=1 ON ;C=0 OFF NOW IN KEYBOARD C=1 OFF(LIGHT OFF) C=0 ON(LIGHT ON)
MOV CAPSLOCK
,
C SETB P1
.0
LJMP RECV_CHK_SEND_END
;
/====================
RECV_CHK_SEND_END:RET
;
====================================
;
IN THIS FUNCTION: RECEIVE ALL 10BIT DATA BY RECEIVEALLBIT() AND CHK WHETHER IT'S CORRECT THEN SEND CORRESPONDING SCANCODE TO PC
;
====================================
;
====================================
;
RECIEVE ALL THE 10BIT DATA FROM PC AND CHECK WHETHER IT'S RIGHT
;
SAVE THE 8BIT DATA TO RECV
;
TEMP1: PARITY FIGURED OUT
;
PARITY: PARITY RECEIVED
;
====================================
RECEIVEALLBIT: MOV LOOPCOUNTER
,
#0008H RECV8BIT_LOOP: LCALL RECEIVEONE
;
GET ONE BIT AND SAVE IT TO CY
RRC A
;
RIGHT MOVE THIS BIT TO A
DJNZ LOOPCOUNTER
,
RECV8BIT_LOOP
;
LOOP FOR 8 TIMES
MOV C
,
P
;
C=P(THIS DOUBLE PARITY JUST RECEIVED
CPL C
;
CHANG TO ODD PARITY
MOV TEMP1
,
C
;
STORE THE ODD PARITY TO TEMP1
MOV RECV
,
A
;
RECV=A
LCALL RECEIVEONE
;
GET PARITY BIT
MOV PARITY
,
C
;
PARITY=C
LCALL RECEIVEONE
;
PC PULL UP DATA LINE
MOV TEMP
,
C
;
RECEIVE TERMINAL BIT(1)
;
MOV P1,RECV ;JUST FOR TESTING
JNB MDATA
,
STOPBIT_ERROR
;
1==MDATA THEN RECEIVEALLBIT_ERROR
;
SEND ACK
LCALL SENDACK MOV C
,
TEMP1
;
C = PARITY FIGURED OUT
MOV A
,
#00H
;
A=OOH
RRC A
;
SAVE C TO A
MOV TEMP8BIT
,
A
;
SAVE A TO TEMP8BIT
MOV C
,
PARITY MOV A
,
#00H RRC A CJNE A
,
TEMP8BIT
,
PARITY_ERROR
;
DATA RECEIV SUCCESSFUL
MOV SCAN
,
#00H LCALL SENDALLBIT
;
IF THE PROGRAM CAN RUN HERE,INDICATE ALL IS IN GOOD CONDICION
;
SCAN 00H TO TELL PC ALL IN IN GOOD
;
MOV SCAN,#00H
;
LCALL SENDALLBIT
LJMP ENDOFSENDALLBIT
;
===================
;
THE PARITY IS WRONG ,SO KEYBOARD RECEIVED A WRONG DATA
PARITY_ERROR: CLR ERROR_LIGHT
;
MOV SCAN,#0FFH
;
LCALL SENDALLBIT
;
/==================
LJMP ENDOFSENDALLBIT
;
===================
;
STOPBIT ERROR
STOPBIT_ERROR: CLR ERROR_LIGHT_STOPBIT
;
MOV SCAN,#0FFH
;
LCALL SENDALLBIT
LJMP ENDOFSENDALLBIT ENDOFSENDALLBIT: RET
;
====================================
;
/RECIEVE ALL THE 10BIT DATA FROM PC AND CHECK WHETHER IT'S RIGHT
;
/SAVE THE 8BIT DATA TO RECV
;
====================================
;
====================================
;
SENDACK
;
====================================
SENDACK: LCALL DELAY15 CLR MDATA LCALL DELAY5 CLR MCLK LCALL DELAY40 SETB MCLK LCALL DELAY5 SETB MDATARET
;
//==================================
;
====================================
;
RECEIVE ONE BIT DATA FROM PC
;
SAVE THIS BIT TO CY
;
====================================
RECEIVEONE: LCALL DELAY20 CLR MCLK LCALL DELAY40 SETB MCLK LCALL DELAY20 MOV C
,
MDATA
;
SAVE THIS BIT OF DATA TO CY,SAVE IT TO RECV LATER
RET
;
====================================
;
/RECEIVE ONE BIT DATA FROM PC
;
====================================
;
====================================
;
INVOKE THIS FUNCTION WHEN K1 IS DOWN
;
====================================
K1DOWN: MOV SCAN
,
#01CH LCALL SENDALLBIT MOV SCAN
,
#0F0H LCALL SENDALLBIT MOV SCAN
,
#01CH LCALL SENDALLBIT
;
LCALL DELAY ;DON'T PRINT SO FAST
RETI
;
====================================
;
//INVOKE THIS FUNCTION WHEN K1 IS DOWN
;
====================================
;
====================================
;
INVOKE THIS FUNCTION WHEN K2 IS DOWN
;
====================================
K2DOWN: MOV SCAN
,
#032H LCALL SENDALLBIT MOV SCAN
,
#0F0H LCALL SENDALLBIT MOV SCAN
,
#032H LCALL SENDALLBIT
;
LCALL DELAY ;DON'T PRINT SO FAST
RETI
;
====================================
;
//INVOKE THIS FUNCTION WHEN K2 IS DOWN
;
====================================
;
====================================
;
INVOKE THIS FUNCTION WHEN K3 IS DOWN
;
====================================
K3DOWN: MOV SCAN
,
#021H LCALL SENDALLBIT MOV SCAN
,
#0F0H LCALL SENDALLBIT MOV SCAN
,
#021H LCALL SENDALLBIT
;
LCALL DELAY ;DON'T PRINT SO FAST
RETI
;
====================================
;
//INVOKE THIS FUNCTION WHEN K3 IS DOWN
;
====================================
;
====================================
;
SEND ALL 11 BIT DATA
;
====================================
SENDALLBIT:
;
THE VAR SCANCODE MUST BE SET BEFORE THE FUNCTION
;
SET CLK AND DATA LINE TO 1
INIT: SETB MCLK
;
SETB MDATA
;
;
CHECK WHETHER THE CLK AND DATA IS IN STATE THAT CAN TRANSFER DATA
CHKCLKHIGH: JNB MCLK
,
CHKCLKHIGH
;
IF CLK=0 THEN GOTO CHKCLKHIGH ELSE CONTINUE actually,this is the cache,wait for clk=1 to send the data
LCALL DELAY50
;
DELAY 50us
JNB MCLK
,
CHKCLKHIGH
;
JNB MDATA
,
CHKCLKHIGH
;
IF DATA=0 THEN GOTO CHKCLKHIGH ELSE CONTINUE
;
NOW CLK=1 FOR 50US AND DATA=1
LCALL DELAY20
;
;DELAY 20US
;
================================
;
CLK AND DATA CHECK FINISHED ,START DATA TRANSFAN
;
================================
STARTSEND: CLR NEXTBIT
;
SET START BIT ;1 PR
LCALL SENDBIT
;
SENT THE START BIT ;170 PR
;
SEND SCAN CODE
;
NOW SCAN IS SET BEFOR SENDALLBIT()
;
MOV SCAN,#035H ;LET SCAN= THE SCAN CODE OF KEY A ;2 PR
LCALL SENDSCAN
;
SEND THE SCAN CODE ;1123 PR
;
SEND PARITY ; #### COULD BE BETTER IN SENDING PARITY #########
LCALL CALPARITY MOV C
,
PARITY LCALL SENDBIT
;
140 PR
;
SEND TERMINAL SIGN
SETB NEXTBIT
;
TERMINAL SIGN=1 ;1 PR
LCALL SENDBIT
;
140 PR
LCALL DELAY50
;
DELAY 50us
LCALL DELAY20
;
DELAY 50us
;
FINISHED SENDING DATA USED 1950 PERIODS
RET
;
SENDALLBIT END
;
=====================================
;
=====================================
;
long time delay
;
=====================================
Delay: MOV R3
,
#000H MOV R4
,
#000H MOV R5
,
#0DHDelay_Loop: DJNZ R3
,
Delay_Loop DJNZ R4
,
Delay_Loop DJNZ R5
,
Delay_Loop RET
;
=====================================
;
=====================================================
;
SEND THE SCAN CODE IN VAR SCAN
;
PERIODS:1123 PR
;
=====================================================
SENDSCAN: CLR C
;
1 PR
MOV A
,
SCAN
;
2 PR
RRC A
;
MOVE RIGHT ;1 PR
LCALL SENDBIT
;
SEND 7TH ;140 PR
RRC A
;
MOVE RIGHT ;1 PR
LCALL SENDBIT
;
SEND 6TH ;140 PR
RRC A
;
MOVE RIGHT ;1 PR
LCALL SENDBIT
;
SEND 5TH ;170 PR
RRC A
;
MOVE RIGHT ;1 PR
LCALL SENDBIT
;
SEND 4TH ;140 PR
RRC A
;
MOVE RIGHT ;1 PR
LCALL SENDBIT
;
SEND 3TH ;140 PR
RRC A
;
MOVE RIGHT ;1 PR
LCALL SENDBIT
;
SEND 2TH ;140 PR
RRC A
;
MOVE RIGHT ;1 PR
LCALL SENDBIT
;
SEND 1TH ;140 PR
RRC A
;
MOVE RIGHT ;1 PR
LCALL SENDBIT
;
SEND 0TH ;140 PR
RET
;
=====================================================
;
=====================================================
;
SEND THE NEXTBIT
;
PERIODS:138 PERIODS
;
=====================================================
SENDBIT: MOV MDATA
,
C
;
1 period
LCALL DELAY20
;
42 PERIODS
CLR MCLK
;
PULL DOWN THE CLOCK LINE
LCALL DELAY40
;
82 PERIODS
SETB MCLK
;
1 PERIODS
LCALL DELAY20
;
12 PERIODS
RET
;
=====================================================
;
======================================================
;
SUB FOR DELAY 16US
;
RAM USED:R0
;
======================================================
DELAY16: MOV R0
,
#003H
;
2
DELAY16_IN:DJNZ R0
,
DELAY16_IN
;
THIS COMMAND WILL BE EXCUTE FOR 15 TIMES, 2period
RET
;
======================================================
;
======================================================
;
SUB FOR DELAY 32US
;
RAM USED:R0
;
======================================================
DELAY32: MOV R0
,
#00EH
;
DELAY32_IN:DJNZ R0
,
DELAY32_IN
;
THIS COMMAND WILL BE EXCUTE FOR 31 TIMES, 2period
RET
;
======================================================
;
======================================================
;
SUB FOR DELAY 50US correct?
;
RAM USED:R0
;
======================================================
DELAY50US: MOV R0
,
#01aH
;
DELAY50_LOOP:DJNZ R0
,
DELAY50_LOOP
;
THIS COMMAND WILL BE EXCUTE FOR 31 TIMES, 2period
RET
;
======================================================
;
======================================================
;
SUB FOR DELAY 20US
;
RAM USED:R0
;
periods:TOTAL 40PERIODS
;
======================================================
DELAY20: MOV R0
,
#013H
;
RUN 19TIMES 2period
DELAY20_IN:DJNZ R0
,
DELAY20_IN
;
THIS COMMAND WILL BE EXCUTE FOR 19 TIMES, 2period
RET
;
======================================================
;
======================================================
;
SUB FOR DELAY 5US
;
RAM USED:R0
;
periods:
;
======================================================
DELAY5: MOV R0
,
#004H
;
RUN 10TIMES ;2period
DELAY5_IN:DJNZ R0
,
DELAY5_IN
;
THIS COMMAND WILL BE EXCUTE FOR 4 TIMES, 2period
RET
;
======================================================
;
======================================================
;
SUB FOR DELAY 20US correct?
;
RAM USED:R0
;
periods:TOTAL 40PERIODS
;
======================================================
DELAY20US: MOV R0
,
#00DH
;
RUN 19TIMES 2period
DELAY20US_LOOP:DJNZ R0
,
DELAY20US_LOOP
;
THIS COMMAND WILL BE EXCUTE FOR 19 TIMES, 2period
RET
;
======================================================
;
======================================================
;
SUB FOR DELAY 50US
;
RAM USED:R0
;
PERIODS:100 PERIODS
;
======================================================
DELAY50: MOV R0
,
#0B1H DELAY50_IN:DJNZ R0
,
DELAY50_IN
;
RET
;
======================================================
;
======================================================
;
SUB FOR DELAY 30US
;
RAM USED:R0
;
PERIODS:60 PERIODS
;
======================================================
DELAY30: MOV R0
,
#01DH DELAY30_IN:DJNZ R0
,
DELAY30_IN
;
RET
;
======================================================
;
======================================================
;
SUB FOR DELAY 15US
;
RAM USED:R0
;
======================================================
DELAY15: MOV R0
,
#00EH DELAY15_IN:DJNZ R0
,
DELAY15_IN
;
RET
;
======================================================
;
======================================================
;
SUB FOR DELAY 40US
;
RAM USED:R0
;
PERIODS:80 PERIODS
;
======================================================
DELAY40: MOV R0
,
#27H DELAY40_IN:DJNZ R0
,
DELAY40_IN
;
RET
;
======================================================
;
======================================================
;
CACULATE THE PARITY OF THE CODE
;
RAM CHANGED:A,C,PARITY
;
INPUT :SCAN
;
RETURN VAR:PARITY
;
======================================================
CALPARITY: MOV A
,
SCAN MOV C
,
P CPL C MOV PARITY
,
CRET
;
======================================================
;
===================================================================================================================
ENDOFFILE:END
;
END OF THE PROGRAM