资讯详情

【正点原子FPGA连载】 第三十章 IO扩展模块实验-摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0

1)实验平台:正点原子领航者ZYNQ开发板 2)平台采购地址:https://item.taobao.com/item.htm?&id=606160108761 3)全套实验源码 手册 视频下载地址:http://www.openedv.com/thread-301505-1-1.html 4)正点原子FPGA感兴趣的同学可以加群讨论:99424016 5)关注正点原子微信官方账号,获取最新信息更新 在这里插入图片描述

第三十章 IO扩展模块实验

随着时间的推移,正点原子 FPGA 开发板的款式越来越多,外设也越来越丰富。从简单的按键流水灯到复杂的光口网口,基本上可以说是满足了广大需求 FPGA 工程师学习和项目开发。但在实际的项目开发过程中,往往会出现一些按钮,LED 灯、数码管、拨码开关等基本设备不足,因此我们正点原子专门推出 IO 本模块旨在帮助您丰富按键,LED 灯具、数码管、拨码开关等基本设备,方便大家灵活开发实际项目。 本章包括以下几个部分: 30.1 简介 30.2 实验任务 30.3 硬件设计 30.4 软件设计 30.5 下载验证 30.1 简介 正点原子推出 IO 包括扩展板 8 个 LED、一个 x8 拨码开关,四个八段数字管和一个 4x4 的矩阵 键盘。八颗LED可以使用FPGA单独控制也可与拨码开关配合使用,当然拨码开关也可用于控制其它外设。四个八段数码管也可以使用 FPGA 当开发板上的数字管不够时,可以单独控制 IO 用扩展板代替。最后就是 4x4 矩阵键盘,共 16 个按键可以提供非常灵活的按键控制,当开发板上的按键不够用的时候就可以使用 IO 扩展板提供额外的按键控制。 30.2 实验任务 本节实验任务是通过拨码开关控制 IO 扩展板上的 LED 灯亮灭,然后在数码管上显示矩阵键盘的按键号。 30.3 硬件设计 IO 扩展板模块的原理图如下:

