1、 FC FPGA中的分频 FPGA中的分频是很重要的一个内容,只要涉及时序电路,几乎都会有分频的情况出现。但分频的语句却各有不同,以下就是不同写法,但不管怎样,分频一句话,就是用计数器来实现的。 下面我就几种不同的写法说说我的看法,在FPGA中是如何实现分频的。不过再说之前首先的了解时钟。我这里以系统时钟clk,f=50MH为例。由此可知其T=20ns,f=50MH也就是说一秒内,时钟高低电平改变50M次。 这
2、里我用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 alwa
3、ys@(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
4、 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 )//此处也可写成249
5、99999 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;
6、我们看到这里实际上定义了一个新时钟名:div_clk,系统此后便可以以此时钟来工作,在这里只要明白这样的解释就行了。 --(1)如进行 N 倍偶数、占空比为 50%的分频,那么可以通过由待分频的时钟触发计数,当计数器从 0 计数到 N/2-1时,输出时钟进行翻转,以此循环下去。 --(2) 如进行N 倍偶数、占空比为 1/N的分频,那么可以通过由待分频的时钟触发计数,当计数器从 0 计数到 N-1 时,输出时钟进行翻转,并给计数器一个复位信号,使得一个时钟从零开始计数,以此循环下去。 一个公式就是:N为分频数,M为计数器的计数值。 N/2-1=M。。。。。此时为,进行 N 倍偶数
7、占空比为 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 时)时,输出时钟再次翻
8、转生成占空比非 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<
9、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:l
10、ed<=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 0
11、000 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 1111
12、111.。。。。。。。。。。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。。。。。。。。。。。。1
13、111111111//计数满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;//各音阶发声
14、时间长短计数器 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<=
15、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) beg
16、in 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<
17、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 //发"米“
18、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)
19、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;
20、 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<=cn
21、t+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!
22、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;
23、 //每一秒闪烁一下 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 //我们的时钟本身是每一秒要变化5
24、0,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灯
25、上面表现出来。 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;
26、 //计数器自加。 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 al
27、ways @(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'b0
28、11111; 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






