OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 3445|回复: 0

[XILINX] 《DFZU2EG_4EV MPSoc之FPGA开发指南》第二十六章 IO扩展模块实验

[复制链接]

1117

主题

1128

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4667
金钱
4667
注册时间
2019-5-8
在线时间
1224 小时
发表于 2023-4-17 14:33:31 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2023-4-17 14:33 编辑

第二十六章  IO扩展模块实验

1)实验平台:正点原子 DFZU2EG/4EV MPSoC开发板

2) 章节摘自【正点原子】DFZU2EG/4EV MPSoC之FPGA开发指南 V1.0


4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz-MPSOC.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)FPGA技术交流QQ群:994244016

155537c2odj87vz1z9vj6l.jpg

155537nfqovl2gg9faaol9.png

随着时间的推进,正点原子的FPGA开发板款式越来越多,外设也越来越丰富,从简单的按键流水灯到复杂的光口网口,基本上可以说是满足了广大FPGA工程师的学习和项目开发。但是在实际项目开发的过程中往往会出现一些按键、LED灯、数码管、拨码开关等基础器件不够用的情况,为此我们正点原子特地推出了IO扩展模块,这个模块旨在帮助大家丰富按键、LED灯、数码管、拨码开关等基础器件,方便大家灵活开发实际项目。
本章包括以下几个部分:
26.1简介
26.2实验任务
26.3硬件设计
26.4软件设计
26.5下载验证

26.1 简介
正点原子推出的 IO 扩展板包含了8个LED、一个x8的拨码开关、四个八段数码管和一个4x4的矩阵键盘。八颗LED可以使用FPGA单独控制也可以配合拨码开关去使用,当然拨码开关也可以用来控制其他外设。四个八段数码管同样可以使用FPGA单独控制,当开发板上的数码管不够用的时候就可以使用IO扩展板去代替。最后就是4x4的矩阵键盘了,共16个按键可以提供非常灵活的按键控制,当开发板上的按键不够用的时候就可以使用IO扩展板去提供一个额外的按键控制。

26.2 实验任务
本节实验任务是通过拨码开关控制IO扩展板上的LED灯亮灭,然后将矩阵键盘的按键编号显示在数码管上。

26.3 硬件设计
IO 扩展板模块的原理图如下所示:                  
image001.png
图 26.3.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开发板的扩展口上去,这样每个端口就可以和 FPGAIO引脚相连了。

IO扩展板实物图如下图所示。
image003.png
图 26.3.2 实物图

本实验中,IO扩展板管脚分配如下表所示。
QQ截图20230417143059.png
QQ截图20230417143127.png
表 26.3.1引脚分配表

