• 北邮22级信通院数电:Verilog-FPGA(4)第三周实验:按键消抖、呼吸灯、流水灯 操作流程&&注意事项


    北邮22信通一枚~

    跟随课程进度更新北邮信通院数字系统设计的笔记、代码和文章

    持续关注作者 迎接数电实验学习~

    获取更多文章,请访问专栏:

    北邮22级信通院数电实验_青山如墨雨如画的博客-CSDN博客

    目录

    一.注意事项

    二.按键消抖

    2.1  LED_debounce代码

    2.2debounce.v代码

     2.3管脚分配

    三.流水灯

    3.1  LED_flash.v代码

    3.2 divide.v代码

    3.3decode38.v代码

    3.4管脚分配

    四.呼吸灯

    4.1 LED_breath.v代码

     4.2管脚分配


    一.注意事项

    烧录之前首先检查这几个参数是否调整完毕:

     

    没调的赶紧去调!!!

    二.按键消抖

    2.1  LED_debounce代码

    1. module LED_debounce (clk,rst,key,led);
    2. input clk;
    3. input rst;
    4. input key;
    5. output reg led;
    6. wire key_pulse;
    7. //当按键按下时产生一个高脉冲,翻转一次led
    8. always @(posedge clk or negedge rst)
    9. begin
    10. if (!rst)
    11. led <= 1'b1;
    12. else if (key_pulse)
    13. led <= ~led;
    14. else
    15. led <= led;
    16. end
    17. //例化消抖module,这里没有传递参数N,采用了默认的N=1
    18. debounce u1 (
    19. .clk (clk),
    20. .rst (rst),
    21. .key (key),
    22. .key_pulse (key_pulse)
    23. );
    24. endmodule

    2.2debounce.v代码

    1. module debounce (clk,rst,key,key_pulse);
    2. parameter N = 1; //要消除的按键的数量
    3. input clk;
    4. input rst;
    5. input [N-1:0] key; //输入的按键
    6. output [N-1:0] key_pulse; //按键动作产生的脉冲
    7. reg [N-1:0] key_rst_pre; //定义一个寄存器型变量存储上一个触发时的按键值
    8. reg [N-1:0] key_rst; //定义一个寄存器变量储存储当前时刻触发的按键值
    9. wire [N-1:0] key_edge; //检测到按键由高到低变化是产生一个高脉冲
    10. //利用非阻塞赋值特点,将两个时钟触发时按键状态存储在两个寄存器变量中
    11. always @(posedge clk or negedge rst)
    12. begin
    13. if (!rst) begin
    14. key_rst <= {N{1'b1}}; //初始化时给key_rst赋值全为1,{}中表示N个1
    15. key_rst_pre <= {N{1'b1}};
    16. end
    17. else begin
    18. key_rst <= key; //第一个时钟上升沿触发之后key的值赋给key_rst,
    19. //同时key_rst的值赋给key_rst_pre
    20. key_rst_pre <= key_rst; //非阻塞赋值。
    21. //相当于经过两个时钟触发,
    22. //key_rst存储的是当前时刻key的值,
    23. //key_rst_pre存储的是前一个时钟的key的值
    24. end
    25. end
    26. assign key_edge = key_rst_pre & (~key_rst);//脉冲边沿检测。
    27. //key检测到下降沿时,
    28. //key_edge产生一个时钟周期的高电平
    29. reg [17:0] cnt; //产生延时所用的计数器,系统时钟12MHz,
    30. //要延时20ms左右时间,至少需要18位计数器
    31. //产生20ms延时,当检测到key_edge有效是计数器清零开始计数
    32. always @(posedge clk or negedge rst)
    33. begin
    34. if(!rst)
    35. cnt <= 18'h0;
    36. else if(key_edge)
    37. cnt <= 18'h0;
    38. else
    39. cnt <= cnt + 1'h1;
    40. end
    41. reg [N-1:0] key_sec_pre; //延时后检测电平寄存器变量
    42. reg [N-1:0] key_sec;
    43. //延时后检测key,如果按键状态变低产生一个时钟的高脉冲。如果按键状态是高的话说明按键无效
    44. always @(posedge clk or negedge rst)
    45. begin
    46. if (!rst)
    47. key_sec <= {N{1'b1}};
    48. else if (cnt==18'h3ffff)
    49. key_sec <= key;
    50. end
    51. always @(posedge clk or negedge rst)
    52. begin
    53. if (!rst)
    54. key_sec_pre <= {N{1'b1}};
    55. else
    56. key_sec_pre <= key_sec;
    57. end
    58. assign key_pulse = key_sec_pre & (~key_sec);
    59. endmodule

     2.3管脚分配

    三.流水灯

    3.1  LED_flash.v代码

    1. module LED_flash(clk,rst,led);
    2. input clk,rst;
    3. output [7:0] led;
    4. reg [2:0] cnt ; //定义了一个3位的计数器,输出可以作为3-8译码器的输入
    5. wire clk1h; //定义一个中间变量,表示分频得到的时钟,用作计数器的触发
    6. //例化module decode38,相当于调用
    7. decode38 u1 (
    8. .sw(cnt), //例化的输入端口连接到cnt,输出端口连接到led
    9. .led(led)
    10. );
    11. //例化分频器模块,产生一个1Hz时钟信号
    12. divide #(.WIDTH(32),.N(12000000)) u2 ( //传递参数
    13. .clk(clk),
    14. .rst_n(rst), //例化的端口信号都连接到定义好的信号
    15. .clkout(clk1h)
    16. );
    17. //1Hz时钟上升沿触发计数器,循环计数
    18. always @(posedge clk1h or negedge rst)
    19. if (!rst)
    20. cnt <= 0;
    21. else
    22. cnt <= cnt +1;
    23. endmodule

    3.2 divide.v代码

    1. module divide ( clk,rst_n,clkout);
    2. input clk,rst_n; //输入信号,其中clk连接到FPGA的C1脚,频率为12MHz
    3. output clkout; //输出信号,可以连接到LED观察分频的时钟
    4. //parameter是verilog里常数语句
    5. parameter WIDTH = 3; //计数器的位数,计数的最大值为 2**WIDTH-1
    6. parameter N = 5; //分频系数,请确保 N < 2**WIDTH-1,否则计数会溢出
    7. reg [WIDTH-1:0] cnt_p,cnt_n; //cnt_p为上升沿触发时的计数器,cnt_n为下降沿触发时的计数器
    8. reg clk_p,clk_n; //clk_p为上升沿触发时分频时钟,clk_n为下降沿触发时分频时钟
    9. //上升沿触发时计数器的控制
    10. always @ (posedge clk or negedge rst_n )
    11. //posedge和negedge是verilog表示信号上升沿和下降沿
    12. //当clk上升沿来临或者rst_n变低的时候执行一次always里的语句
    13. begin
    14. if(!rst_n)
    15. cnt_p<=0;
    16. else if (cnt_p==(N-1))
    17. cnt_p<=0;
    18. else cnt_p<=cnt_p+1;//计数器一直计数,当计数到N-1的时候清零,这是一个模N的计数器
    19. end
    20. //上升沿触发的分频时钟输出,如果N为奇数得到的时钟占空比不是50%;如果N为偶数得到的时钟占空比为50%
    21. always @ (posedge clk or negedge rst_n)
    22. begin
    23. if(!rst_n)
    24. clk_p<=0;
    25. else if (cnt_p<(N>>1)) //N>>1表示右移一位,相当于除以2去掉余数
    26. clk_p<=0;
    27. else
    28. clk_p<=1; //得到的分频时钟正周期比负周期多一个clk时钟
    29. end
    30. //下降沿触发时计数器的控制
    31. always @ (negedge clk or negedge rst_n)
    32. begin
    33. if(!rst_n)
    34. cnt_n<=0;
    35. else if (cnt_n==(N-1))
    36. cnt_n<=0;
    37. else cnt_n<=cnt_n+1;
    38. end
    39. //下降沿触发的分频时钟输出,和clk_p相差半个时钟
    40. always @ (negedge clk)
    41. begin
    42. if(!rst_n)
    43. clk_n<=0;
    44. else if (cnt_n<(N>>1))
    45. clk_n<=0;
    46. else
    47. clk_n<=1; //得到的分频时钟正周期比负周期多一个clk时钟
    48. end
    49. assign clkout = (N==1)?clk:(N[0])?(clk_p&clk_n):clk_p;
    50. //条件判断表达式
    51. //当N=1时,直接输出clk
    52. //当N为偶数也就是N的最低位为0,N(0=0,输出clk_p
    53. //当N为奇数也就是N最低位为1,N(0=1,输出clk_p&clk_n。
    54. //正周期多所以是相与
    55. endmodule

    3.3decode38.v代码

    1. module decode38 (sw,led);
    2. input [2:0] sw; //开关输入信号,利用了其中3个开关作为3-8译码器的输入
    3. output [7:0] led; //输出信号控制特定LED
    4. reg [7:0] led; //定义led为reg型变量,在always过程块中只能对reg型变量赋值
    5. //always过程块,括号中sw为敏感变量,当sw变化一次执行一次always中所有语句,否则保持不变
    6. always @ (sw)
    7. begin
    8. case(sw) //case语句,一定要跟default语句
    9. 3'b000: led=8'b0111_1111; //条件跳转,其中“_”下划线只是为了阅读方便,无实际意义
    10. 3'b001: led=8'b1011_1111; //位宽'进制+数值是Verilog里常数的表达方法,
    11. //进制可以是b、o、d、h(二、八、十、十六进制)
    12. 3'b010: led=8'b1101_1111;
    13. 3'b011: led=8'b1110_1111;
    14. 3'b100: led=8'b1111_0111;
    15. 3'b101: led=8'b1111_1011;
    16. 3'b110: led=8'b1111_1101;
    17. 3'b111: led=8'b1111_1110;
    18. default: ;
    19. endcase
    20. end
    21. endmodule

    3.4管脚分配

    四.呼吸灯

    4.1 LED_breath.v代码

    1. module LED_breath(clk,rst,led);
    2. input clk; //系统时钟输入
    3. input rst; //复位输出
    4. output led; //led输出
    5. reg [24:0] cnt1; //计数器1
    6. reg [24:0] cnt2; //计数器2
    7. reg flag; //呼吸灯变亮和变暗的标志位
    8. //parameter CNT_NUM = 2400; //计数器的最大值 period = (2400^2)*2 ~= 12000000 = 1s由亮到暗0.5s,由暗到亮0.5s
    9. parameter CNT_NUM = 3464; //计数器的最大值 period = (3464^2)*2 ~= 24000000 = 2s由亮到暗1s,由暗到亮1s
    10. //产生计数器cnt1
    11. always@(posedge clk or negedge rst) begin
    12. if(!rst) begin
    13. cnt1<=13'd0;
    14. end
    15. else begin
    16. if(cnt1>=CNT_NUM-1)
    17. cnt1<=1'b0;
    18. else
    19. cnt1<=cnt1+1'b1;
    20. end
    21. end
    22. //产生计数器cnt2
    23. always@(posedge clk or negedge rst) begin
    24. if(!rst) begin
    25. cnt2<=13'd0;
    26. flag<=1'b0;
    27. end
    28. else begin
    29. if(cnt1==CNT_NUM-1) begin //当计数器1计满时计数器2开始计数加一或减一
    30. if(!flag) begin //当标志位为0时计数器2递增计数,表示呼吸灯效果由暗变亮
    31. if(cnt2>=CNT_NUM-1) //计数器2计满时,表示亮度已最大,
    32. //标志位变高,之后计数器2开始递减
    33. flag<=1'b1;
    34. else
    35. cnt2<=cnt2+1'b1;
    36. end else begin //当标志位为高时计数器2递减计数
    37. if(cnt2<=0) //计数器2级到0,表示亮度已最小,标志位变低,之后计数器2开始递增
    38. flag<=1'b0;
    39. else
    40. cnt2<=cnt2-1'b1;
    41. end
    42. end
    43. else cnt2<=cnt2;//计数器1在计数过程中计数器2保持不变
    44. end
    45. end
    46. //比较计数器1和计数器2的值产生自动调整占空比输出的信号,输出到led产生呼吸灯效果
    47. assign led = (cnt1b0:1'b1;
    48. endmodule

     4.2管脚分配

  • 相关阅读:
    文章翻译软件-批量免费翻译软件支持各大翻译接口
    【总结】各层的校验和的特点
    OceanBase开发者大会2023届视频及PPT汇总
    4创建iSCSI共享存储
    图 最小生成树 leetcode 1584. 连接所有点的最小费用
    day24-服务器端渲染技术02
    【linux问题记录】
    Tomcat基础与优化
    音乐格式转换mp3怎么转?跟着步骤操作一遍
    【研0需要知道的那些事01】如何判断期刊是否为核心期刊?
  • 原文地址:https://blog.csdn.net/bc202205/article/details/133692695