LED实验进化六部曲
〇、功能介绍
1.功能描述
1让LED灯按照亮0.25秒,灭0.75秒的状态循环亮灭
2.让LED灯按照亮0.25秒,灭0.5秒,亮0.75秒,灭1秒的状态循环亮灭
3.让LED灯按照指定的亮灭模式亮灭,亮灭模式未知,由用户随机指定。以0.25秒为一个变化周期,8个变化状态为一个循环。
4. 让LED灯按照指定的亮灭模式亮灭,亮灭模式未知,由用户随机指定。8个变化状态为 一个循环,每个变化状态的时间值可以根据不同的应用场景选择。
5. 让多个LED灯按照设置的模式各自在一个变化循环内独立亮灭变化
6.每隔10ms,让LED灯的一个8状态循环执行一次(每个状态的变化时间值小一点,方便测试,比如设置为10us)
一、功能描述1的代码编写
1.设计文件
`timescale 1ns / 1ns
// 让LED灯按照亮0.25秒,灭0.75秒的状态循环亮灭
module counter_led1(clk,reset_n,led
);
input clk;
input reset_n;
output reg led;
reg [25:0]counter;//d50_000_000=b10_1111_1010_1111_0000_1000_0000
parameter MCNT=26'd50_000_000;//以1s为一个周期,1s后计数清零;1s=1000_000_000ns,1000_000_000ns/20ns=50_000_000次
//计数进程
always@(posedge clk or negedge reset_n)
if(!reset_n)
counter <=0;
else if(counter == MCNT -1)//此处的是常数,可以直接相减
counter <=0;
else
counter <= counter + 1'b1;
//led进程
always@(posedge clk or negedge reset_n)
if(!reset_n)
led <=0;
else if(counter == (MCNT/2+MCNT/4) -1)//此处的是常数,可以直接相减
led <=1;
else if(counter == MCNT -1)//此处的是常数,可以直接相减
led <=0;
endmodule
2.激励文件
`timescale 1ns / 1ns
module counter_led_tb(
);
reg clk;
reg reset_n;
wire led;
counter_led1 counter_led1_inst(
.clk(clk),
.reset_n(reset_n),
.led(led)
);
initial clk = 1'b1;
always #10 clk=~clk; //延时半个周期10ns,翻转clk值,模拟实现时钟信号
initial begin
reset_n = 1'b0;
#201;//延迟时间不应为周期的整数倍
reset_n = 1'b1;
// #2000000000;//等待两秒,看实际状态
// $stop;
end
endmodule
3.仿真图
仿真图,横坐标一个大刻度100ms,led灯亮250ms,熄灭750ms,以1s为一个周期循环
二、功能描述2的代码编写
1.设计文件
`timescale 1ns / 1ns
// 让LED灯按照亮0.25秒,灭0.5秒,亮0.75秒,灭1秒的状态循环亮灭
module counter_led2(clk,reset_n,led
);
input clk;
input reset_n;
output reg led;
reg [26:0]counter;//d125_000_000=b111_0111_0011_0101_1001_0100_0000,二进制共需要27位表示
parameter MCNT=27'd125_000_000;//以2.5s为一个周期,2.5s后计数清零;2.5s=2500_000_000ns,2500_000_000ns/20ns=125_000_000次
// parameter MCNT=27'd125;//缩小仿真时间,观察仿真图的波形
//计数进程
always@(posedge clk or negedge reset_n)
if(!reset_n)
counter <=0;
else if(counter == MCNT -1)//此处的是常数,可以直接相减
counter <=0;
else
counter <= counter + 1'b1;
//led进程
always@(posedge clk or negedge reset_n)
if(!reset_n)
led <=1;
else if(counter == MCNT/10 -1)//0.25s是一个周期2.5s的1/10;
led <=0;
else if(counter == (MCNT/10+MCNT/5) -1)//0.5s是一个周期2.5s的1/5;
led <=1;
// else if(counter == (MCNT/10+MCNT/5)*2 -1)
else if(counter == (MCNT/10+MCNT/5+MCNT*3/10) -1)//0.75s是一个周期2.5s的3/10;
led <=0;
else if(counter == MCNT -1)
led <=1;
endmodule
2.激励文件
`timescale 1ns / 1ns
module counter_led_tb(
);
reg clk;
reg reset_n;
wire led;
counter_led2 counter_led2_inst(
.clk(clk),
.reset_n(reset_n),
.led(led)
);
initial clk = 1'b1;
always #10 clk=~clk; //延时半个周期10ns,翻转clk值,模拟实现时钟信号
initial begin
reset_n = 1'b0;
#201;//延迟时间不应为周期的整数倍
reset_n = 1'b1;
// #2000000000;//等待两秒,看实际状态
// $stop;
end
endmodule
3.仿真图
仿真图,让LED灯按照亮0.25秒,灭0.5秒,亮0.75秒,灭1秒的状态循环亮灭,横坐标一个大刻度100ms,led灯亮250ms,熄灭750ms,以1s为一个周期循环
三、功能描述3的代码编写
1.设计文件
让LED灯按照指定的亮灭模式亮灭,亮灭模式未知,由用户随机指定。以0.25秒为一个变化周期,8个变化状态为一个循环。(即2s为一个循环周期,有一个指定亮灭的输入端口)
`timescale 1ns / 1ns
// 让LED灯按照指定的亮灭模式亮灭,亮灭模式未知,由用户随机指定。以0.25秒为一个变化周期,8个变化状态为一个循环。
//(即2s为一个循环周期,有一个指定亮灭的输入端口)
module counter_led3(clk,reset_n,ctrl,led
);
input clk;
input reset_n;
input [7:0] ctrl;
output reg led;
reg [26:0]counter;//d100_000_000=b101_1111_0101_1110_0000_0000_0000,二进制共需要27位表示
// parameter MCNT=27'd100_000_000;//以2s为一个周期,2s后计数清零;2s=2000_000_000ns,2000_000_000ns/20ns=100_000_000次
parameter MCNT=27'd100;//缩小仿真时间,观察仿真图的波形
//计数进程
always@(posedge clk or negedge reset_n)
if(!reset_n)
counter <=0;
else if(counter == MCNT -1)//此处的是常数,可以直接相减
counter <=0;
else
counter <= counter + 1'b1;
//led进程
always@(posedge clk or negedge reset_n)
if(!reset_n)
led <=0;
else case(counter)
MCNT*1/8 -1:led <= ctrl[0];
MCNT*2/8 -1:led <= ctrl[1];
MCNT*3/8 -1:led <= ctrl[2];
MCNT*4/8 -1:led <= ctrl[3];
MCNT*5/8 -1:led <= ctrl[4];
MCNT*6/8 -1:led <= ctrl[5];
MCNT*7/8 -1:led <= ctrl[6];
MCNT*8/8 -1:led <= ctrl[7];
default:led <= led;
endcase
//也可以else if改写
// else if(counter == MCNT/8 -1)//0.2s是一个周期2.0s的1/8;
// led <=ctrl[0];
// else if(counter == MCNT*2/8 -1)
// led <=ctrl[1];
// else if(counter == MCNT*3/8 -1)
// led <=ctrl[2];
// else if(counter == MCNT*4/8 -1)
// led <=ctrl[3];
// else if(counter == MCNT*5/8 -1)
// led <=ctrl[4];
// else if(counter == MCNT*6/8 -1)
// led <=ctrl[5];
// else if(counter == MCNT*7/8 -1)
// led <=ctrl[6];
// else if(counter == MCNT -1)
// led <=ctrl[7];
endmodule
2.激励文件
`timescale 1ns / 1ns
// 功能3的testbench
module counter_led3_tb(
);
reg clk;
reg reset_n;
reg [7:0]ctrl;
wire led;
counter_led3 counter_led3_inst(
.clk(clk),
.reset_n(reset_n),
.ctrl(ctrl),
.led(led)
);
initial clk = 1'b1;
always #10 clk=~clk; //延时半个周期10ns,翻转clk值,模拟实现时钟信号
initial begin
reset_n = 1'b0;
ctrl =8'b0;
#201;//延迟时间不应为周期的整数倍
reset_n = 1'b1;
#2000;
ctrl =8'b1000_0110;
#20000;//等待两秒,看实际状态
ctrl =8'b1110_0110;
#20000;
$stop;
end
endmodule
3.仿真图
仿真图为了缩短时间,减少了6个数量级的计数,一个周期为2us
下面我们继续分析仿真图的led是否与设置的仿真一值;
四、功能描述4的代码编写
1.设计文件
//让LED灯按照指定的亮灭模式亮灭,亮灭模式未知,由用户随机指定。
//且8个变化状态为 一个循环,每个变化状态的时间值可以根据不同的应用场景选择。
//想法:添加一个控制每个变化状态的时间端口Time.
//思想:凑零为整(描述1-3都是化整为零的思想)
module counter_led4(clk,reset_n,ctrl,Time,led
);
input clk;
input reset_n;
input [7:0] ctrl;//LED灯按照指定的亮灭模式亮灭,用8位ctrl代表led的亮灭模式
input [26:0] Time;//控制每个变化状态的时间端口Time
output reg led;
//计数进程
reg [26: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;//用8位ctrl代表led的亮灭模式,一个亮灭模式的计数,8个时间段
always@(posedge clk or negedge reset_n)
if(!reset_n)
counter2 <=0;
else if(counter == Time -1)//此处的是常数,可以直接相减,并且左边的位宽要比右边的位宽大
counter2 <= counter2 + 1'b1;
//led进程
always@(posedge clk or negedge reset_n)
if(!reset_n)
led <=0;
else case(counter2)
3'd000:led <= ctrl[0];
3'd001:led <= ctrl[1];
3'd010:led <= ctrl[2];
3'd011:led <= ctrl[3];
3'd100:led <= ctrl[4];
3'd101:led <= ctrl[5];
3'd110:led <= ctrl[6];
3'd111:led <= ctrl[7];
default:led <= led;
endcase
endmodule
2.激励文件
`timescale 1ns / 1ns
module counter_led4_tb(
);
reg clk;
reg reset_n;
reg [7:0]ctrl;
reg [26:0]Time;
wire led;
counter_led4 counter_led4_inst(
.clk(clk),
.reset_n(reset_n),
.ctrl(ctrl),
.Time(Time),
.led(led)
);
initial clk = 1'b1;
always #10 clk=~clk; //延时半个周期10ns,翻转clk值,模拟实现时钟信号
initial begin
reset_n = 1'b0;
ctrl =8'b0;
Time =8'b0;
#201;
reset_n = 1'b1;
#2000;
Time =2500;//2500ns*8*20=400000ns=0.4ms,一个完整的循环周期是0.4ms
ctrl =8'b1000_0110;
#20000000;//等待20ms
Time =25000;//25000ns*8*20=4000000ns=4ms,一个完整的循环周期是4ms
ctrl =8'b1110_0110;
#20000000;//等待20ms
$finish;
end
endmodule
3.仿真图
功能:让LED灯按照指定的亮灭模式亮灭,亮灭模式未知,由用户随机指定,且8个变化状态为 一个循环,每个变化状态的时间值可以根据不同的应用场景选择。每个变化状态持续20ms。
input [7:0] ctrl;//LED灯按照指定的亮灭模式亮灭,用8位ctrl代表led的亮灭模式
input [26:0] Time;//控制每个变化状态的时间端口Time
reg [26:0]counter;//基本时间计数器
reg [2:0]counter2;//用8位ctrl代表led的亮灭模式,一个亮灭模式的计数,8个时间段
亮灭模式为1000_0110,led亮灭一次计数2500次,8位变化状态为一个循环亮灭模式,一个周期是20ns,所以一个完整的循环周期是0.4ms (2500ns820=400000ns=0.4ms)
counter计数到2499,counter2+1:
亮灭模式为1110_0110,led亮灭一次计数25000次,8位变化状态为一个循环亮灭模式,一个周期是20ns,所以一个完整的循环周期是4ms (25000ns820=4000000ns=4ms)
counter计数到24999,counter2+1:
五、功能描述5的代码编写
1.设计文件
//思想:在功能4的基础上,让2个led灯工作
module counter_led5(clk,reset_n,ctrlA,ctrlB,Time,led
);
input clk;
input reset_n;
input [7:0] ctrlA,ctrlB;//LED灯按照指定的亮灭模式亮灭,用8位ctrl代表led的亮灭模式
input [26:0] Time;//控制每个变化状态的时间端口Time,此处两个led的时间一样
output reg [1:0]led;
//计数进程
reg [26: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;//用8位ctrl代表led的亮灭模式,一个亮灭模式的计数,8个时间段
always@(posedge clk or negedge reset_n)
if(!reset_n)
counter2 <=0;
else if(counter == Time -1)//此处的是常数,可以直接相减,并且左边的位宽要比右边的位宽大
counter2 <= counter2 + 1'b1;
//led进程
always@(posedge clk or negedge reset_n)
if(!reset_n)
led <=0;
else case(counter2)
3'd000: begin led[0] <= ctrlA[0]; led[1] <= ctrlB[0]; end
3'd001: begin led[0] <= ctrlA[1]; led[1] <= ctrlB[1]; end
3'd010: begin led[0] <= ctrlA[2]; led[1] <= ctrlB[2]; end
3'd011: begin led[0] <= ctrlA[3]; led[1] <= ctrlB[3]; end
3'd100: begin led[0] <= ctrlA[4]; led[1] <= ctrlB[4]; end
3'd101: begin led[0] <= ctrlA[5]; led[1] <= ctrlB[5]; end
3'd110: begin led[0] <= ctrlA[6]; led[1] <= ctrlB[6]; end
3'd111: begin led[0] <= ctrlA[7]; led[1] <= ctrlB[7]; end
default:led <= led;
endcase
endmodule
2.激励文件
`timescale 1ns / 1ns
module counter_led5_tb(
);
reg clk;
reg reset_n;
reg [7:0]ctrlA;
reg [7:0] ctrlB;
reg [26:0]Time;
wire [1:0]led;
counter_led5 counter_led5_inst(
.clk(clk),
.reset_n(reset_n),
.ctrlA(ctrlA),
.ctrlB(ctrlB),
.Time(Time),
.led(led)
);
initial clk = 1'b1;
always #10 clk=~clk; //延时半个周期10ns,翻转clk值,模拟实现时钟信号
initial begin
reset_n = 1'b0;
ctrlA =8'b0;
ctrlB =8'b0;
Time =8'b0;
#201;
reset_n = 1'b1;
#2000;
Time =2500;//2500ns*8*20=400000ns=0.4ms,一个完整的循环是0.4ms
ctrlA =8'b1000_0110;
ctrlB =8'b1010_0110;
#20000000;//等待20ms
Time =25000;//25000ns*8*20=4000000ns=4ms,一个完整的循环是4ms
ctrlA =8'b1110_0110;
ctrlB =8'b1000_0011;
#20000000;//等待20ms
$finish;
end
endmodule
3.仿真图
六、功能描述6的代码编写
1.设计文件
// 每隔10ms,让LED灯的一个8状态循环执行一次
//(每个状态的变化时间值小一点,方便测试,比如设置为10us)
//EN=1 counter计数
//EN拉低的条件是8个翻转状态结束之时,我们用的是counter2
//参照功能4
module counter_led6(clk,reset_n,ctrl,Time,led
);
input clk;
input reset_n;
input [7:0] ctrl;//LED灯按照指定的亮灭模式亮灭,用8位ctrl代表led的亮灭模式
input [31:0] Time;//控制每个变化状态的时间端口Time
output reg led;
//计数进程
reg [31:0]counter;//基本时间计数器
reg EN;
reg [18:0] counter0;//10ms=10,000,000ns,10,000,000ns/20ns=500000次
//d500000次=b111_1010_0001_0010_0000,二进制用19位表示
//10ms周期定时器counter0
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)//需要让counter2保持一段时间状态,所以用下面一条语句
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;
//counter2是ctrl代表led的亮灭模式
//counter2也可以加入使能端EN(状态端)
/* reg [2:0]counter2;//用8位ctrl代表led的亮灭模式,一个亮灭模式的计数,8个时间段
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 <=0;
*/
reg [2:0]counter2;//用8位ctrl代表led的亮灭模式,一个亮灭模式的计数,8个时间段
always@(posedge clk or negedge reset_n)
if(!reset_n)
counter2 <=0;
else if(counter == Time -1)//此处的是常数,可以直接相减,并且左边的位宽要比右边的位宽大
counter2 <= counter2 + 1'b1;
//led进程
always@(posedge clk or negedge reset_n)
if(!reset_n)
led <=0;
// else if(EN == 0)//8个状态亮完之后,在10ms之中,led灯熄灭
// led <=0;
// else if(counter0 >= Time*8 -1)//8个状态亮完之后,在10ms之中,led灯熄灭
// led <=0;
else case(counter2)
3'd000:led <= ctrl[0];
3'd001:led <= ctrl[1];
3'd010:led <= ctrl[2];
3'd011:led <= ctrl[3];
3'd100:led <= ctrl[4];
3'd101:led <= ctrl[5];
3'd110:led <= ctrl[6];
3'd111:led <= ctrl[7];
default:led <= led;
endcase
endmodule
2.激励文件
`timescale 1ns / 1ns
module counter_led6_tb(
);
reg clk;
reg reset_n;
reg [7:0]ctrl;
reg [31:0]Time;
wire led;
counter_led6 counter_led6_inst(
.clk(clk),
.reset_n(reset_n),
.ctrl(ctrl),
.Time(Time),
.led(led)
);
initial clk = 1'b1;
always #10 clk=~clk; //延时半个周期10ns,翻转clk值,模拟实现时钟信号
initial begin
reset_n = 1'b0;
ctrl =8'b0;
Time =8'b0;
#201;
reset_n = 1'b1;
#2000;
Time =2500;//2500ns*8*20=400000ns=0.4ms,一个完整的循环是0.4ms
ctrl =8'b1100_0111;
#20000000;//等待20ms,两个周期
Time =25000;//25000ns*8*20=4000000ns=4ms,一个完整的循环是4ms
ctrl =8'b1110_0110;
#20000000;//等待20ms,两个周期
$finish;
end
endmodule
3.仿真图
功能模块,Led灯在8个状态亮完之后,在10ms之中,led灯保持led[0]的状态
功能模块需要在led进程处修改,Led灯在8个状态亮完之后,在10ms之中,led灯熄灭
always@(posedge clk or negedge reset_n)
if(!reset_n)
led <=0;
else if(EN == 0)//8个状态亮完之后,在10ms之中,led灯熄灭
led <=0;
// else if(counter0 >= Time*8 -1)//8个状态亮完之后,在10ms之中,led灯熄灭,也可以用counter0控制
// led <=0;
else case(counter2)
3'd000:led <= ctrl[0];
3'd001:led <= ctrl[1];
3'd010:led <= ctrl[2];
3'd011:led <= ctrl[3];
3'd100:led <= ctrl[4];
3'd101:led <= ctrl[5];
3'd110:led <= ctrl[6];
3'd111:led <= ctrl[7];
default:led <= led;
endcase
七、总结
1.位宽是从0开始;数据的位数是从1开始,所以寄存器是[25:0],而MCNT是26位
reg [25:0]counter;//d50_000_000=b10_1111_1010_1111_0000_1000_0000
parameter MCNT=26'd50_000_000;//以1s为一个周期,1s后计数清零;1s=1000_000_000ns,1000_000_000ns/20ns=50_000_000次
2.同一个工程中,有两个testbench文件,需要将需要仿真的文件置顶,单击右键,选择Set as Top;之后,置顶后,会变成黑色加粗字体
3.case语句,没有列举完的情况,需要加一个默认项列举完
case(counter)
MCNT*1/8 -1:led <= ctrl[0];//语句1
MCNT*2/8 -1:led <= ctrl[1];
MCNT*3/8 -1:led <= ctrl[2];
MCNT*4/8 -1:led <= ctrl[3];
MCNT*5/8 -1:led <= ctrl[4];
MCNT*6/8 -1:led <= ctrl[5];
MCNT*7/8 -1:led <= ctrl[6];
MCNT*8/8 -1:led <= ctrl[7];
default:led <= led;
endcase
4.在功能4描述的设计中,端口不能命名为关键字,设置成time是错误,但是可以设置成Time;在vivado中可以用ctrl进行列选,整体修改case语句;在quartus中可以用alt进行列选,整体修改case语句;
后记:白日何短短,百年苦易短。
如有错误,感谢指正。