PLD与数字系统课程实验报告.docx
《PLD与数字系统课程实验报告.docx》由会员分享,可在线阅读,更多相关《PLD与数字系统课程实验报告.docx(16页珍藏版)》请在冰豆网上搜索。
PLD与数字系统课程实验报告
PLD与数字系统课程实验报告
实验名称:
PS/2键盘
1、实验预习部分
(一)实验目的要求:
学习PS/2的传输协议,利用实验板上的PS/2接口,实现键盘与实验开发板间的数据通信,并且将从键盘接收到的信号解码后在LCD显示屏上显示。
(二)实验理论原理:
1.PS/2协议
PS/2通讯协议是一种双向同步串行通讯协议。
通讯的两端通过Clock(时钟脚)同步,并通过DATA(数据脚)交换数据。
任何一方如果想抑制另外一方通讯时,只需要把Clock(时钟脚)拉到低电平。
如果是pc机和PS/2键盘间的通讯,则pc机必须做主机,也就是说,pc机可以抑制PS/2键盘发送数据,而PS/2键盘则不会抑制pc机发送数据。
2.PS/2与PC机的通信原理
PS/2设备的Clock(时钟脚)和DATA(数据脚)都是集电极开路的,平时都是高电平。
当PS/2设备等待发送数据时,它首先检查Clock(时钟脚)以确认其是否为高电平。
如果是低电平,则认为是pc机抑制了通讯,此时它必须缓冲需要发送的数据直到重新获得总线的控制权(一般PS/2键盘有16个字节的缓冲区,而PS/2鼠标只有一个缓冲区仅存储最后一个要发送的数据)。
如果Clock(时钟脚)为高电平,PS/2设备便开始将数据发送到pc机。
一般都是由PS/2设备产生时钟信号。
发送时一般都是按照数据帧格式顺序发送。
其中数据位在Clock(时钟脚)为高电平时准备好,在Clock(时钟脚)的下降沿被pc机读入。
PS/2设备到pc机的通讯时序如图2所示。
当时钟频率为15kHz时,从Clock(时钟脚)的上升沿到数据位转变时间至少要5μs。
数据变化到Clock(时钟脚)下降沿的时间至少也有5μs,但不能大于25μs,这是由PS/2通讯协议的时序规定的。
如果时钟频率是其它值,参数的内容应稍作调整。
上述讨论中传输的数据是指对特定键盘的编码或者对特定命令的编码。
一般采用第二套扫描码集所规定的码值来编码。
其中键盘码分为通码(make)和断码(Break)。
通码是按键接通时所发送的编码,用两位十六进制数来表示,断码通常是按键断开时所发送的编码,用四位十六进制数来表示。
(三)实验操作原理及测定内容
1、操作原理:
ps/2设备主要用于产生同步时钟信号和读写数据。
①ps/2向pc机发送一个字节
从ps/2向pc机发送一个字节可按照下面的步骤进行:
(1)检测时钟线电平,如果时钟线为低,则延时50μs;
(2)检测判断时钟信号是否为高,为高,则向下执行,为低,则转到
(1); (3)检测数据线是否为高,如果为高则继续执行,如果为低,则放弃发送(此时pc机在向ps/2设备发送数据,所以ps/2设备要转移到接收程序处接收数据); (4)延时20μs(如果此时正在发送起始位,则应延时40μs); (5)输出起始位(0)到数据线上。
这里要注意的是:
在送出每一位后都要检测时钟线,以确保pc机没有抑制ps/2设备,如果有则中止发送; (6)输出8个数据位到数据线上; (7)输出校验位; (8)输出停止位
(1); (9)延时30μs(如果在发送停止位时释放时钟信号则应延时50μs); 通过以下步骤可发送单个位:
(1)准备数据位(将需要发送的数据位放到数据线上);
(2)延时20μs; (3)把时钟线拉低; (4)延时40μs; (5)释放时钟线; (6)延时20μs。
②ps/2设备从pc机接收一个字节
由于ps/2设备能提供串行同步时钟,因此,如果pc机发送数据,则pc机要先把时钟线和数据线置为请求发送的状态。
pc机通过下拉时钟线大于100μs来抑制通讯,并且通过下拉数据线发出请求发送数据的信号,然后释放时钟。
当ps/2设备检测到需要接收的数据时,它会产生时钟信号并记录下面8个数据位和一个停止位。
主机此时在时钟线变为低时准备数据到数据线,并在时钟上升沿锁存数据。
而ps/2设备则要配合pc机才能读到准确的数据。
具体连接步骤如下:
(1)等待时钟线为高电平。
(2)判断数据线是否为低,为高则错误退出,否则继续执行。
(3)读地址线上的数据内容,共8个bit,每读完一个位,都应检测时钟线是否被pc机拉低,如果被拉低则要中止接收。
(4)读地址线上的校验位内容,1个bit。
(5)读停止位。
(6)如果数据线上为0(即还是低电平),ps/2设备继续产生时钟,直到接收到1且产生出错信号为止(因为停止位是1,如果ps/2设备没有读到停止位,则表明此次传输出错)。
(7输出应答位。
(8)检测奇偶校验位,如果校验失败,则产生错误信号以表明此次传输出现错误。
(9)延时45μs,以便pc机进行下一次传输。
读数据线的步骤如下:
(1)延时20μs;
(2)把时钟线拉低 (3)延时40μs (4)释放时钟线 (5)延时20μs (6)读数据线。
下面的步骤可用于发出应答位;
(1)延时15μs;
(2)把数据线拉低; (3)延时5μs; (4)把时钟线拉低; (5)延时40μs; (6)释放时钟线; (7)延时5μs; (8)释放数据线。
PS/2总线时序波图如下:
(4)主要实验仪器
1、XUP(FPGA:
Spartan3S400AN)实验开发板
2、电脑及一键盘
3、一个字模小软件(基于T6963c的12864液晶驱动和电路)
(五)实验操作步骤
1、学习并理解PS/2协议,用verilogHDL语言编写程序,实现pc机与PS/2端之间的对话。
在ISE10.1上编写程序,进行综合、实现,不断调试直至成功。
程序如下:
moduleps2keyboard(clk,rst,ps2_clk,ps2_data,cs1,cs2,reset,di,rw,e,dat,vee,bl);
inputclk;
inputrst;
inputps2_clk;//ps2时钟信号
inputps2_data;//ps2数据信号
outputreset,cs1,cs2,di,rw,e,vee,bl;
output[7:
0]dat;
regreset_r,cs1_r,cs2_r,di_r,rw_r,e_r,vee_r,bl_r;
reg[7:
0]dat_r;
assignreset=reset_r;
assigncs1=cs1_r;
assigncs2=cs2_r;
assigndi=di_r;
assignrw=rw_r;
assigne=e_r;
assignvee=vee_r;
assignbl=bl_r;
assigndat=dat_r;
regps2_clk_r0,ps2_clk_r1,ps2_clk_r2;
wireneg_ps2_clk;//ps2时钟下降沿标志
always@(posedgeclk)
begin
if(!
rst)
begin
ps2_clk_r0<=1'b0;
ps2_clk_r1<=1'b0;
ps2_clk_r2<=1'b0;
end
else
begin
ps2_clk_r0<=ps2_clk;
ps2_clk_r1<=ps2_clk_r0;
ps2_clk_r2<=ps2_clk_r1;
end
end
assignneg_ps2_clk=(~ps2_clk_r1)&(ps2_clk_r2);//检测ps2时钟下降沿
reg[7:
0]temp_data;//当前接收数据寄存器
reg[3:
0]num;//计数寄存器
always@(posedgeclk)
if(!
rst)
begin
num<=4'd0;
temp_data<=8'd0;
end
elseif(neg_ps2_clk)
case(num)
4'd0:
num<=num+1'b1;//第一位是起始位,不接收
4'd1:
beginnum<=num+1'b1;temp_data[0]<=ps2_data;end//bit0
4'd2:
beginnum<=num+1'b1;temp_data[1]<=ps2_data;end//bit1
4'd3:
beginnum<=num+1'b1;temp_data[2]<=ps2_data;end//bit2
4'd4:
beginnum<=num+1'b1;temp_data[3]<=ps2_data;end//bit3
4'd5:
beginnum<=num+1'b1;temp_data[4]<=ps2_data;end//bit4
4'd6:
beginnum<=num+1'b1;temp_data[5]<=ps2_data;end//bit5
4'd7:
beginnum<=num+1'b1;temp_data[6]<=ps2_data;end//bit6
4'd8:
beginnum<=num+1'b1;temp_data[7]<=ps2_data;end//bit7
4'd9:
num<=num+1'b1;//奇偶校验位
4'd10:
num<=4'd0;//停止位
default:
;
endcase
reg[7:
0]ps2_byte_r;
regkey_f0;
wireps2_state;/键盘当前状态,表示有键按下
regps2_state_r;
assignps2_state=ps2_state_r;
always@(posedgeclk)
if(!
rst)
begin
key_f0<=1'b0;
ps2_state_r<=1'b0;
end
elseif(num==4'd10)//刚接受完一个字节的数据
begin
if(temp_data==8'hf0)
key_f0<=1'b1;//表示松键
else
Begin
if(!
key_f0)//若接受到的不是f0,且之前一个数据也不是f0,则表示有键按下
begin
ps2_state_r<=1'b1;//有键按下
ps2_byte_r<=temp_data;//锁存当前键值
end
else
begin
ps2_state_r<=1'b0;
key_f0<=1'b0;
end
end//assignps2_byte=ps2_byte_r;
end
`defineL1COM_S1cs1_r<=1;cs2_r<=0;di_r<=0;rw_r<=0//写CS1指令第一步
`defineL1DAT_S1cs1_r<=1;cs2_r<=0;di_r<=1;rw_r<=0//写CS1数据第一步
`defineL2COM_S1cs1_r<=0;cs2_r<=1;di_r<=0;rw_r<=0//写CS2指令第一步
`defineL2DAT_S1cs1_r<=0;cs2_r<=1;di_r<=1;rw_r<=0//写CS2数据第一步
`defineL1COM_S2e_r<=1//写CS1指令第二步
`defineL1DAT_S2e_r<=1//写CS1数据第二步
`defineL2COM_S2e_r<=1//写CS2指令第二步
`defineL2DAT_S2e_r<=1//写CS2数据第二步
`defineL1COM_S3e_r<=0//写CS1指令第三步
`defineL1DAT_S3e_r<=0//写CS1数据第三步
`defineL2COM_S3e_r<=0//写CS2指令第三步
`defineL2DAT_S3e_r<=0//写CS2数据第三步
`defineL1COM_S4cs1_r<=0;cs2_r<=0;di_r<=0;rw_r<=0//写CS1指令第四步
`defineL1DAT_S4cs1_r<=0;cs2_r<=0;di_r<=0;rw_r<=0//写CS1数据第四步
`defineL2COM_S4cs1_r<=0;cs2_r<=0;di_r<=0;rw_r<=0//写CS2指令第四步
`defineL2DAT_S4cs1_r<=0;cs2_r<=0;di_r<=0;rw_r<=0//写CS2数据第四步
parameterLCD_DISPON=8'h3f;
parameterLCD_DISPOFF=8'h3e;
parameterLCD_STARTROW=8'hc0;/*显示起始行0,可以用LCD_STARTROW+x设置起始行。
(x<64)*/
parameterLCD_ADDRSTRY=8'hb8;/*页起始地址,可以用LCD_ADDRSTRX+x设置当前页(即行)。
(x<8)*/
parameterLCD_ADDRSTRX=8'h40;/*LCD_ADDRSTRY+x设置当前列。
(x<64)*/
reg[9:
0]addr;
reg[7:
0]q;
always@(addr)
case(addr)
10'd0:
q<=8'h00;
10'd1:
q<=8'h00;
10'd2:
q<=8'h00;
10'd3:
q<=8'h00;
10'd4:
q<=8'h00;
10'd5:
q<=8'hc0;
10'd6:
q<=8'h70;
10'd7:
q<=8'h18;
10'd8:
q<=8'h38;
10'd9:
q<=8'hf8;
10'd10:
q<=8'he0;
10'd11:
q<=8'h80;
10'd12:
q<=8'h00;
10'd13:
q<=8'h00;
10'd14:
q<=8'h00;
10'd15:
q<=8'h00;
10'd16:
q<=8'h20;
10'd17:
q<=8'h20;
10'd18:
q<=8'h30;
10'd19:
q<=8'h3c;
10'd20:
q<=8'h27;
10'd21:
q<=8'h23;
10'd22:
q<=8'h02;
10'd23:
q<=8'h02;
10'd24:
q<=8'h02;
10'd25:
q<=8'h02;
10'd26:
q<=8'h27;
10'd27:
q<=8'h3f;
10'd28:
q<=8'h3e;
10'd29:
q<=8'h38;
10'd30:
q<=8'h20;
10'd31:
q<=8'h20;//A
10'd32:
q<=8'h00;
10'd33:
q<=8'h08;
10'd34:
q<=8'hf8;
10'd35:
q<=8'hf8;
10'd36:
q<=8'hf8;
10'd37:
q<=8'h88;
10'd38:
q<=8'h88;
10'd39:
q<=8'h88;
10'd40:
q<=8'h88;
10'd41:
q<=8'h88;
10'd42:
q<=8'h88;
10'd43:
q<=8'hf8;
10'd44:
q<=8'hf8;
10'd45:
q<=8'h70;
10'd46:
q<=8'h00;
10'd47:
q<=8'h00;
10'd48:
q<=8'h20;
10'd49:
q<=8'h20;
10'd50:
q<=8'h3f;
10'd51:
q<=8'h3f;
10'd52:
q<=8'h3f;
10'd53:
q<=8'h21;
10'd54:
q<=8'h21;
10'd55:
q<=8'h21;
10'd56:
q<=8'h21;
10'd57:
q<=8'h21;
10'd58:
q<=8'h21;
10'd59:
q<=8'h31;
10'd60:
q<=8'h3f;
10'd61:
q<=8'h1e;
10'd62:
q<=8'h0c;
10'd63:
q<=8'h00;//B
10'd64:
q<=8'h00;
10'd65:
q<=8'hc0;
10'd66:
q<=8'he0;
10'd67:
q<=8'hf0;
10'd68:
q<=8'h30;
10'd69:
q<=8'h18;
10'd70:
q<=8'h08;
10'd71:
q<=8'h08;
10'd72:
q<=8'h08;
10'd73:
q<=8'h08;
10'd74:
q<=8'h08;
10'd75:
q<=8'h08;
10'd76:
q<=8'h08;
10'd77:
q<=8'h18;
10'd78:
q<=8'h30;
10'd79:
q<=8'h00;
10'd80:
q<=8'h00;
10'd81:
q<=8'h07;
10'd82:
q<=8'h0f;
10'd83:
q<=8'h1f;
10'd84:
q<=8'h38;
10'd85:
q<=8'h30;
10'd86:
q<=8'h20;
10'd87:
q<=8'h20;
10'd88:
q<=8'h20;
10'd89:
q<=8'h20;
10'd90:
q<=8'h20;
10'd91:
q<=8'h20;
10'd92:
q<=8'h10;
10'd93:
q<=8'h10;
10'd94:
q<=8'h0c;
10'd95:
q<=8'h00;//C
default:
;
endcase
reg[29:
0]cnt;
wireen_5ms;
regen_5ms_r;
assignen_5ms=en_5ms_r;
always@(posedgeclk)
if(!
rst)
begin
cnt<=30'd0;
en_5ms_r<=1'b0;
end
elseif(cnt<=50000-1)
begin
cnt<=cnt+1'b1;
en_5ms_r<=1'b0;
end
else
begin
cnt<=30'd0;
en_5ms_r<=1'b1;
end
reg[5:
0]count0;
reg[4:
0]initcount;
reg[2:
0]setxycount;
reg[1:
0]senddatacount;
reg[4:
0]i;
always@(posedgeclk)
if(!
rst)
begin
count0<=6'd0;
initcount<=5'd0;
setxycount<=3'd0;
senddatacount<=2'd0;
addr<=10'd0;
i<=5'd0;
end
elseif(en_5ms)
begin
if(count0==6'd0)
begin
case(initcount)
5'd0:
beginvee_r<=0;bl_r<=1;end
5'd1:
reset_r<=0;
5'd2:
reset_r<=1;
5'd3:
begin`L1COM_S1;dat_r<=LCD_DISPON;end
5'd4:
begin`L1COM_S2;end
5'd5:
begin`L1COM_S3;end
5'd6:
begin`L1COM_S4;end
5'd7:
begin`L1COM_S1;dat_r<=LCD_STARTROW;end
5'd8:
begin`L1COM_S2;end
5'd9:
begin`L1COM_S3;end
5'd10:
begin`L1COM_S4;end
5'd11:
begin`L2COM_S1;dat_r<=LCD_DISPON;end
5'd12:
begin`L2COM_S2;end
5'd13:
begin`L2COM_S3;end
5'd14:
begin`L2COM_S4;end
5'd15:
begin`L2COM_S1;dat_r<=LCD_STARTROW;end
5'd16:
begin`L2COM_S2;end
5'd17:
begin`L2COM_S3;end
5'd18:
begin`L2COM_S4;count0<=6'd1;i<=5'd0;end//EndoftheInitiation
default:
;
endcase
initcount<=initcount+1'b1;
end
elseif(count0==6'd1)//s
begin
case(setxycount)
3'd0:
begin`L1COM_S1;dat_r<=LCD_ADDRSTRY;end
3'd1:
begin`L1COM_S2;end
3'd2:
begin`L1COM_S3;end
3'd3:
begin`L1COM_S4;end
3'd4:
begin`L1COM_S1;dat_r<=LCD_ADDRSTRX;end
3'd5:
begin`L1COM_S2;end
3'd6:
begin`L1COM_S3;end
3'd7:
begin`L1COM_S4;count0<=6'd2;senddatacount<=3'd0;i<=5'd0;end
default:
;
endcase
setxycount<=setxycount+1'b1;
addr<=10'd0;
case(ps2_byte_r)
8'h1c:
addr<=10'd0;//A
8'h32:
addr<=10'd32;//B
8'h21:
addr<=10'd64;//C
default:
;
endcase
end
elseif(count0==6'd2)
begin
if(i<=15)
begin
case(senddatacount)
2'd0:
begin`L1DAT_S1;dat_r<=q;end
2'd1:
begin`L1DAT_S2;end
2'd2:
begin`L1DAT_S3;end
2'd3:
begin`L1DAT_S4;addr<=addr+1'b1;i<=i+1'b1;end
default:
;
endcase
senddatacount<=senddatacount+1'b1;
end
else
begin
i<=5'd0;
setxycount<=3'd0;
count0<=6'd3;
end
end
elseif(count0==6'd3)
begin
case(setxycount)
3'd0:
begin`L1COM_S1;dat_