视频补充中,后续上传
1、方案的选择与设计原理
1.1方案的选择与比较
由于输入信号为谐波信号,其峰峰值范围为30mV-600mV,基频频率为1kHz-100kHz,而TMS320F28335可处理的信号范围是0-3V,考虑到测量精度问题,输入信号必须经放大和偏移之后再送入TMS320F28335的ADC。我们通过理论分析确定了下述两个方案。
方案一:选择可调增益的一个放大电路,对不同的输入电压采用不同的放大倍数,让小信号的放大倍数尽可能大且满足要求。此方案可以根据信号的大小来合理的调整放大倍数,提高测量精度,但调试起来困难。
方案二:选择可调增益的两个放大电路,一个调为4倍,一个调为10倍,将信号同时输入两个放大电路中,两个输出放大信号给电压偏置1.5V,将两个输出与TMS320F28335相连,让TMS320F28335自行判断两端口输入信号峰峰值是否满足0-3.3V的范围,并选择合适的信号进行后续操作,此方案实时性好,测量精度高,操作简便。
综上所述,最终选择了方案二。
主体流程框架见图1:
图1 主体流程框架
2.硬件电路设计及仿真实验
2.1放大电路设计
考虑到信号谐波频率最大值为500kHz(5次谐波频率),所以设计的放大电路带宽应至少大于600kHZ,采用两个OPA695分别构成增益为4和10的放大电路,可以满足带宽要求。
10倍放大电路原理图如图2,幅频特性如图3所示:
4倍放大电路原理图如图4,幅频特性如图5所示:
现以4倍放大电路为例,实验如下:
(1)输入信号,取不同值,频率取1kHZ,实验结果如表1所示:
(2)输入信号,取相同值,频率取不同值,实验结果如表2所示:
可见,放大电路完全满足设计要求。
2.2偏移电路的设计
为了使被测信号的峰-峰值为TMS320F28335可接受的范围,设计偏移电路及仿真结果如图6和图7所示,该电路将放大后的信号偏移1.5V,为了使送给单片机的信号为正,本级采用单电源供电:
与4倍放大电路经大电容级联后,输入信号,取不同值,频率取1kHZ,实验结果表3所示:
2.3谐波信号放大实验
采用任意信号发生器产生的自编辑的谐波信号,见图8和图9,给设计的电路进行测试,改变信号基频频率和幅值,信号均可以得到正常放大,为了保护单片机输入端,在放大+偏移电路末端增加了二极管限幅电路,使输出的电压最大值都在单片机可处理范围。
3、THD检测原理
通过TMS320F28335的定时器控制DMA+ADC12采集模拟信号为数字信号于数组中,通过雷德算法和蝶形运算实现FFT,得到输出频域值。通过频域复数值的模计算和幅角计算得到频率相角数组。通过公式(3-9)进行THD计算,还需进行傅里叶反变换和归一化幅值计算,并在OLED屏幕上显示。程序框图如图10:
图10 程序框图
3.1 FFT算法原理:
3.2THD说明及计算公式:
4.测量与显示结果
(1)输入信号Vipp取200mV,基频为1kHz-100kHZ的三角波,测量结果见表4。
(2)输入信号Vipp取350mV,基频为1kHz-100kHZ的方波,测量结果见表5。
(3)输入信号Vipp取300mV,基频为1kHz-100kHZ的自编辑谐波(波形如图9所示),测量结果见表6。
实验结果表明,以上失真度测量与显示均不超过10秒。
(4)显示屏上显示方波失真度测量值THDx,见图13。
图13 显示屏显示方波失真度测量值
(5)显示屏上显示输入一个信号的一个周期波形,见图14。
(6)显示输入信号基波与谐波的归一化幅值,只显示到 5 次谐波,见图15.
(7)手机显示测量装置测得并显示的输入信号THDx值、 基波与谐波的归一化幅值见图16.
图16 手机显示
5.误差分析
(1)ADC采样有频率限制,当输入信号频率过大时,可能会有误差。
(2)FFT算法有频谱泄露现象,会造成各频点幅值下降。
(3)输入端有杂波的干扰,当输入为小信号时,容易受杂波的干扰。
(4)可能是芯片以及运放的引脚特性导致的,焊接不好也会导致有误差。
6.附录(代码)
FFT函数:
#include "fft.h"
void InitBufInArray()
{
unsigned short i;
float fx;
for(i=0; i<NPT; i++)
{
fx = 1500 * sin(PI2 * i * 350.0 / Fs) + 2700 * sin(PI2 * i * 8400.0 / Fs) + 4000 * sin(PI2 * i * 18725.0 / Fs);
lBufInArray[i].real = fx ;
lBufInArray[i].imag = 0.0;
}
}
void GetPowerMag(void)
{
unsigned int i;
for(i = 0;i < NPT/2;i++) //求变换后结果的模值,存入复数的实部部分
{
if(i==0)
Mag[0]=sqrt(lBufInArray[i].real*lBufInArray[i].real+lBufInArray[i].imag*lBufInArray[i].imag)/NPT;
else
Mag[i]=sqrt(lBufInArray[i].real*lBufInArray[i].real+lBufInArray[i].imag*lBufInArray[i].imag)*2/NPT;
}
}
struct compx EE(struct compx a,struct compx b)
{
struct compx c;
c.real=a.real*b.real-a.imag*b.imag;
c.imag=a.real*b.imag+a.imag*b.real;
return(c);
}
void FFT(struct compx *xin)
{
int f,m,nv2,nm1,i,k,l,j=0;
struct compx u,w,t;
nv2=NPT/2;
//变址运算,即把自然顺序变成倒位序,采用雷德算法
nm1=NPT-1;
for(i=0;i<nm1;i++)
{
if(i<j) //如果i<j,即进行变址
{
t=xin[j];
xin[j]=xin[i];
xin[i]=t;
}
k=nv2;
//求j的下一个倒位序
while(k<=j) //如果k<=j,表示j的最高位为1
{
j=j-k; //把最高位变成0
k=k/2; //k/2,比较次高位,依次类推,逐个比较,直到某个位为0
}
j=j+k; //把0改为1
}
{
int le,lei,ip;//FFT运算核,使用蝶形运算完成FFT运算
f=NPT;
for(l=1;(f=f/2)!=1;l++) ;//计算l的值,即计算蝶形级数////////
for(m=1;m<=l;m++) // 控制蝶形结级数
{
//m表示第m级蝶形,l为蝶形级总数l=log(2)N
le=2<<(m-1); //le蝶形结距离,即第m级蝶形的蝶形结相距le点
lei=le/2; //同一蝶形结中参加运算的两点的距离
u.real=1.0; //u为蝶形结运算系数,初始值为1
u.imag=0.0;
w.real=cos(PI/lei); //w为系数商,即当前系数与前一个系数的商
w.imag=-sin(PI/lei);
for(j=0;j<=lei-1;j++) //控制计算不同种蝶形结,即计算系数不同的蝶形结
{
for(i=j;i<=NPT-1;i=i+le) //控制同一蝶形结运算,即计算系数相同蝶形结
{
ip=i+lei; //i,ip分别表示参加蝶形运算的两个节点
t=EE(xin[ip],u); //蝶形运算,详见公式
xin[ip].real=xin[i].real-t.real;
xin[ip].imag=xin[i].imag-t.imag;
xin[i].real=xin[i].real+t.real;
xin[i].imag=xin[i].imag+t.imag;
}
u=EE(u,w); //改变系数,进行下一个蝶形运算
}
}
}
}
、
THD函数
float THDcount(float *result)
{
unsigned int i = 0, n = 2, m, t = 0, sum = 0;
float THD = 0;
max = result[1];
for (i = 2; i < 128; i++) //gaile
{
if (result[i] > max)
{
max = result[i];
t = i;
}
}
sf = t;
for (i = 1; i < 5; i++)
{
Noramp[i] = 0;
}
phase[0] = -atan2f(lBufInArray[t].imag, lBufInArray[t].real);
m = t * n;
while (m < 128 && n < 6)
{
Noramp[n - 1] = result[m] / max;
phase[n - 1] = -atan2f(lBufInArray[m].imag, lBufInArray[m].real);
sum = result[m] * result[m] + sum;
n++;
m = t * n;
}
THD = 100 * sqrt(sum) / max;
//sum=sum/(max*max);
// THD=100*sqrt(sum);
return THD;
}
波形显示
void zhouqi(void)
{
unsigned char i = 0;
wave[i] =
max
* (sin(2 * 3.141592653589793 * i / 60 + phase[0])
+ Noramp[1]
* sin(2 * 2 * 3.141592653589793 * i / 60
+ phase[1])
+ Noramp[2]
* sin(3 * 2 * 3.141592653589793 * i / 60
+ phase[2])
+ Noramp[3]
* sin(4 * 2 * 3.141592653589793 * i / 60
+ phase[3])
+ Noramp[4]
* sin(5 * 2 * 3.141592653589793 * i / 60
+ phase[4]));
OLED_Clear();
for (i = 1; i < 60; i++)
{
wave[i] = max
* (sin(2 * 3.141592653589793 * i / 60 + phase[0])
+ Noramp[1]
* sin(2 * 2 * 3.141592653589793 * i / 60
+ phase[1])
+ Noramp[2]
* sin(3 * 2 * 3.141592653589793 * i / 60
+ phase[2])
+ Noramp[3]
* sin(4 * 2 * 3.141592653589793 * i / 60
+ phase[3])
+ Noramp[4] * sin(5 * 2 * 3.141592653589793 * i / 60
+ phase[4]));
OLED_DrawLine(i * 4 - 4, 60 - wave[i - 1] * 60 / 4096, 4 * i,
60 - wave[i] * 60 / 4096);
}
}