FPGA-计数器的相关应用
1. 基础知识-3-8译码器的实现
根据3位拨码开关所显示的数字,判断哪一个灯亮。使用case语句列出不同的拨码开关值。
module decoder_3_8(
input a,
input b,
input c,
output reg [7:0] out
);
always@(*) begin
case({a,b,c})
3'b000: out = 8'b0000_0001;
3'b001: out = 8'b0000_0010;
3'b010: out = 8'b0000_0100;
3'b011: out = 8'b0000_1000;
3'b100: out = 8'b0001_0000;
3'b101: out = 8'b0010_0000;
3'b110: out = 8'b0100_0000;
3'b111: out = 8'b1000_0000;
endcase
end
endmodule
2. 设计一个以1秒频率闪烁的LED灯
- 1秒频率进行闪烁,即亮灭更500毫秒,对于A7这块开发板,它的时钟周期是20ns。
- 因为500ms/20ns=25_000_000,即每经过25_000_000个时钟周期,灯反转一下。
- 每一次上升沿,即检测到posedge clk,则代表一个时钟周期,此时就需要使用到计数器来计数一个闪烁周期。
module led_flash(
input clk,
input reset_n,
output reg led
);
reg [24:0] count;
parameter mCount=25'd25000000;
//计数器(一次循环为500ms)
always @(posedge clk) begin
if(!reset_n)begin
count <= 25'b0;
end else if(count==mCount-1) begin
count <= 25'b0;
end else begin
count <= count+1'b1;
end
end
//计时器(当一次计数器循环结束则led灯翻转)
always @(posedge clk) begin
if(!reset_n)begin
led <= 1'b0;
end else if(count == mCount-1) begin
led <= !led;
end
end
endmodule
3. 跑马灯
设计让8个LED灯以每个0.5s的速率循环闪烁
- 8个led灯——>则有8个输出端——>output[7:0] led
- 因为每个led按照顺序依次点亮,所以可以联想到循环队列,将led初始化为0000_0001,再每一次当计数完0.5s时进行一次循环左移。
- 循环左移led<={led[6:0],led[7]}
4. 从计数器到可控线性序列机
让LED灯按照灭0.75秒,亮0.25秒的状态循环亮灭
- 0.25s+0.75s=1s,即一个大的灯闪烁的时间周期为1s。
- 1s/20ns=5000_0000次检测posedge clk
- 先将led置为0,一直灭0.75s,再拉高为1。即当5000_0000的3/4时,可以将led置为1。
module counter_led(
output reg led,
input clk,
input reset_n
);
reg [25:0] count;
parameter mCount=5000_0000;
//计数器(1s)
always @(posedge clk or negedge reset_n) begin
if(!reset_n) begin
count<=0;
end else if(count==mCount-1) begin
count<=0;
end else begin
count<=count+1'b1;
end
end
//计时器
always @(posedge clk or negedge reset_n) begin
if(!reset_n) begin
led<=0;
end else if(count==(mCount*3/4)-1) begin//当在0.75s时变亮
led<=1;
end else if(count==mCount-1) begin
led<=0;
end
end
endmodule
让LED等按照亮0.25秒,灭0.5秒,亮0.75秒,灭1秒的状态循环亮灭
- 0.25+0.5+0.75+1=2.5(秒)
- mCount=2.5s/20ns=125_000_000次 检测posedge clk
- 初始 led=1
当1/10mCount-1时,led=0;—即过了0.25秒灯变暗。
当3/10mCount-1时,led=1;----即过了0.25+0.5秒灯变亮。
当6/10mCount-1时,led=0;----即过了0.25+0.5+0.75秒灯变暗。
当10/10mCount-1时,led=1;----即过了0.25+0.5+0.75+1秒灯变亮。
让LED灯按照指定的亮灭模式亮灭,亮灭模式未知,由用户随机指定。
以0.25秒为一个变化周期,8个变化状态为一个循环。—>2s为一个循环周期
- 亮灭模式由用户指定,用[7:0] ctrl来设置
- 每i/8个mCount-1时,led<=ctrl[i-1]
- 由第二条分析得出,可以使用case语句来对8种情况分别进行处理。注意:别忘记default语句。
让LED灯按照指定的亮灭模式亮灭,亮灭模式未知,由用户随机指定。
8个变化状态为一个循环,每个变化状态的时间值可以根据不同的应用场景选择。
- 前面的几个问题都将每个小单位相加成一个大的灯闪烁的时间周期,再用除法进行计算。
- 现在由于有不同的应用场景,即根据用户给出的灯闪烁的每一小份时间周期-Time,所以没有办法用灯闪烁的时间周期/时钟周期计算出固定的mCount,那么上面所示的先计数器后计时器的方法也不可用
- 现在转换思路,我们知道了每一小份的时间周期Time,那么8个Time就是一个大的灯闪烁的时间周期。
- 综上所述:用一个计数器1算出一小份时间Time,再用一个计数器2算出8个一循环。根据计数器2再使用case语句对led进行赋值。
module counter_led_4(
output reg led,
input clk,
input reset_n,
input [7:0] ctrl,
input [31:0] Time
);
reg [31:0] count;
//现在不知道设置的单位周期,所以mCount不是一个定值,则不需要使用mCount
//parameter mCount=100000000;
//计数器1(Time-一份作为一个小的时间单位)
always @(posedge clk or negedge reset_n) begin
if(!reset_n) begin
count<=0;
end else if(count==Time-1) begin
count<=0;
end else begin
count<=count+1'b1;
end
end
reg [2:0] count2;
//计数器2(8个小的时间单位作为一个周期)
always @(posedge clk or negedge reset_n) begin
if(!reset_n) begin
count2<=0;
end else if(count==Time-1) begin
count2<=count2+1'b1;
end
end
//计时器
always @(posedge clk or negedge reset_n) begin
if(!reset_n) begin
led<=0;
end else begin
case(count2)
3'd0:led<=ctrl[0];
3'd1:led<=ctrl[1];
3'd2:led<=ctrl[2];
3'd3:led<=ctrl[3];
3'd4:led<=ctrl[4];
3'd5:led<=ctrl[5];
3'd6:led<=ctrl[6];
3'd7:led<=ctrl[7];
default:led<=led;
endcase
end
end
endmodule
让多个led灯按照设置的模式各自在一个变化循环内独立亮灭变化。
output [1:0] led;
input [7:0] ctrl0,ctrl1;
.....
case(count2)
3'd0:begin led[0]<=ctrl0[0];led[1]<=ctrl1[0];end
......
每隔10ms,让LED灯的一个8状态循环执行一次(每个状态的变化时间值小一点,方便测试,比如设置为10us)