1、DDS模块的主要构成
DDS模块原理框图
2、DDS器件的工作原理
直接数字合成是生成模拟信号的一种常用方法,简单意义上的DDS,主要由相位调制器、波形查找表和DAC组成。相位调制器产生一个相位信息,使用该相位信息去波形查找表中查找对应的幅值信息,将幅值送DAC,产生对应的模拟信号,这就是DDS的工作原理。相位调制器一般由相位累加器和相位偏移器组成,先说相位累加器,看图,上半部分为幅值图,下半部分为相位图。
3、DDS内部模块介绍
相位累加器:是DDS的核心组成部分,用于实现相位的累加,并输出相应的幅值。相位累加器由M位宽加法器和M位宽寄存器组成,通过时钟控制,将上一次累加结果反馈到加法器输入端实现累加功能,从而使每个时钟周期内的相位递增数为,并取相位累加结果作为地址输出给ROM查找表部分。DDS的相位信息被存放在累加寄存器中,虽然幅值和相位不是线性关系,但寄存器累加值和相位可以是线性关系,很容易用寄存器的累加值表达相位信息。由于累加寄存器的位数是固定的,累加操作从0开始直至寄存器溢出,对应的相位信息是有限个数,相位对应的幅值信息也是有限个数,对于DDS而言,一种比较高效的方法是,将相位信息和幅值信息制作成查找表,根据累加寄存器的值,去波形查找表中查询对应的DAC数值,送到DAC中产生需要的电压信号。这里需要注意,为了降低系统误差,累加器的位数一般大于DAC的位数。
相位偏移器:一般情况下累加器的值是从0开始加的,输出波形的相位也是从0开始的;如果我想要输出信号的相位从90°开始,需要增加一个偏移,从相位累加器取出当前相位+相位偏移器的偏移相位,结果再送波形查找表,即可获得偏移后的幅值信息,DAC就可以输出偏移后的波形了。这个相位偏移值就是原理框图中相位字输入P_WORD。
幅值查找表:属于原理框图中的波形数据表ROM的内容,存储着每个相位对应的二进制数字幅度。在每个时钟周期内,查找表对相位累加器输出的相位地址信息进行寻址,然后输出对应的二进制幅度数字离散值。
假设查找表地址为M位,输出数据位N位,则查找表的容量大小位2^M*N。不难看出,输出信号的相位分辨率为2π/2^M。比如查找表的地址是4位,地址列表与相位列表对应关系:
DAC转换器:将数字信号转换为模拟信号。实际上DAC输出的信号并不是连续的,而是根据每位代码的权重,将每一位输入的数字量进行求和,然后以其分辨率为单位进行模拟的输出。实际输出的信号是阶梯状的模拟线型信号,所以要对其进行平滑处理,一般使用滤波器滤波。
4、DDS器件输出波形的频率
正弦波通常用其幅度来表示: a(t) = sin(ωt) ,其中ω = 2πf。
时间推进∆t,相位的改变量就是:
因为,所以上面的等式可以变换成如下:
由于DDS是时钟驱动的,时间t以固定间隔前进,这个时间间隔
就是DDS器件的工作时钟间隔,DDS器件的工作时钟频率就是
, 时间与频率的关系大家应该都知道吧,
所以等式 ,可以写成
在上式中,就是DDS器件输出信号的频率,
是DDS器件工作的时钟频率,整理等式可以得出DDS器件输出信号的频率
:
DDS器件工作的时候,是确定的,只要确定
,就能确定DDS器件输出信号的频率。
那么问题来了,DDS器件如何确定这个?
拿正弦波来说,根据常识,正弦波的周期是,相位角的变换范围也是0--
。假设DDS器件中存在一个28位的寄存器(好巧不巧,DDS器件里面就有那么一个28位的相位累加器),用寄存器里面的值来代表相位角,怎么个代表法?
28位的寄存器,它的取值范围就是0 -- 2^28-1,最大值就是2^28-1,我们用代替,所对应的相位角范围就是0 --
,这个寄存器的值怎么变化就是关键了,DDS是数字器件,需要数字时钟的驱动,每来一个时钟脉冲,这个28位的寄存器值就增加一下,增加
,那么就可以这么理解了
结合公式,可以得出:
DDS器件的是个确定的值,因为器件设计好之后,这个寄存器的位数就是确定的,这就说明DDS器件的输出信号频率,只与DDS器件的工作时钟
和寄存器数值的单位增量
相关,DDS器件在工作的时候工作时钟频率
是确定的,只要改变
的值就可以改变DDS器件输出信号的频率。
需要输出某个频率就设置的值,
这个也就是原理框图中要输入的频率字F_WORD。
5、重难点理解
频率控制字:也叫做频率字,简称Fword,就是每输入一个Fword,就输出对应的频率
这个Fword就是相位累加器每次加的值,如果一个正弦波的峰峰值可以用256个点表示,那么一个周期需要512(2^9)个点来表示,故相位累加器的位宽要大于9,我们只需要将高9位作为ROM的输入地址,低位作为累积量。
假设相位累加器是16bit位宽,因此高9位作为ROM的地址输入。如果我们要得到这个假设系统的基频,则需要每个时钟沿都对ROM的地址加1,
即16位相位累加器的第7位+1,则输入的控制字为Fword为1000_000,
产生的基频为50M/512 = 97656.25Hz,其中512是ROM的深度,也是2^16循环计数一次的个数,其取值为2^16/Fword。
所以输出频率Fout可用公式表示为Fout=50M/(2^16/Fword)。
假设系统频率为Fclk,相位累加器的位宽为N,则Fout=(Fclk*Fword)/2^N。我们可以通过设置不同频率控制字Fword大小来调节输出的正弦波频率。
相位控制字:相位控制字也叫相位字,就是输出信号的初始相位角,默认情况下相位角是从0开的,但如果我们想让相位角从90°开始,就写入一个相位偏移值,这相位偏移值就是相位字.
4、AD9833介绍
AD9833是一个完全集成的直接数字频率合成(DDS)芯片。该芯片需要一个参考时钟、一个精密低电阻和八个去耦电容,以用数字方式产生高达37.5 MHz的正弦波。除产生这个RF信号之外,该芯片还完全能支持各种简单和复杂的调制方案。这些调制方案完全在数字域内实现,使得可以使用DSP技术精确而轻松地实现复杂的调制算法。
AD9833的内部电路包含以下主要部分:数控振荡器(NCO)、频率和相位调制器、 SIN ROM、 DAC、比较器以及稳压器。
数控振荡器和相位调制器
该子电路由两个频率选择寄存器、一个相位累加器、两个相位偏移寄存器和一个相位偏移加法器组成。
NCO的主要元件是一个28位相位累加器。
连续时间信号的相位范围为0 至2 π。在此数值范围之外,正弦函数以周期方式不断重复。
数字实现并无差别。累加器只是将相位数值范围扩大至多位数字字。
AD9833中的相位累加器利用28位来实现。因此,在AD9833中, 2π 最多可以分成 2^28份。
同样, 项也会扩大至此数值范围: 0 <
< 2^28
将这些代入前面的公式可得:
的最小值是1,因为数字寄存器里面最小的增量就是1,当
最小的时候,频率的分频率最大,为
。
如果 = 1MHz,那么频率分辨率就是1M/2^28 = 0.0037Hz(手册上写的0.004Hz);
如果 = 5MHz,那么频率分辨率就是5M/2^28 = 0.0186Hz;
如果 = 10MHz,那么频率分辨率就是10M/2^28 = 0.0371Hz;
如果 = 25MHz,那么频率分辨率就是25M/2^28 = 0.093Hz(手册上写的0.1Hz);
可见DDS器件的工作频率越高,输出信号的频率分辨率越低。
相位累加器的输入可以从FREQ0寄存器或FREQ1寄存器进行选择并由FSELECT引脚或FSEL bit控制。
NCO本身会产生连续相位信号,因此在频率之间切换时应避免出现任何输出不连续。
SIN ROM
要使用NCO的输出,必须先将其从相位信息转换成正弦数值。
数字相位信息用作查找表的地址并将相位信息转换成幅度。
5、AD9833编程
本示例的目的是使用MCLK=1MHz的AD9833产生1kHz输出频率。
结合公式 ,
就是需要写入频率寄存器中的值FreqReg
本实例DDS器件的工作频率为1MHz,要输出频率为1000Hz的正弦波:
FreqReg = 1000Hz * 2^28 / 1MHz = 268435.456 = 268435 = 0x41893;
用C语言实现可以是这样的:
float temp = (float)(1<<28)/FMCLK;
CurFreqReg = (u32)(Frequency * temp); //CurFreqReg就是要设置的频率寄存器的值
要想控制AD9833,就得了解他的控制寄存器CONTROL REGISTER。
AD9833可以通过SPI总线来控制,控制位是在时钟的下降沿采样,所以SPI通讯的配置要注意极性。
控制AD9833主要就是控制频率和相位。
控制寄存器介绍
AD9833的控制寄存器一共有16位,
写着0的表示这些位不能用,比如DB15、DB14、DB9、DB4、DB2、DB0都是不用的位。
BIT13:B28,与28位频率寄存器相关
BIT12:HLB,把28位频率寄存器看成两个独立的14位寄存器,HLB就是控制往哪个寄存器写数据,是低14位还是高14位
如果让B28 = 1,那么这个28位的频率寄存器的值需要两次连续的写数据,第一次把低14位(14 LSB)的数据写入,第二次把高14位(14 MSB)的数据写入;需要等两次都成功写入之后,数据才生效。
如果让B28 = 0,那么这个28位的频率寄存器可以看成两个独立的14位寄存器,可以单独的写低14位的数据,或者高14位的数据;但这个操作需要BIT12:HLB的配合才能完成,HLB = 1,表示独立写高14位的数据,HLB=0,表示独立写低14位的数据。
这里还有一个问题,不管寄存器的高14位和低14位是否独立,我们写数据都是14位的,这怎么写?一个字节8位,两个字节就是16位,多余的2bit怎么办?放心,有安排,每次写数据的时候,除了那14位的数据,前面的2bit用来表示往那个频率寄存器里面写数据,AD9833和AD9834都是有两个频率寄存器的FREQ0和FREQ1。
比如,把28位寄存器不分开,那就需要连续写两条数据,先写低14位的数据,再写高14位的数据。比如把数据0xFFFC000写到FREQ0
1、先写0x2000命令(仅仅是为了把BIT13,也就是B28设置为1);
2、再写数据0x4000(把数据0xFFFC000的低14位写到FREQ0),
0x4000 = 0100 0000 0000 0000,共16bit,前面两个bit01表示把数据写到FREQ0,后面的14bit才是数据;
3、接着写数据0x3FFF(把数据0xFFFC000的高14位写到FREQ0),
0x3FFF = 0111 1111 1111 1111,共16bit,前面两个bit01表示把数据写到FREQ0,后面的14bit才是数据。
BIT11:FSELECT,频率寄存器选择,选择哪个频率寄存器用于相位累加器。
BIT10:PSELECT,相位寄存器选择,选择哪个相位寄存器用于相位累加器
BIT8:RESET=1,复位,内部寄存器复位为0,模拟输出为中量程。
复位是将某些寄存器的值设置为0;
模拟输出设置位中量程;
复位对相位、频率、控制寄存器不起作用;
当AD9833上电之后,就需要复位;
复位之后,等待8个MCLK时钟周期,DAC将有信号输出;
复位AD9833
void AD9833_Reset(void)
{
write_ad9833_d16(0x0100); //复位AD9833
}
往AD9833里写数据
void write_ad9833_d16(u16 word)
{
u8 TempBuf[2];
XSpiPs_SetSlaveSelect(&SpiInstance_dds, SLAVE_SELECT_1);
TempBuf[0] = (word)>>8;
TempBuf[1] = (word)&0xff;
XSpiPs_Write(&SpiInstance_dds, TempBuf, RecvBuffer, 2);
}
设置输出频率
void AD9833_SetFreqValue(float Frequency)
{
// 通过改变频率字来改变频率
float temp = (float)(1<<28)/FMCLK;
CurFreqReg=(u32)(Frequency * temp);
}
把参数写进AD9833
void AD9833_SetWaveData(void)
{
u16 FRQLW = 0; // MSB 频率字的低14位
u16 FRQHW = 0; // LSB 频率字的高14位
float temp = (float)FMCLK/(1<<28);
float freq = (float)CurFreqReg * temp;
printf("The Current Freq is: %.4f Hz\n",freq); //打印当前频率
FRQHW=(int)((CurFreqReg & 0xFFFC000) >> 14); // 获取频率字的高14位数据
FRQLW=(int)(CurFreqReg & 0x3FFF); // 获取频率字的低14位数据
FRQLW |= 0x4000; //14位数据前面添加两位,01,表示频率寄存器0, 10表示频率寄存器1
FRQHW |= 0x4000;
write_ad9833_d16(0x2000); //选择数据一次写入
write_ad9833_d16(FRQLW); //L14,先写低14位的数据,器件就是这么规定的
write_ad9833_d16(FRQHW); //H14,再写高14位的数据
write_ad9833_d16(0x2000); //芯片进入工作状态,寄存器频率0输出1KHz波形
//write_ad9833_d16(0x2028); // 输出方波
}
到这里AD9833和AD9834就可以用起来了,后面有机会再继续深入展开,也欢迎有兴趣的伙伴一起来探讨做一些小产品。