对应的XDC约束语句如下所示:
  1. #时钟周期约束
  2. create_clock-name sys_clk_p -period 10.000 [get_ports sys_clk_p]
  3. #IO管脚约束
  4. set_propertyIOSTANDARD DIFF_HSTL_I_12 [get_ports sys_clk_p]
  5. set_propertyIOSTANDARD DIFF_HSTL_I_12 [get_ports sys_clk_n]
  6. set_propertyPACKAGE_PIN AE5 [get_ports sys_clk_p]
  7. set_propertyPACKAGE_PIN AF5 [get_ports sys_clk_n]
  8. set_property-dict {PACKAGE_PIN AH11 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]
  9. set_property-dict {PACKAGE_PIN J14 IOSTANDARD LVCMOS33} [get_ports seg_led_t[0]]
  10. set_property-dict {PACKAGE_PIN F13 IOSTANDARD LVCMOS33} [get_ports seg_led_t[1]]
  11. set_property-dict {PACKAGE_PIN G14 IOSTANDARD LVCMOS33} [get_ports seg_led_t[2]]
  12. set_property-dict {PACKAGE_PIN L13 IOSTANDARD LVCMOS33} [get_ports seg_led_t[3]]
  13. set_property-dict {PACKAGE_PIN L14 IOSTANDARD LVCMOS33} [get_ports seg_led_t[4]]
  14. set_property-dict {PACKAGE_PIN H14 IOSTANDARD LVCMOS33} [get_ports seg_led_t[5]]
  15. set_property-dict {PACKAGE_PIN G13 IOSTANDARD LVCMOS33} [get_ports seg_led_t[6]]
  16. set_property-dict {PACKAGE_PIN G15 IOSTANDARD LVCMOS33} [get_ports seg_led_t[7]]
  17. set_property-dict {PACKAGE_PIN E15 IOSTANDARD LVCMOS33} [get_ports sel_t[0]]
  18. set_property-dict {PACKAGE_PIN F15 IOSTANDARD LVCMOS33} [get_ports sel_t[1]]
  19. set_property-dict {PACKAGE_PIN H13 IOSTANDARD LVCMOS33} [get_ports sel_t[2]]
  20. set_property-dict {PACKAGE_PIN K14 IOSTANDARD LVCMOS33} [get_ports sel_t[3]]
  21. set_property-dict {PACKAGE_PIN E12 IOSTANDARD LVCMOS33} [get_ports key_row[0]]
  22. set_property-dict {PACKAGE_PIN C11 IOSTANDARD LVCMOS33} [get_ports key_row[1]]
  23. set_property-dict {PACKAGE_PIN B10 IOSTANDARD LVCMOS33} [get_ports key_row[2]]
  24. set_property-dict {PACKAGE_PIN E10 IOSTANDARD LVCMOS33} [get_ports key_row[3]]
  25. set_property-dict {PACKAGE_PIN D10 IOSTANDARD LVCMOS33} [get_ports key_col[0]]
  26. set_property-dict {PACKAGE_PIN B11 IOSTANDARD LVCMOS33} [get_ports key_col[1]]
  27. set_property-dict {PACKAGE_PIN A10 IOSTANDARD LVCMOS33} [get_ports key_col[2]]
  28. set_property-dict {PACKAGE_PIN D11 IOSTANDARD LVCMOS33} [get_ports key_col[3]]
  29. set_property-dict {PACKAGE_PIN D15 IOSTANDARD LVCMOS33} [get_ports led[0]]
  30. set_property-dict {PACKAGE_PIN E14 IOSTANDARD LVCMOS33} [get_ports led[1]]
  31. set_property-dict {PACKAGE_PIN D14 IOSTANDARD LVCMOS33} [get_ports led[2]]
  32. set_property-dict {PACKAGE_PIN E13 IOSTANDARD LVCMOS33} [get_ports led[3]]
  33. set_property-dict {PACKAGE_PIN B13 IOSTANDARD LVCMOS33} [get_ports led[4]]
  34. set_property-dict {PACKAGE_PIN C14 IOSTANDARD LVCMOS33} [get_ports led[5]]
  35. set_property-dict {PACKAGE_PIN A13 IOSTANDARD LVCMOS33} [get_ports led[6]]
  36. set_property-dict {PACKAGE_PIN C13 IOSTANDARD LVCMOS33} [get_ports led[7]]
  37. set_property-dict {PACKAGE_PIN C12 IOSTANDARD LVCMOS33} [get_ports swi[0]]
  38. set_property-dict {PACKAGE_PIN A11 IOSTANDARD LVCMOS33} [get_ports swi[1]]
  39. set_property-dict {PACKAGE_PIN D12 IOSTANDARD LVCMOS33} [get_ports swi[2]]
  40. set_property-dict {PACKAGE_PIN A12 IOSTANDARD LVCMOS33} [get_ports swi[3]]
  41. set_property-dict {PACKAGE_PIN A14 IOSTANDARD LVCMOS33} [get_ports swi[4]]
  42. set_property-dict {PACKAGE_PIN A15 IOSTANDARD LVCMOS33} [get_ports swi[5]]
  43. set_property-dict {PACKAGE_PIN B14 IOSTANDARD LVCMOS33} [get_ports swi[6]]
  44. set_property-dict {PACKAGE_PIN B15 IOSTANDARD LVCMOS33} [get_ports swi[7]]
复制代码

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

