从计数器到可控制线性序列机——LED实验进化六部曲
- 一:让LED灯按照亮0.25s,灭0.75s的状态循环亮灭
- 二:让LED灯按照亮0.25s,灭0.5s,亮0.75s,灭1s的状态循环亮灭
- 三:让LED灯按照指定的亮灭模式亮灭,亮灭模式未知,由用户随机指定。以0.25s为一个变化周期,8个变化状态为一个循环
- 四:让LED灯按照指定的亮灭模式亮灭,亮灭模式未知,由用户随机指定。8个变化状态为一个循环,每个变化状态的时间值可以根据不同的应用场景选择
- 五:让多个LED灯按照设置的模式各自在一个变化循环内独立亮灭变化
- 六:每隔10ms,让LED灯的一个8状态循环执行一次(每个状态的变化时间值小一点,方便测试,比如设置为10us)
一:让LED灯按照亮0.25s,灭0.75s的状态循环亮灭
源文件
module counter_led_0(
input clk,
input reset,
output reg led
);
reg [25:0] cnt;
parameter MCNT=50000000;
always@(posedge clk or negedge reset)begin
if(!reset)
cnt<=0;
else if(cnt==MCNT-1)
cnt<=0;
else
cnt<=cnt+1'b1;
end
always@(posedge clk or negedge reset)begin
if(!reset)
led<=0;
else if(cnt==(MCNT/4+MCNT/2)-1)
led<=1;
else if(cnt==MCNT-1)
led<=0;
else
led<=led;
end
endmodule
测试文件
`timescale 1ns / 1ns
module counter_led_0_tb();
reg clk;
reg reset_n;
wire led;
counter_led_0 counter_led_0(
.clk(clk),
.reset(reset_n),
.led(led)
);
defparam counter_led_0.MCNT=26'd25000;
initial begin
clk=0;
end
always#10 clk=!clk;
initial begin
reset_n=0;
#201;
reset_n=1;
#40000000;
$stop;
end
endmodule
仿真截图
收获:
1:用parameter定义变量的时候,对变量除以2还是很容易实现的,因为其对应的是一个常量的值,但是若是一个变量在过程中会实时变化的话直接/2是会有一些问题的~
2:需要这道题需要注意亮0.25s不代表是要在0的基础上计数到0.25s在变为1,而是在1的基础上保留的时间是0.25s,这里也是需要转过弯儿的地方~
二:让LED灯按照亮0.25s,灭0.5s,亮0.75s,灭1s的状态循环亮灭
源代码
module counter_led_1(
input clk,
input reset,
output reg led
);
reg [26:0] cnt;
parameter MCNT=125000000;
always@(posedge clk or negedge reset)begin
if(!reset)
cnt<=0;
else if(cnt==MCNT-1)
cnt<=0;
else
cnt<=cnt+1'b1;
end
always@(posedge clk or negedge reset)begin
if(!reset)
led<=1;
else if(cnt==MCNT/10-1)
led<=0;
else if(cnt==MCNT/10+MCNT/5-1)
led<=1;
else if(cnt==(MCNT/10+MCNT/5)*2-1)
led<=0;
else if(cnt==MCNT-1)
led<=1;
else
led<=led;
end
endmodule
仿真截图
收获
1:这里为了使亮灭计数好想,设置了复位状态时led灯为灭~
三:让LED灯按照指定的亮灭模式亮灭,亮灭模式未知,由用户随机指定。以0.25s为一个变化周期,8个变化状态为一个循环
源代码
//该模块的功能是让LED灯按照指定的亮灭模式亮灭,亮灭模式未知,由用户随机指定。以0.25s为一个变化周期,8个变化状态为一个循环
module counter_led_2(
input clk,
input reset,
input [7:0]ctrl,
output reg led
);
reg [26:0] cnt;
parameter MCNT=100000000;
always@(posedge clk or negedge reset)begin
if(!reset)
cnt<=0;
else if(cnt==MCNT-1)
cnt<=0;
else
cnt<=cnt+1'b1;
end
always@(posedge clk or negedge reset)begin
if(!reset)
led<=0;
else if(cnt==MCNT/8-1)
led<=ctrl[0];
else if(cnt==MCNT*2/8-1)
led<=ctrl[1];
else if(cnt==MCNT*3/8-1)
led<=ctrl[2];
else if(cnt==MCNT*4/8-1)
led<=ctrl[3];
else if(cnt==MCNT*5/8-1)
led<=ctrl[4];
else if(cnt==MCNT*6/8-1)
led<=ctrl[5];
else if(cnt==MCNT*7/8-1)
led<=ctrl[6];
else if(cnt==MCNT*8/8-1)
led<=ctrl[7];
else
led<=led;
end
endmodule
测试文件
`timescale 1ns / 1ns
module counter_led_2_tb();
reg clk;
reg reset_n;
reg [7:0]ctrl;
wire led;
counter_led_2 counter_led_2(
.clk(clk),
.reset(reset_n),
.ctrl(ctrl),
.led(led)
);
defparam counter_led_2.MCNT=100000;
initial begin
clk=0;
end
always#10 clk=!clk;
initial begin
reset_n=0;
ctrl=0;
#201;
reset_n=1;
#200;
ctrl=8'b1001_0001;
#40000000;
$stop;
end
endmodule
仿真图
四:让LED灯按照指定的亮灭模式亮灭,亮灭模式未知,由用户随机指定。8个变化状态为一个循环,每个变化状态的时间值可以根据不同的应用场景选择
思路:在原有的基础上因为时间是用户自己设定的,所以设置times变量作为输入,因为MCNT变量以往都是固定值,但是times会随时变化,所以这里就又定义了一个cnt2记到1——8,每到一个值灯就会反应出对应的亮度
源代码
//该模块的功能是让LED灯按照指定的亮灭模式亮灭,亮灭模式未知,由用户随机指定。8个变化状态为一个循环,每个变化状态的时间值可以根据不同的应用场景选择
module counter_led_3(
input clk,
input reset,
input [31:0]times,
input [7:0]ctrl,
output reg led
);
reg [31:0] cnt;
always@(posedge clk or negedge reset)begin
if(!reset)
cnt<=0;
else if(cnt==times-1)
cnt<=0;
else
cnt<=cnt+1'b1;
end
reg [2:0]cnt2;
always@(posedge clk or negedge reset)begin
if(!reset)
cnt2<=3'b0;
else if(cnt==times-1)
cnt2<=cnt2+1;
end
always@(posedge clk or negedge reset)begin
if(!reset)
led<=0;
else begin
case(cnt2)
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
end
end
endmodule
测试文件
`timescale 1ns / 1ns
module counter_led_3_tb();
reg clk;
reg reset_n;
reg [7:0]ctrl;
reg [31:0]times;
wire led;
counter_led_3 counter_led_3(
.clk(clk),
.reset(reset_n),
.ctrl(ctrl),
.times(times),
.led(led)
);
initial begin
clk=0;
end
always#10 clk=!clk;
initial begin
reset_n=0;
ctrl=0;
times=0;
#201;
reset_n=1;
#200;
ctrl=8'b1001_0001;
#201;
times=2500;
#2000001;
times=25000;
#400000000;
$stop;
end
endmodule
仿真截图
五:让多个LED灯按照设置的模式各自在一个变化循环内独立亮灭变化
答:在四的基础上增加led的位宽,以及设置多个控制信号进行赋值
module counter_led_5(
Clk,
Reset_n,
CtrlA,
CtrlB,
Time,
Led
);
input Clk;
input Reset_n;
input [7:0]CtrlA;
input [7:0]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)
counter2 <= counter2 + 1'b1;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Led <= 2'd0;
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[6]; Led[1] <= CtrlB[6]; end
7:begin Led[0] <= CtrlA[7]; Led[1] <= CtrlB[7]; end
default:begin Led[0] <= Led; Led[1] <= Led[1]; end
endcase
endmodule
测试文件
`timescale 1ns/1ns
module counter_led_5_tb;
reg Clk;
reg Reset_n;
reg [7:0]CtrlA,CtrlB;
reg [31:0]Time;
wire [1:0]Led;
counter_led_5 counter_led_5(
.Clk(Clk),
.Reset_n(Reset_n),
.CtrlA(CtrlA),
.CtrlB(CtrlB),
.Time(Time),
.Led(Led)
);
initial Clk = 1;
always #10 Clk = !Clk;
initial begin
Reset_n = 0;
CtrlA = 0;
CtrlB = 0;
Time = 0;
#201;
Reset_n = 1;
#2000;
Time = 2500;
CtrlA = 8'b1000_0110;
CtrlB = 8'b1101_0010;
#20000000;
Time = 25000;
CtrlA = 8'b1010_0110;
CtrlB = 8'b1100_1010;
#20000000;
$stop;
end
endmodule
六:每隔10ms,让LED灯的一个8状态循环执行一次(每个状态的变化时间值小一点,方便测试,比如设置为10us)
最开始自己的理解是在8个循环之后间隔10ms,所以才有了以下代码的理解,但是后来才懂原来周期是10msemmm有些许思想出路
注:以下代码的逻辑思想正确,即在原有计数的基础上又加了一个整体的计数周期=8*times+10ms,但是这里的8需要对输入变量做乘法,在运算时很有可能一个周期运算不出来,就会出现一些错误,所以这里还是要谨慎的使用;(小梅哥采用的是设置en进行控制,自己没有写出来,这里看完他的代码后自行默写了一下~)
//该模块的功能是每隔10ms,让LED灯的一个8状态循环执行一次(每个状态的变化时间值小一点,方便测试,比如设置为10us)
module counter_led_4(
input clk,
input reset,
input [31:0]times,
input [7:0]ctrl,
output reg led
);
reg [31:0] cnt;
reg en;//设置使能,只有在en为1的时候
parameter count=5000;
always@(posedge clk or negedge reset)begin
if(!reset)
cnt<=0;
else if(cnt==times-1)
cnt<=0;
else
cnt<=cnt+1'b1;
end
reg [2:0]cnt2;
always@(posedge clk or negedge reset)begin
if(!reset)
cnt2<=3'b0;
else if(cnt==times-1)
cnt2<=cnt2+1;
end
reg [35:0] cnt3;
always@(posedge clk or negedge reset)begin
if(!reset)
cnt3<=0;
else if(cnt3==times*8-1+count)
cnt3<=0;
else
cnt3<=cnt3+1'b1;
end
always@(posedge clk or negedge reset)begin
if(!reset)
led<=0;
else if(cnt3<=8*times-1)
begin
case(cnt2)
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
end
else if(cnt3<=8*times-1+count)
begin
led<=0;
end
end
endmodule
`timescale 1ns / 1ns
module counter_led_4_tb();
reg clk;
reg reset_n;
reg [7:0]ctrl;
reg [31:0]times;
wire led;
counter_led_4 counter_led_4(
.clk(clk),
.reset(reset_n),
.ctrl(ctrl),
.times(times),
.led(led)
);
initial begin
clk=0;
end
always#10 clk=!clk;
initial begin
reset_n=0;
ctrl=0;
times=0;
#201;
reset_n=1;
#200;
ctrl=8'b1001_0001;
#201;
times=2500;
#2000001;
times=25000;
#40000000;
$stop;
end
endmodule
小梅哥的官方源码
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;
reg EN;
reg [18:0]counter0;
//10ms���ڶ�ʱ��
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter0 <= 0;
else if(counter0 == 500000 - 1)
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;
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 [3: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
counter2 <= 4'd8;
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];
8:Led <= 0;
default:Led <= Led;
endcase
endmodule
测试文件
`timescale 1ns / 1ns
module counter_led_4_tb();
reg clk;
reg reset_n;
reg [7:0]ctrl;
reg [31:0]times;
wire led;
counter_led_4 counter_led_4(
.Clk(clk),
.Reset_n(reset_n),
.Ctrl(ctrl),
.Time(times),
.Led(led)
);
initial begin
clk=0;
end
always#10 clk=!clk;
initial begin
reset_n=0;
ctrl=0;
times=0;
#201;
reset_n=1;
#2000;
ctrl=8'b1000_0111;
times=2500;
#20000001;
times=25000;
ctrl=8'b1010_0110;
#20000000;
$stop;
end
endmodule
仿真截图
收获:
1:锻炼了自己分析波形的能力,自己调试,最初小梅哥的代码在间隔10ms的时间里会一直保持ctrl[0]的状态,让波形看的很乱,找不到是什么时候开始算起的,于是我对counter2进行计数更改,当当前处于不计数的状态时,不为0.因为为0 的话需要在后面case循环中进行判断等于ctrl[0],于是我把不计数的状态设为不在判断范围内的8,使其为0,这样使得波形比较规律易懂
2:学会了添加模块中其他参数的方法,好开心~,这样查错就更容易了