// ************************************************************************************************
//
// Nixie Voltmeter using the
//
// PIC 16F627A @ 4.194304MHz
//
// Copyright www.jb-electronics.de
//
// ************************************************************************************************

// ************************************************************************************************
// PIC-Header einbinden
#include <pic.h>

// ************************************************************************************************
// Methodenprototypen
void sendeWert (uchar wert1, uchar wert2);
int getADC ();

// ************************************************************************************************
// Globale Variablen
static int TimeCounter, WertADC, oldADC, Spannung, mean;
static uchar einer, zehner, hunderter, einheit, mean_counter;

// ************************************************************************************************
// Hauptmethode
void main (void) {

    // ********************************************************************************************
	// Konfigurationsbits setzen
	__CONFIG(0x3F2A);

	// ********************************************************************************************
	// Portspezifikationen
	#define	DATA		RB0
	#define CLK			RB1
	#define STROBE		RB2
	#define ENABLE		RB3
	#define	ADC			RB4
	#define CS			RB5
	#define CLK_ADC		RB6

	// Tristate von PORTB setzen
	TRISB = 0b0010000;

	// ********************************************************************************************
	// TIMER setzen
	T0CS = 0;
	PSA = 1;
	PS0 = 1;
	PS1 = 1;
	PS2 = 1;

	// ********************************************************************************************
	// Enable interrupt at timer overflow
	T0IE = 1;

	// activate interrupts
	GIE = 1;

	// tubes ON
	ENABLE = 1;

	// mean and voltage reset to 0
	mean = 0;
	Spannung = 0;

	// ********************************************************************************************
	// main loop
	while (true) {

	}

}

// ************************************************************************************************
// interrupt routine for ADC polling
static void interrupt isr (void) {

	// ********************************************************************************************
	// some time lapsed?
	if (T0IF) {

		TimeCounter++;
		 if (TimeCounter >= 10) {

			// get ADC value
			WertADC = getADC();

			// calculate mean
			mean_counter++;
			mean += WertADC * (0.3215 + 0.004); // 0.3215 out of calculation, additional term is tweaked
			if (mean_counter >= 50) {
				Spannung = mean / mean_counter;
				mean = 0;
				mean_counter = 0;
			}
	
			// calculate places
			hunderter = Spannung / 100;
			zehner = Spannung / 10 - (Spannung / 100) * 10;
			einer = Spannung % 10;
			einheit = 0; // 0 entspricht Volt
	
			// send value to shift registers
			sendeWert(hunderter << 4 | zehner, einer << 4 | einheit);

			TimeCounter = 0;
		}

		// reset interrupt
		T0IF = 0;

	}

}


// ************************************************************************************************
// send values wert1 and wert2 to shift registers
void sendeWert (uchar wert1, uchar wert2) {

	// variables
	int Index;

	STROBE = 0;
	CLK = 0;
	for (Index = 7; Index >= 0; Index--) {
		NOP();
		if (wert1 & (1 << Index)) {
			DATA = 1;
		} else {
		 	DATA = 0;
		}
		NOP();
		CLK = 1;
		NOP();
		CLK = 0;
	}
	for (Index = 7; Index >= 0; Index--) {
		NOP();
		if (wert2 & (1 << Index)) {
			DATA = 1;
		} else {
		 	DATA = 0;
		}
		NOP();
		CLK = 1;
		NOP();
		CLK = 0;
	}
	STROBE = 1;
	NOP();
	STROBE = 0;

}

// ************************************************************************************************
// this methode polls the ADC
int getADC () {

	// variables
	int Index;
	int adc_wert = 0;

	// select ADC (/ChipSelect = 0)
	CS = 0;
	NOP();

	// poll ten data bits (sart: MSB...LSB)
	for (Index = 0; Index < 10; Index++) {

		// set data bit?
		if (ADC) {
			adc_wert | = 1 << (10 - Index);
		}
		NOP();

		// positive flank
		CLK_ADC = 1;
		NOP();
		CLK_ADC = 0;
		NOP();

	}

	CS = 1;

	return adc_wert;

}
