差别
这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
8._计时控制 [2017/03/23 00:56] zhijun |
8._计时控制 [2021/10/03 01:29] (当前版本) gongyu |
||
---|---|---|---|
行 1: | 行 1: | ||
- | =====计时控制===== | + | ## 计时控制 |
在之前的实验中我们掌握了如何进行时钟分频、如何进行数码管显示与按键消抖的处理,那么在本节实验之中,我们将会实现一个篮球赛场上常见的24秒计时器。 | 在之前的实验中我们掌握了如何进行时钟分频、如何进行数码管显示与按键消抖的处理,那么在本节实验之中,我们将会实现一个篮球赛场上常见的24秒计时器。 | ||
\\ | \\ | ||
- | ====硬件说明==== | + | |
- | ------- | + | ### 1. 硬件说明 |
在之前的实验中我们为读者详细介绍过小脚丫MXO2板卡上的按键、数码管、LED等硬件外设,在此不再赘述。本节将实现由数码管作为显示模块,按键作为控制信号的输入(包含复位信号和暂停信号),Lattice MXO2 4000HC作为控制核心的篮球读秒系统,实现框图如下: | 在之前的实验中我们为读者详细介绍过小脚丫MXO2板卡上的按键、数码管、LED等硬件外设,在此不再赘述。本节将实现由数码管作为显示模块,按键作为控制信号的输入(包含复位信号和暂停信号),Lattice MXO2 4000HC作为控制核心的篮球读秒系统,实现框图如下: | ||
- | {{ ::计时器框图.png?500 |}} | + | |
+ | {{ ::计时器框图.png?800 |}} | ||
\\ | \\ | ||
- | ====Verilog代码==== | + | ### 2. Verilog代码 |
- | ------ | + | |
<code verilog> | <code verilog> | ||
行 17: | 行 18: | ||
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< | // >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< | ||
// ******************************************************************** | // ******************************************************************** | ||
- | // File name : debounce.v | + | // File name : counter.v |
- | // Module name : debounce | + | // Module name : counter |
// Author : STEP | // Author : STEP | ||
// Description : | // Description : | ||
行 29: | 行 30: | ||
// V1.0 |2017/03/02 |Initial ver | // V1.0 |2017/03/02 |Initial ver | ||
// -------------------------------------------------------------------- | // -------------------------------------------------------------------- | ||
- | // Module Function:按键消抖 | + | // Module Function:24秒倒计时计数器 |
+ | |||
+ | module counter | ||
+ | ( | ||
+ | clk , //时钟 | ||
+ | rst , //复位 | ||
+ | hold , //启动暂停按键 | ||
+ | seg_led_1 , //数码管1 | ||
+ | seg_led_2 , //数码管2 | ||
+ | led //led | ||
+ | ); | ||
+ | |||
+ | input clk,rst; | ||
+ | input hold; | ||
+ | |||
+ | output [8:0] seg_led_1,seg_led_2; | ||
+ | output reg [7:0] led; | ||
+ | |||
+ | wire clk1h; //1Hz时钟 | ||
+ | wire hold_pulse; //按键消抖后信号 | ||
+ | reg hold_flag; //按键标志位 | ||
+ | reg back_to_zero_flag ; //计时完成信号 | ||
+ | reg [6:0] seg [9:0]; | ||
+ | reg [3:0] cnt_ge; //个位 | ||
+ | reg [3:0] cnt_shi; //十位 | ||
+ | |||
+ | initial | ||
+ | begin | ||
+ | seg[0] = 7'h3f; // 0 | ||
+ | seg[1] = 7'h06; // 1 | ||
+ | seg[2] = 7'h5b; // 2 | ||
+ | seg[3] = 7'h4f; // 3 | ||
+ | seg[4] = 7'h66; // 4 | ||
+ | seg[5] = 7'h6d; // 5 | ||
+ | seg[6] = 7'h7d; // 6 | ||
+ | seg[7] = 7'h07; // 7 | ||
+ | seg[8] = 7'h7f; // 8 | ||
+ | seg[9] = 7'h6f; // 9 | ||
+ | /*若需要显示A-F,解除此段注释即可 | ||
+ | seg[10]= 7'hf7; // A | ||
+ | seg[11]= 7'h7c; // b | ||
+ | seg[12]= 7'h39; // C | ||
+ | seg[13]= 7'h5e; // d | ||
+ | seg[14]= 7'h79; // E | ||
+ | seg[15]= 7'h71; // F*/ | ||
+ | end | ||
+ | |||
- | module debounce (clk,rst,key,key_pulse); | + | |
+ | // 启动/暂停按键进行消抖 | ||
+ | debounce U2 ( | ||
+ | .clk(clk), | ||
+ | .rst(rst), | ||
+ | .key(hold), | ||
+ | .key_pulse(hold_pulse) | ||
+ | ); | ||
+ | // 用于分出一个1Hz的频率 | ||
+ | divide #(.WIDTH(32),.N(12000000)) U1 ( | ||
+ | .clk(clk), | ||
+ | .rst_n(rst), | ||
+ | .clkout(clk1h) | ||
+ | ); | ||
+ | //按键动作标志信号产生 | ||
+ | always @ (posedge hold_pulse) | ||
+ | if(!rst==1) | ||
+ | hold_flag <= 0; | ||
+ | else | ||
+ | hold_flag <= ~hold_flag; | ||
+ | //计时完成标志信号产生 | ||
+ | always @ (*) | ||
+ | if(!rst == 1) | ||
+ | back_to_zero_flag <= 0; | ||
+ | else if(cnt_shi==0 && cnt_ge==0) | ||
+ | back_to_zero_flag <= 1; | ||
+ | else | ||
+ | back_to_zero_flag <= 0; | ||
+ | //24秒倒计时控制 | ||
+ | always @ (posedge clk1h or negedge rst) begin | ||
+ | if (!rst == 1) begin | ||
+ | cnt_ge <= 4'd4; | ||
+ | cnt_shi <= 4'd2; | ||
+ | end | ||
+ | else if(hold_flag == 1)begin | ||
+ | cnt_ge <= cnt_ge; | ||
+ | cnt_shi <= cnt_shi; | ||
+ | end | ||
+ | else if(cnt_shi==0 && cnt_ge==0) begin | ||
+ | cnt_shi <= cnt_shi; | ||
+ | cnt_ge <= cnt_ge; | ||
+ | end | ||
+ | else if(cnt_ge==0)begin | ||
+ | cnt_ge <= 4'd9; | ||
+ | cnt_shi <= cnt_shi-1;end | ||
+ | else | ||
+ | cnt_ge <= cnt_ge -1; | ||
+ | end | ||
+ | //计时完成点亮led | ||
+ | always @ ( back_to_zero_flag)begin | ||
+ | if (back_to_zero_flag==1) | ||
+ | led = 8'b0; | ||
+ | else | ||
+ | led = 8'b11111111; | ||
+ | end | ||
- | parameter N = 1; //要消除的按键的数量 | + | assign seg_led_1[8:0] = {2'b00,seg[cnt_ge]}; |
+ | |||
+ | assign seg_led_2[8:0] = {2'b00,seg[cnt_shi]}; | ||
- | input clk; | + | |
- | input rst; | + | endmodule |
- | input [N-1:0] key; //输入的按键 | + | |
- | output [N-1:0] key_pulse; //按键动作产生的脉冲 | + | |
- | + | ||
- | reg [N-1:0] key_rst_pre; //定义一个寄存器型变量存储上一个触发时的按键值 | + | |
- | reg [N-1:0] key_rst; //定义一个寄存器变量储存储当前时刻触发的按键值 | + | |
- | + | ||
- | wire [N-1:0] key_edge; //检测到按键由高到低变化是产生一个高脉冲 | + | |
- | + | ||
- | //利用非阻塞赋值特点,将两个时钟触发时按键状态存储在两个寄存器变量中 | + | |
- | always @(posedge clk or negedge rst) | + | |
- | begin | + | |
- | if (!rst) begin | + | |
- | key_rst <= {N{1'b1}}; //初始化时给key_rst赋值全为1,{}中表示N个1 | + | |
- | key_rst_pre <= {N{1'b1}}; | + | |
- | end | + | |
- | else begin | + | |
- | key_rst <= key; //第一个时钟上升沿触发之后key的值赋给key_rst,同时key_rst的值赋给key_rst_pre | + | |
- | key_rst_pre <= key_rst; //非阻塞赋值。相当于经过两个时钟触发,key_rst存储的是当前时刻key的值,key_rst_pre存储的是前一个时钟的key的值 | + | |
- | end | + | |
- | end | + | |
- | + | ||
- | assign key_edge = key_rst_pre & (~key_rst);//脉冲边沿检测。当key检测到下降沿时,key_edge产生一个时钟周期的高电平 | + | |
- | + | ||
- | reg [17:0] cnt; //产生延时所用的计数器,系统时钟12MHz,要延时20ms左右时间,至少需要18位计数器 | + | |
- | + | ||
- | //产生20ms延时,当检测到key_edge有效是计数器清零开始计数 | + | |
- | always @(posedge clk or negedge rst) | + | |
- | begin | + | |
- | if(!rst) | + | |
- | cnt <= 18'h0; | + | |
- | else if(key_edge) | + | |
- | cnt <= 18'h0; | + | |
- | else | + | |
- | cnt <= cnt + 1'h1; | + | |
- | end | + | |
- | + | ||
- | reg [N-1:0] key_sec_pre; //延时后检测电平寄存器变量 | + | |
- | reg [N-1:0] key_sec; | + | |
- | + | ||
- | + | ||
- | //延时后检测key,如果按键状态变低产生一个时钟的高脉冲。如果按键状态是高的话说明按键无效 | + | |
- | always @(posedge clk or negedge rst) | + | |
- | begin | + | |
- | if (!rst) | + | |
- | key_sec <= {N{1'b1}}; | + | |
- | else if (cnt==18'h3ffff) | + | |
- | key_sec <= key; | + | |
- | end | + | |
- | always @(posedge clk or negedge rst) | + | |
- | begin | + | |
- | if (!rst) | + | |
- | key_sec_pre <= {N{1'b1}}; | + | |
- | else | + | |
- | key_sec_pre <= key_sec; | + | |
- | end | + | |
- | end | + | |
- | assign key_pulse = key_sec_pre & (~key_sec); | + | |
- | + | ||
- | endmodule | + | |
</code> | </code> | ||
行 101: | 行 145: | ||
\\ | \\ | ||
- | 以上就是一个N位按键的消抖程序,如果有按键按下会输出一个时钟周期的高脉冲。下面我们可以试试用这个按键消抖的输出来触发LED的显示,既按键一次LED翻转。你也可以不加按键消抖试试用按键来控制LED(按一次变亮,再按一次灭掉)。 | + | ### 3. 引脚分配 |
- | \\ | ||
- | 下面的程序是例化调用debounce模块来控制LED | ||
- | \\ | ||
- | <code verilog> | ||
- | // ******************************************************************** | ||
- | // >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< | ||
- | // ******************************************************************** | ||
- | // File name : top.v | ||
- | // Module name : top | ||
- | // Author : STEP | ||
- | // Description : | ||
- | // Web : www.stepfpga.com | ||
- | // | ||
- | // -------------------------------------------------------------------- | ||
- | // Code Revision History : | ||
- | // -------------------------------------------------------------------- | ||
- | // Version: |Mod. Date: |Changes Made: | ||
- | // V1.0 |2017/03/02 |Initial ver | ||
- | // -------------------------------------------------------------------- | ||
- | // Module Function:进过按键消抖后控制led显示翻转 | ||
- | |||
- | module top (clk,rst,key,led); | ||
- | |||
- | input clk; | ||
- | input rst; | ||
- | input key; | ||
- | output reg led; | ||
- | |||
- | wire key_pulse; | ||
- | | ||
- | //当按键按下时产生一个高脉冲,翻转一次led | ||
- | always @(posedge clk or negedge rst) | ||
- | begin | ||
- | if (!rst) | ||
- | led <= 1'b1; | ||
- | else if (key_pulse) | ||
- | led <= ~led; | ||
- | else | ||
- | led <= led; | ||
- | end | ||
- | //例化消抖module,这里没有传递参数N,采用了默认的N=1 | ||
- | debounce u1 ( | ||
- | .clk (clk), | ||
- | .rst (rst), | ||
- | .key (key), | ||
- | .key_pulse (key_pulse) | ||
- | ); | ||
- | endmodule | ||
- | </code> | ||
- | |||
- | \\ | ||
- | ====引脚分配==== | ||
- | ------- | ||
设置好复位键可消抖的按键,编译完成后下载,通过按键就可以翻转LED。你也可以定义多个按键控制多个LED,还可以比较不加按键消抖情况下实际的效果对比如何。 | 设置好复位键可消抖的按键,编译完成后下载,通过按键就可以翻转LED。你也可以定义多个按键控制多个LED,还可以比较不加按键消抖情况下实际的效果对比如何。 | ||
\\ | \\ | ||
行 161: | 行 152: | ||
|clk |C1 ^ | |clk |C1 ^ | ||
|rst |L14 ^ | |rst |L14 ^ | ||
- | |key |N14 ^ | + | |hold |M13 ^ |
- | |led |N13 ^ | + | |seg_led_1[0] |C12 ^ |
+ | |seg_led_1[1] |B14 ^ | ||
+ | |seg_led_1[2] |J1 ^ | ||
+ | |seg_led_1[3] |H1 ^ | ||
+ | |seg_led_1[4] |H2 ^ | ||
+ | |seg_led_1[5] |B12 ^ | ||
+ | |seg_led_1[6] |A11 ^ | ||
+ | |seg_led_1[7] |K1 ^ | ||
+ | |seg_led_1[8] |A12 ^ | ||
+ | |seg_led_2[0] |A10 ^ | ||
+ | |seg_led_2[1] |C11 ^ | ||
+ | |seg_led_2[2] |F2 ^ | ||
+ | |seg_led_2[3] |E1 ^ | ||
+ | |seg_led_2[4] |E2 ^ | ||
+ | |seg_led_2[5] |A9 ^ | ||
+ | |seg_led_2[6] |B9 ^ | ||
+ | |seg_led_2[7] |F1 ^ | ||
+ | |seg_led_2[8] |C9 ^ | ||
+ | |||
+ | ### 4. 小结 | ||
- | ====小结==== | + | 本实验主要介绍了计时器的实现方式,并且包含了复位与暂停功能,读者可自行修改程序内部的时钟参数来调节计时时间。下一节将介绍PWM调制技术的应用[[9. 呼吸灯|呼吸灯]]。 |
- | ------ | + | |
- | 在本实验学习了如何进行按键的消抖。在很多应用情况下我们必须采取消抖才能更好地控制逻辑。在下一个实验[[8. 计时控制|计时控制]]中我们将学习计时的显示和控制,在这里我们要用到按键的消抖以及数码管,我们甚至可以用小脚丫做一个计时器甚至电子表。 | + | |