差别
这里会显示出您选择的修订版和当前版本之间的差别。
| 两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
|
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. 计时控制|计时控制]]中我们将学习计时的显示和控制,在这里我们要用到按键的消抖以及数码管,我们甚至可以用小脚丫做一个计时器甚至电子表。 | + | |