FPGA学习(7)-线性序列机原理与应用,不同类型的LED控制开关

目录

1.实现1

2.实现2

2.1方法1

2.2方法2

3.实现3

3.1实验现象

4.实现4

4.1分析

4.2实现过程

[4.2.1 counter的计数](#4.2.1 counter的计数)

[4.2.2 en_counter2的判断](#4.2.2 en_counter2的判断)

[4.2.3 en_counter0的判断](#4.2.3 en_counter0的判断)

[4.2.4 对case语句加判断条件](#4.2.4 对case语句加判断条件)

4.3仿真结果

​编辑

4.4实验现象


1.实现1

1.实现以下图示功能,直接计到1s时,counter清零,在0~0.25s之间高电平,0.25s~1s之间低电平。

2.仿真波形

3.问题,像C那样写却不行,只能在某个时刻点亮才行。

计数器相当于时间的一把尺子,用计数器的每一个计数值当作一个刻度,能够得到各个需要的时刻,在指定的时刻,执行需要的操作。

2.实现2

实现下图方式的方波:

2.1方法1

跟第一种方式一样,找出每个高低信号跳变的位置。要减小仿真时间,多个参数下如何设定最简便的参数,按照之前的思路需要加4个参数。这里直接用他们都共有的倍数来进行,仿真时间缩小1000倍。

复制代码
parameter MCNT = 1000;
    
    always@(posedge clk or negedge reset)
    if(!reset)
    counter<=1'b0;
    else if (counter == 125_000*MCNT-1)
    counter<=1'b0;
    else
    counter<=counter+1'd1;
    
    always@(posedge clk or negedge reset)
    if(!reset)
    led<=1'b0;
    else if(counter == 0)
        led<=1'd1;
    else if(counter == 125_00*MCNT)
        led<=1'd0;
    else if(counter == 375_00*MCNT)
        led<=1'd1;
    else if(counter == 75_000*MCNT)
        led<=1'd0;

仿真波形:

2.2方法2

把划分成几个区间,取每个时间间隔的最大公约数,分成几段。用一个计数器记到该最大公约数,再用一个计数器记到9,每次对应不同的高低电平。

源文件代码,中间有段if else 没用begin...end为什么也没问题。

复制代码
module led_light1(
    reset,
    clk,
    led
    );
    
    input reset;
    input clk;
    output reg led;
    reg [26:0]counter;
    parameter MCNT = 12500_000-1;
    
    always@(posedge clk or negedge reset)
    if(!reset)
        counter <= 1'd0;
    else if (counter == MCNT)
        counter <= 1'd0;
    else
        counter <= counter+1'd1;
    
    reg [3:0]counter1;
    always@(posedge clk or negedge reset)
    if(!reset)
        counter1 <= 1'd0;
    else if(counter == MCNT)
        if(counter1 == 9)
            counter1 <= 1'd0;
        else
            counter1 <= counter1+1'd1;   
    else
        counter1=counter1;
    
    always@(posedge clk or negedge reset)
    if(!reset)
        led<=1'd0;
    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

仿真结果:

3.实现3

即直接新建立一个数值,让counter1依次计数,每次计数后,切换到对应的SW状态。

源文件:

复制代码
module led_light2(
    reset,
    clk,
    led,
    SW
    );
    
    input reset;
    input clk;
    input [7:0]SW;
    output reg led;
    reg [26:0]counter;
    parameter MCNT = 12500_000-1;
    
    always@(posedge clk or negedge reset)
    if(!reset)
        counter <= 1'd0;
    else if (counter == MCNT)
        counter <= 1'd0;
    else
        counter <= counter+1'd1;
    
    reg [2:0]counter1;
    always@(posedge clk or negedge reset)
    if(!reset)
        counter1 <= 1'd0;
    else
        counter1 <= counter1+1'd1;   
    
    always@(posedge clk or negedge reset)
    if(!reset)
        led<=1'd0;
    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

仿真文件:

改变SW的值,进行延时后,再进行改变。

复制代码
`timescale 1ns / 1ns
module led_light_tb();
reg clk;
reg reset;
wire led;
reg [7:0]SW;
led_light2 led_light2(
    .reset(reset),
    .clk(clk),
    .led(led),
    .SW(SW)
);

initial clk=1;
always #10 clk=~clk;

initial begin
    reset =0;
    SW=8'b10101010;
    #201;
    reset=1;
    #2000_000_000;
    SW=8'b00000001;
    #2000_000_000;
    $stop;
end
endmodule

前半段符合10101010

2s后变为00000001,仿真图如下:

代码中counter1的值等每次计数来一次才加一次,不然就不是按照设定的值增加。 将间隔时间设为1s。

3.1实验现象

开关设置为10101010时,LED灯每间隔1s闪烁。多上拉几个,就多亮会儿。

4.实现4

4.1分析

实现下图所示的动态变化,1s时间是可变的,只是这里指代为1s。学习计数器不是一定要不停的计数,而是要有静有动,中途可暂停。

需要用到三个计数器,counter1~3。

counter0:用于SW切换的间隔时间0.25s

1.计数最大值:计数的最大值为0.25*10^9/20-1=12500_000-1。

2.计数的条件为前面1s的空闲时间到,8个状态马上开始或已经开始。

3.清零条件:计数到最大值,或还在前1s的空闲状态中。

counter1:计数当前第几个LED的状态计数器

1.计数最大值:7

2.计数条件:counter0计满1次加1

3.清零条件:计数到最大值

counter2:1s的空闲状态计数器

1.计数最大值:1*10^9/20-1=5000_0000-1。

2.计数条件:8个LED状态切换完毕,counter1计满。

3.清零条件:计数至最大值

4.2实现过程

4.2.1 counter的计数

对于counter0来说,需要一会儿计数一会儿停,所以加入一个变量en_counter,当en_counter=1时,开始计数,否则就停下。

复制代码
always@(posedge clk or negedge reset)
begin
    if(!reset)
        counter0 <= 1'd0;
    else if (en_counter0)
    begin
        if(counter0 == MCNT)
            counter0 <= 1'd0;
        else
            counter0 <= counter0+1'd1;
    end
end

对于counter1,直接counter0计满一次加1,满8次清0。

复制代码
always@(posedge clk or negedge reset)
    if(!reset)
        counter1 <= 1'd0;
    else if (counter0 == 7)
        counter1 <= 1'd0;   
    else if (counter0 == MCNT)
        counter1 <= counter1+1'd1;   
    else
        counter1 <= counter1;

对于counter2,空闲段计数,8个0.25s内不计数。

复制代码
always@(posedge clk or negedge reset)
begin
    if(!reset)
        counter2 <= 1'd0;
    else if(en_counter2)
    begin
        if(counter2 == MCNT2)
            counter2 <= 1'd0;
        else
            counter2 <= counter2+1'd1;
    end
end

4.2.2 en_counter2的判断

counter2保持计数的条件有:

(1)从复位到正常运行,需要立即开始计数。

(2)每次LED8个状态切换完,重新开始计数。

counter2停止计数的条件有:

(1)计数满1s

总结:

(1)复位时,让en_counter2置1。

(2)counter1==7 时,让en_counter2置1。

(3)counter2计满时,让en_counter2置0。

关于第(2)个条件,我认为不附加sw计数也能满足,因为counter1==7已经都记录了8个状态了。

复制代码
always@(posedge clk or negedge reset)
begin
    if(!reset)
        en_counter2 <= 1'd1;
    else if(counter1 ==7)
        en_counter2 <= 1'd1;
    else if(counter2 <= MCNT2)
        en_counter2 <=1'd0;
    else
        en_counter2 <= en_counter2;
end

4.2.3 en_counter0的判断

counter0保持计数的条件:

(1)counter2计数计满的时候

counter0停止计数的条件;

(1)程序复位,counter0需要停止。

(2)LED的8个状态完成,counter停止。

总结:

(1)复位时,en_counter0置0;

(2)counter1==7时,en_counter0置0;

(3)counter2计满最大值时,en_counter0置1;

复制代码
always@(posedge clk or negedge reset)
begin
    if(!reset)
        en_counter0 <= 1'd0;
    else if(counter1 ==7)
        en_counter0 <= 1'd0;
    else if(counter2 <= MCNT2)
        en_counter0 <=1'd1;
    else
        en_counter0 <= en_counter0;
end

4.2.4 对case语句加判断条件

led在空闲状态置0,所以要么en_counter1==0,要么en_counter2==1。

复制代码
always@(posedge clk or negedge reset)
begin
if(!reset)
    led <= 1'd0;
else if(en_counter2 ==1)
    led <= 1'd0;
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
end  

4.3仿真结果

出现问题,并未按预期波形出现。

在en_counter处加入了附加的判断条件,else if((counter1 ==7) && (counter0 == MCNT)),仿真波形还是不对。

错误1,将这里的判断用了非阻塞符号,修改后,仿真波形仍不对。

en_counter0的错误也是如此。仿真波形在第二段开始出现错误,counter2计数一直为0。

问题在于:程序中counter1计数到7时就要清0,而此时在counter1计数到6完成时,counter0就已经记到0.25s了,在下一个时钟脉冲来临时,counter1被清0,所以后面的en_counter2永远不会置1。

最后仿真结果相对应,led显示的波形与SW相反的原因是,在工程文件的case语句,从左到右是对应从低到高。

4.4实验现象

SW全部拉低,LED灯不亮;SW全部拉低,LED灯熄1s,亮2s。SW一高一低,LED熄灭一s,闪烁2s。

相关推荐
西岸行者6 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
ZPC82106 天前
docker 镜像备份
人工智能·算法·fpga开发·机器人
ZPC82106 天前
docker 使用GUI ROS2
人工智能·算法·fpga开发·机器人
悠哉悠哉愿意6 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码6 天前
嵌入式学习路线
学习
毛小茛6 天前
计算机系统概论——校验码
学习
babe小鑫6 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms6 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下6 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。6 天前
2026.2.25监控学习
学习