STM32 关于ADC采交直流问题探讨(二)

5. 硬件电路设计

5.1 电压比较器电路

电压比较器的功能是对两个输入电压的大小进行比较,并根据比较结果输出高低电平。
       通常用阈值电压和传输特性来描述比较器的工作特性。 阈值电压(又称门槛电平)是使比较器输出电 压发生跳变时的输入电压值,简称为阈值,用符号UTH表示。估算阈值主要应抓住输入信号使输出电压发生跳变时的临界条件。这个临界条件 是集成运放两个输入端的电位相等(两个输入端的电流也视为零),即U+=U–。

5.1.1 零电平比较器(过零比较器)

电压比较器是将一个模拟输入信号ui与一个固定的参考电压UR进行比较和鉴别的电路。  
       参考电压为零的比较器称为零 电平比较器。按输入方式的不同可分为反相输入和同相输入两种零电位比较器
4

5.1.2 任意电平比较器(俘零比较器)

将零电平比较器中的接地端改接为一个参考电压UR(设 为直流电压),由于UR的大小和极性均可调整,电路成为任意电平比较器或称俘零比较器。
5

5.1.3 滞回电压比较器

滞回比较器又称施密特触发器,迟滞比较器。这种比较器的特点是当输入信号ui逐渐增大或逐渐减小时,它有两个阈值,且不相等,其传输特性具有“滞回”曲线的形状。
       滞回比较器也有反相输入和同相输入两种方式。   
       UR是某一固定电压,改变UR值能改变阈值及回差大小。
6
本次中,我们选择电力电子中常用的LM393比较芯片,下图为比较原理图
7
在输入端,我们直接输入交流正弦型号,,Dz稳压二极管会将电压抬至一定高度,供芯片比较。
       如下图所示
8

5.2 交流采样电路

当输出电压、电流是双极性的交流信号,AD只能采单极性的交流信号。采用如下图所示的直流偏移电路可以把信号转换一下。
9
电解电容C取470uF/16V,R1、R2取2k,这样高通截止频率为2Hz左右,用于采样20Hz以上的信号。
10
计算过程中,先用求平均的方式把直流偏置算出来,然后扣除该值就得到交流信号的实际值了。不过要注意这部分电路要放尽量在单片机那头。

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得到如下结果
11

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);

实验现象图
12

6.3 交流采样问题

对于交流采样,采取的是同步采样过程,具体步骤是电压比较电路触发定时器TIM4实时,定时器根据输入信号频率计算定时间隔,等间隔采样。

6.3.1定时器中断程序

/*****************************************************
*   函数功能:TIM4中断程序,同步采样处理
*     作者:klaus 邮箱:[email protected]
*     功能介绍:触发同步采样
*   版本: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);

实验现象图
13
至于造成峰峰值不准的原因是:没供地,即博主单片机是电脑jlink供电,电路是电源供电,输入信号是信号源供电,三者地没有连在一起。

6.4 正弦波画图函数

将AD采样回来的函数进行相应数值变化以适合OLED显示,先画点,连线成图
程序代码如下

        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;
        }        

实验现象图
14

7.实验总图

15

猜你喜欢

转载自blog.csdn.net/klaus_x/article/details/81032250