lcytms
发表于 2016-11-23 22:45:50
查看实际效果。
展示开发板和PS2键盘。
lcytms
发表于 2016-11-23 22:49:28
使用SignalTap II逻辑分析仪查看PS2实际波形。
按下数字键1时,波形如下。
对应的8位二进制为8'h69,与波形一致。
lcytms
发表于 2016-11-23 22:59:29
先从键盘矩阵模块ps2.v做起吧!
新建工程文件夹ps2。
新建ps2.v文件。
module ps2 (clk, rst_n, ps2_sclk, ps2_sda, key_value, flag);
input clk, rst_n;
input ps2_sclk;
input ps2_sda;
output reg key_value;
output reg flag;
reg ps2_data;
reg valid;
wire neg;
reg ps2_sclk_r;
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
ps2_sclk_r <= 0;
end
else
begin
ps2_sclk_r <= ps2_sclk;
end
end
assign neg = ps2_sclk_r && (~ps2_sclk);
reg num;
reg watchdog; // 1kHz
//////// transfer serial to parallel
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
num <= 0;
valid <= 0;
ps2_data <= 0;
watchdog <= 0;
end
else
begin
if (!neg)
begin
if (watchdog < 999)
begin
watchdog <= watchdog + 16'd1;
valid <= 0;
end
else
begin
watchdog <= 0;
num <= 0;
end
end
else
begin
watchdog <= 0;
case (num)
0 : num <= num + 4'd1;
1 : begin
num <= num + 4'd1;
ps2_data <= ps2_sda;
end
2 : begin
num <= num + 4'd1;
ps2_data <= ps2_sda;
end
3 : begin
num <= num + 4'd1;
ps2_data <= ps2_sda;
end
4 : begin
num <= num + 4'd1;
ps2_data <= ps2_sda;
end
5 : begin
num <= num + 4'd1;
ps2_data <= ps2_sda;
end
6 : begin
num <= num + 4'd1;
ps2_data <= ps2_sda;
end
7 : begin
num <= num + 4'd1;
ps2_data <= ps2_sda;
end
8 : begin
num <= num + 4'd1;
ps2_data <= ps2_sda;
end
9 : begin
num <= num + 4'd1;
valid <= 1;
end
10 : begin
num <= 0;
end
default : num <= 0;
endcase
end
end
end
//////////// key value decoder
reg key_store;
reg state;
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
key_store <= 'b0;
flag <= 0;
key_value <= 4'h0;
state <= 0;
end
else
begin
if (!valid)
flag <= 0;
else
begin
case (state)
0 : begin // key pressed
case (ps2_data)
8'h70 : key_store <= 1'b1; // KP0={70,F0_70}
8'h69 : key_store <= 1'b1; // KP1={69,F0_69}
8'h72 : key_store <= 1'b1; // KP2={72,F0_72}
8'h7A : key_store <= 1'b1; // KP3={7A,F0_7A}
8'h6B : key_store <= 1'b1; // KP4={6B,F0_6B}
8'h73 : key_store <= 1'b1; // KP5={73,F0_73}
8'h74 : key_store <= 1'b1; // KP6={74,F0_74}
8'h6C : key_store <= 1'b1; // KP7={6C,F0_6C}
8'h75 : key_store <= 1'b1; // KP8={75,F0_75}
8'h7D : key_store <= 1'b1; // KP9={7D,F0_7D}
8'hF0 : state <= 1;
default : key_store <= key_store;
endcase
end
1 : begin // key released
case (ps2_data)
8'h70 : begin // KP0={70,F0_70}
key_store <= 1'b0;
flag <= 1;
key_value <= 4'h0;
state <= 0;
end
8'h69 : begin // KP1={69,F0_69}
key_store <= 1'b0;
flag <= 1;
key_value <= 4'h1;
state <= 0;
end
8'h72 : begin // KP2={72,F0_72}
key_store <= 1'b0;
flag <= 1;
key_value <= 4'h2;
state <= 0;
end
8'h7A : begin // KP3={7A,F0_7A}
key_store <= 1'b0;
flag <= 1;
key_value <= 4'h3;
state <= 0;
end
8'h6B : begin // KP4={6B,F0_6B}
key_store <= 1'b0;
flag <= 1;
key_value <= 4'h4;
state <= 0;
end
8'h73 : begin // KP5={73,F0_73}
key_store <= 1'b0;
flag <= 1;
key_value <= 4'h5;
state <= 0;
end
8'h74 : begin // KP6={74,F0_74}
key_store <= 1'b0;
flag <= 1;
key_value <= 4'h6;
state <= 0;
end
8'h6C : begin // KP7={6C,F0_6C}
key_store <= 1'b0;
flag <= 1;
key_value <= 4'h7;
state <= 0;
end
8'h75 : begin // KP8={75,F0_75}
key_store <= 1'b0;
flag <= 1;
key_value <= 4'h8;
state <= 0;
end
8'h7D : begin // KP9={7D,F0_7D}
key_store <= 1'b0;
flag <= 1;
key_value <= 4'h9;
state <= 0;
end
default : state <= 0;
endcase
state <= 0;
end
endcase
end
end
end
endmodule
lcytms
发表于 2016-11-23 23:03:22
进行分析综合检查。
编写Testbench。
新建ps2_tb.v文件。
`timescale 1ns/1ps
module ps2_tb;
reg clk, rst_n;
reg ps2_sclk;
reg ps2_sda;
wire key_value;
wire flag;
ps2 dut (.clk(clk), .rst_n(rst_n), .ps2_sclk(ps2_sclk), .ps2_sda(ps2_sda), .key_value(key_value), .flag(flag));
initial
begin
clk = 1;
rst_n = 0;
ps2_sclk = 1;
ps2_sda = 1;
#200.1
rst_n = 1;
#60 ps2_sda = 0; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //start bit
#60 ps2_sda = 1; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d0, d=8'h69, num=1
#60 ps2_sda = 0; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d1
#60 ps2_sda = 0; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d2
#60 ps2_sda = 1; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d3
#60 ps2_sda = 0; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d4
#60 ps2_sda = 1; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d5
#60 ps2_sda = 1; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d6
#60 ps2_sda = 0; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d7
#60 ps2_sda = 1; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //parity bit
#60 ps2_sda = 1; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //stop bit
#2000
#60 ps2_sda = 0; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //start bit
#60 ps2_sda = 0; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d0, d=8'hF0, num=1
#60 ps2_sda = 0; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d1
#60 ps2_sda = 0; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d2
#60 ps2_sda = 0; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d3
#60 ps2_sda = 1; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d4
#60 ps2_sda = 1; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d5
#60 ps2_sda = 1; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d6
#60 ps2_sda = 1; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d7
#60 ps2_sda = 1; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //parity bit
#60 ps2_sda = 1; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //stop bit
#2000
#60 ps2_sda = 0; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //start bit
#60 ps2_sda = 1; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d0, d=8'h69, num=1
#60 ps2_sda = 0; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d1
#60 ps2_sda = 0; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d2
#60 ps2_sda = 1; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d3
#60 ps2_sda = 0; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d4
#60 ps2_sda = 1; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d5
#60 ps2_sda = 1; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d6
#60 ps2_sda = 0; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //d7
#60 ps2_sda = 1; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //parity bit
#60 ps2_sda = 1; #60 ps2_sclk = 0; #120 ps2_sclk = 1; //stop bit
#2000 $stop;
end
always #10 clk = ~clk;
endmodule
lcytms
发表于 2016-11-23 23:04:47
进行分析综合检查。
进行仿真设置。
查看仿真结果。
每次按键按下之后,数据位key_value和标志位flag都有对应数字按键1的输出。
仿真功能正确。
lcytms
发表于 2016-11-23 23:08:49
向工程文件夹添加第四讲的数码管模块seg7.v。
第十一讲 键盘矩阵也用到此模块。
module seg7 (clk, rst_n, data, sel, seg);
input clk, rst_n;
input data;
output reg sel;
output reg seg;
reg clk_1k;
parameter T1ms = 25_000; //half_width
reg count;
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
count <= 0;
clk_1k <= 1;
end
else
begin
if (count < T1ms - 1)
begin
count <= count + 20'b1;
end
else
begin
count <= 0;
clk_1k <= ~clk_1k;
end
end
end
reg state;
reg data_temp;
always @ (posedge clk_1k or negedge rst_n)
begin
if (!rst_n)
begin
sel <= 3'b000;
state <= 0;
end
else
begin
case (state)
0 : begin
sel <= 3'b000;
data_temp <= data;
state <= 1;
end
1 : begin
sel <= 3'b001;
data_temp <= data;
state <= 2;
end
2 : begin
sel <= 3'b010;
data_temp <= data;
state <= 3;
end
3 : begin
sel <= 3'b011;
data_temp <= data;
state <= 4;
end
4 : begin
sel <= 3'b100;
data_temp <= data;
state <= 5;
end
5 : begin
sel <= 3'b101;
data_temp <= data;
state <= 0;
end
default : state <= 0;
endcase
end
end
always @ (*)
begin
if (!rst_n)
begin
seg = 8'b1111_1111; //null
end
else
begin
case (data_temp)
0 : seg = 8'b1100_0000; //d0
1 : seg = 8'b1111_1001; //d1
2 : seg = 8'b1010_0100; //d2
3 : seg = 8'b1011_0000; //d3
4 : seg = 8'b1001_1001; //d4
5 : seg = 8'b1001_0010; //d5
6 : seg = 8'b1000_0010; //d6
7 : seg = 8'b1111_1000; //d7
8 : seg = 8'b1000_0000; //d8
9 : seg = 8'b1001_0000; //d9
10 : seg = 8'b1000_1000; //dA
11 : seg = 8'b1000_0011; //db
12 : seg = 8'b1100_0110; //dC
13 : seg = 8'b1010_0001; //dd
14 : seg = 8'b1000_0110; //dE
15 : seg = 8'b1000_1110; //dF
default : seg = 8'b1111_1111; //null
endcase
end
end
endmodule
lcytms
发表于 2016-11-23 23:11:08
向工程添加第十一讲 键盘矩阵中的数码管数据预处理模块pre_seg7.v。
module pre_seg7 (clk, rst_n, data, flag, data6);
input clk, rst_n;
input data;
input flag;
output reg data6;
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
data6 <= 24'h000000;
end
else
begin
if (!flag)
data6 <= data6;
else
data6 <= {data6, data};
end
end
endmodule
lcytms
发表于 2016-11-23 23:12:38
本帖最后由 lcytms 于 2016-11-23 23:15 编辑
新建分频模块divider.v。
编写divider.v模块如下。
module divider (clk, rst_n, clk_1M);
input clk,rst_n;
output reg clk_1M;
reg count;
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
clk_1M <= 0;
count <= 0;
end
else
begin
if (count <24)
count <= count + 8'd1;
else
begin
count <= 0;
clk_1M <= ~clk_1M;
end
end
end
endmodule
lcytms
发表于 2016-11-23 23:14:51
新建top.v顶层模块,将以上四个模块都加入到top.v顶层模块中进行例化。
将top.v模块设置为顶层模块。
编写top.v模块如下。
module top (clk, rst_n, ps2_sclk, ps2_sda, sel, seg);
input clk, rst_n;
input ps2_sclk, ps2_sda;
output sel;
output seg;
wire clk_1M;
wire data;
wire flag;
wire data6;
divider divider (.clk(clk), .rst_n(rst_n), .clk_1M(clk_1M));
ps2 ps2 (.clk(clk_1M), .rst_n(rst_n), .ps2_sclk(ps2_sclk), .ps2_sda(ps2_sda), .key_value(data), .flag(flag));
pre_seg7 pre_seg7 (.clk(clk_1M), .rst_n(rst_n), .data(data), .flag(flag), .data6(data6));
seg7 seg7 (.clk(clk), .rst_n(rst_n), .data(data6), .sel(sel), .seg(seg));
endmodule
lcytms
发表于 2016-11-23 23:17:10
本帖最后由 lcytms 于 2016-11-23 23:21 编辑
参照.tcl文件设置好FPGA管脚
参照EP4CE10F17C8Nzx_1.tcl文件内容,对FPGA芯片管脚进行设置。
EP4CE10F17C8Nzx_1.tcl文件相关内容如下所示。FPGA芯片配置结果如图所示。
#set_global_assignment -name FAMILY "Cyclone IV"
#set_global_assignment -name DEVICE ep4ce10f17c8n
set_location_assignment PIN_E1 -to clk
# PS2
set_location_assignment PIN_N8 -to ps2_sclk
set_location_assignment PIN_L8 -to ps2_sda
# KEY 轻触按键
set_location_assignment PIN_L3 -to key
# SEG7 x 8 七段数码管
set_location_assignment PIN_L6 -to sel
set_location_assignment PIN_N6 -to sel
set_location_assignment PIN_M7 -to sel
set_location_assignment PIN_T11 -to seg
set_location_assignment PIN_T10 -to seg
set_location_assignment PIN_T9 -to seg
set_location_assignment PIN_T8 -to seg
set_location_assignment PIN_T7 -to seg
set_location_assignment PIN_T6 -to seg
set_location_assignment PIN_T5 -to seg
set_location_assignment PIN_T4 -to seg