图 7.5.13.1 IO扩展板硬件原理图 从上图可以看出,IO 8个扩展口模块LED灯是共阴极接法,其8个阳极全部引出,可通过 FPGA(其 他的单片机芯片也可以)这八个 LED 控制灯。拨码开关(Switch)当拨码开关断开时,它也是一种共阴极连接另一端全部上拉。 SW0~SW7 都是高电平,当拨码开关关闭合时 SW0~SW7 全部是低电平。我们可以使用拨码开关来控制其他一些外设。例如,在本节实验中,我们可以使用拨码开关来控制颗粒 LED 灯。大家都很熟悉数码管,IO 扩展模块的 4 八段数字管的位选也是共阴极接法,可以使用引出位选信号和段选信号 FPGA(其他单片机芯片也可以)控制。矩阵键盘是由的 16 由按钮组成的矩阵分为四行四列, 四行都是 3.3V 上拉,四列全部引出端口 8 一个端口(四行四列 8 所有端口都可以连接到 FPGA上(其他单片机芯片也可以)可以通过 FPGA(其它单片机芯片也可以)扫描矩阵键盘,判断哪个按钮被按下,下面将详细说明扫描方法。最后是一排 20x2 我们所有的端口都连接到这个针上,每个人都在使用它 IO 扩展板时,可将排针插入正原子 FPGA 开发板的扩展口上去,这样每个端口都可以和谐 FPGA IO 引脚相连。 IO 扩展板实物图如下图所示 所示。

图 7.5.13.2 实物图 本实验中,IO 膨胀板管脚的分布如下表所示。 表 30.3.1引脚分配表 sys_clk input U18 频率:50Mhz sys_rst_n input N16 系统复位,低电平有效 sel_t[0] output P19 选择数字管位信号sel_t[0] sel_t[1] output T20 选择数字管位信号sel_t[1] sel_t[2] output M20 选择数字管位信号sel_t[2] sel_t[3] output K14 选择数字管位信号sel_t[3] seg_led_t[0] output J14 数字管a段选择信号seg_led_t[0] seg_led_t[1] output N18 选择数码管b段的信号seg_led_t[1] seg_led_t[2] output M19 选择数字管c段的信号seg_led_t[2] seg_led_t[3] output L16 数字管d段选择信号seg_led_t[3] seg_led_t[4] output M15 选择数字管e段的信号seg_led_t[4] seg_led_t[5] output N20 选择数码管f段的信号seg_led_t[5] seg_led_t[6] output U20 选择数字管g段的信号seg_led_t[6] seg_led_t[7] output U19 选择数码管h段的信号seg_led_t[7] led[0] output N17 LED灯 led[1] output P18 LED灯 led[2] output V20 LED灯 led[3] output W20 LED灯 led[4] output R17 LED灯 led[5] output R16 LED灯 led[6] output W19 LED灯 led[7] output W18 LED灯 swi[0] input V17 拨码开关 swi[1] input V18 拨码开关 swi[2] input T17 拨码开关 swi[3] input R18 拨码开关 swi[4] input Y18 拨码开关 swi[5] input Y19 拨码开关 swi[6] input P15 拨码开关 swi[7] input P16 拨码开关 key_row[0] input T14 行扫描矩阵键盘 key_row[1] input U17 行扫描矩阵键盘 key_row[2] input Y16 行扫描矩阵键盘 key_row[3] input T15 行扫描矩阵键盘 key_col[0] output W16 扫描矩阵键盘列 key_col[1] output T16 扫描矩阵键盘列 key_col[2] output Y17 扫描矩阵键盘列 key_col[3] output V16 扫描矩阵键盘列 对应的XDC约束语句如下:

set_property -dict { 
        PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports sys_clk] set_property -dict { 
        PACKAGE_PIN N16 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]  set_property -dict { 
        PACKAGE_PIN J14 IOSTANDARD LVCMOS33} [get_ports seg_led_t[0]] set_property -dict { 
        /span>PACKAGE_PIN N18 IOSTANDARD LVCMOS33} [get_ports seg_led_t[1]] set_property -dict { 
         PACKAGE_PIN M19 IOSTANDARD LVCMOS33} [get_ports seg_led_t[2]] set_property -dict { 
         PACKAGE_PIN L16 IOSTANDARD LVCMOS33} [get_ports seg_led_t[3]] set_property -dict { 
         PACKAGE_PIN M15 IOSTANDARD LVCMOS33} [get_ports seg_led_t[4]] set_property -dict { 
         PACKAGE_PIN N20 IOSTANDARD LVCMOS33} [get_ports seg_led_t[5]] set_property -dict { 
         PACKAGE_PIN U20 IOSTANDARD LVCMOS33} [get_ports seg_led_t[6]] set_property -dict { 
         PACKAGE_PIN U19 IOSTANDARD LVCMOS33} [get_ports seg_led_t[7]] set_property -dict { 
         PACKAGE_PIN P19 IOSTANDARD LVCMOS33} [get_ports sel_t[0]] set_property -dict { 
         PACKAGE_PIN T20 IOSTANDARD LVCMOS33} [get_ports sel_t[1]] set_property -dict { 
         PACKAGE_PIN M20 IOSTANDARD LVCMOS33} [get_ports sel_t[2]] set_property -dict { 
         PACKAGE_PIN K14 IOSTANDARD LVCMOS33} [get_ports sel_t[3]] set_property -dict { 
         PACKAGE_PIN T14 IOSTANDARD LVCMOS33} [get_ports key_row[0]] set_property -dict { 
         PACKAGE_PIN U17 IOSTANDARD LVCMOS33} [get_ports key_row[1]] set_property -dict { 
         PACKAGE_PIN Y16 IOSTANDARD LVCMOS33} [get_ports key_row[2]] set_property -dict { 
         PACKAGE_PIN T15 IOSTANDARD LVCMOS33} [get_ports key_row[3]] set_property -dict { 
         PACKAGE_PIN W16 IOSTANDARD LVCMOS33} [get_ports key_col[0]] set_property -dict { 
         PACKAGE_PIN T16 IOSTANDARD LVCMOS33} [get_ports key_col[1]] set_property -dict { 
         PACKAGE_PIN Y17 IOSTANDARD LVCMOS33} [get_ports key_col[2]] set_property -dict { 
         PACKAGE_PIN V16 IOSTANDARD LVCMOS33} [get_ports key_col[3]] set_property -dict { 
         PACKAGE_PIN N17 IOSTANDARD LVCMOS33} [get_ports led[0]] set_property -dict { 
         PACKAGE_PIN P18 IOSTANDARD LVCMOS33} [get_ports led[1]] set_property -dict { 
         PACKAGE_PIN V20 IOSTANDARD LVCMOS33} [get_ports led[2]] set_property -dict { 
         PACKAGE_PIN W20 IOSTANDARD LVCMOS33} [get_ports led[3]] set_property -dict { 
         PACKAGE_PIN R17 IOSTANDARD LVCMOS33} [get_ports led[4]] set_property -dict { 
         PACKAGE_PIN R16 IOSTANDARD LVCMOS33} [get_ports led[5]] set_property -dict { 
         PACKAGE_PIN W19 IOSTANDARD LVCMOS33} [get_ports led[6]] set_property -dict { 
         PACKAGE_PIN W18 IOSTANDARD LVCMOS33} [get_ports led[7]] set_property -dict { 
         PACKAGE_PIN V17 IOSTANDARD LVCMOS33} [get_ports swi[0]] set_property -dict { 
         PACKAGE_PIN V18 IOSTANDARD LVCMOS33} [get_ports swi[1]] set_property -dict { 
         PACKAGE_PIN T17 IOSTANDARD LVCMOS33} [get_ports swi[2]] set_property -dict { 
         PACKAGE_PIN R18 IOSTANDARD LVCMOS33} [get_ports swi[3]] set_property -dict { 
         PACKAGE_PIN Y18 IOSTANDARD LVCMOS33} [get_ports swi[4]] set_property -dict { 
         PACKAGE_PIN Y19 IOSTANDARD LVCMOS33} [get_ports swi[5]] set_property -dict { 
         PACKAGE_PIN P15 IOSTANDARD LVCMOS33} [get_ports swi[6]] set_property -dict { 
         PACKAGE_PIN P16 IOSTANDARD LVCMOS33} [get_ports swi[7]] 

30.4 程序设计 根据实验任务,我们画出程序框图,整个程序框架由三个子模块构成,第一个模块是矩阵键盘扫描模块,通过行列扫描检测按键的按下情况,并将按下的那个按键编号传递给数码管模块。数码管模块会把按键的编号显示在数码管上。拨码开关模块其实是和LED模块连在一起的,在代码中我们会直接把拨码开关的值赋给LED灯,因此当拨码开关断开swi端口被上拉,LED 灯点亮,反之LED灯熄灭。系统框图如下图所示:

图 7.5.13.1 程序框图 各模块端口及信号连接如下图所示:

图 7.5.13.2 RTL视图 由上图可知,FPGA 部分包括四个模块,顶层模块(top_matrix_keyboard)、矩阵键盘扫描模块(key_4x4)、数码管显示模块(seg_led)、拨码开关控制 led 灯模块(swi_led)。在顶层模块中完成对其它三个模块的例化,并实现各模块之间的信号传递。 1)顶层模块(top_matrix_keyboard):顶层模块主要是对其它三个子模块进行例化,实现子模块间的信号连接。 2)矩阵键盘扫描模块(key_4x4):矩阵键盘扫描模块主要是对IO扩展板上的矩阵键盘进行行列扫描, 定位出哪一个按键被按下并将其对应的编号传递给数码管显示模块。 3)数码管显示模块(seg_led):接收矩阵键盘扫描模块(key_4x4)传递出的按键编号值并将数据显示出来。 4)拨码开关模块(swi_led):主要检测拨码开关的开合状态,并将状态值赋给 led 灯控制 led 灯的亮灭。 顶层模块的代码如下:

