Stoppt die Vorratsdatenspeicherung! Jetzt klicken && handeln!Willst du auch bei der Aktion teilnehmen? Hier findest du alle relevanten Infos und Materialien:
 
Spulenmessgerät
von Plapperkatze am 25.Juni 2010 um 02:05
zurück zur Kategorie "Tutorials"

In diesem Tutorial werde ich den Aufbau eines Induktivitäts-Messgerätes schildern, wobei ich nicht das fertige Projekt beschreiben, sondern auf die Entwicklung (und all die Kleinigkeiten, mit denen man dabei konfrontiert ist) eingehen will. Das Tutorial sollte auch für Leute interessant sein, die nur vorhaben, einen Atmega und ein LCD in Gang zu bekommen.

Die Spule, das unbekannte Wesen
Schon lange empfand ich es als Handicap, Spulen nicht, wie Widerstände oder Kondensatoren, mit dem Multimeter ausmessen zu können. Ein Spulenmessgerät sollte her, kein Zauberwerk an Präzision, aber wenigstens für eine grobe Messung der Induktivität ausreichend. Eine kleine Recherche führte mich auf folgende Seiten, die sich mit der Aufgabe befassen:
http://www.sprut.de/electronic/pic/projekte/lcmeter/lcmeter.htm
http://www.mikrocontroller.net/topic/60797
http://www.dl8nci.de/lc-meter-001.html

