采用ADuC7026单片机的电感线圈微距测量系统设计
0赞在工业控制领域,距离测量颇为常见,利用电容极板距离变化对电容值的影响可以设计基于电容原理的测距传感器。这是一个简单的方法,可能大家都很熟悉。但极板一般做不大,而利用电感线圈进行距离测量却不受这一条件限制,在铁芯上绕上线圈,然后将线圈封装好,留出一个待测平面作为基准,再选一块铁板,当铁板平面与铁芯接近时,线圈的电感也会增加,这样就可以构建一个电感大小与距离的函数,进行测距。这种方法精度较高,可以轻松达到毫米级,而且对恶劣环境有较好的适应性。
为了将电感变化转换为模拟电压值,设计了一系列电路,如下图1所示:
激励信号为正弦波信号,可以利用ADuC7026的DAC进行产生,然后用运放进行放大即可,电感线圈和一个1.5K的电阻分压,可见电感值变化将会导致分压值变化,分压值此时仍为一个正弦信号,只不过比激励信号幅值要小,那么如何测量这个正弦波信号的大小呢,直接进行AD采样显然不行,这里采用了绝对值电路。绝对值电路可以把交流信号变为直流信号,经过绝对值电路的信号仍为幅值跳动的信号,类似二极管整流过的信号,所以还要进行低通滤波,频率设为10HZ左右,这样就会输出一个可以供单片机采用的信号了。
还要说明的是,电感一般在通断电是会产生一个冲击电压,这一点不需要过多解释,处理办法一般是用续流二极管将其放掉,图1左上角的两个二极管就是起这个作用。

图1 分压网络及绝对值电路、滤波电路
为了保证测量的准确,还要对激励信号进行采样,这是因为激励信号也可能随温度进行变化,包括幅值和频率的变化,激励信号如果变化那么电感分压必定要随之变化,采样值也会随之变化,如何减小这种误差呢?一种简单的方法就是采用归一化思想,即对激励信号也采样,将电感分压值除以激励信号的幅值,这个比值一般随温度不会变化,就像常说的“水涨船高”,激励信号就是“水”,电感分压输出信号就好比是船,这种方法是一种简单易行的方法。
图2是对激励信号做绝对值并滤波的电路,由于电压超过了单片机ADC的输入范围,后面要经过缓冲和分压电路。

图2 激励信号绝对值电路、滤波及分压电路
下面是ADUC7026的芯片外围电路,系统主要用了ADUC7026的模拟接口,即ADC与DAC接口,电路图如下:

