5. 硬件电路设计
5.1 电压比较器电路
电压比较器的功能是对两个输入电压的大小进行比较,并根据比较结果输出高低电平。
通常用阈值电压和传输特性来描述比较器的工作特性。 阈值电压(又称门槛电平)是使比较器输出电 压发生跳变时的输入电压值,简称为阈值,用符号UTH表示。估算阈值主要应抓住输入信号使输出电压发生跳变时的临界条件。这个临界条件 是集成运放两个输入端的电位相等(两个输入端的电流也视为零),即U+=U–。
5.1.1 零电平比较器(过零比较器)
电压比较器是将一个模拟输入信号ui与一个固定的参考电压UR进行比较和鉴别的电路。
参考电压为零的比较器称为零 电平比较器。按输入方式的不同可分为反相输入和同相输入两种零电位比较器
5.1.2 任意电平比较器(俘零比较器)
将零电平比较器中的接地端改接为一个参考电压UR(设 为直流电压),由于UR的大小和极性均可调整,电路成为任意电平比较器或称俘零比较器。
5.1.3 滞回电压比较器
滞回比较器又称施密特触发器,迟滞比较器。这种比较器的特点是当输入信号ui逐渐增大或逐渐减小时,它有两个阈值,且不相等,其传输特性具有“滞回”曲线的形状。
滞回比较器也有反相输入和同相输入两种方式。
UR是某一固定电压,改变UR值能改变阈值及回差大小。
本次中,我们选择电力电子中常用的LM393比较芯片,下图为比较原理图
在输入端,我们直接输入交流正弦型号,,Dz稳压二极管会将电压抬至一定高度,供芯片比较。
如下图所示
5.2 交流采样电路
当输出电压、电流是双极性的交流信号,AD只能采单极性的交流信号。采用如下图所示的直流偏移电路可以把信号转换一下。
电解电容C取470uF/16V,R1、R2取2k,这样高通截止频率为2Hz左右,用于采样20Hz以上的信号。
计算过程中,先用求平均的方式把直流偏置算出来,然后扣除该值就得到交流信号的实际值了。不过要注意这部分电路要放尽量在单片机那头。
6.软件设计
根据题目要求,我们配置ADC成ADC1和ADC2双重模式,即由主ADC1带动从ADC2进行交替采样,详细代码见源文件,具体配置过程参照前面博客,这里是链接。
6.1 直流采样问题
直流采样,让ADC一直循环取值,相关程序如下
double adc_getvalue[10];
u32 adSumDC[10];
memset(&adSumDC,0,sizeof(adSumDC));
for(k=0;k<10;k++){
for(j=0;j<40;j++)
adSumDC[k] += aADCConvertedValue[2];
adc_getvalue[k]=(double)adSumDC[k]*33/(4096*400);
}
memset(&adSumDC,0,sizeof(adSumDC));
AdLight=adc_filter(10,adc_getvalue);
同时为直流采样设计滤波函数,代码如下
double adc_filter(u32 num,double *adc_num_value)
{
u8 i,j,k;
u8 noswap=1;
double adc_sum_tmp=0,adc_ave_tmp=0;
for(i=0;i<num-1;++i){
for(j=0;j<num-i-1;++j)
{
if(adc_num_value[j]>adc_num_value[j+1]){
adc_num_value[j]=adc_num_value[j]+adc_num_value[j+1];
adc_num_value[j+1]=adc_num_value[j]-adc_num_value[j+1];
adc_num_value[j]=adc_num_value[j]-adc_num_value[j+1];
noswap=0;
}
}
if(noswap) break;
}
for(k=2;k<num-2;k++)adc_sum_tmp += adc_num_value[k];
//adc_sum_tmp -= (adc_num_value[0]+adc_num_value[1]+adc_num_value[num-2]+adc_num_value[num-1]);
adc_ave_tmp=adc_sum_tmp/(num-4);
adc_sum_tmp=0;
return adc_ave_tmp;
}
我们直接将采样引脚连接到3.3v和0vde得到如下结果
6.2 交流频率测量
对于测交流正弦波信号的频率,我们将双极性的正弦波通过电压比较整形成脉冲波供单片机采样。
这里利用定时器的输入捕获功能,相应定时器的底层配置可参照之前博客,这里是链接。
相关计算频率过程代码如下
相关计算频率过程代
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)
{
TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);
//ADStartTim4Flag=1;
TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
}
IC2Value=TIM_GetCapture2(TIM2);
IC1Value=TIM_GetCapture1(TIM2);
//GPIO_SetBits(GPIOD,GPIO_Pin_1);
if(IC2Value!=0){
DutyCycle=(float)IC1Value*100/IC2Value;
Frequency =(float)1000000/IC2Value;
}
else{
GPIO_ResetBits(GPIOD,GPIO_Pin_1);
DutyCycle=0;
Frequency=0;
}
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
显示函数如下
OLED_ShowString(0,0,"MeasureResult:",16);
OLED_ShowString(0,16,"IC2Value:",16);
OLED_ShowNum(72,16,IC2Value,7,16);
OLED_ShowString(0,32,"DutyCycle:",16);
OLED_ShowFloatNum(80,32,DutyCycle,7,16);
OLED_ShowString(0,48,"Frequency:",16);
OLED_ShowFloatNum(80,48,Frequency,7,16);
实验现象图
6.3 交流采样问题
对于交流采样,采取的是同步采样过程,具体步骤是电压比较电路触发定时器TIM4实时,定时器根据输入信号频率计算定时间隔,等间隔采样。
6.3.1定时器中断程序
/*****************************************************
* 函数功能:TIM4中断程序,同步采样处理
* 作者:klaus 邮箱:xcf2016a@outlook.com
* 功能介绍:触发同步采样
* 版本:1.1(暂无改动)
******************************************************/
void TIM4_IRQHandler(void)
{
if(TIM_GetITStatus(TIM4,TIM_IT_Update)==SET)
{
ADArrayStay[ArrayCnt]=aADCConvertedValue[3];
ArrayCnt++;
if(ArrayCnt>=ADPointGet){
ArrayCnt=0;ADGetDowmFlag=1;
TIM_ITConfig(TIM4,TIM_IT_Update,DISABLE);
cnt_test++;
if(cnt_test%2==0)GPIO_ResetBits(GPIOD,GPIO_Pin_1);else GPIO_SetBits(GPIOD,GPIO_Pin_1);
}
}
TIM_ClearITPendingBit(TIM4,TIM_IT_Update);
}
6.3.2 AD计算程序
if(ADGetDowmFlag==1)
{
sampleMax=sampleMin=ADArrayStay[0];
for(k=1;k<ADPointGet;k++)
{
if(ADArrayStay[k]>=sampleMax)sampleMax=ADArrayStay[k];
if(ADArrayStay[k]<=sampleMin && ADArrayStay[k]!=0)sampleMin=ADArrayStay[k];
}
sampleIndex=3.3f*(sampleMax-sampleMin)/4096;
VppValue=sampleIndex;
VoltageRms[AdCnt]=sampleIndex/2.828f;
AdCnt++;
if(AdCnt>=FILTER_POINTS){AdCnt=0;ADVoltage=adc_filter(FILTER_POINTS,VoltageRms);}
}ADGetDowmFlag=0;
6.3.3 显示函数
OLED_ShowString(0,0,"ACModeTake:",16);
OLED_ShowString(0,16,"VppVal:",16);
OLED_ShowFloatNum(56,16,VppValue,7,16);
OLED_ShowString(104,16,"v",16);
OLED_ShowString(0,32,"RmsVal:",16);
OLED_ShowFloatNum(56,32,ADVoltage,7,16);
OLED_ShowString(104,32,"v",16);
OLED_ShowString(0,48,"Frequ:",16);
OLED_ShowFloatNum(48,48,Frequency,7,16);
OLED_ShowString(96,48,"Hz",16);
实验现象图
至于造成峰峰值不准的原因是:没供地,即博主单片机是电脑jlink供电,电路是电源供电,输入信号是信号源供电,三者地没有连在一起。
6.4 正弦波画图函数
将AD采样回来的函数进行相应数值变化以适合OLED显示,先画点,连线成图
程序代码如下
void sin_table_plot{
OLED_ShowChar(0,0,'^',16,1);
OLED_DrawLine(4,1,4,62,1);
OLED_DrawLine(4,60,70,60,1);
OLED_ShowChar(70,54,'>',12,1);
for(i=4;i<66;i++){
xi=i;
yi=60*ADArrayStay[i-4]/4096;
OLED_DrawLine(xn,yn,xi,yi,1);
xn=i;
yn=yi;
}
}
实验现象图