各模块端口及信号连接如下图所示:
image007.png
图 26.4.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. 1 module top_matrix_keyboard(
  2. 2      input        sys_clk_p  ,
  3. 3      input        sys_clk_n  ,
  4. 4      input        sys_rst_n  ,
  5. 5      input   [3:0 key_row    ,
  6. 6      input   [7:0 swi        ,
  7. 7      output  [3:0 key_col    ,
  8. 8      output  [3:0 sel_t      ,
  9. 9      output  [7:0 seg_led_t  ,
  10. 10     output  [7:0 led   
  11. 11
  12. 12     );
  13. 13
  14. 14 //wire define   
  15. 15 wire [3:0    key_value   ;
  16. 16 wire         key_flag     ;
  17. 17
  18. 18 //*****************************************************
  19. 19 //**                   main code                     
  20. 20 //*****************************************************
  21. 21
  22. 22 //转换差分信号
  23. 23 IBUFDS diff_clock
  24. 24 (
  25. 25     .I (sys_clk_p),    //差分输入时钟
  26. 26     .IB(sys_clk_n),    //系统差分输入时钟
  27. 27     .O (sys_clk)       //输出系统时钟
  28. 28 );
  29. 29
  30. 30 //矩阵键盘扫描模块
  31. 31 key_4x4    u_key_4x4(
  32. 32     .sys_clk    (sys_clk  ),
  33. 33     .sys_rst_n  (sys_rst_n),
  34. 34     .key_row    (key_row  ),
  35. 35     .key_col    (key_col  ),
  36. 36     .key_value  (key_value),
  37. 37     .key_flag   (key_flag )
  38. 38 );
  39. 39
  40. 40 //数码管显示模块
  41. 41 seg_led    u_seg_led(
  42. 42     .clk       (sys_clk  ),
  43. 43     .rst_n     (sys_rst_n),
  44. 44     .key_value (key_value),
  45. 45     .key_flag  (key_flag ),
  46. 46     .sel_t     (sel_t    ),
  47. 47     .seg_led_t (seg_led_t)   
  48. 48 );
  49. 49
  50. 50 //拨码开关模块
  51. 51 swi_led     u_swi_led(
  52. 52     .clk     (sys_clk  ),
  53. 53     .rst_n   (sys_rst_n),
  54. 54     .swi     (swi      ),
  55. 55     .led     (led      )
  56. 56 );
  57. 57 endmodule
复制代码
顶层模块主要就是例化三个子模块,在这里就不作过多介绍了,下面我们直接开始看矩阵键盘扫描模块,矩阵键盘扫描模块的代码如下:
  1. 1  module key_4x4(
  2. 2       input               sys_clk   ,   //100MHZ
  3. 3       input               sys_rst_n ,
  4. 4       input       [3:0   key_row   ,   //行
  5. 5       output reg  [3:0    key_col  ,   //列
  6. 6       output reg  [3:0    key_value ,   //键值
  7. 7       output reg           key_flag
  8. 8  
  9. 9  );
  10. 10
  11. 11 //reg define
  12. 12 reg [2:0 state       ;  //状态标志
  13. 13 reg [3:0 key_col_reg ;  //寄存扫描列值
  14. 14 reg [3:0 key_row_reg ;  //寄存扫描行值
  15. 15 reg [31:0delay_cnt   ;
  16. 16 reg [3:0 key_reg     ;
  17. 17 reg       key_flag_row;  //消抖完成标志
  18. 18
  19. 19 //*****************************************************
  20. 20 //**                    main code                     
  21. 21 //*****************************************************
  22. 22
  23. 23 always @(posedge sys_clk or negedge sys_rst_n) begin
  24. 24      if (!sys_rst_n) begin
  25. 25          key_reg   <=4'b1;
  26. 26          delay_cnt <= 32'd0;
  27. 27      end
  28. 28      else begin
  29. 29          key_reg <= key_row;
  30. 30              if(key_reg != key_row)            
  31. 31                  delay_cnt <= 32'd1000000;      
  32. 32              else if(key_reg == key_row) begin  
  33. 33                  if(delay_cnt > 32'd0)
  34. 34                      delay_cnt <= delay_cnt - 1'b1;
  35. 35                  else
  36. 36                      delay_cnt <= delay_cnt;
  37. 37              end           
  38. 38     end   
  39. 39 end
  40. 40
  41. 41 always @(posedge sys_clk or negedge sys_rst_n) begin
  42. 42      if (!sys_rst_n)  
  43. 43          key_flag_row  <= 1'b0;              
  44. 44      else begin
  45. 45              if(delay_cnt == 32'd1)   
  46. 46                  key_flag_row  <= 1'b1;
  47. 47              else
  48. 48                  key_flag_row  <= 1'b0;
  49. 49      end   
  50. 50 end
  51. 51
  52. 52 always @(posedge sys_clk or negedge sys_rst_n)
  53. 53      if(!sys_rst_n) begin
  54. 54      key_col<=4'b0000;
  55. 55      state<=0;
  56. 56      end
  57. 57      else begin
  58. 58      case (state)
  59. 59          0:
  60. 60              begin
  61. 61                  key_col[3:0]<=4'b0000;
  62. 62                  key_flag<=1'b0;
  63. 63                  if((key_row[3:0]!=4'b1111)&&(key_flag_row)) begin   
  64. 64                      state<=1;
  65. 65                      key_col[3:0]<=4'b1110;
  66. 66                      end
  67. 67                  else
  68. 68                      state<=0;
  69. 69              end
  70. 70          1:
  71. 71              begin
  72. 72                      if(key_row[3:0]!=4'b1111)
  73. 73                      state<=5;
  74. 74                      else  begin
  75. 75                      state<=2;
  76. 76                      key_col[3:0]<=4'b1101;
  77. 77                      end
  78. 78              end  
  79. 79         2:
  80. 80              begin   
  81. 81                      if(key_row[3:0]!=4'b1111)
  82. 82                      state<=5;
  83. 83                      else  begin               
  84. 84                      state<=3;
  85. 85                      key_col[3:0]<=4'b1011;
  86. 86                      end  
  87. 87              end
  88. 88          3:
  89. 89              begin   
  90. 90                      if(key_row[3:0]!=4'b1111)  
  91. 91                      state<=5;   
  92. 92                      else begin
  93. 93                      state<=4;
  94. 94                      key_col[3:0]<=4'b0111;
  95. 95                      end  
  96. 96              end
  97. 97          4:
  98. 98              begin   
  99. 99                      if (key_row[3:0]!=4'b1111)
  100. 100                     state<=5;
  101. 101                     else  
  102. 102                     state<=0;
  103. 103             end
  104. 104         5:
  105. 105             begin  
  106. 106                     if(key_row[3:0]!=4'b1111)  begin
  107. 107                     key_col_reg<=key_col;  
  108. 108                     key_row_reg<=key_row;  
  109. 109                     state<=5;
  110. 110                     key_flag<=1'b1;  
  111. 111                     end            
  112. 112                     else
  113. 113                     state<=0;
  114. 114             end   
  115. 115     endcase
  116. 116 end            
  117. 117
  118. 118 always @ ( posedge sys_clk ) begin
  119. 119     if(key_flag==1'b1)
  120. 120     begin
  121. 121         case ({key_col_reg,key_row_reg})
  122. 122
  123. 123             8'b1110_1110:key_value<=4'd0;
  124. 124             8'b1110_1101:key_value<=4'd4;
  125. 125             8'b1110_1011:key_value<=4'd8;
  126. 126             8'b1110_0111:key_value<=4'd12;
  127. 127            
  128. 128             8'b1101_1110:key_value<=4'd1;
  129. 129             8'b1101_1101:key_value<=4'd5;
  130. 130             8'b1101_1011:key_value<=4'd9;
  131. 131             8'b1101_0111:key_value<=4'd13;
  132. 132            
  133. 133             8'b1011_1110:key_value<=4'd2;
  134. 134             8'b1011_1101:key_value<=4'd6;
  135. 135             8'b1011_1011:key_value<=4'd10;
  136. 136             8'b1011_0111:key_value<=4'd14;
  137. 137            
  138. 138             8'b0111_1110:key_value<=4'd3;
  139. 139             8'b0111_1101:key_value<=4'd7;
  140. 140             8'b0111_1011:key_value<=4'd11;
  141. 141             8'b0111_0111:key_value<=4'd15;
  142. 142         endcase
  143. 143     end   
  144. 144 end  
  145. 145
  146. 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(注意只拉高一个时钟)。代码第52~116行就是实现整个按键扫描的过程,它算一个简单的状态机,共有6个状态,其中状态0~4就是判断按键具体在哪一列,主要就是改变输出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. 1  module seg_led(
  2. 2       input       clk          ,
  3. 3       input       rst_n        ,
  4. 4       input  [3:0 key_value    ,
  5. 5       input       key_flag     ,
  6. 6       output [3:0 sel_t        ,
  7. 7       output [7:0 seg_led_t   
  8. 8  );
  9. 9  
  10. 10 //reg define
  11. 11 reg [3:0  sel    ;
  12. 12 reg [7:0  seg_led;
  13. 13
  14. 14 //*****************************************************
  15. 15 //**                    main code                     
  16. 16 //*****************************************************
  17. 17
  18. 18 assign sel_t     = ~sel    ;//共阴极接法这里取反,如果共阳极这里就不取反
  19. 19 assign seg_led_t = ~seg_led;//共阴极接法这里取反,如果共阳极这里就不取反
  20. 20
  21. 21 always @(posedge clk or negedge rst_n)begin
  22. 22      if(!rst_n)
  23. 23          sel <= 4'b1111;
  24. 24      else if(key_flag)
  25. 25          sel <= 4'b0000;
  26. 26      else
  27. 27          sel <= 4'b1111;
  28. 28 end
  29. 29
  30. 30 always  @(posedge clk or negedge rst_n)begin
  31. 31      if(rst_n==1'b0)
  32. 32          seg_led <= 8'b0;
  33. 33      else if (key_flag)begin
  34. 34          case (key_value)
  35. 35              4'd0 : seg_led <= 8'b01000000;
  36. 36              4'd1 : seg_led <= 8'b01111001;
  37. 37              4'd2 : seg_led <= 8'b00100100;
  38. 38              4'd3 : seg_led <= 8'b00110000;
  39. 39              4'd4 : seg_led <= 8'b00011001;
  40. 40              4'd5 : seg_led <= 8'b00010010;
  41. 41              4'd6 : seg_led <= 8'b00000010;
  42. 42              4'd7 : seg_led <= 8'b01111000;
  43. 43              4'd8 : seg_led <= 8'b00000000;
  44. 44              4'd9 : seg_led <= 8'b00010000;
  45. 45              4'd10: seg_led <= 8'b00011000;           
  46. 46              4'd11: seg_led <= 8'b00000011;        
  47. 47              4'd12: seg_led <= 8'b01000110;
  48. 48              4'd13: seg_led <= 8'b00100001;
  49. 49              4'd14: seg_led <= 8'b00000110;
  50. 50              4'd15: seg_led <= 8'b00001110;
  51. 51          default:
  52. 52              seg_led <= 8'b1111_1111;
  53. 53          endcase
  54. 54          end
  55. 55      else
  56. 56          seg_led <= 8'b1111_1111;
  57. 57 end   
  58. 58   
  59. 59 endmodule
复制代码
数码管模块的代码是非常简单的,本次实验是使用静态数码管来显示矩阵按键的编号的,因此四个数 码管显示的数字都一样。代码21~28行是控制数码管的位选信号,因为IO扩展模块的数码管是共阴极接法,所以是高电平点亮数码管,因此在代码的18和19行我们做了一个阴阳极转换,如果大家拿到的是共阴极接法的数码管就可以直接使用本节实验代码,如果是共阳极大家就可以将代码18和19行的取反运算去掉就行。从代码中可以看到当按键按下后标志位key_flag就会拉高,此时我们位选信号全部选中,如果按键没有按下即key_flag的值为低电平,则放开所有位选信号。接下来再看代码30~57行,这段代码是控制数码管的段选信号,当按键按下即key_flag的值为高电平,此时开始检测按键编号key_value的值,通过一个case语句把按键值翻译成数码管的段选显示信号,这样就可以控制数码管显示数据了。

最后我们再来看看拨码开关模块(swi_led)的代码,如下所示:
  1. 1  module swi_led(
  2. 2       input             clk    ,
  3. 3       input             rst_n  ,
  4. 4       input     [7:0    swi    ,
  5. 5       output reg[7:0    led   
  6. 6  );
  7. 7  
  8. 8  //*****************************************************
  9. 9  //**                    main code                     
  10. 10 //*****************************************************
  11. 11
  12. 12 always @(posedge clk or negedge rst_n)begin
  13. 13      if(!rst_n)
  14. 14      led <= 8'b0000_0000;
  15. 15      else
  16. 16      led <= swi;
  17. 17 end
  18. 18
  19. 19 endmodule
复制代码
拨码开关模块(swi_led)的代码是非常简单的,它一共就一个always语句块,目的就是为了把拨码开 关的值赋给led灯的阳极端口用来控制led灯。

到此整个IO拓展口模块实验的代码就全部讲解完了。

26.5 下载验证
首先我们将下载器与DFZU2EG/4EV MPSoC开发板上的JTAG接口连接,下载器另外一端与电脑连接。然后将IO扩展板模块插到板子上的J19扩展口去,最后连接电源线后拨动开关按键给开发板上电,如下图所示:
image009.png
图 26.5.1硬件连接图

回到Vivado界面,我们将生成好的bit流文件下载到开发板中去,按下矩阵按键就可以看到数码管会显示矩阵按键的编号(注意是16进制显示),上下拨动拨码开关就可以控制led灯亮灭,效果如下图所示:
image011.png
图 26.5.2 最终效果图
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-23 04:55

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表