/********************************************************************* Author : Wang Yan Date : Aug. 2012 File : ADCsing.c Hardware : Currently targetting ADuC7026. Description : DISTANCE MEASURING *********************************************************************/ #include#include /**********全局变量声明**************/ float w; float d,pred,distance; int m = 0; const static unsigned int TableS[8] = { 0x07FF0000, 0x0DA70000, 0x0FFF0000, 0x0DA70000, 0x07FF0000, 0x02570000, 0x00000000, 0x02570000 }; /********中断服务*******/ void IRQ_Handler() __irq { DAC0DAT = TableS[m] ; m++; m &= 0x07; T1CLRI = 0x01; } /**********ADC上电程序**************/ void ADCpoweron(int time) { ADCCON = 0x20; // power-on the ADC while (time >=0) // wait for ADC to be fully powered on time--; } /***********************交换两个数的值,供中值滤波用(整型)*******************************************/ void Swap(int *x,int *y) { int temp; temp = *x; *x = *y; *y = temp; } /***********************************中值滤波程序,是寻找具有奇数(odd)个数的采样序列的中值(整型)***************************/ int FindMedian(int n,int SortData[]) { // int i,j,k; // float sum=0.0, avg=0.0; int i,j; for(i = 0;i< n;i++) { for(j = n-1;j > i;j--) { if(SortData[j] < SortData[j-1]) { Swap(&SortData[j],&SortData[j-1]); } } } return SortData[(n-1)/2]; /* for(k =1;k< n-1;k++) { sum += SortData[k]; avg = sum/(n-2); } sum=0.0; return avg ; */ } /***********************交换两个数的值,供中值滤波用(浮点型)*******************************************/ void Swap1(float *x,float *y) { float temp; temp = *x; *x = *y; *y = temp; } /***********************************中值滤波程序,是寻找具有奇数(odd)个数的采样序列的中值(浮点型)***************************/ float FindMedian1(int n,float SortData[]) { // int i,j,k; int k; float sum=0.0, avg=0.0; /* for(i = 0;i< n;i++) { for(j = n-1;j > i;j--) { if(SortData[j] < SortData[j-1]) { Swap1(&SortData[j],&SortData[j-1]); } } } */ for(k =0;k< n;k++) { sum += SortData[k]; avg = sum/n; } sum=0.0; return avg; // return SortData[(n-1)/2]; } /*******测量正弦波发生电路产生的源信号均值,ADC1************/ float ReadSource() { float SourceValue; int i,SampleData[15]; ADCCP = 0x01; for(i = 0;i < 15;i++) { ADCCON = 0x7E3; while(!ADCSTA){} ADCCON &=0xF7F; SampleData[i] = (ADCDAT >> 16); } SourceValue = FindMedian(15,SampleData); return SourceValue; } /*******测量电感分压信号均值,ADC2************/ float ReadOutput() { float OutputValue; int i,SampleData[15]; ADCCP = 0x02; for(i = 0;i < 15;i++) { ADCCON = 0x7E3; while(!ADCSTA){} ADCCON &=0xF7F; SampleData[i] = (ADCDAT >> 16); } OutputValue = FindMedian(15,SampleData); return OutputValue; } /****************低通平缓滤波*******************/ float LowPassFilter2(float Factor,float NowData,float PreData) { return (1-Factor)*PreData + Factor * NowData; } /*******************供打印输出距离使用****************************/ int putchar(int ch) { /* Write character to Serial Port */ if (ch == '\n') { while(!(0x020==(COMSTA0 & 0x020))) {} COMTX = 0x0D; /* output CR */ } while(!(0x020==(COMSTA0 & 0x020))) {} return (COMTX = ch); } /********************延时函数*********************/ void delay(int length) { while(length>=0) length--; } /**************主函数****************/ int main (void) { float Vs=3000.0,Vo=3000.0; int j=0,DAC1value; float ComData[185]; //此处修改数据更新频率!!!!!!!! // PLLKEY1=0xAA; // PLLCON=0x01; // PLLKEY2=0x55; POWKEY1 = 0x01; POWCON = 0x00; POWKEY2 = 0xF4; // DAC configuration DAC0CON = 0x12; // DAC configuration DAC1CON = 0x12; // DAC configuration REFCON = 0x01; // range AREF/AGND // DAC1 is updated with falling edge of core clock DAC0DAT = 0x08000000; // start from midscale DAC1DAT = 655; // 400mV----4mA T1LD = 0x80; // Counter Value ,0x90=36khz,0x80=40khz; T1CON = 0xC0; // Enabled,Periodic,Binary and CLK/16 IRQEN = GP_TIMER_BIT; // Enable XIRQ0 and Timer1 IRQ ADCpoweron(20000); // power on ADC ADCCP = 0x02; // REFCON = 0x01; // connect internal 2.5V reference to Vref pin GP0CON = 0x010100000; // enable ECLK output on P0.7, and ADCbusy on P0.5 // GP4DAT = 0x04000000; // P4.2 configured as an output. LED is turned on GP1CON = 0x011; // Setup tx & rx pins on P1.0 and P1.1 // Setting up UART at 9600 (CD=0) COMCON0 = 0x80; // Setting DLAB COMDIV0 = 0x88; // COMDIV1 = 0x00; COMCON0 = 0x07; // Clearing DLAB while(1) { // t = ADCRead(); // w = t/100.0; // d = 0.0657351*w*w*w*w*w - 9.1878362*w*w*w*w + 513.6387866*w*w*w - 14355.5936574*w*w + 200581.1607954*w -1120847.94507; // d = 0.1698214*w*w*w*w - 18.8880471*w*w*w + 787.933324*w*w - 14608.094629*w + 101539.6810149-0.2; GP4DAT = 0x02000000; for(j = 0;j < 185;j++) { Vs = ReadSource(); Vo = ReadOutput(); w = (Vs+0.000001)/(Vo+0.000001)*30.0; // ComData[j] = 0.4696771683*w*w*w*w - 51.9252290524*w*w*w + 2152.6956573411*w*w - 39667.9356070098*w + 274163.653; ComData[j] = 0.3813314*w*w*w*w - 42.2315498*w*w*w + 1754.0923438*w*w - 32387.8436677*w + 224332.59168; } d = FindMedian1(185,ComData); // distance= LowPassFilter2(0.5,d,pred); if(d<0.0) { d=0.0; } DAC1value=(int)(655.2+262.08*d); if(DAC1value>4095) { DAC1value=4095; } // DAC1DAT = DAC1value<<16; DAC1DAT = 0x2BC<<16; GP4DAT = 0x04000000; printf("%f\n",Vs); // printf("%f\n",d); } }
