数字频率合成算法又称为DDS算法,实际上就是信号采样过程的一个逆用。当我们进行信号采样时,会使用一个大于信号频率2倍以上的采样频率去采点,把这个点存储在存储器中,称为波形ROM表。而DDS恰好相反,它是先把波形ROM表制作好,然后以等于采样频率的输出频率输出电压。由于在采样时,原始信号是经过了一个零阶保持器,然后被量化存储到存储器的;所以在输出电压时,也相应的加一个滤波器,把阶梯波滤除,来恢复原信号。
在这样的思想指导下,只要平台具有定时输出不同电压的功能,都能设计波形发生器。我们本次主要采用最简单的STC89C52和一片DAC芯片实现该设计。使用Keil5来编写代码,proteus 8来进行仿真。效果如下图所示,系统输出了100Hz的正弦信号。
硬件设计:
首先要选择一个DAC芯片。我们可以选用PCF8591,因为它是使用IIC协议通信,所以只占据2个引脚。但我们将在原理图中使用两片DAC芯片。一片DAC芯片用来发出正弦波,另一片DAC用来改变波形输出的幅度。由于DAC能输出的电压范围在AGND~Vref之间,我们可以把一个DAC的输出Vdac1_out,接到另一片DAC的Vref,然后改变Vdac1_out来实现幅度控制。其中AGND接地,为0V。
有些同学此刻会问了,不是说输送到DAC的数字量能改变输出电压的大小吗?我们把整个波形ROM表的数据乘以一个系数,就能控制输出幅度了,何必用两片DAC呢?这里要注意,虽然数字量乘以一个系数可以改变电压大小,但输出波形的分辨率就降低了。也就是说,原本0~255可以将输出电压分割成256个等级。但如果将数字量乘以0.5,则只能以0~127来分割电压,这个衡量不同电压的“尺子”变得更粗糙以后,输出的波形失真度会加大。极端的,如果把数字量乘以1/255,那么在输出端只能看到输出0V和0.02V两种电压,根本无法表示一个正弦信号。
软件设计:
测得PCF8591输出一个电压的时间是1ms。这意味着输出电压最大的频率是Fdac_max = 1000Hz。为了预留一些余量,就设置输出频率为Fdac=512Hz好了。此时这个Fdac相当于信号采样过程的采样频率。根据采样定理,采样频率必须大于或等于信号频率的2倍。所以理论上,这个系统可以输出256Hz的正弦波。但是为了减小设计难度,取最高输出频率100Hz。相当于在正弦波的一个周期内,至少有5个点。
由于PCF8591是8bit的DAC,所以最大有256个电压等级。我们可定义一个长度为256点的波形ROM表,如下定义的数组。这个表存储的是一个周期正弦波的电压数字量。
//ROM点数:256 虚拟表长:512 中断频率:512Hz 中断时间:1.95ms 最高正弦输出频率:512/5 = 102.4
code unsigned char Wave_ROM_Sine[WAVE_ROM_SIZE] =
{
128,131,134,137,140,143,146,149,152,155,
158,162,165,167,170,173,176,179,182,185,
188,190,193,196,198,201,203,206,208,211,
213,215,218,220,222,224,226,228,230,232,
234,235,237,238,240,241,243,244,245,246,
248,249,250,250,251,252,253,253,254,254,
254,255,255,255,255,255,255,255,254,254,
254,253,253,252,251,250,250,249,248,246,
245,244,243,241,240,238,237,235,234,232,
230,228,226,224,222,220,218,215,213,211,
208,206,203,201,198,196,193,190,188,185,
182,179,176,173,170,167,165,162,158,155,
152,149,146,143,140,137,134,131,128,124,
121,118,115,112,109,106,103,100,97,93,
90,88,85,82,79,76,73,70,67,65,
62,59,57,54,52,49,47,44,42,40,
37,35,33,31,29,27,25,23,21,20,
18,17,15,14,12,11,10,9, 7, 6,
5, 5, 4, 3, 2, 2, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 2,
2, 3, 4, 5, 5, 6, 7, 9, 10,11,
12,14,15,17,18,20,21,23,25,27,
29,31,33,35,37,40,42,44,47,49,
52,54,57,59,62,65,67,70,73,76,
79,82,85,88,90,93,97,100,103,106,
109,112,115,118,121,124
};
中断频率是512Hz,表的长度只有256,如果要输出1Hz的正弦波怎么办?只能做一个虚拟表长度RomLen = 512。实际上,对ROM寻址时是把数组索引先除以二再进行寻址的。所以当输出1Hz正弦波时,包含的阶梯波实际上是256Hz的阶梯信号。
ROM表只存储了一个周期的波形,那如何实现持续输出多个周期?这个其实就是循环寻址,不必赘述。中断服务函数的关键代码如下:
......
wave_dds.data_index += wave_dds.freq;
if(wave_dds.data_index >= wave_dds.rom_len)
{
wave_dds.data_index-= wave_dds.rom_len;
}
if(wave_dds.wave == sin)
{
wave_dds.Rom_data = Wave_ROM_Sine[wave_dds.data_index>>1];
}
pcf8591_set_dac_value(PCF8591_SLAVE_ADDRESS_0,wave_dds.Rom_data);
......
根据以上设计,输出正弦波最高频率为100Hz,夹杂的阶梯波最低频率为256Hz。那么可以设计一个截止频率为100Hz的低通滤波器,恢复原信号。
该波形发生器可以输出正弦,那如何输出三角波、锯齿波?其实只需要再多定义几个ROM表即可。但是注意,通过正弦ROM输出的信号经过低通滤波器,可以得到较好的波形;但是如果是三角波,却不适合经过低通滤波器。三角波本身就存在很多高频分量,故它不太适合使用低通滤波器。
所有的源代码和仿真文件: