目录
已成功上板实现!
顶层:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2022/12/28 16:28:25
// Design Name:
// Module Name: top
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module top(
input clk_in_p,
input clk_in_n,
input I_rst, // 复位信号(低有效)
input [3:0] I_col,
output [3:0] O_raw,
//seg_led interface
output [7:0] seg_sel , // 数码管位选信号
output [7:0] seg_led // 数码管段选信号
);
wire I_clk;
IBUFDS clkinl_buf(
.O (I_clk),
.I (clk_in_p),
.IB (clk_in_n)
);
wire [4:0]S_led_data;
martix u_martix(
.I_sys_clk (I_clk),
.I_sys_rst_n(I_rst),
.I_key_in (I_col),
.O_key_out (O_raw),
.O_led (S_led_data)
);
seg_led u_seg_led(
.I_clk (I_clk),
.I_rst (!I_rst),
.I_data (S_led_data),
.O_seg_sel(seg_sel),
.O_seg_led(seg_led)
);
endmodule
矩阵键盘(消抖):
module martix
#(
parameter CNT_MAX=24'd2000_000//扫描时间 20ms
)
(
input wire I_sys_clk ,
input wire I_sys_rst_n ,
input wire [3:0] I_key_in ,//从键盘输入信号
output reg [3:0] O_key_out , //输出信号到板子
output reg [4:0] O_led
);
reg [31:0] cnt ;
reg cnt_clk ;
reg [1:0] q ;
always @ (posedge I_sys_clk or negedge I_sys_rst_n)begin
if(!I_sys_rst_n)
cnt<=32'd0;
else if (cnt==CNT_MAX)
cnt<=32'd0;
else cnt<=cnt+32'd1;
end
always @ (posedge I_sys_clk or negedge I_sys_rst_n)begin
if(!I_sys_rst_n)begin
cnt_clk<=1'b0;
end else if (cnt==CNT_MAX-16'd1)begin
cnt_clk<=1'b1;
end else begin
cnt_clk<=1'd0;
end
end
always @ (posedge cnt_clk or negedge I_sys_rst_n)begin
if(!I_sys_rst_n)begin
O_key_out <= 4'b1110;
end else begin
O_key_out <= {O_key_out[2:0],O_key_out[3]};
end
end
always @ (posedge cnt_clk or negedge I_sys_rst_n)begin
if(!I_sys_rst_n)begin
O_led<= 5'd16;
end else begin
case ({O_key_out,S_key_in})
//按下第1个按键后则全灭,0000 -> 1111,按下第16个按键后全部的灯亮,1111 -> 0000。
//每一行扫描信号,对应的扫描信号灯输出
8'b1110_1110:begin O_led<=5'd0; end
8'b1110_1101:begin O_led<=5'd1; end
8'b1110_1011:begin O_led<=5'd2; end
8'b1110_0111:begin O_led<=5'd3; end
8'b1101_1110:begin O_led<=5'd4; end
8'b1101_1101:begin O_led<=5'd5; end
8'b1101_1011:begin O_led<=5'd6; end
8'b1101_0111:begin O_led<=5'd7; end
8'b1011_1110:begin O_led<=5'd8; end
8'b1011_1101:begin O_led<=5'd9; end
8'b1011_1011:begin O_led<=5'd10; end
8'b1011_0111:begin O_led<=5'd11; end
8'b0111_1110:begin O_led<=5'd12; end
8'b0111_1101:begin O_led<=5'd13; end
8'b0111_1011:begin O_led<=5'd14; end
8'b0111_0111:begin O_led<=5'd15; end
default: begin O_led<=5'd16; end//没有按键按下灯不亮
endcase
end
end
endmodule
数码管:
`timescale 1ns / 1ps
module seg_led(
input I_clk , // 时钟信号
input I_rst , // 复位信号
input [31:0] I_data , // 8 位数码管要显示的数值
output reg [7:0] O_seg_sel ,// 数码管位选,最左侧数码管为最高位
output reg [7:0] O_seg_led // 数码管段选
);
//每当计数器对数码管驱动时钟计数时间达 1ms,输出一个时钟周期的脉冲信号
reg [15:0] S_cnt0;
reg S_flag;
always @ (posedge I_clk) begin
if (I_rst) begin
S_cnt0 <= 16'b0;
S_flag <= 1'b0;
end else if (S_cnt0 < 99_999) begin//(1000/(1/100) = 100_000)
S_cnt0 <= S_cnt0 + 1'b1;
S_flag <= 1'b0;
end else begin
S_cnt0 <= 16'b0;
S_flag <= 1'b1;
end
end
//S_cnt_sel 从 0 计数到 7,用于选择当前处于显示状态的数码管
reg [3:0] S_cnt_sel;
always @ (posedge I_clk) begin
if (I_rst)begin
S_cnt_sel <= 3'b0;
end else if(S_flag) begin//输出1ms标志信号
if(S_cnt_sel < 3'd7)begin
S_cnt_sel <= S_cnt_sel + 1'b1;
end else begin
S_cnt_sel <= 3'b0;
end
end else begin
S_cnt_sel <= S_cnt_sel;
end
end
//控制数码管位选信号,使 8 位数码管轮流显示
reg [3:0] S_num_disp;
always @ (posedge I_clk) begin
if(I_rst) begin
O_seg_sel <= 8'b11111111; //位选信号低电平有效
S_num_disp <= 4'b0; //数码管显示数据
end else begin
case (S_cnt_sel)
3'd0 :begin
O_seg_sel <= 8'b11111110; //显示数码管最低
S_num_disp <= I_data[3:0] ; //显示的数据
end
3'd1 :begin
O_seg_sel <= 8'b11111101; //显示数码管第 1 位
S_num_disp <= I_data[7:4] ;
end
3'd2 :begin
O_seg_sel <= 8'b11111011; //显示数码管第 2 位
S_num_disp <= I_data[11:8];
end
3'd3 :begin
O_seg_sel <= 8'b11110111; //显示数码管第 3 位
S_num_disp <= I_data[15:12];
end
3'd4 :begin
O_seg_sel <= 8'b11101111; //显示数码管第 4 位
S_num_disp <= I_data[19:16];
end
3'd5 :begin
O_seg_sel <= 8'b11011111; //显示数码管第 5 位
S_num_disp <= I_data[23:20];
end
3'd6 :begin
O_seg_sel <= 8'b10111111; //显示数码管第 5 位
S_num_disp <= I_data[27:24];
end
3'd7 :begin
O_seg_sel <= 8'b01111111; //显示数码管第 5 位
S_num_disp <= I_data[31:28];
end
default :begin
O_seg_sel <= 8'b11111111;
S_num_disp <= 4'b0;
end
endcase
end
end
//控制数码管段选信号,显示字符(共阴极)
always @ (posedge I_clk) begin
if (I_rst)
O_seg_led <= 8'd0;
else begin
case (S_num_disp)
4'd0 : O_seg_led <= {8'b00111111}; //显示数字 0
4'd1 : O_seg_led <= {8'b00000110}; //显示数字 1
4'd2 : O_seg_led <= {8'b01011011}; //显示数字 2
4'd3 : O_seg_led <= {8'b01001111}; //显示数字 3
4'd4 : O_seg_led <= {8'b01100110}; //显示数字 4
4'd5 : O_seg_led <= {8'b01101101}; //显示数字 5
4'd6 : O_seg_led <= {8'b01111101}; //显示数字 6
4'd7 : O_seg_led <= {8'b00000111}; //显示数字 7
4'd8 : O_seg_led <= {8'b01111111}; //显示数字 8
4'd9 : O_seg_led <= {8'b01101111}; //显示数字 9
4'd10 : O_seg_led <= {8'b01110111}; //显示a
4'd11 : O_seg_led <= {8'b01111100}; //显示b
4'd12 : O_seg_led <= {8'b00111001}; //显示c
4'd13 : O_seg_led <= {8'b01011110}; //显示d
4'd14 : O_seg_led <= {8'b01111001}; //显示e
4'd15 : O_seg_led <= {8'b01110001}; //显示f
default:
O_seg_led <= {8'b00000000};//不显示任何字符
endcase
end
end
endmodule
矩阵键盘(持续输出或只输出一次)
/*
因使用4x4矩阵按键,通过扫描方法实现,所以这里使用状态机实现,共分为4种状态
在其中的某一状态时间里,对应的4个按键相当于独立按键,可按独立按键的周期采样法采样
周期采样时每隔20ms采样一次,对应这里状态机每隔20ms循环一次,每个状态对应5ms时间
对矩阵按键实现原理不明白的,请去了解矩阵按键实现原理
*/
module Array_KeyBoard #
(
//250_000//为了缩短仿真用时,仿真的时候用5;实际测试的时候用250_000
parameter CNT_200HZ = 5
)
(
input clk ,//50MHz系统时钟
input rst_n ,//系统复位,低电平有效
input [3:0] col ,//矩阵键盘列输入
output reg [3:0] row ,//矩阵键盘行输出
output reg [15:0] key_out ,//按键值(只要按键按下就一直输出)
output [15:0] key_pulse //按键值(不管按键按下多长时间,只输出一次)
);
localparam STATE0 = 2'b00;//四行对应四个状态
localparam STATE1 = 2'b01;//四行对应四个状态
localparam STATE2 = 2'b10;//四行对应四个状态
localparam STATE3 = 2'b11;//四行对应四个状态
//计数器计数分频实现5ms周期信号clk_200hz
reg [15:0] cnt;
reg clk_200hz;
always@(posedge clk or negedge rst_n) begin //复位时计数器cnt清零,clk_200hz信号起始电平为低电平
if(!rst_n) begin
cnt <= 16'd0;
clk_200hz <= 1'b0;
end else begin
if(cnt >= ((CNT_200HZ>>1) - 1)) begin //数字逻辑中右移1位相当于除2
cnt <= 16'd0;
clk_200hz <= ~clk_200hz; //clk_200hz信号取反
end else begin
cnt <= cnt + 1'b1;
clk_200hz <= clk_200hz;
end
end
end
reg [1:0] c_state;
//状态机根据clk_200hz信号在4个状态间循环,每个状态对矩阵按键的行接口单行有效
always@(posedge clk_200hz or negedge rst_n) begin
if(!rst_n) begin
c_state <= STATE0;
row <= 4'b1110;
end else begin
case(c_state)
//状态c_state跳转及对应状态下矩阵按键的row输出
STATE0: begin c_state <= STATE1; row <= 4'b1101; end
STATE1: begin c_state <= STATE2; row <= 4'b1011; end
STATE2: begin c_state <= STATE3; row <= 4'b0111; end
STATE3: begin c_state <= STATE0; row <= 4'b1110; end
default:begin c_state <= STATE0; row <= 4'b1110; end
endcase
end
end
reg [15:0] key,key_r;
//因为每个状态中单行有效,通过对列接口的电平状态采样得到对应4个按键的状态,依次循环
always@(negedge clk_200hz or negedge rst_n) begin
if(!rst_n) begin
key_out <= 16'hffff; key_r <= 16'hffff; key <= 16'hffff;
end else begin
case(c_state)
//采集当前状态的列数据赋值给对应的寄存器位
//对键盘采样数据进行判定,连续两次采样低电平判定为按键按下
STATE0: begin key_out[ 3: 0] <= key_r[ 3: 0]|key[ 3: 0]; key_r[ 3: 0] <= key[ 3: 0]; key[ 3: 0] <= col; end
STATE1: begin key_out[ 7: 4] <= key_r[ 7: 4]|key[ 7: 4]; key_r[ 7: 4] <= key[ 7: 4]; key[ 7: 4] <= col; end
STATE2: begin key_out[11: 8] <= key_r[11: 8]|key[11: 8]; key_r[11: 8] <= key[11: 8]; key[11: 8] <= col; end
STATE3: begin key_out[15:12] <= key_r[15:12]|key[15:12]; key_r[15:12] <= key[15:12]; key[15:12] <= col; end
default:begin key_out <= 16'hffff; key_r <= 16'hffff; key <= 16'hffff; end
endcase
end
end
reg [15:0] key_out_r;
always @ ( posedge clk or negedge rst_n )
if (!rst_n) key_out_r <= 16'hffff;
else key_out_r <= key_out; //将前一刻的值延迟锁存
assign key_pulse= key_out_r & ( ~key_out); //通过前后两个时刻的值判断
endmodule
module Decoder
(
input clk,
input rst_n,
input [15:0] key_pulse,
output reg [7:0] seg_data //表示按下的按键数是多少
);
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
seg_data <= 8'h00; //清零
end
else if(rst_n)
begin case(key_pulse) //key_pulse脉宽等于clk_in的周期,用于表示摁下第几个按键
16'h0001: seg_data <= 8'h01; //1
16'h0002: seg_data <= 8'h02; //2
16'h0004: seg_data <= 8'h03; //3
16'h0008: seg_data <= 8'h04; //4
16'h0010: seg_data <= 8'h05; //5
16'h0020: seg_data <= 8'h06; //6
16'h0040: seg_data <= 8'h07; //7
16'h0080: seg_data <= 8'h08; //8
16'h0100: seg_data <= 8'h09; //9
16'h0200: seg_data <= 8'h10; //10
16'h0400: seg_data <= 8'h11; //11
16'h0800: seg_data <= 8'h12; //12
16'h1000: seg_data <= 8'h13; //13
16'h2000: seg_data <= 8'h14; //14
16'h4000: seg_data <= 8'h15; //15
16'h8000: seg_data <= 8'h16; //16
default: seg_data <= 8'h00; //无按键按动
endcase
end
end
endmodule