Springe zum Inhalt

Quellcode des LC-Meters

Im folgenden ist der gesamte Quellcode des LC-Meters dargestellt, wie ich ihn für die einzelnen Experimente und Meßvorschriften im Hauptartikel benutzt habe. Die verwendete CPU ist ein Atmel ATmega163. Wird eine andere CPU aus der ATmega-Reihe verwendet, dann sind gegebenenfalls ein paar Namen (etwa bei den Timern und bei den Interruptvektoren) auszuwechseln. Wird eine völlig andere CPU verwendet, dann sind natürlich alle die Ports, Counter und Interrupts betreffenden Stellen anzupassen. Der Bequemlichkeit halber kann der Qullcode auch hier direkt heruntergeladen werden. Außerdem müssen die Anmerkungen im Artikel Verdrahtung berücksichtigt werden. Dort ist neben der Verkabelung vor allem beschrieben, wie eine notwendige Modifikation durchzuführen ist.

// Firmware for an experimental LC-Meter
// (c) 2005-2009 by DL8NCI
// Compiled with WinAVR 20071221
// and avr libc 1.6.0
// fuse bits: LOW: 0xda HIGH:0xff
//  BOOTSZ   = 128 word (irrelevant)
//  BOOTRST  = not checked
//  BODLEVEL = 2,7 V (irrelevant)
//  BODEN    = not checked
//  SPIEN	 = checked
//  CKSEL    = Crystal Oscillator slowly raising power
// Lockbits: 0xff

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <util/delay.h>
#include <avr/pgmspace.h>

// for the LC-Display
#define	LCD_DDR		DDRA
#define	LCD_PORT	PORTA	
#define	LCD_PIN		PINA
#define	LCD_PORT_E	PORTC
#define	LCD_DDR_E	DDRC
#define	LCD_E		2
#define	LCD_RW		3
#define LCD_RS		4

// some convenient functions
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

// global variables for LC-display
uint8_t LCD_pos;
uint8_t LCD_esc;

// counter value and ready status for interrupt
volatile uint32_t CNT;
volatile uint8_t ready;
uint8_t n_CNT;

// measuring mode, calibration mode and range 
uint8_t mmode;
uint8_t cmode;
uint8_t rmode;

// key status
uint8_t keys;

#define NX_MAX 16

// some fixed values
double C13 = 470e-12;
double C15 = 330e-12;

#define PI 3.1415926535
#define TF 200		// ca. 50 ms
double t_gate;

// the relays/switches
#define SHORT_LX cbi(PORTB,4);
#define OPEN_LX  sbi(PORTB,4);
#define ON_330   cbi(PORTB,3);
#define OFF_330  sbi(PORTB,3);
#define SHORT_L3 cbi(PORTB,2);
#define OPEN_L3  sbi(PORTB,2);

#define ON_33k   cbi(PORTD, 1);
#define OFF_33k  sbi(PORTD, 1);
#define ON_1M    cbi(PORTD, 2);
#define OFF_1M   sbi(PORTD, 2);
#define ON_1k    cbi(PORTD, 0);
#define OFF_1k   sbi(PORTD, 0);

// all the LCD stuff - connected to the printf statement
void LCD_PulseE(void) {
	sbi(LCD_PORT_E,LCD_E);
    _delay_us(2);		// 2 us
	cbi(LCD_PORT_E,LCD_E);
	}

void LCD_WaitBusy(void) {
	uint8_t h=0x80;

	LCD_DDR = 0x0c;
	LCD_PORT = 0x00;

	while(h!=0) {
		LCD_PORT = 0x04;
		sbi(LCD_PORT_E,LCD_E);
 		_delay_us(1);		// 1 us	
		h = LCD_PIN & 0x80;
		cbi(LCD_PORT_E,LCD_E);
		LCD_PulseE();
		}
	LCD_DDR = 0xfc;
	}

void LCD_SendCmd(char c) {
	char ch;
		
	LCD_WaitBusy();
	LCD_PORT = c & 0xf0;
	LCD_PulseE();

	ch = c;
	asm("swap %0" : "=r" (ch) : "0" (ch));
	LCD_PORT = ch & 0xf0;
	LCD_PulseE();
	}

void LCD_SendChar_int(char c) {
	char ch;

	LCD_WaitBusy();
	LCD_PORT = (c & 0xf0) | 0x08;
	LCD_PulseE();

	ch = c;
	asm("swap %0" : "=r" (ch) : "0" (ch));
	LCD_PORT = (ch & 0xf0) | 0x08;
	LCD_PulseE();
	LCD_pos++;
	}

