2119|0

14

帖子

0

TA的资源

一粒金砂(初级)

楼主
 

按键+led+fsm [复制链接]


key_led_breath

一、项目要求

   用一个按键来控制四个led灯,按键按下一次第一个LED灯亮起,按键再次按下第一个和第二个LED同时亮起,按键第三次按下4led灯变成呼吸灯(呼吸灯4秒完成一次呼吸),呼吸8s后停止。按键再次按键重新执行上述要求。

二、项目分析

   此项目我们采用Top-Down的设计思路,分模块来设计

  
file:///C:/Users/wwt/AppData/Local/Temp/msohtmlclip1/01/clip_image002.png
  

三、下面我们对各个模块进行分析

   总体思路设计好以后我们从最底层模块开始编写

1.led_breath模块

要做出呼吸的led(初始值为高电平)要求led的低电平逐渐积累至完全为低电平,然后将生成的改波形取反即可。我们要求LED灯从亮到暗需要2s,期间我们要进行1000份,故每一份为2ms,因此低电平时间为20us,系统时钟为50MHz

  
file:///C:/Users/wwt/AppData/Local/Temp/msohtmlclip1/01/clip_image004.png
  
  

代码

五、代码

  
module led_breath(
  
    input       wire            sclk,//50MHz
  
    input       wire            rst_n,//系统复位
  
   
  
    output      wire    [3:0]    led//输出的led
  
);
  
  
parameter   CNT_2US_MAX = 7'd99;//计数20us
  
parameter   CNT_2MS_MAX = 10'd999;//计数2ms
  
parameter   CNT_2S_MAX = 10'd999;//计数2s
  
  
  
reg [6:0]       cnt_2us ;
  
reg              cnt_2us_flag;
  
reg [9:0]       cnt_2ms ;
  
reg              cnt_2ms_flag;
  
reg [9:0]       cnt_2s;
  
reg              cnt_4s;
  
  
reg             pwm;
  
wire            pwm_r;
  
wire             pwm_led;
  
  
assign          pwm_r = ~pwm;
  
  
//计数2us进程
  
always@(posedge sclk or negedge rst_n)
  
    if(!rst_n)
  
         cnt_2us <= 7'd0 ;
  
    else if(cnt_2us == CNT_2US_MAX)
  
         cnt_2us <= 7'd0 ;
  
    else
  
         cnt_2us <= cnt_2us + 1'b1;      
  
  
//产生2us标志信号
  
always@(posedge sclk or negedge rst_n)
  
    if(!rst_n)
  
         cnt_2us_flag <= 1'b0    ;
  
    else if(cnt_2us == CNT_2US_MAX)
  
         cnt_2us_flag <= 1'b1    ;   
  
    else
  
         cnt_2us_flag <= 1'b0    ;   
  
         
  
//计数2ms进程
  
always@(posedge sclk or negedge rst_n)
  
    if(!rst_n)
  
         cnt_2ms <= 10'd0        ;
  
    else if((cnt_2ms == CNT_2MS_MAX) && (cnt_2us_flag))
  
         cnt_2ms <= 10'd0        ;
  
    else if(cnt_2us_flag)
  
         cnt_2ms <= cnt_2ms + 1'b1;      
  
         
  
//产生2ms标志信号
  
always@(posedge sclk or negedge rst_n)
  
    if(!rst_n)
  
         cnt_2ms_flag <= 1'b0    ;
  
    else if((cnt_2ms == CNT_2MS_MAX) && (cnt_2us_flag))
  
         cnt_2ms_flag <= 1'b1    ;
  
    else
  
         cnt_2ms_flag <= 1'b0    ;           
  
         
  
//计数2s进程
  
always@(posedge sclk or negedge rst_n)
  
    if(!rst_n)
  
         cnt_2s <= 10'd0;
  
    else if((cnt_2s == CNT_2S_MAX) && (cnt_2ms_flag))
  
         cnt_2s <= 10'd0;
  
    else if(cnt_2ms_flag)
  
         cnt_2s <= cnt_2s + 1'b1;
  
         
  