In allen diesen Projekten wird ein LM311 (Datenblatt: http://www.national.com/ds/LM/LM111.pdf) zur Anregung eines LC-Schwingkreises benutzt. Wenn man die Schwingungsfrequenz misst und die verwendete Kapazität C bekannt ist, kann dann mit der Thomsonschen Schwingungsgleichung die Induktivität L bestimmt werden.



Ein Schaltplan, der dafür verwendet werden kann ist hier:



Eure Katze setzte sich also hin, mit Oszi und Taschenrechner bewaffnet, und begann verschiedene Spulen zu messen. Dabei galt es auch immer zu beachten, dass die zu messenden Frequenzen nicht zu hoch werden - sonst kommt man später mit dem Microcontroller, der die Frequenz ja messen soll, an seine Grenzen.

Mit den oben angegebenen Werten (29,9nF und 10µH) kommt man auf eine maximale Frequenz von 291kHz, das ist ok. Zu messende Spulen werden später in Reihe mit der 10µH Induktivität geschalten, so dass die Frequenz dann nur noch kleiner werden kann.

Microcontroller Grundschaltung
Mit einem Atmega8 und einem 16MHz Quarz wird eine Grundschaltung aufgebaut. Ein 7805 Spannungsregler sorgt für stabile 5V. Das ganze soll später mit einem 9V Block betrieben werden, da ist ein Linearregler natürlich nicht optimal, denn 4 der 9V werden am Regler abfallen und verbraten. Allerdings wird das Gerät wohl nur gelegentlich im Einsatz sein, da ist das verkraftbar.

Wichtig ist natürlich der ISP-Anschluss, um den Controller später programmieren zu können. Als Programmier-Adapter kommt der (auf http://katze.dead-men.de/index.php?content=101 beschriebene) Parallelport ISP zum Einsatz; noch habe ich ja einen Parallelport am Rechner. Die Steckerbelegung ist übrigens kein Standard, das habe ich mir so ausgedacht. So lange es konsistent ist, ist das ja egal, ich fürchte aber, dass ich damit Anfänger verwirren könnte. Baut also nicht blindlings meine Steckerbelegung auf und verwendet dann einen gekauften ISP-Adapter und andersrum, verwendet nicht "meinen" ISP Adapter an fremden Schaltungen.

Der Atmega ist nun also aufgebaut, ein Test (ohne µC) zeigt, dass brav 5V überall dort anliegen, wo sie sein sollen. Ein Netzgerät mit Strombegrenzung finde ich da übrigens immer praktisch, dann kann man vorsichtig hochregeln und bemerkt Kurzschlüsse (durch unbeabsichtigte Lötbrücken oder so) rechtzeitig.

Auf dem Bild links erkennt man meine kleine Grundschaltung. Hinter dem Atmega sitzen die 6 Pins meines ISP-Steckers, links davon ist der 7805 mit Kondensatoren (wichtig die 100nF auf der 5V Seite, um die Versorgungsspannung nicht zu verdrecken und damit der Spannungsregler nicht etwa schwingt). Generell sollte man mit den 100nF Kerkos nicht sparsam sein, ich habe schon Schaltungen mit 2-3 Stück davon gesehen. Einer hat bisher bei mir aber immer gereicht.

Der Atmega ist nun zwar programmierbar, hat allerdings noch überhaupt nichts dran, um das unter Beweis zu stellen. Normal ist mein nächster Schritt, eine LED an einen freien Port zu hängen, und diese zum blinken zu bringen. Diesmal habe ich mir das aber erspart und gleich das LCD drangefrickelt.

Das Display
In Bastlerkreisen beliebt sind ja Textdisplays, wie es sie mit 1, 2 oder 4 Zeilen zu haben gibt. Ich hatte noch ein Displaytech 161A herumliegen, das hat nur eine Zeile und 16 Stellen, wird aber hoffentlich für meine Zwecke ausreichen. Ein Blick ins Datenblatt (http://www.microcontroller-online.de/microcontroller/datenbl%E4tter/PDF/LCD161A.pdf) bewahrt vor unangenehmen Überraschungen.

Das Display wird mit 8 Kabelchen mit dem Atmega Board verbunden (siehe dazu hier http://www.rn-wissen.de/index.php/LCD-Modul_am_AVR und in dem Schaltbild, das ich noch anfügen werde), das ist die Konfiguration ohne R/W (R/W auf Masse, das heisst es können keine Daten vom Display gelesen werden). 2 Kabelchen dienen der Versorgung mit 5V, 6 weitere (RS, Enable und 4 Datenleitungen) werden mit beliebigen Ports des Atmega verbunden.

Wenn man in C programmiert, bietet sich die Bibliothek von Peter Fleury (http://homepage.hispeed.ch/peterfleury/avr-lcd44780.html) an, allerdings setzt diese voraus, dass R/W angeschlossen ist. Quick&Dirty wurde diese Library modifiziert, so dass sie ohne R/W auskommt (statt den Status des Displays abzufragen wird einfach gewartet, bis eigentlich alle Befehle erledigt sein müssten).

Also Moment; noch einmal langsam zum mitdenken, was gemacht wird:
Als Software benötigt man als Compiler WinAVR (http://winavr.sourceforge.net/), Programmers Notepad (als Oberfläche, ist dabei) und PonyProg2000 (http://www.lancos.com/ppwin95.html) als Software um den Controller zu programmieren. Hier http://katze.dead-men.de/upload/109_L-Meter_01.rar habe ich mal das Projekt zum testen des Displays hochgeladen. Öffnet "L-Meter.pnproj" mit Programmers Notepad, danach sollte es ohne Probleme mit "Tools->Make All" kompilierbar sein. Das dabei entstandene HEX-File (main.hex) kann nun mit PonyProg in den Controller geschrieben werden.

Bei der Gelegenheit kann man auch gleich die Fuse-Bits ändern, um den Quarz in Betrieb zu nehmen (fabrikneu verwenden die Atmega Controller einen internen Takt von 1MHz). Dazu ist der Fuse Calculator (http://www.engbedded.com/fusecalc/) sehr nützlich.

Übrigens entspricht das "µ" auf dem Display dem "ä" im ASCII-Zeichensatz, wie man mit dem Datenblatt und einer ASCII-Tabelle (http://www.torsten-horn.de/techdocs/ascii.htm) herausfinden kann.

Die Frequenzmessung
Um eine Frequenz zu messen, benötigt man eine genaue Zeitbasis. Dazu wurde ein bisschen im Datenblatt des Atmega8 gestöbert und dann folgendes programmiert:
...
#include <avr/interrupt.h>
...
volatile unsigned char ready=0,seconds=0;
...
SIGNAL(SIG_OUTPUT_COMPARE1A)
{
 seconds++;
 if(seconds>=60)seconds=0;
 ready=1;
 return;
}
...
int main(void)
{
 ...

 // Timer1 läuft mit 16MHz interner Taktfrequenz
 // (CTC mode, Prescaler 256, port disconnected, Interrupt enabled)
 // und erzeugt Interrupt (Compare Match) jede Sekunde
 TCCR1B=(1<<WGM12)|(1<<CS12);
 OCR1A=62499;
 TIMSK=(1<<OCIE1A);

 sei();

 for(;;)
 {
   if(ready)
   {
     // eine sekunde ist vergangen,
     // kann nun aufs display gebracht werden oder so..
     ready=0;
   }
 }
}

Das ist nicht der Code, wie er bei mir dann weiterverwendet wurde (denn ich will nicht 60 Sekunden zählen), aber es zeigt recht gut, wie es im Prinzip funktioniert. Übrigens zur Berechnung von OCR1A, das ist der Wert, an dem der Timer wieder auf 0 geht und ein Interrupt erzeugt wird:
Frequenz = 16MHz / ( 256(Prescaler) * 1(Interruptaufrufe bis "seconds" erhöht wird) * 62499(OCR1A Wert) + 1 ) = 1Hz
(siehe dazu Datenblatt Seite 89)

Nun, weiterhin braucht man zum Frequenzmessen eine Frequenz; deshalb wurde nun die oben gezeigte Schaltung mit dem LM311 aufgebaut und der Ausgang mit dem "ext. Interrupt 0" (Pin 4 am Atmega) verbunden. Der Pin muss hochohmig sein, sonst ist keine Frequenz messbar und evtl geht der LM311 kaputt.

Wir können nun bei jeder steigenden (oder fallenden) Flanke am Pin einen Interrupt erzeugen, das ist nicht schwer:
...
volatile unsigned int temp_freq1=0,temp_freq2=0;
...
SIGNAL(SIG_INTERRUPT0)
{
 temp_freq1++;
 if(temp_freq1==0)temp_freq2++;
}
...
int main(void)
{
 ...
 
 // externer interrupt bei steigender flanke
 MCUCR=(1<<ISC00)|(1<<ISC01);
 GICR=(1<<INT0);
 
 sei();

Bei hohen Frequenzen (oben berechnete 291kHz) wird der Interrupt sehr häufig (alle 55 Takte) aufgerufen. Eigentlich wäre Assembler für solche zeitkritischen Sachen günstiger, ich wollte aber bei C bleiben. temp_freq als unsigned long (4byte) zu definieren scheidet schon einmal aus; da dauert der erzeugte Code zu lange. Deshalb hier dieses Workaround mit temp_freq1 und temp_freq2 als unsigned int, was zeitlich genug Vorteil bringt, um das Programm lauffähig zu machen.Optimaler wäre natürlich, feste Register dafür zu reservieren, um das Stack gepoppe zu minimieren. Es geht aber auch so.

Der externe Interrupt zählt nun also Schwingungen, und wenn der Timer-Interrupt nach einer vollen Sekunde kommt, wird diese Anzahl Schwingungen als Ergebnis gespeichert und temp_freq1&2 für eine neue Messung wieder auf 0 gesetzt:
...
volatile unsigned long frequency=0;
...
SIGNAL(SIG_OUTPUT_COMPARE1A)
{
 frequency=temp_freq2;
 frequency<<=16;
 frequency+=temp_freq1;
 temp_freq1=0;
 temp_freq2=0;
 ready=1;
 return;
}
...

Mit 10µH Spule und 29,9nF Kondi erhalte ich da 283,5kHz. Die Abweichung erklärt sich mit den Streukapazitäten; der Kondi ist zwar auf 1% genau (was bedeutet zwischen 29,601 und 30,199nF) aber mein Aufbau hat sicher auch einige pF. Ausserdem hat die Spule nur 10% Genauigkeit, das bedeutet, dass sie bis zu 11µH haben kann; diese Extremwerte in die Formel gesetzt ergäbe eine Frequenz von 276KHz - wir liegen also noch ziemlich im grünen Bereich.

Berechnung der Induktivität
In der Main-Funktion haben wir nun eine ganze Sekunde Zeit, um aus der gemessenen Frequenz eine Induktivität zu berechnen. Der Atmega ist auch noch relativ leer, also brauchen wir keine raffinierten Tricks. Eher problematisch ist das relativ kleine Display, denn ich wollte sowohl die Induktivität als auch die gemessene Frequenz darstellen. Es entstand folgender Code (der aber noch nicht perfekt ist):
 char buffer[17],buf2[8],l,einheit;
 double d;
 unsigned int ganzzahl,nachkomma;

 for(;;)
 {
   if(ready)
{
 memset(buffer,0,17);

 if(frequency!=0)
 {
   // die Induktivität berechnen
       d=0.000001181983;// 4*pi*pi*C
       d*=frequency;
       d*=frequency;
   d=1/d;
 
   // jetzt formatieren
   einheit=0;
   while(d<1)
   {
     d*=1000;
 einheit++;
   }
 
   ganzzahl=d;
   nachkomma=(d-(int)d)*1000;
 
   if(einheit==0) // Henry
   {
 sprintf(buffer,"%d.%dH",ganzzahl,nachkomma);
   }
   else if(einheit==1) // mH
   {
 sprintf(buffer,"%d.%dmH",ganzzahl,nachkomma);
   }
   else if(einheit==2) // µH
   {
 sprintf(buffer,"%d.%däH",ganzzahl,nachkomma);
   }
   else if(einheit==3) // nH
   {
 sprintf(buffer,"%d.%dnH",ganzzahl,nachkomma);
   }
   else sprintf(buffer,"small!");
 }
 else sprintf(buffer,"open!");
 
     ultoa(frequency,buf2,10);
 
 for(l=strlen(buffer);l<16-strlen(buf2);l++)buffer[l]=' ';
 
 strcat(buffer,buf2);
 
 lcd_puts(buffer);
 ready=0;
}
 }

Wenn d negativ ist (was in dieser Phase des Projekts aber nicht der Fall sein kann) wird aber ja endlos mit *1000 multipliziert, der Controller hängt fest; ausserdem findet eine Division durch 0 statt, wenn frequency mal 0 ist - soweit zu den Problemen mit diesem Code, den ich aber trotzdem mal hier lasse, weil er einfach zu verstehen ist.

Übrigens nutze ich hier aus, dass das Display nach 16 Zeichen wieder an der ersten Stelle steht; die Induktivität wird links angezeigt, rechts steht die gemessene Frequenz, insgesamt immer 16 Zeichen, so dass der Cursor immer wieder am Anfang der Zeile steht. Auf diese Art spare ich mir ein LCD_clrscr(), das zu flackern führen würde.

Die Kapazität C ist in der Zeile "d=0.000001181983;// 4*pi*pi*C" bereits mit reingerechnet (ist also hardcoded und muss auch im Code geändert werden, wenn ihr andere Kondensator Werte verwendet), danach wird das mit dem Quadrat der Frequenz multipliziert und dann der Kehrwert gebildet. Danach wird so lange mit Faktor 1000 multipliziert, bis ein Wert größer 1 herauskommt. Die Anzahl dieser Multiplikationen bestimmt dann, welche Einheit die errechnete Induktivität bekommt.

Nun können wir zwar den Wert der verbauten 10µH Induktivität (die in obigem Schaltplan übrigens L1 heisst) prima berechnen und darstellen, allerdings ist das ja noch gar nicht was wir wollen: Eine weitere Induktivität Lx soll ja in Reihe mit den 10µH geschalten werden und die Differenz der Frequenz soll zur Ermittlung von Lx dienen. Nun, wir könnten erstmal den berechneten Wert für L1 auf Knopfdruck ("Calibration") ins EEPROM schreiben. Wenn dann gemessen wird, subtrahieren wir diesen Wert von dem dann berechneten Wert (der ja dann L1+Lx ist) und erhalten Lx.

Fertigstellung
Zuerst wurde der Schwingkreis, bestehend in obigem Schaltplan aus L1 und C1, bei L1 aufgetrennt und zwei Klemmen für Lx in Reihe drangebaut.

Dann wurde ein Taster an einen Pin (PD7) gelötet, der gegen Masse schaltet. Wenn man nun den internen Pullup aktiviert, kann man den Tasterdruck erkennen. Entprellen müssen wir den Taster nicht, es reicht, nach einem Tasterdruck einige 100ms zu warten.

...
 // Taster gegen GND an PD7, Pullup einschalten
 PORTD|=(1<<7);
...
for(;;)
{
 // PD7 abfragen - ist low (Taster gedrückt)?
 if(!(PIND & (1<<PIND7)))
 {
  lcd_puts("Calibration!    ");
  EEPROM_write(0,d2);
  delta=d2;
  //_delay_ms(200);
 }
 ...
}

Was ist das "EEPROM_write(0,d2);"? Nun, d2 ist ein double, der den aktuellen Messwert erhält; und zwar bevor er mehrmals mit 1000 multipliziert wird. Also quasi der Messwert für L1 (die Klemmen für Lx sind zum Kalibrieren gebrückt, so dass Lx annähernd 0 ist) in Henry. Dieser wird in der Funktion EEPROM_write an die Adresse 0 geschrieben und belegt damit die ersten 4 Byte des 512Byte grossen EEPROMS. In einer weiteren Funktion EEPROM_read() wird dieser Wert nach jedem Reset/Einschalten aus dem EEPROM gelesen und in dem double "delta" gespeichert. Bei der Kalibrierung wird delta aktualisiert. Hier diese beiden Funktionen, die im Grunde aus dem Atmega Datenblatt (Seite 22,23) stammen:
...
void EEPROM_write(unsigned int uiAddress, double ucData)
{
 int i;

 unsigned char *p=(unsigned char*)&ucData;

 cli();

 for(i=0;i<4;i++)
 {
   /* Wait for completion of previous write */
   while(EECR & (1<<EEWE));
   /* Set up address and data registers */
   EEAR = uiAddress+i;
   EEDR = *(p+i);
   /* Write logical one to EEMWE */
   EECR |= (1<<EEMWE);
   /* Start eeprom write by setting EEWE */
   EECR |= (1<<EEWE);
 }
 sei();
}

double EEPROM_read(unsigned int uiAddress)
{
 int i;

 double val=1;

 unsigned char *p=(unsigned char*)&val;

 cli();

 for(i=0;i<4;i++)
 {
   /* Wait for completion of previous write */
   while(EECR & (1<<EEWE));
   /* Set up address register */
   EEAR = uiAddress+i;
   /* Start eeprom read by writing EERE */
   EECR |= (1<<EERE);
   /* Return data from data register */
   *(p+i)=EEDR;
 }
 sei();
 return val;
}
...

Nun gab es noch einige kleinere Veränderungen an der Auswertefunktion ,zB muss ja nun delta (das ist L1) von dem Messwert subtrahiert werden, um auf Lx zu kommen; ausserdem sollte man Divisionen durch 0 vermeiden (wenn frequency 0 wird bei offenen Klemmen) etc.

Der fertige Code ist hier: http://katze.dead-men.de/upload/110_L-Meter_03.rar

Hier der fertige Aufbau ohne Gehäuse im Test. Die verwendete Luftspule hat 3 Windungen, einen Durchmesser von 5mm und 4mm Länge. Mit der Formel L(nH)=(pi²*D(cm)²*N²)/l(cm) kommt man auf 55nH, so dass mein Messwert (trotz der wirklich kleinen Spule) recht gut passt, obwohl eigentlich Messungen in nH Bereich gar nicht erwartet waren.

Insgesamt wird sich noch zeigen, wie zufrieden ich mit dem Gerät bin, erstmal abwarten. Zuerst muss der Kram noch in ein Gehäuse eingebaut werden, eine Aufgabe, die mir immer gar nicht so liegt. Es scheint aber, als wäre damit eine ausreichend genaue Bestimmung der Induktivität möglich.

Erfahrungsgemäss gibt es immer wieder Leute, die sich an einen Nachbau wagen, ohne das Gerät selbst zu entwickeln und dabei kennenzulernen. Das geht, Knackpunkt ist aber, dass die Frequenz des Schwingkreises (ohne Lx, also mit gebrückten Klemmen) unter 300kHz bleibt, sonst kommt die Interrupt-Routine nicht mehr nach.

Weiterhin zu beachten: Ein Schreibvorgang mit PonyProg wird das EEPROM löschen. Der dann gelesene Wert für nahezu jede Spule wird "small!" sein. Dann erst mit gebrückten Klemmen kalibrieren, bevor etwas gemessen wird.

Es würde mich freuen, wenn jemand dieses Tutorial hilfreich findet oder sich sogar zu einem Nachbau inspiriert fühlt. Bei Unklarheiten mailt mir (Addi im Impressum) oder benutzt das Kommentarfeld.

Gruesse von der Plapperkatze

zurück zur Kategorie "Tutorials"
[0 Kommentare]

Name


Kommentar




Bitte abtippen


 
(C) 2006-20012 Plapperkatze - 220878 Besucher seit dem 23.01.2012 Login