int LCD_SendChar(char c, FILE *stream) {
    if (LCD_esc==0) {
		if (c!='\001') {
			LCD_SendChar_int(c);
			}
		else {
			LCD_esc = 1;
			}
		}
	else {
		switch(c) {
			case '\001':
				LCD_SendCmd(0x80);
				LCD_pos = 0;
				LCD_esc = 0;
				break;
			case '\002':
				LCD_SendCmd(0xc0);
				LCD_pos = 0;
				LCD_esc = 0;
				break;
			case '\003':
				LCD_SendCmd(0x94);
				LCD_pos = 0;
				LCD_esc = 0;
				break;
			case '\004':
				LCD_SendCmd(0xd4);
				LCD_pos = 0;
				LCD_esc = 0;
				break;
			case '\005':
				while(LCD_pos<20) LCD_SendChar_int(0x20);
				LCD_esc = 0;
				break;
			}
		}
	return 0;
	}

void LCD_Goto(uint8_t line, uint8_t column) {
	LCD_SendCmd(87 + column + 40*line);
	LCD_pos = column - 1;
	}

void LCD_Init(void) {
	uint8_t i;

    for (i=0;i<20;i++) { _delay_ms(15.0); }	// 300 ms
	sbi(LCD_DDR_E,LCD_E);
	LCD_DDR = 0xfc;
	LCD_PORT = 0x30;
	LCD_PulseE();

 	_delay_ms(15.0);	// 15 ms

	LCD_PORT = 0x30;
	LCD_PulseE();

 	_delay_ms(1.0);		// 1 ms

	LCD_PORT = 0x30;
	LCD_PulseE();

	LCD_PORT = 0x20;
	LCD_PulseE();
				
	LCD_SendCmd(0x28);
	LCD_SendCmd(0x0c);
	LCD_SendCmd(0x06);

	LCD_SendCmd(0x01);

	static FILE mystdout = FDEV_SETUP_STREAM(LCD_SendChar,NULL,_FDEV_SETUP_WRITE);
	stdout = &mystdout;

	LCD_pos = 0;
	LCD_esc = 0;
	}


// sometimes we have nothing to do
void idle(void) { asm("nop" "\n\t"	::); }

// the handlers for key status changes
void key0on(void) { }	// not used

void key0off(void) {	// togle light on/off
	if ((PORTD & 0x80)==0x80) cbi(PORTD,7); else sbi(PORTD,7);
	}

void key1on(void) { }	// not used

void key1off(void) {	// range selection
	rmode++;
	cmode = 0;
	if (rmode==3) rmode=0;
	}

void key2on(void) { }	// not used

void key2off(void) {	// toggle calibration/measurement
	if (cmode==0) cmode=1; else cmode=0;
	}

void key3on(void) { }	// not used

void key3off(void) {	// switch to next measuring mode
	mmode++;
	cmode = 0;
	rmode = 0;
	if (mmode==5) mmode=0;
	}


// the timer2-compare interrupt
ISR(TIMER2_COMP_vect) {
	uint16_t l,h;

	TCCR1B = 0;		// stop counter
	TCCR2 = 0;		// stop timer

	// read counter and ...
	l = TCNT1L;
	h = TCNT1H;
	// ... add to global counter
	CNT = CNT + l + (h << 8);
	ready=1;
	}

// the timer1-capture interrupt
ISR(TIMER1_CAPT_vect) {
	uint16_t l,h;

	TCCR1B = 0;		// stop timer

	// read counter and ...
	l = TCNT1L;
	h = TCNT1H;
	// ... add to global counter
	CNT = CNT + l + (h << 8);
	ready = 1;
	}

// some preparation - mainly determine t_gate (done once only)
void Calibrate(void) {

	CNT = 0;

	// setup timer 1 as counter and timer 2 as timer	
	cli();

	TCCR1A = 0x00;
	TCCR1B = 0x00;

	TCNT1H = 0;
	TCNT1L = 0;

	TCCR2 = 0x00;
	OCR2 = TF;
	TCNT2 = 0;

	sbi(SFIOR,PSR2);		// Resest Timer Prescaler

	//start counter
	TCCR1B = 0x02;			// 0.5 MHz

	//start timer
	TCCR2 = 0x07;
	//sbi(PORTD,5);

	//some further initializations
    ready = 0;

	sbi(TIMSK,OCIE2);				// timer2-compare interrupt on
	sbi(TIFR,OCF2);					// terminate active interrupts, if existing
	sei();

	while (ready==0) idle();

	t_gate = 8*(double)CNT/(double)F_CPU;
	}

