频率发生器可以产生特定频率的信号激励外部复阻抗。复阻抗的响应信号由片上模数转换器ADC采样后,再通过片上上数字信号处理器进行离散傅立叶变换(DFT)。在每个输出频率,DFT运算处理后都会返回一个实值(R)和虚值(I)。校正后,扫频轨迹上的每个频点的阻抗幅值和阻抗相对相位很容易计算。

AD5933有一个电压输出引脚Vout (图2.2)。它能发出一定频率的正弦扫描信号(>1kHz)对外部阻抗 Z(ω) 进行激励。信号通过被测样品后,再经放大、滤波后被模数转换器取样,并进行离散傅立叶变换,最终计算出待测阻抗值。

AD5933是通过芯片内部的DDS(直接数字合成器)来产生正弦扫描信号,该信号具有小于1Hz(0.1Hz)的分辨率。为DDS提供时钟频率的,既可以是外部时钟,也可以是内置的振荡器,可通过软件进行设置。DDS合成的信号经过数模转换和放大后,即可变为测试需要的扫描激励信号。该正弦激励信号有四个幅值可供选择,其值分别为2v,1v,400mv,200mv。而这些信号的起始频率,频率的增加量,和增加的次数,必须预先确定,它们都可通过软件进行设定。
注意事项:
1 AD5933使用I2C配置寄存器,第一步确保i2c通讯正常。
2 参数设置:
1>起始频率,步进频率,频率点数。reg82-reg89等寄存器;
2>反馈电阻值、输出电压范围和增益PGA都需要合适的设置,不能超过ADC的线性范围。
3>系统时钟MCLK:可以选用内部时钟或外部时钟。默认内部是16.776 MHz, 选择外部需要编程(写寄存器)。而且时钟的数值在编程中要用到。
4> reg8F是状态寄存器,有数据有效标志位和数据扫描完成标志位等。
先配置寄存器。再读取reg8f判断状态位,然后读取数据寄存器(reg94-reg97)并输出。
5>其中最重要的是校准,如果校准值不对,出来的数据当然不对。
校准要选择合适的已知电阻,一般选择和RFB反馈电阻相同的阻值;校准值一般认为是线性的。
本人发现并不十分线性,有些波动。所以在需要的每个频率点都校准了一下,校准值通过uart输出,然后再以一个const static double的数组写到程序中,通过查表法设置每个点的校准值。
可以两点校准,然后插值(假设线性),文档是这样做的。
6> 扫描点数最大511,如果点数过多可以分段。
参考分段代码:

for(int i=0;i<10;i++){//10 segment
AD5933_Init(sFreq[i],STEP_FREQ,IncNum[i]);
sweep (i, sFreq[i] , eFreq[i]);
}

初始化和扫描代码:

#define MCLK 16.776e6 //系统时钟
#define SFreq 5000
#define IncFreq 100
#define IncNum 451

/* AD5933初始化函数 */
void AD5933_Init(void)
{ //Gain = 1;clk_internal;vol = 2v;
/* 起始频率码 */
code1=code1|((int)((SFreq/(MCLK/4))*pow(2,27))&0xFF); //低8位
code2=code2|((((int)((SFreq/(MCLK/4))*pow(2,27)))>>8)&0xFF); //中间8位
code3=code3|((((int)((SFreq/(MCLK/4))*pow(2,27)))>>16)&0xFF); //高8位

/* 频率增量码 */
code4=code4|((int)((IncFreq/(MCLK/4))*pow(2,27))&0xFF); //低8位
code5=code5|(((int)((IncFreq/(MCLK/4))*pow(2,27))>>8)&0xFF); //中间8位
code6=code6|((int)((IncFreq/(MCLK/4))*pow(2,27))>>16); //高8位

/* 增量数 */
code7=code7|(IncNum & 0xFF);
code8=code8|((IncNum>>8)&0x1);

delay_ms(100);

/* 将起始频率码写入寄存器,此时使用内部16.776MHz时钟 */
i2c_write ( 0x84, code1);
i2c_write ( 0x83, code2);
i2c_write ( 0x82, code3);

/* 将频率增量码写入寄存器,此时使用内部16.776MHz时钟 */
i2c_write ( 0x87, code4);
i2c_write ( 0x86, code5);
i2c_write ( 0x85, code6);

/* 将增量数写入寄存器 */
i2c_write ( 0x89, code7);
i2c_write ( 0x88, code8);

/* 将预激励周期数15写入寄存器 */
i2c_write ( 0x8B, 0x0F);
i2c_write ( 0x8A, 0x00);

/* 将AD5933置于待命模式 */
i2c_write ( 0x80, 0xB0);

/* 选择使用内部时钟 */
i2c_write ( 0x81, 0x00);

/* 以用户所设定的参数进入初始化模式 */
i2c_write ( 0x80, 0x11);

delay_ms(10);//这里的延时是为了使电路在发出初始化命令后达到稳定状态

/* 进入频率扫描模式 */
i2c_write ( 0x80, 0x21);

}

/* 频率扫描函数 */
void sweep (void)
{
unsigned int real_byte_high;
unsigned int real_byte_low;

unsigned int imag_byte_high;
unsigned int imag_byte_low;

signed short int imag_data;
signed short int real_data;

int m = 0; //增益因子与系统相位数组递增变量

printf ("Start of Frequency sweep\n");
delay_ms(10);
for(;;){
status_register = AD5933_read(0x8F); //read Status Reg
status_register = (status_register & 0x2); //Get D1 bit(data valid bit)

if( ((status_register)| 0xFD )== 0xFF){ //1:data valid

if( (AD5933_read(0x8F)| 0xFB )!= 0xFF){ //1:freq sweep over (D2 bit)
LCD_Clear();
real_byte_high = AD5933_read(0x94); //读取实部寄存器高8位数据
real_byte_low = AD5933_read(0x95); //读取实部寄存器低8位数据
imag_byte_high = AD5933_read(0x96); //读取虚部寄存器高8位数据
imag_byte_low = AD5933_read(0x97); //读取虚部寄存器低8位数据

real_data = ((real_byte_high << 8) | real_byte_low);
imag_data = ((imag_byte_high << 8) | imag_byte_low);

Re = (int) real_data;
Im = (int) imag_data;
Magnitude = sqrt(pow(Re,2) + pow(Im,2)); //计算DFT幅度 DFT_ amp
Impedance = 1/(Magnitude * (GAIN_FAC[m])); //GainFactor_My
//#define CAL_5933
#ifdef CAL_5933
cal_value = 50000.0/Magnitude;//1/20000=50000 res:20kou
printf("Freq=%d,Magnitude=%f,GainFac=%f\n",freq1,Magnitude,cal_value);
// printf("Freq=%d,Re=%d,Im=%d,GainFac=%f\n",freq1,Re,Im,cal_value);
#else
// printf("m=%d,Freq=%d,Re=%d,Im=%d,Impedance=%fK\n",m,freq1,Re,Im,Impedance/1000);
// printf("Freq=%d,Re=%d,Im=%d,Impedance=%fK\n",freq1,Re,Im,Impedance/1000);
printf("%d , %fK\n",freq1,Impedance/1000);
#endif

delay_ms(1);
if(m++>IncNum-1) m=0; //增益因子与系统相位数组下标递增 TODO
freq1 = freq1 + IncFreq; // 频率递增

i2c_write ( 0x80, 0x31); //add freq gain=1 递增至下一个频率点

} //Query D2_bit over
else{ // invalid data,break
continue;
}
} //Query D1_bit over
} // sweep loop over
}