如何用verilog实现用两个按键分别控制四位宽的led流水灯的流向和流速?

上板后只有第一个led亮,一直没有找到问题在哪里,恳请各位指点。


`timescale 1ns / 1ps


module ztji(
    input           sysclk,
    input           rst_n,
    input           key1,
    input           key2,
    output reg [3:0]    led

    );
    parameter idle =3'b001;
    parameter S1 =3'b010;
    parameter S2 =3'b100;
    reg [2:0]now;
    reg [2:0]next;
    parameter delay_1s=50_000_000;//流水灯的1s√
    parameter delay_05s=25_000_000;//流水灯的0.5s√
    parameter delay_2s=100_000_000;//流水灯的2s√
    parameter delay_15s=75_000_000;//流水灯的1.5s√
    parameter delay_10ms=50_000_0;//消抖10ms√
    wire key_flag1;//按键一使能信号--尖峰脉冲(20ns)
    wire key_flag2;//按键二使能信号--尖峰脉冲(20ns)
    reg [18:0] cnt1;//消抖计时√
    reg [18:0] cnt2;//消抖计时√
    reg [31:0] cnt_1s;//总时间计数器
    reg [1:0]   cnt_key2;//速度按键计数
    reg[28:0]  cnt_led;//流水计时√
    //三段状态机第一段
    always@(posedge sysclk)begin
    if (!rst_n)
    now<=idle;
    else
    now<=next;
    end
    //三段状态机第二段!
   always @(*) begin
    if (!rst_n)
        next = idle;
    else case (now)
        idle: begin
            if (key_flag1 == 0)
                next = S1;
            else
                next = idle;
        end
        S1: begin
            if (key_flag1 == 0)
                next = S2;
            else
                next = S1;
        end
        S2: begin
            if (key_flag1 == 0)
                next = idle;
            else
                next = S2;
        end    
        default:
            next = idle;
    endcase
end
      //三段状态机第三段!   实现流水灯的翻转        
     always @(posedge sysclk) begin
    if (!rst_n)
        led <= 4'b0001;
    else case (now)
        idle: begin
            led <= 4'b0001;
        end
        S1: begin
            if (cnt_1s == cnt_led - 1)
                led <= {led[0], led[3:1]};
            else
                led <= led;
        end
        S2: begin
            if (cnt_1s == cnt_led - 1)
                led <= {led[2:0], led[3]};
            else
                led <= led;
        end
        default:
            next = idle;
    endcase
end
      //key1消抖计时器            
      always@(posedge sysclk)begin
        if(!rst_n)
            cnt1<=0;
        else if(key1==1'b0)begin
            if(cnt1==delay_10ms-2)
                cnt1<=cnt1;
            else
                cnt1<=cnt1+1;
            end
        else
            cnt1<=0;
    end
    assign key_flag1 = (cnt1==delay_10ms-2)?1:0;
    //key2消抖↓
    always@(posedge sysclk)begin
        if(!rst_n)
            cnt2<=0;
        else if(key2==1'b0)begin
            if(cnt2==delay_10ms-2)
                cnt2<=cnt2;
            else
                cnt2<=cnt2+1;
            end
        else
            cnt2<=0;
    end
    assign key_flag2 = (cnt2==delay_10ms-2)?1:0;
         //key2按键计数模块↓
     always@(posedge sysclk)begin 
       if(!rst_n)
       cnt_key2<=0;
  else if(key_flag2==0)
        cnt_key2<=cnt_key2+1;
 
 else
        cnt_key2<=cnt_key2;
        end
      //流水灯流速计时模块
     always@(posedge sysclk)begin
        if(!rst_n)
       cnt_led<=delay_1s;
       else case(cnt_key2)
       2'b00:cnt_led<=delay_05s;
       2'b01:cnt_led<=delay_1s;
       2'b10:cnt_led<=delay_15s;
       2'b11:cnt_led<=delay_2s;
       default: cnt_led <= delay_1s;
       endcase  
       end
//总流水计时器
        always@(posedge sysclk)begin
    if(!rst_n)
        cnt_1s<=0;
    else if( cnt_1s==cnt_led-1)
       cnt_1s<=0;
    else
      cnt_1s<=cnt_1s+1;      
    end
    
    
endmodule

在rst_n为0时,给key_flag1,key_flag2,cnt_1s和cnt_led赋初值,例如0。
在rst_n为1时,根据按键输入和计数器值更新key_flag1,key_flag2,cnt_1s和cnt_led的值。
试下

采用chatgpt:
在你提供的Verilog代码中,我发现了几个问题,可能导致你的LED流水灯没有按预期工作。

1、你没有为 sysclk 和 rst_n 信号添加时钟和复位控制。在模块声明中,你定义了 sysclk 作为输入信号,但没有使用它来驱动其他逻辑。你需要将 sysclk 用作状态机和计时器的时钟。同时,你需要确保 rst_n 信号在系统复位时正确初始化内部寄存器和计数器。

2、按键的消抖计时器没有正确工作。你需要在按下按键后开始计时,并在计时达到一定值后产生按键有效的信号。在你的代码中,你对 cnt1 和 cnt2 的计数值进行了比较,但在计时达到 delay_10ms-2 时并没有停止计数,而是继续增加计数值。你需要修改计数器的逻辑,确保计数在达到一定值后停止,并根据计数器的值生成消抖后的按键有效信号。

3、cnt_key2 计数器的逻辑有误。在你的代码中,你使用 key_flag2 作为 cnt_key2 的控制信号。但实际上,key_flag2 是按键消抖后的有效信号,而不是用于控制速度按键计数的信号。你应该使用按键消抖后的按键有效信号作为 cnt_key2 的时钟触发,以确保计数器在按键按下时递增。

4、在状态机的第三段中,你有一个错误的 default 分支语句,将 next 赋值给 idle,而不是 led。你应该将 led 赋值给 led。

以下是经过修改的代码示例,修复了上述问题:

`timescale 1ns / 1ps