// scan the keyboard
void scan_keys(void) {
    uint8_t h1,h2,h3;
    
    h1 = PINC;

	h2 = h1 & 0x10;		// current key0
	h3 = keys & 0x10;	// previous key0
	if ((h3==0x10)&&(h2==0x00)) key0on();
	if ((h3==0x00)&&(h2==0x10)) key0off();

	h2 = h1 & 0x20;		// current key1
	h3 = keys & 0x20;	// previous key1
	if ((h3==0x20)&&(h2==0x00)) key1on();
	if ((h3==0x00)&&(h2==0x20)) key1off();

	h2 = h1 & 0x40;		// current key2
	h3 = keys & 0x40;	// previous key2
	if ((h3==0x40)&&(h2==0x00)) key2on();
	if ((h3==0x00)&&(h2==0x40)) key2off();

    h2 = h1 & 0x80;		// current key3
	h3 = keys & 0x80;	// previous key3
	if ((h3==0x80)&&(h2==0x00)) key3on();
	if ((h3==0x00)&&(h2==0x80)) key3off();

	keys = h1;

	}


// measure one time the current frequency
void Measure_1(void) {

	// prepare timer 1 as counter and timer 2 as timer	
	cli();

	TCCR1A = 0x00;
	TCCR1B = 0x00;

	TCNT1H = 0;
	TCNT1L = 0;

	TCCR2 = 0x00;
	OCR2 = TF;
	TCNT2 = 0;

	sbi(SFIOR,PSR2);				// Resest Timer Prescaler

	//start counter
	TCCR1B = 0x06;

	//start timer
	TCCR2 = 0x07;

	//some further initializations
    ready = 0;

	sbi(TIMSK,OCIE2);				// timer2-compare interrupt on
	sbi(TIFR,OCF2);					// terminate active interrupts, if existing
	sei();

	while (ready==0) idle();

	n_CNT++;
	// ready for next block

	cbi(TIMSK,OCIE2);				// timer2-compare interrupt off
	scan_keys();
    
	}

// measure one time the pulse length of the NE555
void Measure_555_1(void) {
	cli();						// disable all interrupts
	TCCR1A = 0x00;				// prepare timer 1 and ...
	TCCR1B = 0;					// ... stop timer 1

	TCNT1H = 0;					// set timer1 count value to 0
	TCNT1L = 0;

	// start measurement
    ready = 0;					// clear the ready status
	sbi(TIFR,ICF1);				// terminate active interrupts, if existing
	sbi(TIMSK,TICIE1);			// timer1-capture Interrupt on

	sbi(SFIOR,PSR10);			// reset prescaler
	TCCR1B=0x01;				// start Timer 1

	cbi(PORTD,5);				// start NE555
	sei();						// enable interrupts
	sbi(PORTD,5);				// return to high level

	while (ready==0) idle();	// wait until ready

	n_CNT++;					// this was one more measurement
	cbi(TIMSK,TICIE1);			// timer1-capture Interrupt off
	scan_keys();				// the keys

	}

// execute the frequency measuremnt n times
double Measure_M(uint8_t n) {
	double f;
    CNT = 0;
    n_CNT = 0;
	while (n_CNT<n) Measure_1();
	cli();
	f=(double)CNT/((double)n*t_gate);
	return f;
	}

// execute the pulse length measurement n times
double Measure_555_N(uint8_t n) {
	double t;
    CNT = 0;
    n_CNT = 0;
	while (n_CNT<n) Measure_555_1();
	cli();
	t=(double)CNT/((double)F_CPU*(double)n);
	return t;
	}