1   module top_matrix_keyboard(
2       input         sys_clk    ,
3       input         sys_rst_n  ,
4       input   [3:0] key_row    ,
5       input   [7:0] swi        ,
6       output  [3:0] key_col    ,
7       output  [3:0] sel_t      ,
8       output  [7:0] seg_led_t  ,
9       output  [7:0] led
10  
11      );
12  
13  //wire define
14  wire [3:0]    key_value    ;
15  wire          key_flag     ;
16  
17  //*****************************************************
18  //** main code 
19  //*****************************************************
20  
21  //矩阵键盘扫描模块
22  key_4x4    u_key_4x4(
23      .sys_clk    (sys_clk  ),
24      .sys_rst_n  (sys_rst_n),
25      .key_row    (key_row  ),
26      .key_col    (key_col  ),
27      .key_value  (key_value),
28      .key_flag   (key_flag )
29      );
30  
31  //数码管显示模块
32  seg_led    u_seg_led(
33      .clk       (sys_clk  ),
34      .rst_n     (sys_rst_n),
35      .key_value (key_value),
36      .key_flag  (key_flag ),
37      .sel_t     (sel_t    ),
38      .seg_led_t (seg_led_t)
39      );
40  
41  //拨码开关模块
42  swi_led     u_swi_led(
43      .clk     (sys_clk  ),
44      .rst_n   (sys_rst_n),
45      .swi     (swi      ),
46      .led     (led      )
47  );
48  endmodule

