本文主要从下面四个任务的讲解中带大家逐步吃透线性序列机原理与应用

任务一:LED灯按照1/4周期进行闪烁;
任务二:让LED以亮0.25秒 ->灭0.5秒 ->亮0.75秒 ->灭1秒的规律,持续循环闪烁
任务三:以0.25秒为基本的LED状态变化间隔(最小时间单元),以8个小段为一个循环周期(参考任务2的10个小段),LED在每一小段该点亮还是熄灭,由一个8位的输入端口指定
任务四:在第三个任务的基础上,实现每隔一定时间,比如1秒钟,执行一轮LED 8个状态的切换控制

任务一:LED灯按照1/4周期进行闪烁

其实代码很简单,和之前学习三:LED灯闪烁的代码类似,只需要改动计数器的判断

Verilog代码
module led_ctrl10(
    Clk,
    Reset_n,
    Led
    );
    input Clk;
    input Reset_n;
    output reg Led;
    
    reg [25:0]counter;
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        counter<=0;
    else if (counter==50_000_000-1)
        counter<=0;
    else 
        counter<=counter+1'd1; 
        
    always@(posedge Clk or negedge Reset_n)
        if(!Reset_n)
            Led<=1'b0;
        else if(counter==0)
            Led<=1'd1;
        else if(counter==12500_000)
            Led<=1'd0;
            
        
    
endmodule
仿真代码
`timescale 1ns / 1ns
 
 
module led_ctrl10_tb(    );
    reg Clk;
    reg Reset_n;
    wire Led;
 
led_ctrl10 led_ctrl10_inst0(
    .Clk(Clk),
    .Reset_n(Reset_n),
    .Led(Led)
    );
    
    initial Clk=1;
    always #10 Clk=~Clk;
    
    initial begin
        Reset_n=0;
        #201;
        Reset_n=1;
        #2000_000_000;
        $stop;
    
    end
      
endmodule

可以看到仿真结果:

任务总结

计数器我们不仅可以取最大值,还可以取中间的任意值。可以将计数器当作一把时间的尺子。用计数器的每一个计数值当作一个刻度,从而得到各个需要的时刻。在指定的时刻,执行需要的操作。

任务二:让LED以亮0.25秒 ->灭0.5秒 ->亮0.75秒 ->灭1秒的规律,持续循环闪烁

同样是可以利用计数counter

verilog代码
module led_ctrl11(
    Clk,
    Reset_n,
    Led
    );
    input Clk;
    input Reset_n;
    output reg Led;
    parameter TIME_MS=1000;
    reg [25:0]counter;
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        counter<=0;
    else if (counter==125_000*TIME_MS-1)
        counter<=0;
    else 
        counter<=counter+1'd1; 
        
    always@(posedge Clk or negedge Reset_n)
        if(!Reset_n)
            Led<=1'b0;
        else if(counter==0)
            Led<=1'd1;
        else if(counter==12500*TIME_MS)
            Led<=1'd0;
        else if(counter==37500*TIME_MS)
            Led<=1'd1;            
        else if(counter==75000*TIME_MS)
            Led<=1'd0;        
    
endmodule
仿真代码
`timescale 1ns / 1ns
 
 
module led_ctrl11_tb(    );
    reg Clk;
    reg Reset_n;
    wire Led;
 
led_ctrl11 led_ctrl11_inst0(
    .Clk(Clk),
    .Reset_n(Reset_n),
    .Led(Led)
    );
    defparam led_ctrl11.TIME_MS=1;
    initial Clk=1;
    always #10 Clk=~Clk;
    
    initial begin
        Reset_n=0;
        #201;
        Reset_n=1;
        #20_000_000;
        $stop;
    
    end
      
endmodule

另一个思路

这个思路的核心就是把每0.25s看成一个分段。

verilog代码2

module led_ctrl11(
    Clk,
    Reset_n,
    Led
    );
    input Clk;
    input Reset_n;
    output reg Led;


    reg [26:0]counter0;
    reg [3:0]counter1;
    parameter MCNT=12500000-1;//0.25s
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        counter0<=0;
    else if (counter0==MCNT)
        counter0<=0;
    else 
        counter0<=counter0+1'd1; 
        
    always@(posedge Clk or negedge Reset_n)
            if(!Reset_n)
                counter1<=0;
            else if (counter0==MCNT)begin
            if(counter1==9)
                counter1<=0;
            else 
                counter1<=counter1+1'd1; 
            end
            else
                counter1<=counter1;
            
     always@(posedge Clk or negedge Reset_n)
          if(!Reset_n)
               Led<=0;   
        else begin
            case(counter1)
                0:Led<=1'd1;
                1:Led<=1'd0;
                2:Led<=1'd0;
                3:Led<=1'd1;
                4:Led<=1'd1;
                5:Led<=1'd1;
                6:Led<=1'd0;
                7:Led<=1'd0;
                8:Led<=1'd0;
                9:Led<=1'd0;
                default:Led<=Led;
            endcase
         end
        
        
        endmodule