// the main program
int main (void) {
	uint8_t i,lcmode;
	double f1,f2,f3,f4,Lx,L3,Cy,t1,t2,C13x,Cxa, Cxb, Cx, R;

	cli();

	LCD_Init();
    printf_P(PSTR("\001\001LC-Meter Evaluation\001\005\001\002 (c) 2005 by DL8NCI\001\005"));
	for(i=0; i<100; i++) _delay_ms(10.0);

	sbi(DDRB,4);	// for shorting Lx
	sbi(DDRB,3);	// for 330 pF
	sbi(DDRB,2);    // for shorting L0

	sbi(DDRD,0);	// PD0..PD2 as output
	sbi(DDRD,1);
	sbi(DDRD,2);

	SHORT_LX
	ON_330
	SHORT_L3

	OFF_1k
	OFF_33k
	OFF_1M

    keys = PINC;

	DDRC = DDRC & 0x0f;		// PC4..PC7 as input
	PORTC = PORTC | 0xf0;	// PC4..PC7 with Pull Up

	cbi(PORTD,7);			// PD7 - background light for LCD (LED off)
	sbi(DDRD,7);			// PD7 as output - LED

	sbi(DDRD,5);			// PD5 as output
	sbi(PORTD,5);

	t_gate = 0;
	Calibrate();			// determine t_gate

	mmode = 0;				// the initial measuring mode
	cmode = 0;				// calibrating mode
	rmode = 0;				// range

	// forever ...
	while(1) {

    lcmode = cmode;

	switch (mmode) {
		case 0: // normal operation with L0 and f1, f2, f3
		    OPEN_L3

			OPEN_LX
			OFF_330
			for(i=0; i<10; i++) { _delay_ms(10.0); }
			f1=Measure_M(8);	// L0 + Lx; 470 pF + Cx

			SHORT_LX
			for(i=0; i<10; i++) { _delay_ms(10.0); }
			f2=Measure_M(8);	// L0; 470 pF + Cx

			ON_330
			for(i=0; i<10; i++) { _delay_ms(10.0); }
			f3=Measure_M(8);	// L0; 470 pF + 330 pF + Cx

			if (f1>100) {
				L3 = (f2*f2-f3*f3)/(4*PI*PI*f2*f2*f3*f3*C15);
				Cy = (C15*f3*f3)/(f2*f2-f3*f3)-C13;
				Lx = (f2*f2-f1*f1)*(f2*f2-f3*f3)/(4*PI*PI*f1*f1*f2*f2*f3*f3*C15);

				if (Lx>0.1) {
					printf_P(PSTR("\001\001%7.4f  H  %5.1f pF\001\005"),Lx, Cy*1e12);
					}
				else {
					printf_P(PSTR("\001\001%7.2f uH  %5.1f pF\001\005"),Lx*1e6, Cy*1e12);
					}
				printf_P(PSTR("\001\002%7.2f uH  ----- pF\001\005"),L3*1e6);
				}
			else {
				printf_P(PSTR("\001\001------- uH  ----- pF\001\005"));
				printf_P(PSTR("\001\002------- uH  ----- pF\001\005"));
				}

			printf_P(PSTR("\001\003%8.3f %8.3f\001\005"),f1/1000, f2/1000);
			printf_P(PSTR("\001\004%8.3f %8.3f ms\001\005"),f3/1000, t_gate*1e3);
			break;

		case 1: // normal operation with L0 and f1, f2, f4

		    OPEN_L3

			SHORT_LX
			OFF_330
			for(i=0; i<10; i++) { _delay_ms(10.0); }
			f2=Measure_M(8);	// L0 + Lx; 470 pF + Cx

			OPEN_LX
			for(i=0; i<10; i++) { _delay_ms(10.0); }
			f1=Measure_M(8);	// L0; 470 pF + Cx

			ON_330
			for(i=0; i<10; i++) { _delay_ms(10.0); }
			f4=Measure_M(8);	// L0 + Lx; 470 pF + 330 pF + Cx

			if (f1>100) {
				L3 = (f1*f1-f4*f4)/(4*PI*PI*f2*f2*f4*f4*C15);
				Cy = (C15*f4*f4)/(f1*f1-f4*f4)-C13;
				Lx = (f2*f2-f1*f1)*(f1*f1-f4*f4)/(4*PI*PI*f1*f1*f2*f2*f4*f4*C15);


				if (Lx>0.1) {
					printf_P(PSTR("\001\001%7.4f  H  %5.1f pF\001\005"),Lx, Cy*1e12);
					}
				else {
					printf_P(PSTR("\001\001%7.2f uH  %5.1f pF\001\005"),Lx*1e6, Cy*1e12);
					}
				printf_P(PSTR("\001\002%7.2f uH  ----- pF\001\005"),L3*1e6);
				}
			else {
				printf_P(PSTR("\001\001------- uH  ----- pF\001\005"));
				printf_P(PSTR("\001\002------- uH  ----- pF\001\005"));
				}
			
			printf_P(PSTR("\001\003%8.3f %8.3f\001\005"),f1/1000, f2/1000);
			printf_P(PSTR("\001\004%8.3f %8.3f ms\001\005"),f4/1000, t_gate*1e3);
			break;

        case 2:	// normal operation without L0

			SHORT_L3
			OPEN_LX

			OFF_330
			for(i=0; i<10; i++) { _delay_ms(10.0); }
			f1=Measure_M(8);	// Lx, C13, Cy

			ON_330
			for(i=0; i<10; i++) { _delay_ms(10.0); }
			f2=Measure_M(8);	// Lx, C13, Cy, C15

			if (f1>100) {

				Lx = (f1*f1-f2*f2)/(4*PI*PI*f1*f1*f2*f2*C15);
				Cy = ((C13+C15)*f2*f2-C13*f1*f1)/(f1*f1-f2*f2);

				if (Lx>0.1) {
					printf_P(PSTR("\001\001%7.4f  H  %5.1f pF\001\005"),Lx, Cy*1e12);
					}
				else {
					printf_P(PSTR("\001\001%7.2f uH  %5.1f pF\001\005"),Lx*1e6, Cy*1e12);
					}
				printf_P(PSTR("\001\002\005"));
				}
			else {
				printf_P(PSTR("\001\001------- uH  ----- pF\001\005"));
				printf_P(PSTR("\001\002------- uH  ----- pF\001\005"));
				}
			
			printf_P(PSTR("\001\003%8.3f %8.3f\001\005"),f1/1000, f2/1000);
			printf_P(PSTR("\001\004-------- %8.3f ms\001\005"), t_gate*1e3);
			break;

		case 3: // C-Measurement with LC-Oscillator

			if (cmode==0) {
				printf_P(PSTR("\001\001     Reference      "));
				OPEN_L3
				SHORT_LX

				OFF_330
				for(i=0; i<10; i++) { _delay_ms(10.0); }
				f1=Measure_M(8);	// L3, C13

				ON_330
				for(i=0; i<10; i++) { _delay_ms(10.0); }
				f2=Measure_M(8);	// L3, C13, C15

				C13x = C15*f2*f2/(f1*f1-f2*f2);
				L3 = (f1*f1-f2*f2)/(4*PI*PI*f1*f1*f2*f2*C15);

				printf_P(PSTR("\001\002\001\005"));
				printf_P(PSTR("\001\003\001\005"));

				}
			else {
				printf_P(PSTR("\001\001Measuring   Mode 6.1"));
				OPEN_L3
				SHORT_LX

				OFF_330
				for(i=0; i<10; i++) { _delay_ms(10.0); }
				f3=Measure_M(8);	// L3, C13

				ON_330
				for(i=0; i<10; i++) { _delay_ms(10.0); }
				f4=Measure_M(8);	// L3, C13

				Cxa = (C15*f2*f2*(f1*f1-f3*f3))/(f3*f3*(f1*f1-f2*f2));
				Cxb = (C15*f1*f1*(f2*f2-f4*f4))/(f4*f4*(f1*f1-f2*f2));


				printf_P(PSTR("\001\002%9.2f pF     (a)"),Cxa*1e12);
				printf_P(PSTR("\001\003%9.2f pF     (b)"),Cxb*1e12);


				}

			printf_P(PSTR("\001\004%6.2f uH %7.2f pF"),L3*1e6, C13x*1e12);

			break;



		case 4:	// C-measurement with NE555

			switch (rmode) {
				case 0:
					R = 1e6;
					OFF_1k
					OFF_33k
					ON_1M
					break;
				case 1:
					R = 33e3;
					OFF_1k
					ON_33k
					OFF_1M
					break;
				case 2:
					R = 1e3;
					ON_1k
					OFF_33k
					OFF_1M
					break;
				}




			if (cmode==0) {
				printf_P(PSTR("\001\001  NE555  Reference  "));
				
				t1=Measure_555_N(64);
				printf_P(PSTR("\001\002t1 = %9.2f us\001\005"),t1*1e6);

				Cy = t1/(1.1*R);
				printf_P(PSTR("\001\003Cy = %9.2f pF\001\005"),Cy*1e12);
				}
			else {
				printf_P(PSTR("\001\001  NE555  Measurement"));
				
				t2=Measure_555_N(64);
				printf_P(PSTR("\001\002t2 = %9.2f us\001\005"),t2*1e6);

				Cx = (t2-t1)/(1.1*R);
				printf_P(PSTR("\001\003Cx = %9.2f pF\001\005"),Cx*1e12);
				}

			printf_P(PSTR("\001\004R  = %4.0f kOhm\001\005"),R*1e-3);

			for(i=0; i<30; i++) _delay_ms(10.0);

			break;

			}
		}
    return (0);
	}