本次我们采用FPGA自带频率100MHZ分频到115200MHZ用于UART波特率的实现。
DDS原理分析
在介绍任意分频技术之前我们先来看一下直接数字频率合成技术DDS的原理以利于后学的程序设计:
这里要介绍以下直接数字频率合成技术DDS的原理:
直接数字频率合成技术DDS的核心是一个N位的相位寄存器(N一般为24~32,N越大,精度越高),在一个固定的参考时钟下,相位寄存器以步长K递增计数。相位寄存器输出结果用于正弦查找表上。正弦查找表中存储一个周期的正弦波幅度量化数据,用于实现从相位累加器输出的相位值到正弦波幅度的转换。
相位寄存器每经过
2NK
个参考时钟回到初始状态,即一个周期结束。所以频率控制字K取最小值1时,每
2N
个时钟周期输出一个周期的正弦波,所以此时有**
更一般的情况当频率控制字时K时,每经过 2NK 个参考时钟输出一个周期的正弦波。所以此时有:
任意分频技术原理分析:
方案一:直接计数分频法
采用最常用的直接计数分频法,所需的计数器上限为
计数器上限取整数,将此值反馈带入输出频率计算式。如下所示:
由公式可以看出,结果值与预期相差大约7HZ,误差如此之大我们难以忍受。
方案二:任意频率发生器。
这个任意频率发生器是根据直接数字频率合成技术DDS演变而来。
根据公式:
若根据不同的K和N的值可以将fc分频到不同的值。下面我们看它的误差在多少范围。
我们设定N的值为32即设置一个32位的计数器,首先计算控制字:
由于FPGA中不能进行浮点运算,所以取K为整数4947803。将此值反馈代入输出频率计算表达式,如下所示 :
与预期相差在0.007左右,比较符合所需的频率分频。
Verilog程序设计
怎样将上述原理转化为我们的Verilog程序呢?
我们设置一个计数值cnt【31:0】作为相位累加器,k作为步长。在时钟clk下,以k为步长,cnt=cnt+k;
参数选择如下:
1)输出频率为115200,选择k=4947806;
2)占空比50%,我们采用如下操作:
(1)当
cnt<=2N2
时,fo=0,即输出为低电平;
(2)当
cnt>=2N2
时,fo=1,即输出为高电平;
程序设计步骤:
1)设置一个32位寄存器在时钟作用下计数。
2)设置“门限电压”,实现合成频率的方波输出。
3)分频时钟使能信号的产生。
(由于在FPGA中,除了全局时钟以外,不允许用其他的门控时钟来驱动电路,不然不利于综合电路而且也无法保证电路的稳定性。因此为了便于后续模块的调用,我们设计了一个时钟使能信号,采用边沿检测技术。)
Verilog程序:
module precise_divider
#(
//DEVIDE_CNT = 42.94967296 * fo 步长DEVIDE_CNT
// parameter DEVIDE_CNT = 32'd175921860 //256000bps * 16
// parameter DEVIDE_CNT = 32'd87960930 //128000bps * 16
// parameter DEVIDE_CNT = 32'd79164837 //115200bps * 16
parameter DEVIDE_CNT = 32'd6597070 //9600bps * 16
)
(
input clk,
input rst_n,
output divide_clk, //分频时钟信号
output divide_clken //时钟使能信号
);
reg [31:0] cnt; //设置一个32位相位寄存器cnt
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt <= 0;
else
cnt <= cnt + DEVIDE_CNT; //在参考时钟下以步长DEVIDE_CNT计数
end
reg cnt_equal; //输出频率位fo的方波
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt_equal <= 0;
else if(cnt < 32'h7FFF_FFFF)
cnt_equal <= 0;
else
cnt_equal <= 1;
end
reg cnt_equal_r; //边沿(上升沿)检测
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt_equal_r <= 0;
else
cnt_equal_r <= cnt_equal;
end //上升沿检测输出使能信号
assign divide_clken = (~cnt_equal_r & cnt_equal) ? 1'b1 : 1'b0;
assign divide_clk = cnt_equal_r;
endmodule