//4s产生标志信号
  
always@(posedge sclk or negedge rst_n)
  
    if(!rst_n)
  
         cnt_4s <= 1'b0;
  
    else if((cnt_2s == CNT_2S_MAX) && (cnt_2ms_flag))
  
         cnt_4s <= ~cnt_4s;                  
  
  
//产生呼吸灯效果
  
always@(posedge sclk or negedge rst_n)
  
    if(!rst_n)
  
         pwm <= 1'b0;
  
    else if(cnt_2ms >= cnt_2s)  
  
         pwm <= 1'b1;
  
    else
  
         pwm <= 1'b0;
  
                        
  
assign pwm_led = (cnt_4s == 1'b0) ? pwm : pwm_r;        
  
assign led = {pwm_led,pwm_led,pwm_led,pwm_led};
  
  
endmodule
  

测试脚本

  
`timescale 1ns/1ns
  
  
module tb_led_breath();
  
  
    reg    sclk;//50MHz
  
    reg    rst_n;//系统复位
  
   
  
    wire[3:0]  led;//输出的led
  
  
defparam   led_breath_inst.CNT_2US_MAX = 9;
  
defparam   led_breath_inst.CNT_2MS_MAX = 19;
  
defparam   led_breath_inst.CNT_2S_MAX = 19;
  
  
led_breath led_breath_inst(
  
    .sclk      (sclk),//50MHz
  
    .rst_n (rst_n),//系统复位
  
   
  
    .led       (led)//输出的led
  
);
  
  
initial sclk = 1;
  
always #10 sclk = ~sclk;
  
  
initial begin
  
    rst_n <= 1'b0;
  
    #33
  
    rst_n <= 1'b1;
  
end
  
endmodule
  

仿真图

  
file:///C:/Users/wwt/AppData/Local/Temp/msohtmlclip1/01/clip_image006.jpg
  

2、key_filter模块

独立按键简介

   我们用的机械按键有一种物理特性按键按下时会抖动5~10ms,当我们想要用按键来控制我们的外设时就必须对按键进行消抖。

按键的实际波形

  
file:///C:/Users/wwt/AppData/Local/Temp/msohtmlclip1/01/clip_image008.png
  

我们采用计数器的方法来做按键消抖(滤波)波形图如下

  
file:///C:/Users/wwt/AppData/Local/Temp/msohtmlclip1/01/clip_image010.png
  

代码

  
module key_filter(
  
    input      wire          sclk,
  
    input      wire          rst_n,
  
    input      wire          key_in,
  
   
  
    output     reg        key_flag
  
);
  
  
parameter  CNT_10MS_MAX  = 24'd499_999;
  
  
reg [23:0] cnt_10ms;
  
reg        cnt_flag;
  
  
//10ms计数器计数
  
always@(posedge sclk or negedge rst_n)
  
    if(!rst_n)
  
       cnt_10ms <= 24'd0;
  
    else if(key_in == 1'b0)
  
       cnt_10ms <= cnt_10ms + 1'b1;
  
    else
  
       cnt_10ms <= 1'b0;
  
      
  
//10ms标志位产生
  
always@(posedge sclk or negedge rst_n)
  
    if(!rst_n)
  
       cnt_flag <= 1'b0;
  
    else if(cnt_10ms == CNT_10MS_MAX)
  
       cnt_flag <= 1'b1;
  
    else if(key_in == 1'b1)
  
       cnt_flag <= 1'b0;               
  
  
//按键标志产生
  
always@(posedge sclk or negedge rst_n)
  
    if(!rst_n)
  
       key_flag <= 1'b0;
  
    else if(cnt_10ms == CNT_10MS_MAX && cnt_flag == 1'b0)
  
       key_flag <= 1'b1;
  
    else
  
       key_flag <= 1'b0;   
  
  
endmodule
  

【测试脚本】

测试脚本我们利用一个40ms的计数器在产生我们的按键波形,如图所示

  
file:///C:/Users/wwt/AppData/Local/Temp/msohtmlclip1/01/clip_image012.png
  


  
`timescale 1ns/1ns
  
  
module tb_key_filter();
  
  
    reg         sclk;
  
    reg         rst_n;
  
    reg         key_in;
  
   
  
    wire            key_flag;
  
   
  
    reg [20:0]  cnt_40ms;
  
  
key_filter key_filter_inst(
  
    .sclk       (sclk        ),
  
    .rst_n      (rst_n   ),
  
    .key_in     (key_in ),
  
  
    .key_flag   (key_flag    )
  
);
  
  
initial sclk = 1;
  
always #10 sclk = ~sclk;
  
  
initial begin
  
     rst_n <= 0;
  
    #35
  
     rst_n <= 1;
  
end
  
  
always@(posedge sclk or negedge rst_n)
  
    if(!rst_n)
  
         cnt_40ms <= 21'd0;
  
    else if(cnt_40ms == 21'd1_999_999)  
  
         cnt_40ms <= 20'd0;
  
    else
  
         cnt_40ms <= cnt_40ms + 1'b1;
  
         
  
always@(posedge sclk or negedge rst_n)
  
    if(!rst_n)
  
         key_in <= 1'b1;
  
    else if(cnt_40ms < 249_999)
  
         key_in <= 1'b1;
  
    else if(cnt_40ms < 499_999 && cnt_40ms > 249_999)
  
         key_in <= {$random};
  
    else if(cnt_40ms < 999_999 && cnt_40ms > 499_999)
  
         key_in <= 0;
  
    else if(cnt_40ms > 999_999 && cnt_40ms < 1_499_999)
  
         key_in <= {$random};
  
    else
  
         key_in <= 1'b1;                     
  
  
endmodule
  

3、fsm模块

我们利用key_flag来驱动状态的转换

  
file:///C:/Users/wwt/AppData/Local/Temp/msohtmlclip1/01/clip_image014.png
  

【代码】

  
module fsm(
  
    input      wire          sclk,
  
    input      wire          rst_n,
  
    input      wire          pi_money,
  
   
  
    output reg [3:0]      state
  
);
  
  
parameter  IDLE    = 4'b0001;
  
parameter  ONE     = 4'b0010;
  
parameter  TWO     = 4'b0100;
  
parameter  THERE  = 4'b1000;
  
  
parameter  CNT_4S_MAX  = 29'd399_999_999;
  
  
reg [28:0] cnt_4s;
  
reg    en_cnt_4s;
  
reg    cnt_4s_flag;
  
  
//cnt_4s计数进程
  
always@(posedge sclk or negedge rst_n)
  
    if(!rst_n)
  
       cnt_4s <= 29'd0;
  
    else if(cnt_4s == CNT_4S_MAX)
  
       cnt_4s <= 29'd0;
  
    else if(en_cnt_4s)
  
       cnt_4s <= cnt_4s + 1'b1;
  
      
  
always@(posedge sclk or negedge rst_n)
  
    if(!rst_n)
  
       cnt_4s_flag  <= 1'b0;
  
    else if(cnt_4s == CNT_4S_MAX)   
  
       cnt_4s_flag  <= 1'b1;
  
    else
  
       cnt_4s_flag  <= 1'b0;               
  
  
//fsm2-1  状态转移
  
always@(posedge sclk or negedge rst_n)
  
    if(!rst_n)
  
       state <= IDLE;
  
    else
  
       case(state)
  
           IDLE : begin
  
              if(pi_money == 1'b1)
  
                  state  <= ONE;
  
              else
  
                  state  <= IDLE;   
  
           end
  
           ONE : begin
  
              if(pi_money == 1'b1)
  
                  state  <= TWO;
  
              else
  
                  state  <= ONE;
  
           end
  
           TWO : begin
  
              if(pi_money == 1'b1)
  
                  state  <= THERE;
  
              else
  
                  state  <= TWO;
  
           end
  
           THERE : begin
  
              en_cnt_4s  <= 1'b1;
  
              if(cnt_4s_flag == 1'b1)
  
                  state <= IDLE;
  
              else
  
                  state  <= THERE;
  
           end
  
           default : state <= IDLE;
  
       endcase      
  
  
endmodule
  

【测试脚本】

  
`timescale 1ns/1ns
  
  
module tb_fsm();
  
  
    reg           sclk;
  
    reg           rst_n;
  
    reg           pi_money;
  
   
  
    wire   [3:0]  state;
  
  
defparam fsm_inst.CNT_4S_MAX = 20;
  
  
fsm fsm_inst(
  
    .sclk      (sclk      ),
  
    .rst_n     (rst_n     ),
  
    .pi_money  (pi_money  ),
  
  
    .state     (state )
  
);
  
  
initial sclk = 1;
  
always #5 sclk = ~sclk;
  
  
initial begin
  
    rst_n <= 0;
  
    pi_money <= 0;
  
    #25;
  
    rst_n <= 1;
  
    #30;
  
    pi_money <= 1;
  
    #50;
  
    pi_money <= 0;
  
    #20;
  
    pi_money <= 1;
  
    #20;
  
    pi_money <= 0;
  
    #40;
  
    pi_money <= 1;
  
    #60;
  
    pi_money <= 0;
  
    #500;
  
    pi_money <= 1;
  
    #100;
  
    //$stop;
  
end
  
  
endmodule
  

4、led模块

Led模块将fsm模块输出的state作为控制led变化的信号,静led_breath模块例化到led模块中实现led的控制。

  
module led(
  
    input      wire          sclk,
  
    input      wire          rst_n,
  
    input      wire   [3:0]  state,
  
   
  
    output     reg [3:0]      po_led
  
);
  
  
wire   [3:0]      led;
  
  
led_breath led_breath_inst(
  
    .sclk      (sclk),//50MHz
  
    .rst_n     (rst_n),//系统复位
  
   
  
    .led       (led)//输出的led
  
);
  
  
always@(posedge sclk or negedge rst_n)
  
    if(!rst_n)
  
       po_led <= 4'b0000;
  
    else
  
       case(state)
  
           4'b0001 : begin
  
              po_led  <= 4'b0000;
  
           end
  
           4'b0010 : begin
  
              po_led  <= 4'b0001;
  
           end
  
           4'b0100 : begin
  
              po_led  <= 4'b0011;
  
           end
  
           4'b1000 : begin
  
              po_led  <= led;
  
           end
  
           default : po_led <= 4'b0000;
  
       endcase   
  
  
endmodule
  

5、top模块

在顶层模块中我们将key_filter模块、fsm模块、led模块

【代码】

  
module key_led_breath_top(
  
    input      wire          sclk,
  
    input      wire          rst_n,
  
    input      wire          pi_key,
  
   
  
    output wire   [3:0]      po_led
  
);
  
  
wire          key_flag;
  
wire   [3:0]      state;
  
  
key_filter key_filter_inst(
  
    .sclk      (sclk      ),
  
    .rst_n     (rst_n ),
  
    .key_in       (pi_key    ),
  
  
    .key_flag  (key_flag  )
  
);
  
  
fsm fsm_inst(
  
    .sclk      (sclk      ),
  
    .rst_n     (rst_n ),
  
    .pi_money  (key_flag  ),
  
  
    .state     (state )
  
);
  
  
led led_inst(
  
    .sclk      (sclk      ),
  
    .rst_n     (rst_n ),
  
    .state     (state ),
  
  
    .po_led       (po_led    )
  
);
  
  
endmodule
  


警告:Top-Down的设计方法中层层之间要分清楚即下一级的输入一定来自上一级下一级的输出一定返回上一级。即

  
file:///C:/Users/wwt/AppData/Local/Temp/msohtmlclip1/01/clip_image016.png
  



2016年10月28日星期五

file:///C:/Users/wwt/AppData/Local/Temp/msohtmlclip1/01/clip_image018.png

10-28按键 状态机 呼吸灯.pdf

523.83 KB, 下载次数: 6

此帖出自FPGA/CPLD论坛
点赞 关注
 

回复
举报
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/6 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表