module ztji (
  input           sysclk,
  input           rst_n,
  input           key1,
  input           key2,
  output reg [3:0] led
);

  parameter idle = 3'b001;
  parameter S1   = 3'b010;
  parameter S2   = 3'b100;

  reg [2:0] now;
  reg [2:0] next;

  parameter delay_1s   = 50_000_000;  // 流水灯的1s
  parameter delay_05s  = 25_000_000;  // 流水灯的0.5s
  parameter delay_2s   = 100_000_000; // 流水灯的2s
  parameter delay_15s  = 75_000_000;  // 流水灯的1.5s
  parameter delay_10ms = 50_000;      // 消抖10ms

  wire key_flag1; // 按键一使能信号--尖峰脉冲(20ns)
  wire key_flag2; // 按键二使能信号--尖峰脉冲(20ns)

  reg [18:0] cnt1;       // 消抖计时
  reg [18:0] cnt2;       // 消抖计时
  reg [31:0] cnt_1s;     // 总时间计数器
  reg [1:0]  cnt_key2;   // 速度按键计数
  reg [28:0] cnt_led;    // 流水计时

  // 三段状态机第一段
  always @(posedge sysclk) begin
    if (!rst_n)
      now <= idle;
    else
      now <= next;
  end

  // 三段状态机第二段
  always @(*) begin
    if (!rst_n)
      next = idle;
    else case (now)
      idle: begin
        if (key_flag1 == 1'b1)
          next = S1;
        else
          next = idle;
      end
      S1: begin
        if (key_flag1 == 1'b1)
          next = S2;
        else
          next = S1;
      end
      S2: begin
        if (key_flag1 == 1'b1)
          next = idle;
        else
          next = S2;
      end    
      default:
        next = idle;
    endcase
  end

  // 三段状态机第三段,实现流水灯的翻转        
  always @(posedge sysclk) begin
    if (!rst_n)
      led <= 4'b0001;
    else case (now)
      idle: begin
        led <= 4'b0001;
      end
      S1: begin
        if (cnt_1s == cnt_led - 1)
          led <= {led[0], led[3:1]};
      end
      S2: begin
        if (cnt_1s == cnt_led - 1)
          led <= {led[2:0], led[3]};
      end
      default:
        led <= led;
    endcase
  end

  // key1消抖计时器            
  always @(posedge sysclk) begin
    if (!rst_n)
      cnt1 <= 0;
    else if (key1 == 1'b0) begin
      if (cnt1 == delay_10ms - 1)
        cnt1 <= 0;
      else
        cnt1 <= cnt1 + 1;
    end
  end
  assign key_flag1 = (cnt1 == delay_10ms - 1) ? 1'b1 : 1'b0;

  // key2消抖
  always @(posedge sysclk) begin
    if (!rst_n)
      cnt2 <= 0;
    else if (key2 == 1'b0) begin
      if (cnt2 == delay_10ms - 1)
        cnt2 <= 0;
      else
        cnt2 <= cnt2 + 1;
    end
  end
  assign key_flag2 = (cnt2 == delay_10ms - 1) ? 1'b1 : 1'b0;

  // key2按键计数模块
  always @(posedge key_flag2) begin
    if (!rst_n)
      cnt_key2 <= 0;
    else
      cnt_key2 <= cnt_key2 + 1;
  end

  // 流水灯流速计时模块
  always @(posedge sysclk) begin
    if (!rst_n)
      cnt_led <= delay_1s;
    else case (cnt_key2)
      2'b00: cnt_led <= delay_05s;
      2'b01: cnt_led <= delay_1s;
      2'b10: cnt_led <= delay_15s;
      2'b11: cnt_led <= delay_2s;
      default: cnt_led <= delay_1s;
    endcase  
  end

  // 总流水计时器
  always @(posedge sysclk) begin
    if (!rst_n)
      cnt_1s <= 0;
    else if (cnt_1s == cnt_led - 1)
      cnt_1s <= 0;
    else
      cnt_1s <= cnt_1s + 1;      
  end

endmodule

以上修改仅涉及你提供的代码中的问题,并没有对其他可能存在的问题进行检查。

根据您提供的Verilog代码,出现问题的可能原因可能是以下几个方面:

  1. 时钟信号:确保sysclk信号正常工作并与目标板上的时钟信号同步。

  2. 复位信号:确保rst_n信号正确地进行复位和恢复。

  3. 输入按键:检查输入按键信号的连接和触发条件,确保在按下按键时产生正确的触发信号。

  4. 时序问题:检查时序相关的部分,如延时计数器和状态转换的时序是否正确。

  5. LED输出:确保LED输出信号与目标板上的LED连接正确,并且输出逻辑正确。

在调试代码时,您可以通过使用仿真工具来验证电路的功能和时序。使用仿真可以帮助您观察信号的波形、调试时序问题和验证代码逻辑。

如果问题仍然存在,请检查和调试每个模块的功能,确保输入和输出信号的正确性,并逐步进行排除故障。

此外,还请确保硬件平台与代码的要求和限制相匹配,并根据实际情况进行适当的调整。

请注意,由于无法提供完整的环境和硬件设置,以上建议仅供参考。对于更具体的问题和调试步骤,您可能需要参考硬件平台和开发工具的文档,或咨询相关的技术支持。

按键控制 LED 灯实验
按键控制 led 模块的代码

1 module key_led(
2 input sys_clk , //50Mhz 系统时钟
3 input sys_rst_n, //系统复位,低有效
4 input [3:0] key, //按键输入信号
5 output reg [3:0] led //LED 输出信号
6 );
7 
8 //reg define 
9 reg [23:0] cnt;
10 reg [1:0] led_control;
11 
12 //用于计数 0.2s 的计数器
13 always @ (posedge sys_clk or negedge sys_rst_n) begin
14 if(!sys_rst_n)
15 cnt<=24'd9_999_999;
16 else if(cnt<24'd9_999_999)
17 cnt<=cnt+1;
18 else
19 cnt<=0;
20 end
21 
22 //用于 led 灯状态的选择
23 always @(posedge sys_clk or negedge sys_rst_n) begin
24 if (!sys_rst_n)
25 led_control <= 2'b00;
26 else if(cnt == 24'd9_999_999)
27 led_control <= led_control + 1'b1;
28 else
29 led_control <= led_control;
30 end
31 
32 //识别按键,切换显示模式
33 always @(posedge sys_clk or negedge sys_rst_n) begin
34 if(!sys_rst_n) begin
35 led<=4'b 0000;
36 end
37 else if(key[0]== 0) //按键 1 按下时,从右向左的流水灯效果
38 case (led_control)
39 2'b00 : led<=4'b1000;
40 2'b01 : led<=4'b0100;
41 2'b10 : led<=4'b0010;
42 2'b11 : led<=4'b0001;
43 default : led<=4'b0000;
44 endcase
45 else if (key[1]==0) //按键 2 按下时,从左向右的流水灯效果
46 case (led_control)
47 2'b00 : led<=4'b0001;
48 2'b01 : led<=4'b0010;
49 2'b10 : led<=4'b0100;
50 2'b11 : led<=4'b1000;
51 default : led<=4'b0000;
52 endcase
53 else if (key[2]==0) //按键 3 按下时,LED 闪烁
54 case (led_control)
55 2'b00 : led<=4'b1111;
56 2'b01 : led<=4'b0000;
57 2'b10 : led<=4'b1111;
58 2'b11 : led<=4'b0000;
59 default : led<=4'b0000;
60 endcase
61 else if (key[3]==0) //按键 4 按下时,LED 全亮
62 led=4'b1111;
63 else
64 led<=4'b0000; //无按键按下时,LED 熄灭 
65 end
66 
67 endmodule

你的Verilog代码存在几个问题,可能导致你的流水灯无法正常工作:

  • 错误的 always 块:在第三段的状态机中,你的第三个 always @(posedge sysclk) 块中的 default 分支应该是对 led 的赋值,而不是对 next 的赋值。修改为 led <= led。
  • else 分支中的 next 赋值:在第二段的状态机中,else 分支中的 next 赋值应该是对 next 的赋值,而不是对 now 的赋值。修改为 next = next。
  • always @(posedge sysclk) 块中的 next 赋值:在第二段的状态机中,你可以将 always @(posedge sysclk) 块中的 next = next 修改为 now = next,以确保状态的正确转换。
  • always @(posedge sysclk) 块中的 else 分支:在 always @(posedge sysclk) 块中,对于没有变化的变量,可以直接将 else 分支赋值为原值,而不需要显式写出。
  • 引脚声明和连接:在模块的引脚声明中,确保你正确地将 sysclk、rst_n、key1 和 key2 与实际的输入信号连接起来,并将 led 与实际的 LED 输出引脚连接起来。
  • always @() 块中的 case 语句:在 always @() 块中,你可以使用 casez 语句来简化对 key_flag1 和 key_flag2 的赋值。例如,key_flag1 = (~key1) & {1'b1, cnt1[17:1]} == 18'b1_1111_1111_1111_1110。
  • 消抖计时器:你的消抖计时器的逻辑是正确的,但是注意消抖计时器的触发条件和计数器的范围是否正确。
  • 计时器的初始值:在模块的起始处,你可以为 cnt1、cnt2、cnt_key2 和 cnt_led 设置适当的初始值。

请仔细检查并修复上述问题,然后重新运行代码,看看是否能够解决你的问题。此外,确保在仿真或实际硬件测试之前对代码进行适当的验证和调试,以确保逻辑正确性。

您好,我是有问必答小助手,您的问题已经有小伙伴帮您解答,感谢您对有问必答的支持与关注!
PS:问答VIP年卡 【限时加赠:IT技术图书免费领】,了解详情>>> https://vip.csdn.net/askvip?utm_source=1146287632

根据你提供的Verilog代码,以下是我对你的代码进行的一些修改和建议:

  1. 输入信号命名约定:根据常规的Verilog命名约定,建议将输入信号的名称以小写字母开头,例如sysclk改为sysClkrst_n改为rstNkey1改为key1key2改为key2

  2. 信号宽度定义:建议为信号指定明确的宽度。例如,你定义的nownext信号的宽度为3位,可以将其定义为reg [2:0] nowreg [2:0] next,以明确表示信号的位宽。

  3. 状态机实现:在你的代码中,状态机的第三段部分存在问题。在always @(posedge sysclk)块中,你使用了next来驱动led信号,但是应该使用now来决定led的值。请修改代码,将now用于驱动led

  4. 计数器的初始化:在计数器模块中,建议在复位时明确初始化计数器的值,例如将cnt1 <= 0;cnt2 <= 0;添加到if(!rst_n)条件中。

  5. 信号赋值的else分支:在你的代码中,有一些赋值语句的else分支是赋值给自身,例如cnt1 <= cnt1;。这是不必要的,可以直接省略这些else分支,因为在没有赋值的情况下,信号会保持其先前的值。

  6. 注释标记:你的代码中有一些注释标记是不正确的,例如消抖计时器注释的位置。请确保注释标记对应于其下方的代码段。

请根据上述建议修改你的Verilog代码,并进行综合和仿真以验证修复后的代码。如果问题仍然存在,请提供更多的细节和相关的仿真结果或错误信息,以便我能够更好地帮助你解决问题。