/*
Beispielprogramm fuer 'Reziproker Frequenzzaehler' fuer AT90S2313
kompatibel zu IAR C-Compiler.
Funktionsbereich etwa 0,0025Hz - 140kHz bei 10MHz Prozessortakt

2003-10-02 Aenderung Vorteiler / 256 schaltbar:
   PD3 = offen: AIN0 als Eingang, ohne Vorteiler
   PD3 = 0V:    ICP als Eingang, Vorteiler / 256 aktiv
   neue max. Eingangsfrequenz = 35 MHz
   6-stellige Anzeige der Ergebnisse

Schaltung dazu siehe:
http://www.mino-elektronik.de

Alle Angaben wie immer ohne Gewaehr !
*/

#include <io2313.h>

#pragma function=intrinsic(0)	// spezielle Befehle
  void _SEI(void);
#pragma function=default

#define uchar unsigned char
#define uint  unsigned int

#define STELLEN		6
#define MESSZEIT	50      // max. 3 Messungen/Sek.
#define LED_EIN		14	// 0,1 Sek.
#define F_CLOCK		1E7     // 10MHz

#define LCD_ZEICHEN	4
#define LCD_CMD		0
#define LCD_STROBE	0x8
#define ZEILE1	0x7f
#define ZEILE2	0xbf

#define SKAL_SCHALTER	8	// PortD.3


enum status {MESSEN=0, AUSLESEN, AUSWERTEN};

char flash text1[]={"F.:"};
char flash text2[]={"P.:"};
char flash f_dim[][4]={"MHz","kHz","Hz ","mHz","sek","ms ","us ","ns "};

float x;			// float-Ergebnis

unsigned char	sperre,		// zur Unterdrueckung der Auswertung
		mess_dauer,	// minimale Wartezeit
		mess_status,	// Ablaufsteuerung
		led_cnt;	// etwas leuchten lassen

char i,j,k,dimension;		// fuer float -> Anzeige
char alt_pind;			// letzten Zustand des Schalter merken

unsigned int	zeit_low,	// Timer1-Anteil
		zeit_high,      // Ueberlauf-Anteil
		ueberlauf;
unsigned long 	count,		// Impulse per Int.
		ereignis, 	// Zwischenwert
		zeit,
		mess_start,	// rel. Beginn der Messung
		mess_zeit,	// genaue Zeit der Messung
		mess_anzahl;	// Impulse einer Messung

void mini_delay() {}		// nur ret aufrufen

void warten()
{
uint n=50000;
  while(--n);
}

void lcd_impuls()
{
  PORTB |= LCD_STROBE;
  mini_delay();
  PORTB &= ~LCD_STROBE;
}

void lcd_nibble(uchar c)
{
  PORTB = c&0xf0;
  lcd_impuls();
  warten();
}

void lcd_out(uchar z, uchar mode)
{
uchar n=0;
  PORTB = (z&0xf0) | mode;
  PORTB |= LCD_STROBE;
  z <<= 4;			// unteres nibble nach oben
  PORTB &= ~LCD_STROBE;
  PORTB = (z&0xf0) | mode;
  lcd_impuls();
  while(--n);
}

void lcd_cmd(char z)
{
  lcd_out(z,LCD_CMD);
}

void lcd_putchar(char z)
{
  lcd_out(z,LCD_ZEICHEN);
}

void lcd_f_string(char flash *s)
{
char temp;
  while(temp=*s++) {
     lcd_putchar(temp);
  }
}

void zeigex(float x,char ft)
{
  k=-6,dimension=0;
  if(x!=0) {
    if(!ft) {x=1/x;dimension=2;}
    while(x<1e6) {x*=1000;dimension++;}
    while(x>=10.0) {
      x*=0.1;
      k++;
    }
  } else { k=0;dimension=1;}
  for(i=0;i<STELLEN;i++) {
    j=x;
    lcd_putchar(j+'0');
    if(i== k) lcd_putchar('.');
    x-=j;
    x*=10;
  }
  lcd_putchar(' ');
  lcd_f_string(f_dim[dimension]);
  lcd_putchar(' ');
}

