【FPGA】从计数器到可控线性序列机

小边要日更!flag已倒。我不是把所有知识点一股脑塞给你,而是从实践中逐渐熟悉!

LED实验进化六部曲:

  1. LED亮0.25s,灭0.75s的状态循环亮灭
  2. LED亮0.25s,灭0.5s,亮0.75秒,灭1s的方式循环亮灭
  3. 让LED灯按照指定的亮灭模式亮灭,亮灭模式由用户随机指定。以0.25s为一个变化周期,8个变化周期为一个循环
  4. 让LED灯按照指定的亮灭模式亮灭,亮灭模式由用户随机指定。8个变化周期为一个循环,变化是兼职由不同应用场景决定
  5. 让多个LED灯按照设置的模式各自在一个变化循环内独立亮灭变化
  6. 每隔10ms,让LED灯的一个8状态循环执行一次(每隔状态变化时间值下一点,方便测试,比如设置为10us)

热身 —— 亮灭各0.5s 直接把我们之前的flash贴过来改两下子

couter_led_0.v

module counter_led_0 (
    Clk,
    Reset_n,
    Led
);
    input Clk;
    input Reset_n;
    output reg Led;
    
    reg[25:0] counter;
    
    parameter MCNT = 50000000;
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        counter <= 0;
    else if(counter == MCNT-1) //49999999
        counter <= 0;
    else
        counter <= counter + 1'b1;
            
    always@(posedge Clk or negedge Reset_n) 
    if(!Reset_n)
        Led <=0;
    else if(counter == MCNT/2 -1) //24999999
        Led <= 1;
    else if(counter == MCNT-1)
        Led <= 0;
        
endmodule

couter_led_0_tb.v

我们依旧把时间缩小1000倍,也就是ms变us

`timescale 1ns/1ns

module counter_led_0_tb();
    //激励信号
    reg Clk;
    reg Reset_n;
    wire Led;
    
    counter_led_0
    #(
        .MCNT(50000) 
    )
    counter_led_0_inst(
        //连线
        .Clk(Clk),
        .Reset_n(Reset_n), 
        .Led(Led)
    );
    
    initial Clk = 1;
    always #10 Clk = !Clk; //每延时10s,翻转
    
    initial begin
        Reset_n = 0;
        #201;
        Reset_n = 1;
        #2000000000;
        $stop;
    end
endmodule
image-20220921154325464

1. episode 1

LED亮0.25s,灭0.75s的状态循环亮灭

counter_led_1.v

module counter_led_1 (
    Clk,
    Reset_n,
    Led
);
    input Clk;
    input Reset_n;
    output reg Led;
    
    reg[25:0] counter;
    
    parameter MCNT = 50000000;
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        counter <= 0;
    else if(counter == MCNT-1) //49999999
        counter <= 0;
    else
        counter <= counter + 1'b1;
            
    always@(posedge Clk or negedge Reset_n) 
    if(!Reset_n)
        Led <=0;
    else if(counter == MCNT/2 + MCNT/4 -1) //24999999
        Led <= 1;
    else if(counter == MCNT-1)
        Led <= 0;
        
endmodule
image-20220921154723196

2. episode 2

LED亮0.25s,灭0.5s,亮0.75秒,灭1s的方式循环亮灭

2.5s一个周期,那么就需要计数2500000000ns/20ns = 125000000次

image-20220921155749801

划分为 —— 1/10T, 1/5T, 3/5T, 2/5T

module counter_led_2 (
    Clk,
    Reset_n,
    Led
);
    input Clk;
    input Reset_n;
    output reg Led;
    
    reg[26:0] counter;
    
    parameter MCNT = 125000000;
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        counter <= 0;
    else if(counter == MCNT-1) 
        counter <= 0;
    else
        counter <= counter + 1'b1;
            
    always@(posedge Clk or negedge Reset_n) 
    if(!Reset_n)
        Led <=1;
    else if(counter == MCNT/10 -1) //过0.25s灭掉
        Led <= 0;
    else if(counter == MCNT/10 + MCNT/5-1) //过0.5s后再亮
        Led <= 1;
    else if(counter == MCNT/10 + MCNT/5 + 3*MCNT/10-1) //过0.75s灭掉
        Led <= 0;
     else if(counter == MCNT-1) //过1s后再亮
        Led <= 1;
endmodule

别忘了把仿真文件改成1250000啊,2.5s → 0.025s = 25ms

image-20220921163354015

哎!好像挺简单?

3. episode 3

让LED灯按照指定的亮灭模式亮灭,亮灭模式由用户随机指定。以0.25s为一个变化周期,8个变化周期为一个循环

怎么指定亮灭模式??那么我们需要有一个输入端口Ctrl来指定亮灭~~

一个周期2s,则计数器需计数2000,000,000ns/20ns = 100,000,000次

image-20220921164536814
module counter_led_3 (
    Clk,
    Reset_n,
    Ctrl,
    Led
);
    input Clk;
    input Reset_n;
    input[7:0] Ctrl;
    output reg Led;
    
    reg[29:0] counter;
    
    parameter MCNT = 100000000;
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        counter <= 0;
    else if(counter == MCNT-1) 
        counter <= 0;
    else
        counter <= counter + 1'b1;
            
    always@(posedge Clk or negedge Reset_n) 
    if(!Reset_n)
        Led <=0;
    else if(counter == MCNT/8 - 1)
        Led <= Ctrl[0];
    else if(counter == 2*MCNT/8 - 1)
        Led <= Ctrl[1];
    else if(counter == 3*MCNT/8 - 1)
        Led <= Ctrl[2];
    else if(counter == 4*MCNT/8 - 1)
        Led <= Ctrl[3];
    else if(counter == 5*MCNT/8 - 1)
        Led <= Ctrl[4];
    else if(counter == 6*MCNT/8 - 1)
        Led <= Ctrl[5];
    else if(counter == 7*MCNT/8 - 1)
        Led <= Ctrl[6];
     else if(counter == MCNT - 1)
        Led <= Ctrl[7];
        
endmodule

这个仿真文件要重新写啦~ 另外别忘了右键set as top哦

`timescale 1ns/1ns