仿真代码2
`timescale 1ns / 1ns
 
 
module led_ctrl11_tb(    );
    reg Clk;
    reg Reset_n;
    wire Led;
 
led_ctrl11 led_ctrl11_inst0(
    .Clk(Clk),
    .Reset_n(Reset_n),
    .Led(Led)
    );
    //defparam led_ctrl11.TIME_MS=1;
    defparam led_ctrl11.MCNT=12500-1;
    initial Clk=1;
    always #10 Clk=~Clk;
    
    initial begin
        Reset_n=0;
        #201;
        Reset_n=1;
        #20_000_000;
        $stop;
    
    end
      
endmodule

也能达到一样的效果

任务三:LED在每一小段该点亮还是熄灭,由一个8位的输入端口指定

以0.25秒为基本的LED状态变化间隔(最小时间单元),以8个小段为一个循环周期(参考任务2的10个小段),LED在每一小段该点亮还是熄灭,由一个8位的输入端口指定。

verilog代码

module led_ctrl12(
    Clk,
    Reset_n,
    SW,
    Led
    );
    input Clk;
    input Reset_n;
    input [7:0]SW;
    output reg Led;


    reg [26:0]counter0;
    reg [2:0]counter1;
    parameter MCNT=12500000-1;//0.25s
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        counter0<=0;
    else if (counter0==MCNT)
        counter0<=0;
    else 
        counter0<=counter0+1'd1; 
        
    always@(posedge Clk or negedge Reset_n)
            if(!Reset_n)
                counter1<=0;
            else if (counter0==MCNT)
                counter1<=counter1+1'd1; 

            
     always@(posedge Clk or negedge Reset_n)
          if(!Reset_n)
               Led<=0;   
        else begin
            case(counter1)
                0:Led<=SW[0];
                1:Led<=SW[1];
                2:Led<=SW[2];
                3:Led<=SW[3];
                4:Led<=SW[4];
                5:Led<=SW[5];
                6:Led<=SW[6];
                7:Led<=SW[7];
                default:Led<=Led;
            endcase
         end
endmodule
仿真代码
`timescale 1ns / 1ns
 
 
module led_ctrl12_tb(    );
    reg Clk;
    reg Reset_n;
    reg [7:0]SW;
    wire Led;
 
led_ctrl12 led_ctrl12_inst0(
    .Clk(Clk),
    .Reset_n(Reset_n),
    .SW(SW),
    .Led(Led)
    );
    //defparam led_ctrl11.TIME_MS=1;
    defparam led_ctrl12.MCNT=12500-1;
    initial Clk=1;
    always #10 Clk=~Clk;
    
    initial begin
        Reset_n=0;
        SW=8'b10101010;
        #201;
        Reset_n=1;
        #4000000;
        SW=8'b00000001;
        #20_000_000;
        $stop;
    
    end
      
endmodule
 
 
 

任务四:在第三个任务的基础上,实现每隔一定时间,比如1秒钟,执行一轮LED 8个状态的切换控制

思路一

参考任务1的解法,将1秒的空闲态和2秒的动态变化状态看成一个整体,总共3秒的时间。
写一个计时周期为3秒的计数器,将这个计数器作为一个时间刻度尺,找到1秒,1.25秒,1.5秒,1.75秒、2秒、2.25秒、2.5秒、2.75秒、3秒时对应的计数器的值。
分别在计数器等于这些值的时候,控制LED输出SW对应位,或者空闲态的0的值

思路二

这个思路比较复杂,但是非常有用,因为有时候空闲状态不一定是1秒也就是0.25秒的倍数,这时候可以考虑这种比较复杂的思路了。

用于控制LED切换间隔的0.25秒定时器counter0)
计数最大值:MCNT0=0.25*1000*1000*1000/20-1=12500000-1计数条件:1秒时间到,也就是counter2计数满,8个状态切换还没完,也就是counter1一轮没结束清零条件:计数到最大值,或空闲等待计数器(counter2)处于计数过程中。


用于计数当前输出LED第几个状态的计数器(counter1)
计数最大值:MCNT1=8-1=7
计数条件:0.25秒计数器计满清零条件:计满到计数最大值
用于空闲状态计时的计数器(counter2)
计数最犬值:MCNT2=1*1000*1000*1000/20-1=50000000-1计数条件:LED8个状态输出完成清零条件:计满到计数最大值

关键状态点en_counter2
counter2保持计数:
程序从复位转到正常运行状态后,counter2需要立即开始计数。

每次LED的8个状态输出完成后,counter2需要开始重新计数。

重新开始计数的起点位置是一轮8个状态输出完成,也就是SW7的值输出满0.25秒的时刻,也就是counter7的值为1,且0.25秒的计数器计满的的位置。

若 counter1==7 且 counter0 ==MCNT0; 则en_counter2 <=1;
counter2停止计数:
counter2计数满设定值的时候。

总结

复位时候,让en_counter2=1;

当counter1==7&&counter0=MCNT0的时候,让en_counter2=1;

当counter2==MCNT2的时候,也就是计满的时候,让en_counter2=0;

具体代码现在懒得写了,以后什么时候有精力补上来吧

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