void init_lcd()
{
  warten();
  lcd_nibble(0x30);
  lcd_nibble(0x30);
  lcd_nibble(0x30);
  lcd_nibble(0x20);
  lcd_cmd(0x28);	/* 4-bit mode */

  lcd_cmd(0xc);
  lcd_cmd(1);
  warten();
  lcd_f_string(text1);
  lcd_cmd(ZEILE2+1);
  lcd_f_string(text2);
}

void interrupt [TIMER1_OVF1_vect] t1_ovfl_int()
{
  mess_dauer++;
  ueberlauf++;
  led_cnt++;				// LED ein bisschen leuchten lassen
}

void interrupt [TIMER1_CAPT1_vect] t1_cap_int()
{
  count++;
  if(mess_status == AUSLESEN) {		//ergebnisse ablegen
    ereignis = count;			// merken
    count = 0;				// und wieder von vorne beginnen
    zeit_low = ICR1;			// capture-reg lesen
    zeit_high = ueberlauf;
    if((TIFR & 0x80) && (zeit_low < 0x8000))  	// evtl. Ueberlauf T1 ?
      zeit_high++;				// nur, wenn capture-int + overflow-int gleichzeitig !
    mess_status = AUSWERTEN;		// Daten fertig fuer Auswertung
  }
}

C_task main()
{
  DDRB = 0xfc;		/* fuer LC-Anzeige */
  DDRD = 0x20;		/* fuer LED */
  init_lcd();
  sperre=1;		/* 1.Ergebnis unterdruecken, weil es falsch ist */
  ACSR = 4;		// Ana.-Comp.-Eingang default
  TCCR1B = 0x41;	// capture neg. Flanke
  TIMSK = 0x88;		// t1-overflow und cap. interrupts
  PORTD = 0x4c;         /* Pullup ./.256 Eingang und PD2+PD3 */
  _SEI();
  for(;;) {

// Skalierung /256 schaltbar, 2003-10-02
    if((PIND ^ alt_pind) & SKAL_SCHALTER) {	// bei Aenderung des Schalters
      alt_pind ^= SKAL_SCHALTER;	// Aenderung speichern
      if(alt_pind & SKAL_SCHALTER) 	// Schalter ist offen: Skalierung = 1.0
	ACSR = 4;			// Ana.-Comp.-Eingang
      else	
	ACSR &= ~4;			// ICP-Eingang fuer Teiler / 256
      sperre = 1;			// Ergebnis verwerfen
      mess_status = AUSLESEN;		// und neue Messung erzwingen
    }

    if(mess_status==MESSEN  && mess_dauer >= MESSZEIT) {
      mess_status=AUSLESEN;		// Auswertung starten
      mess_dauer = 0;			// wieder Messzeit abwarten
    }
    if(mess_status==AUSWERTEN) {
      zeit = zeit_high*0x10000 + zeit_low;
      mess_zeit = zeit - mess_start;	// differenz bilden
      mess_start = zeit;		// neue startzeit merken
      mess_anzahl = ereignis;		// und die impulse dazu
      mess_status = MESSEN;		// wieder warten
      if(!sperre) {
	PORTD |= 0x20;        		// LED ein
	led_cnt = 0;			//
	x=((float)mess_anzahl * F_CLOCK) / mess_zeit;	// Frequenz
	if(!(alt_pind & SKAL_SCHALTER)) x *= 256.0;
	lcd_cmd(ZEILE1+5);
	zeigex(x,1);
	lcd_cmd(ZEILE2+5);
	zeigex(x,0);
      }
      else sperre=0;			// sperre wieder aufheben
    }
    if(led_cnt > LED_EIN) PORTD &= 0xdf;// LED aus
  }
}