module counter_led_3_tb();
    //激励信号
    reg Clk;
    reg Reset_n;
    reg[7:0] Ctrl;
    wire Led;
    
    counter_led_3
    #(
        .MCNT(100000) 
    )
    counter_led_2_inst(
        //连线
        .Clk(Clk),
        .Reset_n(Reset_n),
        .Ctrl(Ctrl) ,
        .Led(Led)
    );
    
    initial Clk = 1; 
    always #10 Clk = !Clk; //每延时10s,翻转
    
    initial begin
        Reset_n = 0;
        Ctrl = 0;
        #201;
        Reset_n = 1;
        Ctrl = 8'b1010_0101;
        #2000000000;
        $stop;
    end
endmodule
image-20220921190050077

计数器中的每个值都可以作为刻度标尺。

是的,咱们现在这个if - else 太多了,我们也可以改成case-endcase

    else case(counter) 
        1*MCNT/8 - 1:   Led <= Ctrl[0];
        2*MCNT/8 - 1:   Led <= Ctrl[1];
        3*MCNT/8 - 1:   Led <= Ctrl[2];
        4*MCNT/8 - 1:   Led <= Ctrl[3];
        5*MCNT/8 - 1:   Led <= Ctrl[4];
        6*MCNT/8 - 1:   Led <= Ctrl[5];
        7*MCNT/8 - 1:   Led <= Ctrl[6];
        MCNT - 1:           Led <= Ctrl[7];      
        default:   Led <= Led;
    endcase

4. episode 4

让LED灯按照指定的亮灭模式亮灭,亮灭模式由用户随机指定。8个变化周期为一个循环,变化周期(每个变化周期相同)是由不同应用场景决定—— 依然通过端口

episode1-3都是采用“化整为零”的思路,而现在我们“凑零为整”,思路类似于3-8译码器:即counter记录时间流逝,counter2记录8个状态,合并起来即为一个循环。

