资源描述
FC
FPGA中的分频
FPGA中的分频是很重要的一个内容,只要涉及时序电路,几乎都会有分频的情况出现。但分频的语句却各有不同,以下就是不同写法,但不管怎样,分频一句话,就是用计数器来实现的。
下面我就几种不同的写法说说我的看法,在FPGA中是如何实现分频的。不过再说之前首先的了解时钟。我这里以系统时钟clk,f=50MH为例。由此可知其T=20ns,f=50MH也就是说一秒内,时钟高低电平改变50M次。
这里我用verilog为例,对于vhdl类同。讲解时只取分频部分语句后面附有
完正的程序
第一种写法:
。。。。。。
always@(posedge clk or negedge rst)
begin 。
if(!rst) begin
clk_div1<=0;
end
else begin
if(clk_div1!=11)
clk_div1<=clk_div1+1;
else
clk_div1<=0;
end
end
always@(posedge clk or negedge rst)
begin
if(!rst) begin
clk_div2<=0;
state<=0;
cnt<=0;
out<=0;
end
else if(clk_div1==11) begin
。。。。//执行什么功能
。。。。。
注意在这里此语句中,我没有写完整,只是把分频的关键地方写出来了,特别是红色标注的地方。这里暂时不管定义的rst ,state。。。。这些变量。关键是此句话
if(clk_div1!=11)
clk_div1<=clk_div1+1;
else
clk_div1<=0;
此句话可知计数器clk_divl对系统时钟进行计数,并且只计数到11,clk_divl变等于0。这里有点类似于延时,下面以图形来说明。
由上图可知,clk=5MH的时钟,在计数器clk_divl下计数11,产生约4MH的时钟,此后系统便在4MH的频率下工作。这里他并未把新时钟提出来,而是在原来的时钟上分出4MH情况来工作。
第二种写法:
。。。。。
。。。。。
always @ ( posedge clk_50M )
begin
if ( count==25000000 )//此处也可写成24999999
begin
div_clk<=~div_clk;
end
else
count<=count+1;
led_out<=div_clk;
end
。。。。
。。。。
在这里我们也不去关注分频以外的东西,关键此语句
if ( count==25000000 )
begin
div_clk<=~div_clk;
end
else
count<=count+1;
我们看到这里实际上定义了一个新时钟名:div_clk,系统此后便可以以此时钟来工作,在这里只要明白这样的解释就行了。
--(1)如进行 N 倍偶数、占空比为 50%的分频,那么可以通过由待分频的时钟触发计数,当计数器从 0 计数到 N/2-1时,输出时钟进行翻转,以此循环下去。
--(2) 如进行N 倍偶数、占空比为 1/N的分频,那么可以通过由待分频的时钟触发计数,当计数器从 0 计数到 N-1 时,输出时钟进行翻转,并给计数器一个复位信号,使得一个时钟从零开始计数,以此循环下去。
一个公式就是:N为分频数,M为计数器的计数值。
N/2-1=M。。。。。此时为,进行 N 倍偶数、占空比为 50%的分频。
N-1=M。。。。。。。此时进行N 倍偶数、占空比为 1/N的分频。
例如一个50MH的分频其图为:
其计数值M为25000000或24999999
对于基数的分频:
对于实现占空比为 50%的 N 倍奇数分频,首先进行上升沿触发进
行模 N 计数,计数到(N-1)/2 进行输出时钟翻转,然后经过(N-1)/2 (即计数到 N-1 时)再次进行翻转得到一个占空比非 50%奇数 n 分频时钟。再者同时进行下降沿触发的模 N 计数,到(N-1)/2 时,进行输出时钟时钟翻转,同样经过(N-1)/2(即计数到 N-1 时)时,输出时钟再次翻转生成占空比非 50%的奇数 n 分频时钟。两个占空比非 50%的 n 分频时钟相或运算,得到占空比为 50%的奇数n分频时钟。
第三种写法:
always @(posedge clk,negedge res)
begin
if(!res)
begin
led<=6'b000000;
end
else
begin
case(count[26:23])
4'd0: led<=6'b111110; //X miao
4'd1: led<=6'b111101; //Y miao
4'd2: led<=6'b111011;
4'd3: led<=6'b110111;
4'd4: led<=6'b101111;
4'd5: led<=6'b011111;
4'd6: led<=6'b011111;
4'd7: led<=6'b101111;
4'd8: led<=6'b110111;
4'd9: led<=6'b111011;
4'd10:led<=6'b111101;
4'd11:led<=6'b111110;
4'd12:led<=6'b000000;
4'd13:led<=6'b111111;
4'd14:led<=6'b000111;
4'd15:led<=6'b111000;
default:led<=6'b000000;
endcase
在这里我们也不去关注分频以外的东西,关键此语句
case(count[26:23])
实际上这里定义了一个26位的计数器,但实际上为后22位计数,前4位定义状态。这种分频可用于数码管的动态显示或流水灯跑马灯等。其具体分析为:
26位:
25 24 23 22 21。。。。。。。。。。。。。。。。。。。。0
4'd0:0 0 0 0 00 0000 0000 0000 0000 0000//第一个状态
0 0 0 0 1 111111。。。。。。。。。。。。。。1 X秒//计数满22位为80ms
4'd1:0 0 0 1 0000000000。。。。。。。。。00000//第二个状态
0 0 0 1 1111111。。。。。。。。。。。。。。11111 Y秒//计数满22位为80ms
4’d2:0 0 1 0 0000000。。。。。。。。。。。。。。00000
0 0 1 0 1111111.。。。。。。。。。。11111111111 Z秒//计数满22位为80ms
4’d3:0 0 1 1 0000000。。。。。。。。。。。。00000000 //第三个
0 0 1 1 11111111111。。。。。。。。1111111111 W秒//计数满22位为80ms
。
。
。
。
。
。
4’d15:1 1 1 0 00000000000000000。。。。。。。000000//第四个
1 1 1 0 1111111。。。。。。。。。。。。1111111111//计数满22位为80ms
从面可以看出每个状态停留的时间均为80ms,而每个状态在下一个时钟来时
又加一,从而转到下一个状态。这样状态连续,而每个状态时间有一样,变可以实现动态显示和流水灯。
附以上三个完整程序:
第一个:
module buzzer(clk,rst,out);
input clk,rst;
output out;
reg out;
reg[3:0] clk_div1; //基频分频计数器,基频为4M
reg[12:0] clk_div2;//音阶分频计数器,由基频分频产生各个音阶
reg[21:0] cnt;//各音阶发声时间长短计数器
reg[2:0] state;
parameter duo=3822, //各个音调的分频系数
lai=3405,
mi=3034,
fa=2865,
suo=2551,
la=2273,
xi=2024,
duo1=1911;
always@(posedge clk or negedge rst)//这里采用了,分频的另一种手法,相当于延时。
begin //分频的实现方式有多种。
if(!rst) begin
clk_div1<=0;
end
else begin
if(clk_div1!=11)//在原来的时钟上分频,并未加入一个新的时钟。及原来的时钟到11个时钟 (波形)//才进行下一步做法。
clk_div1<=clk_div1+1;//相当于每4MH,做一次指令功能。因为11个时钟为220ns,所以周期为220ns,//频率为
else //4MH。及原来的时钟到11个时钟(波形)才进行下一步做法(计时11个波形)
clk_div1<=0;
end
end
always@(posedge clk or negedge rst)
begin
if(!rst) begin
clk_div2<=0;
state<=0;
cnt<=0;
out<=0;
end
else if(clk_div1==11) begin
case(state)
3'b000: begin //发“多”
cnt<=cnt+1;
if(cnt==22'h3fffff)
state<=3'b001;
if(clk_div2!=duo) //+3822
clk_div2<=clk_div2+1;
else begin
clk_div2<=0;
out<=~out;
end
end
3'b001: begin //发“来”
cnt<=cnt+1;
if(cnt==22'h3fffff)
state<=3'b010;
if(clk_div2!=lai)
clk_div2<=clk_div2+1; //
else begin
clk_div2<=0;
out<=~out;
end
end
3'b010:begin //发"米“
cnt<=cnt+1;
if(cnt==22'h3fffff)
state<=3'b011;
if(clk_div2!=mi)
clk_div2<=clk_div2+1;
else begin
clk_div2<=0;
out<=~out;
end
end
3'b011: begin //发"法“
cnt<=cnt+1;
if(cnt==22'h3fffff)
state<=3'b100;
if(clk_div2!=fa)
clk_div2<=clk_div2+1;
else begin
clk_div2<=0;
out<=~out;
end
end
3'b100: begin //发"梭“
cnt<=cnt+1;
if(cnt==22'h3fffff)
state<=3'b101;
if(clk_div2!=suo)
clk_div2<=clk_div2+1;
else begin
clk_div2<=0;
out<=~out;
end
end
3'b101: begin //发"拉“
cnt<=cnt+1;
if(cnt==22'h3fffff)
state<=3'b110;
if(clk_div2!=la)
clk_div2<=clk_div2+1;
else begin
clk_div2<=0;
out<=~out;
end
end
3'b110: begin //发"西“
cnt<=cnt+1;
if(cnt==22'h3fffff)
state<=3'b111;
if(clk_div2!=xi)
clk_div2<=clk_div2+1;
else begin
clk_div2<=0;
out<=~out;
end
end
3'b111: begin //发"多“(高音)
cnt<=cnt+1;
if(cnt==22'h3fffff)
state<=3'b000;
if(clk_div2!=duo1)
clk_div2<=clk_div2+1;
else begin
clk_div2<=0;
out<=~out;
end
end
endcase
end
end
endmodule
第二个:
module ledwater (clk_50M,led_out,f_led_out);
input clk_50M; //系统时钟输入50M
//意味每一秒要变化50,000,000HZ
output led_out; //每一秒闪烁一下
output f_led_out; //每二秒闪烁一下
reg [24:0] count; //分频计数器,25000000分频 一秒
reg [24:0] f_count;//分频计数器,12500000分频 0.5秒
reg div_clk, f_div_clk;
reg led_out, f_led_out;
//分频计数器。得到一秒的频率
always @ ( posedge clk_50M )
begin
if ( count==25000000 )
begin //我们的时钟本身是每一秒要变化50,000,000HZ
//我们现在用count这个计数器让它自加到25,000,000HZ
div_clk<=~div_clk; //在这里我们就得到了0.5秒变化一次的信号。
//所以一个周期就是1Hz也就是一秒。
count<=0; //把计数器清零。
end
else
count<=count+1; //计数器自加。
led_out<=div_clk; //利用分频计数器得到显示一秒的闪烁效果
//在LED灯上面表现出来。
end
//分频计数器。得到0.5秒的频率
always @ ( posedge clk_50M )
begin
if ( f_count==12500000 ) //我们现在用count这个计数器让它自加到12,500,000HZ
begin
f_div_clk<=~f_div_clk; //在这里我们就得到了0.25秒变化一次的信号。
//所以一个周期就是0.5也就是2HZ。
f_count<=0;
end
else
f_count<=f_count+1; //计数器自加。
f_led_out<=f_div_clk; //利用分频计数器得到显示一秒的闪烁效果
//在LED灯上面表现出来。
end
endmodule
第三个:
module ledsan(clk,led,res);
input clk;
output[5:0] led;
input res;
reg [26:0] count;
reg [5:0] led;
always @(posedge clk)
begin
count<=count+1;
end
always @(posedge clk,negedge res)
begin
if(!res)
begin
led<=6'b000000;
end
else
begin
case(count[26:23])
4'd0: led<=6'b111110; //X miao
4'd1: led<=6'b111101; //Y miao
4'd2: led<=6'b111011;
4'd3: led<=6'b110111;
4'd4: led<=6'b101111;
4'd5: led<=6'b011111;
4'd6: led<=6'b011111;
4'd7: led<=6'b101111;
4'd8: led<=6'b110111;
4'd9: led<=6'b111011;
4'd10:led<=6'b111101;
4'd11:led<=6'b111110;
4'd12:led<=6'b000000;
4'd13:led<=6'b111111;
4'd14:led<=6'b000111;
4'd15:led<=6'b111000;
default:led<=6'b000000;
endcase
end
end
endmodule
展开阅读全文