大家好,欢迎来到IT知识分享网。
一、简介
FPGA的IO扩展板是一种硬件扩展设备,用于扩展FPGA开发板的输入和输出接口,以便连接外部设备、传感器或执行特定的任务。这些扩展板通常包括一系列的物理连接接口、数字输入输出(GPIO)引脚、模拟输入输出(Analog I/O)接口等,使用户能够更灵活地定制和扩展其FPGA系统的功能。
二、实验目标
使用FPGA开发板驱动IO扩展板,按下矩阵键盘的按键数码管,显示出对应的按键编号,拨动拨码开关控制对应的LED灯。
三、硬件原理
(一)程序框图
(二)4×4矩阵键盘
首先需要对按键进行消抖,以免得不到想要的输出结果。按键消抖参考我的博文边缘检测与按键消抖-CSDN博客
对于矩阵键盘,如何确定哪个键被按下了呢?
只要确定了按键的行号和列号,就可以唯一确定哪个按键被按下。假如按键KEY10被按下,先给所有的列信号COL低电平,再给所有的行信号ROW接高电平3.3V,由于KEY10被按下,因此ROW3处电平为低,因此得以确定是哪一行。如何确定是哪一列呢?同样的先给所有的行信号高电平,然后给COL1低电平,其余列给高电平,则此时ROW3处为高电平,说明按键不在COLI列,同样的,给COL2低电平,其余列高电平,则此时ROW3为低电平,因此确定按键列号为COL2。
因此需要一个4bits的行总线和列总线,其中列为输出,行为输入。同时定义一个状态机,在初始状态时给列信号COL[3:0]赋4个0,然后去判断4个行是否为高电平,如果都是高电平,则说明没有按键被按下,若ROW[i]为低电平,则说明 i 行有按键被按下,此时切换到下一个状态。给列信号COL[3:0]赋值为1110(代表COL1接低电平,其余接高电平),然后去判断行信号ROW[3:0]是否等于1111,如果ROW[i]是低电平,则说明列COL1有按键被按下,因此确定出按键位置。如果ROW[3:0]为1111,则说明全为高电平,此时按键不在COL1列,切换到下一个状态,给列信号COL[3:0]赋值1101(代表COL2接低电平,其余接高电平),同样去判断ROW[i]是否为低,为低则按键在列COL2,不为低则切换到下一个状态,使COL3为低电平,其余为高电平。状态转换图如下:
(三)拨码开关控制LED
得到了按键的行号和列号以后,需要对其进行编号,从上到下,从左到右编号为1-16,然后传输到数码管进行显示。如何实现拨码开关控制LED灯的亮灭呢?需要使用SW作为输入信号,将此信号赋值给输出LED,这样拨码开关的开关就可以控制LED灯的高低电平变化了
(四)数码管显示
这里采用数码管静态显示方法,当按键标志信号key_flag来到时,对数码管的段选和位选进行静态赋值。
四 、程序原理
(一)4×4矩阵键盘
1.按键消抖
给一个延迟信号delay_cnt,对输入的行信号row打一拍,若在20ms内(1_000_000个时钟周期),row_reg和row保持相等,则认为输入按键信号稳定,消抖完成,输出一个按键消抖标志信号key_debounce。
//按键消抖 always @(posedge clk or negedge rst_n)begin if(!rst_n)begin delay_cnt <= 32'd0 ; row_reg <= 3'd0 ; end else begin row_reg <= row; if(row != row_reg)begin delay_cnt <= 32'd0 ; key_debounce <= 1'b0 ; end else begin if(delay_cnt == 32'd1000_000)begin delay_cnt <= 32'd0 ; key_debounce <= 1'b1 ; end else begin delay_cnt <= delay_cnt + 1'b1; key_debounce <= 1'b0 ; end end end end
2.状态转换
标志信号flag为高电平时表示该列有按键被按下,此时转到输出,为低电平时表示该列没有按键被按下,此时转到下一状态,对其他的列进行扫描。
//状态转换 always @(posedge clk or negedge rst_n)begin if(!rst_n)begin state <= 3'd0 ; row <= 4'b0000 ; end else begin case(state) 0:begin col <= 4'b0000 ; flag <= 1'b0 ;//表示没有按键被按下 if((row[3:0] != 4'b1111) && key_debounce)begin state <= 1 ;//如果行信号有低电平,且按键消抖已完成,则切换状态 col <= 4'b1110 ;//给第一列低电平,其余列高电平 end else state <=state; end 1:begin if(row[3:0] != 4'b1111) && key_debounce)begin flag <= 1'b1 ;//表示第一列有按键被按下 else begin flag <= 1'b0 ;//表示第一列没有按键被按下 state <= 2 ;//如果行全为高电平,则说明第一列没有按键被按下,切换状态 col <= 4'b1101;//重新给列赋值,使第二列为低电平 end end end 2:begin if(row[3:0] != 4'b1111) && key_debounce)begin flag <= 1'b1 ;//表示第二列有按键被按下 else begin flag <= 1'b0 ; state <= 3 ; col <= 4'b1011;//重新赋值,使第三列为低电平 end end end 3:begin if(row[3:0] != 4'b1111) && key_debounce)begin flag <= 1'b1; else begin flag <= 1'b0 ; state <= 4 ; col <= 4'b0111; end end end 4:begin if(row[3:0] != 4'b1111) && key_debounce)begin flag <= 1'b1; else begin flag <= 1'b0 ; state <= 0 ; end end end end
3.进行编号
对行列坐标进行编号,例如当行列分别为0,0时,编号为0。
//按键编号 always @(posedge clk )begin//不需要复位 if(flag)begin//有按键按下时才采取动作 case({col , row})//位拼接 8'b1110_1110:value<=4'd0 ; 8'b1110_1101:value<=4'd4 ; 8'b1110_1011:value<=4'd8 ; 8'b1110_0111:value<=4'd12 ; 8'b1101_1110:value<=4'd1 ; 8'b1101_1101:value<=4'd5 ; 8'b1101_1011:value<=4'd9 ; 8'b1101_0111:value<=4'd13 ; 8'b1011_1110:value<=4'd2 ; 8'b1011_1101:value<=4'd6 ; 8'b1011_1011:value<=4'd10 ; 8'b1011_0111:value<=4'd14 ; 8'b0111_1110:value<=4'd3 ; 8'b0111_1101:value<=4'd7 ; 8'b0111_1011:value<=4'd11 ; 8'b0111_0111:value<=4'd15 ; endcase end end
(二)按键控制LED灯
本部分代码比较简单,不再赘述。
module switch_led( input clk , input rst_n , input [7:0] switch, output [7:0] led ); always @(posedge clk or negedge rst_n) if(!rst_n) led <= 8'b0000_0000; else led <= switch; endmodule
(三)数码管显示
利用数码管静态显示。
always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0) seg_led <= 8'b0; else if (flag)begin case (value) 4'd0 : seg_led <= 8'b0; 4'd1 : seg_led <= 8'b0; 4'd2 : seg_led <= 8'b00; 4'd3 : seg_led <= 8'b00; 4'd4 : seg_led <= 8'b00011001; 4'd5 : seg_led <= 8'b00010010; 4'd6 : seg_led <= 8'b00000010; 4'd7 : seg_led <= 8'b0; 4'd8 : seg_led <= 8'b00000000; 4'd9 : seg_led <= 8'b00010000; 4'd10: seg_led <= 8'b00011000; 4'd11: seg_led <= 8'b00000011; 4'd12: seg_led <= 8'b0; 4'd13: seg_led <= 8'b00; 4'd14: seg_led <= 8'b00000110; 4'd15: seg_led <= 8'b00001110; default: seg_led <= 8'b1111_1111; endcase end else seg_led <= 8'b1111_1111; end
五、代码
(一)矩阵键盘
module key_matrix( input clk , input rst_n, input [3:0] row , output [3:0] value, output [3:0] col , output flag//有无按键按下的标志 ); reg [31:0] delay_cnt ; //延迟计数,用于按键消抖 reg [3:0] row_reg ; //列寄存信号 reg key_debounce ; //按键消抖结束标志,为0时按键消抖未结束 reg [2:0] state ; //状态转换 //按键消抖 always @(posedge clk or negedge rst_n)begin if(!rst_n)begin delay_cnt <= 32'd0 ; row_reg <= 3'd0 ; end else begin row_reg <= row; if(row != row_reg)begin delay_cnt <= 32'd0 ; key_debounce <= 1'b0 ; end else begin if(delay_cnt == 32'd1000_000)begin delay_cnt <= 32'd0 ; key_debounce <= 1'b1 ; end else begin delay_cnt <= delay_cnt + 1'b1; key_debounce <= 1'b0 ; end end end end //状态转换 always @(posedge clk or negedge rst_n)begin if(!rst_n)begin state <= 3'd0 ; row <= 4'b0000 ; end else begin case(state) 0:begin col <= 4'b0000 ; flag <= 1'b0 ;//表示没有按键被按下 if((row[3:0] != 4'b1111) && key_debounce)begin state <= 1 ;//如果行信号有低电平,且按键消抖已完成,则切换状态 col <= 4'b1110 ;//给第一列低电平,其余列高电平 end else state <=state; end 1:begin if(row[3:0] != 4'b1111) && key_debounce)begin flag <= 1'b1 ;//表示第一列有按键被按下 else begin flag <= 1'b0 ;//表示第一列没有按键被按下 state <= 2 ;//如果行全为高电平,则说明第一列没有按键被按下,切换状态 col <= 4'b1101;//重新给列赋值,使第二列为低电平 end end end 2:begin if(row[3:0] != 4'b1111) && key_debounce)begin flag <= 1'b1 ;//表示第二列有按键被按下 else begin flag <= 1'b0 ; state <= 3 ; col <= 4'b1011;//重新赋值,使第三列为低电平 end end end 3:begin if(row[3:0] != 4'b1111) && key_debounce)begin flag <= 1'b1; else begin flag <= 1'b0 ; state <= 4 ; col <= 4'b0111; end end end 4:begin if(row[3:0] != 4'b1111) && key_debounce)begin flag <= 1'b1; else begin flag <= 1'b0 ; state <= 0 ; end end end end //按键编号 always @(posedge clk )begin//不需要复位 if(flag)begin//有按键按下时才采取动作 case({col , row})//位拼接 8'b1110_1110:value<=4'd0 ; 8'b1110_1101:value<=4'd4 ; 8'b1110_1011:value<=4'd8 ; 8'b1110_0111:value<=4'd12 ; 8'b1101_1110:value<=4'd1 ; 8'b1101_1101:value<=4'd5 ; 8'b1101_1011:value<=4'd9 ; 8'b1101_0111:value<=4'd13 ; 8'b1011_1110:value<=4'd2 ; 8'b1011_1101:value<=4'd6 ; 8'b1011_1011:value<=4'd10 ; 8'b1011_0111:value<=4'd14 ; 8'b0111_1110:value<=4'd3 ; 8'b0111_1101:value<=4'd7 ; 8'b0111_1011:value<=4'd11 ; 8'b0111_0111:value<=4'd15 ; endcase end end endmodule
(二)按键控制
module switch_led( input clk , input rst_n , input [7:0] switch, output [7:0] led ); always @(posedge clk or negedge rst_n) if(!rst_n) led <= 8'b0000_0000; else led <= switch; endmodule
(三)数码管显示
module seg_sel_led( input clk , input rst_n , input value , input flag , output [3:0] sel_t , output [7:0] seg_led_t ); reg [3:0] sel ; reg [7:0] seg_led; //目的是为了增强代码的移植性 assign sel_t = ~sel ;//共阴极接法这里取反,如果共阳极这里就不取反 assign seg_led_t = ~seg_led;//共阴极接法这里取反,如果共阳极这里就不取反 always @(posedge clk or negedge rst_n)begin if(!rst_n) sel <= 4'b1111; else if(flag) sel <= 4'b0000; else sel <= 4'b1111; end always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0) seg_led <= 8'b0; else if (flag)begin case (value) 4'd0 : seg_led <= 8'b0; 4'd1 : seg_led <= 8'b0; 4'd2 : seg_led <= 8'b00; 4'd3 : seg_led <= 8'b00; 4'd4 : seg_led <= 8'b00011001; 4'd5 : seg_led <= 8'b00010010; 4'd6 : seg_led <= 8'b00000010; 4'd7 : seg_led <= 8'b0; 4'd8 : seg_led <= 8'b00000000; 4'd9 : seg_led <= 8'b00010000; 4'd10: seg_led <= 8'b00011000; 4'd11: seg_led <= 8'b00000011; 4'd12: seg_led <= 8'b0; 4'd13: seg_led <= 8'b00; 4'd14: seg_led <= 8'b00000110; 4'd15: seg_led <= 8'b00001110; default: seg_led <= 8'b1111_1111; endcase end else seg_led <= 8'b1111_1111; end endmodule endmodule
(四)顶层模块
对各模块进行连接例化。
`timescale 1ns / 1ps moudle top_IO( input sys_clk , input sys_rst_n , input [3:0] row , input [7:0] switch , output [3:0] col , output [3:0] sel_t , output [7:0] seg_led_t , output [7:0] led ); wire [3:0] key_value ; wire key_flag ; key_matrix u_key_matrix( .sys_clk (sys_clk ), .sys_rst_n (sys_rst_n), .row (row ), .col (col ), .value (value ), .flag (flag ) ); seg_sel_led u_seg_sel_led( .clk (sys_clk ), .rst_n (sys_rst_n), .value (value ), .flag (flag ), .sel_t (sel_t ), .seg_led_t (seg_led_t) ); switch_led u_switch_led( .clk (sys_clk ), .rst_n (sys_rst_n), .switch (switch ), .led (led ) ); endmodule
本文思路参考正点原子,如有侵权,请联系删除。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/132415.html