counter_led_4.v

module counter_led_4 (
    Clk,
    Reset_n,
    Ctrl,
    Time, //设置周期
    Led
);
    input Clk;
    input Reset_n;
    input[7:0] Ctrl;
    input[31:0] Time;
    output reg Led;
    
    reg[31:0] counter;
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        counter <= 0;
    else if(counter == Time-1) 
        counter <= 0;
    else
        counter <= counter + 1'b1;
            
    reg[2:0] counter2;
    always@(posedge Clk or negedge Reset_n) 
    if(!Reset_n)
        counter2 <= 0;
    else if(counter == Time-1) //计满一个小区间+1,且counter2计满自动清0
        counter2 <= counter2 + 1'b1;
      
    always@(posedge Clk or negedge Reset_n) 
    if(!Reset_n)  
         Led <= 0;
    else case(counter2) 
        0:   Led <= Ctrl[0];
        1:   Led <= Ctrl[1];
        2:   Led <= Ctrl[2];
        3:   Led <= Ctrl[3];
        4:   Led <= Ctrl[4];
        5:   Led <= Ctrl[5];
        6:   Led <= Ctrl[6];
        7:   Led <= Ctrl[7];
    default:   Led <= Led;
    endcase
      
endmodule

counter_led_4_tb.v

`timescale 1ns/1ns

module counter_led_4_tb();
    //激励信号
    reg Clk;
    reg Reset_n;
    reg[7:0] Ctrl;
    reg[31:0] Time;
    wire Led;
    
    counter_led_4 counter_led_4_inst(
        //连线
        .Clk(Clk),
        .Reset_n(Reset_n),
        .Ctrl(Ctrl) ,
        .Time(Time),
        .Led(Led)
    );
    
    initial Clk = 1; 
    always #10 Clk = !Clk; //每延时10s,翻转
    
    initial begin
        Reset_n = 0;
        Ctrl = 0;
        Time = 0;
        #201;
        Reset_n = 1;
        Time = 2500;
        Ctrl = 8'b1010_0101;
        #2000000000;
        $stop;
    end
endmodule

500us → 50us

image-20220921202816474

5. episode 5

让多个LED灯按照设置的模式各自在一个变化循环内独立亮灭变化

我们改成2个灯泡~ 设置2个Ctrl来分别控制灯的亮灭 —— 同时控制多个信号

counter_led_5.v

module counter_led_5 (
    Clk,
    Reset_n,
    CtrlA,
    CtrlB,
    Time, //设置周期
    Led
);
    input Clk;
    input Reset_n;
    input[7:0] CtrlA, CtrlB;
    input[31:0] Time;
    output reg [1:0]Led;
    
    reg[31:0] counter;
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        counter <= 0;
    else if(counter == Time-1) 
        counter <= 0;
    else
        counter <= counter + 1'b1;
            
    reg[2:0] counter2;
    always@(posedge Clk or negedge Reset_n) 
    if(!Reset_n)
        counter2 <= 0;
    else if(counter == Time-1) //计满一个小区间+1,且counter2计满自动清0
        counter2 <= counter2 + 1'b1;
      
    always@(posedge Clk or negedge Reset_n) 
    if(!Reset_n)  
         Led <= 0;
    else case(counter2) 
        0:  begin Led[0] <= CtrlA[0];  Led[1] <= CtrlB[0];	end
        1:  begin Led[0] <= CtrlA[1];  Led[1] <= CtrlB[1];	end
        2:  begin Led[0] <= CtrlA[2];  Led[1] <= CtrlB[2];	end
        3:  begin Led[0] <= CtrlA[3];  Led[1] <= CtrlB[3];	end
        4:  begin Led[0] <= CtrlA[4];  Led[1] <= CtrlB[4];	end
        5:  begin Led[0] <= CtrlA[5];  Led[1] <= CtrlB[5];	end
        6:  begin Led[0] <= CtrlA[5];  Led[1] <= CtrlB[6];	end
        7:  begin Led[0] <= CtrlA[7];  Led[1] <= CtrlB[7];	end
        default:   Led <= Led;
    endcase
      
endmodule

咳咳,我没写仿真文件。。

6. episode 6 受控线性序列机

每隔10ms,让LED灯的一个8状态循环执行一次,其余时间不运行 (小区间时间段小一点,方便测试,比如设置为10us)

怎么在10ms中只计数一次呢?本质是要让小区间上的counter(和counter2)不计数,于是我们设置一个En使能端,它的电平状态如下图所示,所谓受控就是这个意思 ——

image-20220921214249206
  • counter记录时间流逝
  • counter2记录8个变化区间
  • counter0记录10ms

其中和En 的变化时刻相关的 ——

  • 10ms拉高,则需计数10,000,000ns/20ns = 50,000次,由计数器counter0控制,这个很简单~

    image-20220922202701397
  • 8个小周期结束,拉低,由计数器counter2控制

要注意两处难点 ——

  • En高电平1,counter和counter2才要计数;其余时刻无情的给0
  • En我们要在8个周期结束时拉低,事实上,(counter2 == 7) && (counter == Time -1)就是恰切的时间点

counter_led_6.v

module counter_led_6 (
    Clk,
    Reset_n,
    Ctrl,
    Time, //设置周期
    Led
);
    input Clk;
    input Reset_n;
    input[7:0] Ctrl;
    input[31:0] Time;
    reg En;
    output reg Led;
    
    reg[31:0] counter; //记录时间的流逝
   
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        counter <= 0;
    else if(En)  begin
        if(counter == Time-1) 
            counter <= 0;
        else
            counter <= counter + 1'b1;
    	end 
    else 
        counter <= 0;
            
    reg[2:0] counter2; //小状态区间计数器
    
    always@(posedge Clk or negedge Reset_n) 
    if(!Reset_n)
        counter2 <= 0;
    else if(En)  begin
        if(counter == Time-1) 
            counter2 <= counter2 + 1'b1;
    end
    else
        counter <= 2;
        
    always@(posedge Clk or negedge Reset_n) 
    if(!Reset_n)  
         Led <= 0;
    else case(counter2) 
        0:   Led <= Ctrl[0];
        1:   Led <= Ctrl[1];
        2:   Led <= Ctrl[2];
        3:   Led <= Ctrl[3];
        4:   Led <= Ctrl[4];
        5:   Led <= Ctrl[5];
        6:   Led <= Ctrl[6];
        7:   Led <= Ctrl[7];
    default:   Led <= Led;
    endcase
      
    reg[19:0] counter0;  //10ms计数器
    always@(posedge Clk or negedge Reset_n) 
    if(!Reset_n)  
        counter0 <= 0;
    else if(counter0 == 499999) 
        counter0 <= 0;
    else
        counter0 <= counter0 + 1'b1;
        
    always@(posedge Clk or negedge Reset_n) 
    if(!Reset_n)  
        En <= 0;
    else if(counter0 == 0)
        En <= 1;
    else if((counter2 == 7) && (counter == Time -1))
        En <= 0;
endmodule

复用4的test bench,counter_led_6_tb.v

10us,则需计数10,000ns/20ns = 500次

`timescale 1ns/1ns

module counter_led_4_tb();
    //激励信号
    reg Clk;
    reg Reset_n;
    reg[7:0] Ctrl;
    reg[31:0] Time;
    wire Led;
    
    counter_led_4 counter_led_4_inst(
        //连线
        .Clk(Clk),
        .Reset_n(Reset_n),
        .Ctrl(Ctrl) ,
        .Time(Time),
        .Led(Led)
    );
    
    initial Clk = 1; 
    always #10 Clk = !Clk; //每延时10s,翻转
    
    initial begin
        Reset_n = 0;
        Ctrl = 0;
        Time = 0;
        #201;
        Reset_n = 1;
        Time = 499;
        Ctrl = 8'b1010_0101;
        #2000000000;
        $stop;
    end
endmodule

image-20220922214433246

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浮光 掠影

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值