顶层模块主要就是例化三个子模块,在这里就不作过多介绍了,下面我们直接开始看矩阵键盘扫描模块,矩阵键盘扫描模块的代码如下:

1   module key_4x4(
2       input                sys_clk   ,   //50MHZ
3       input                sys_rst_n ,
4       input       [3:0]    key_row   ,   //行
5       output reg  [3:0]    key_col   ,   //列
6       output reg  [3:0]    key_value ,   //键值
7       output reg           key_flag
8   
9   );
10  
11  //reg define
12  reg [2:0] state       ;  //状态标志
13  reg [3:0] key_col_reg ;  //寄存扫描列值
14  reg [3:0] key_row_reg ;  //寄存扫描行值
15  reg [31:0]delay_cnt   ;
16  reg [3:0] key_reg     ;
17  reg       key_flag_row;  //消抖完成标志
18  
19  //*****************************************************
20  //** main code 
21  //*****************************************************
22  
23  always @(posedge sys_clk or negedge sys_rst_n) begin 
24      if (!sys_rst_n) begin 
25          key_reg   <= 4'b1;
26          delay_cnt <= 32'd0;
27      end
28      else begin
29          key_reg <= key_row;
30              if(key_reg != key_row)             
31                  delay_cnt <= 32'd1000000;      
32              else if(key_reg == key_row) begin  
33                  if(delay_cnt > 32'd0)
34                      delay_cnt <= delay_cnt - 1'b1;
35                  else
36                      delay_cnt <= delay_cnt;
37              end           
38      end   
39  end
40  
41  always @(posedge sys_clk or negedge sys_rst_n) begin 
42      if (!sys_rst_n)  
43          key_flag_row  <= 1'b0;              
44      else begin
45              if(delay_cnt == 32'd1)    
46                  key_flag_row  <= 1'b1;
47              else 
48                  key_flag_row  <= 1'b0;
49      end   
50  end
51  
52  always @(posedge sys_clk or negedge sys_rst_n)
53      if(!sys_rst_n) begin
54      key_col<=4'b0000;
55      state<=0;
56      end
57      else begin 
58      case (state)
59          0: 
60              begin
61                  key_col[3:0]<=4'b0000;
62                  key_flag<=1'b0;
63                  if((key_row[3:0]!=4'b1111)&&(key_flag_row)) begin   
64                      state<=1;
65                      key_col[3:0]<=4'b1110;
66                      end 
67                  else 
68                      state<=0;
69              end
70          1: 
71              begin
72                      if(key_row[3:0]!=4'b1111) 
73                      state<=5;
74                      else  begin
75                      state<=2;
76                      key_col[3:0]<=4'b1101; 
77                      end
78              end  
79          2:
80              begin    
81                      if(key_row[3:0]!=4'b1111) 
82                      state<=5;
83                      else  begin               
84                      state<=3;
85                      key_col[3:0]<=4'b1011;
86                      end  
87              end
88          3:
89              begin    
90                      if(key_row[3:0]!=4'b1111)  
91                      state<=5;   
92                      else begin 
93                      state<=4;
94                      key_col[3:0]<=4'b0111;
95                      end  
96              end
97          4:
98              begin    
99                      if (key_row[3:0]!=4'b1111) 
100                     state<=5;
101                     else  
102                     state<=0;
103             end
104         5:
105             begin  
106                     if(key_row[3:0]!=4'b1111)  begin
107                     key_col_reg<=key_col;  
108                     key_row_reg<=key_row;  
109                     state<=5;
110                     key_flag<=1'b1;  
111                     end             
112                     else
113                     state<=0;
114             end    
115     endcase 
116 end             
117 
118 always @ ( posedge sys_clk ) begin
119     if(key_flag==1'b1) 
120     begin
121         case ({ 
        key_col_reg,key_row_reg})
122 
123             8'b1110_1110:key_value<=4'd0;
124             8'b1110_1101:key_value<=4'd4;
125             8'b1110_1011:key_value<=4'd8;
126             8'b1110_0111:key_value<=4'd12;
127             
128             8'b1101_1110:key_value<=4'd1;
129             8'b1101_1101:key_value<=4'd5;
130             8'b1101_1011:key_value<=4'd9;
131             8'b1101_0111:key_value<=4'd13;
132             
133             8'b1011_1110:key_value<=4'd2;
134             8'b1011_1101:key_value<=4'd6;
135             8'b1011_1011:key_value<=4'd10;
136             8'b1011_0111:key_value<=4'd14;
137             
138             8'b0111_1110:key_value<=4'd3;
139             8'b0111_1101:key_value<=4'd7;
140             8'b0111_1011:key_value<=4'd11;
141             8'b0111_0111:key_value<=4'd15; 
142         endcase 
143     end   
144 end  
145 
146 endmodule

其实要想看懂矩阵键盘的扫描代码就要先吃透矩阵键盘的硬件设计,在上文已经跟大家介绍了我们矩阵键盘的行扫描信号全部3.3V上拉,并且行列信号(四个行信号四个列信号)是全部接到 FPGA 引脚上的,我们要想检测具体哪个按键被按下只要扫描它的行列序号就可以。矩阵键盘的按键编号是按照从左往右的 顺序编码的,例如第0行第0列的按键编号就是“0”(编号从0开始),第 0 行第1列的按键编号就是“1”,依次类推矩阵16个按键编号就是0~15,每一个编号都有自已唯一对应的行列号。那怎么得到这个行列号呢?这里我们就以按键6(对应的行列号为第一行第二列,对应扩展板上的KEY7)按下为例给大家讲解,首先按键没被按下之前所有的行端口(key_row [3:0])因为上拉的关系全为高电平,此时我们让所有与列端口相连的FPGA IO输出低电平(也就是key_col [3:0]等于4’b0000),这样当按键6被按下时,行端口1(key_row [1])会因为与列端口2(key_col[2])导通(按键闭合)而由原本的上拉高电平变成低电平,这样行端口就被检测出来了,哪一行端口电平变成低电平就说明按键就在那一行。接下来我们再来扫描列端口,按键按下之前四个列端口全部输出为低电平,按键按下后先扫列端口0(第一列)。我们将列端口0保持低电平,列端口1~3全部拉高(也就是 key_col [3:0] 等于 4’b1110)看行端口(key_row [3:0])的值是否发生变化(行端口1是否由低电平恢复成高电平即key_row[3:0]是否等于 4’b1111),如果没有变化则说明按键的列序号就是0,如果发生变化了说明按键不在列端口0的位置(第一列),因为只有当列端口为低电平时才能拉低其对应行端口为低电平,否则行端口会恢复成高电平,那么我们就继续扫描下一列,将列端口1(第二列)置0,其他端口置1,看行端口电平是否变化(即判断key_row[3:0]是否恢复成4’b1111),如果不变化说明被按下的按键对应列序号就是1,反之则不是,我们继续扫描下一列,直到找到对应列为止。采用这种扫描的方法就可以找到被按下按键的具体行列位置了,就可以找到对应编号,我们把这个编号传递给数码管模块,让数码管把编号显示出来。了解了矩阵按键扫描原理后我们再来分析代码,代码第22~50行是按键消抖模块,它的工作原理很简单,就是将按键的值先打一拍(key_reg <= key_row),然后检测当前时钟下按键的状态和上一个时钟的状态是否一致,如果不一致则将计数器 delay_cnt 赋初值100万,如果一致则计数器从初值开始作减法计数,直到计数器计数到“1”,说明按键的状态一直稳定了100万个时钟周期,此时我们认为是一次有效的按键 触发,这时我们就可以拉高消抖完成标志key_flag_row(注意只拉高一个时钟)。代码第52116行就是实现整个按键扫描的过程,它算一个简单的状态机,共有6个状态,其中状态04就是判断按键具体在哪一列,主要就是改变输出key_col[3:0]的值,看key_row[3:0]是否等于4’b1111,只要key_row[3:0]不等于4’b1111就说明一定有按键按下,然后再看列端口的值,四个列端口只保留一个端口为低电平,其余都为高电平,这样只有当被按下的按键刚好处于列端口为低电平的位置时 key_row[3:0]才能不等于4’b1111,因为按键闭合会使行端口上拉 3.3V 与列端口低电平导通,行端口的值被下拉成 0,否则行端口会一直处于上拉状态即key_row[3:0]等于 4’b1111。按照这个原理我们就把被按下的按键行列位置找到了,接下来就进入状态5将行列值寄存下来。最后代码118~144行会根据行列值把按键的编号翻译出来,然后传递给数码管模块去显示。 看完了矩阵按键扫描模块后我们再来看看数码管显示模块(seg_led)的代码,如下所示:

1   module seg_led(
2       input        clk          ,
3       input        rst_n        ,
4       input  [3:0] key_value    ,
5       input        key_flag     ,
6       output [3:0] sel_t        ,
7       output [7:0] seg_led_t    
8   );
9   
10  //reg define
11  reg [3:0]  sel    ;
12  reg [7:0]  seg_led;
13  
14  //*****************************************************
15  //** main code 
16  //*****************************************************
17  
18  assign sel_t     = ~sel    ;//共阴极接法这里取反,如果共阳极这里就不取反
19  assign seg_led_t = ~seg_led;//共阴极接法这里取反,如果共阳极这里就不取反
20  
21  always @(posedge clk or negedge rst_n)begin
22      if(!rst_n)
23          sel <= 4'b1111;
24      else if(key_flag)
25          sel <= 4'b0000;
26      else
27          sel <= 4'b1111;
28  end
29  
30  always  @(posedge clk or negedge rst_n)begin
31      if(rst_n==1'b0)
32          seg_led <= 8'b0;
33      else if (key_flag)begin
34          case (key_value)
35               

标签: 连接器y16hy16连接器y16p

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台