// reziproke Frequenzmessung mit STM32F4-Discovery-Board

/*
Beispielprogramm für Frequenzmessung an PE5 mit Timer9.
Das Ergebnis wird 8-stellig mit max. 2 Mess./s oder minimal 1 Periodendauer
angezeigt. Der Frequenzbereich ist 0,05Hz - 2,5MHz im direkten Modus ohne Vorteiler.
Wird "MIT_VORTEILER" definiert verschiebt sich die obere Grenzfrequenz auf 20MHz.
Da ein interner Vorteiler ./.8 zugeschaltet wird, sollte die minimale Eingangsfrequenz
> 20Hz sein, um die Messdauer (minimal 8 Perioden) klein zu halten.

Das Messverfahren wird unter http://www.mino-elektronik.de/fmeter/fmeter.htm#funk
näher beschrieben. Dort finden sich Beispielprogramme und weitere Schaltungen
für AVR-Controller.

Für die Anzeige der Werte wird ein 4,3" TFT-Display verwendet. Die TFT-Ansteuerung
ist unabhängig von der Messfunktion. Bei eigenen Schaltungen muß eine passende
Anzeige samt Ausgaberoutinen ergänzt werden (RS232-Ausgabe, LCD, eigenes TFT).

Das vorliegende Programm wurde mit IAR-Embedded Workbench 6.0 Kickstart entwickelt.
Der Prozessor muß mit 168MHz initialisiert sein.
Die abs. Genauigkeit für 8 Stellen wird nur durch einen hochstabilen HSE-Takt erreicht.
Beim Testaufbau mit dem STM32F4-Discovery-Board wird ein 20MHz TCXO verwendet.
Alle Angaben ohne Gewähr!

www.mino-elektronik.de
2012-10-28
*/

//#define MIT_VORTEILER    // für Eingangsfrequenzen bis 20MHz


/* Includes ------------------------------------------------------------------*/
#include <stdio.h>
#include "stdint.h"
#include "stm32f4xx.h"

extern void tft_str_einmitten(char *s, uint16_t y_pos, char farbe, uint8_t zoom);

enum status {MESSEN=0, AUSLESEN, AUSWERTEN};    // die Phasen der Messung

#define F_CLOCK 168E6
#define MESSZEIT  1260  // ca. 2 Messungen/sek.
#define STELLEN 8       // Auflösung
#define RUNDUNG 5e-8    // Wert zum Runden

double frequenz;        // aktueller Messwert

char f_text[]={"F = "};      // 1.Zeile der Anzeige
char p_text[]={"P = "};      // 2.Zeile der Anzeige
char f_dim[][4]={"GHz","MHz","kHz","Hz ","mHz","sek","ms ","us ","ns ","ps "};  // Dimensionen zum Messwert
volatile uint8_t messwert_vorhanden,  // nur gueltige Messungen anzeigen
        mess_status;        // Ablaufsteuerung
volatile uint16_t temp_dauer;
uint16_t zeit_low,          // Timer1-Anteil
        zeit_high;          // T1 Ueberlauf-Anteil
volatile uint16_t mess_dauer,         // minimale Wartezeit
        ueberlauf;

uint32_t start_ereignis,    // Impulse zu Beginn der Messung
        start_zeit,         // rel. Zeit: Beginn der Messung
        end_zeit,           // Zeitpunkt der Auswertung
        mess_zeit,          // genaue Zeit der Messung
        mess_ereignisse;    // Impulse der Messung
volatile uint32_t end_ereignis; // Impulse am Ende der Messung


// Farben für TFT
#define SCHWARZ	  0x00
#define ROT	  0x03
#define GRUEN	  0x0c
#define BLAU	  0x30
#define HELLGRAU  0x2a
#define WEISS	  (ROT+BLAU+GRUEN)

// Routine zur Wandlung und Ausgabe eines Meßwertes

void zeige_x(double x, char zeige_periode, char *s)    // Anzeige von Frequenz oder Periode
{
int8_t i,j,dez_punkt,dimension;
char *p;

  dez_punkt = 0;
  dimension = 3;
  if(x >= 0.001) {                          // 1mHz ist Untergrenze
    if(zeige_periode) {
      x = 1/x;                              // Kehrwert bilden
      dimension=5;                          // und in Sekunden als Dimension
      p = p_text;
      while(*p) *s++ = *p++;                // "P = " für Periodendauer
    } else {
      p = f_text;
      while(*p) *s++ = *p++;                // "F = " für Frequenz
    }
    while(x<1.0) {x*=1000.0;dimension++;}   // in den Hz-Bereich bringen
    while(x>=1000.0) {x*=0.001;dimension--;}
    while(x >= 10.0) {
      x *= 0.1;
      dez_punkt++;
    }
    x += RUNDUNG;                           // runden
    if(x >= 10.0) {			    // Ueberlauf bei Rundung
      x *= 0.1;                             // korrigieren
      dez_punkt++;
      if(dez_punkt > 2) {		    // Ueberlauf der Dimension
        dimension--;
        dez_punkt = 0;
      }
    }
  } else {
    x = 0.0; dez_punkt=0; dimension=3;      // 0.00000 Hz ausgeben
  }
  for(i=0;i<STELLEN;i++) {
    j = (char)x;
    *s++ = (j+'0');
    if(i == dez_punkt) *s++ = ('.');
    x -= j;
    x *= 10;
  }
  *s++ = (' ');
  p = f_dim[dimension];
  while(*p) *s++ = *p++;                    // Dimension anhängen
  *s++ = 0;                                 // string abschliessen
}


void TIM1_BRK_TIM9_IRQHandler(void)
{
static uint32_t anzahl;
  if(TIM9->SR & TIM_SR_CC1IF) {
     TIM9->SR = ~TIM_SR_CC1IF;
     anzahl++;
    if(mess_status == AUSLESEN) {   // Ergebnisse synchron zum Eingangsimpuls auslesen+ablegen
      end_ereignis = anzahl;        // Anzahl der Impulse lesen
      zeit_low = TIM9->CCR1;        // capture-reg lesen: untere 16bit
      zeit_high = ueberlauf;        // dazu die oberen 16bit
      if((TIM9->SR & TIM_SR_UIF) && (zeit_low < 0x8000))    // evtl. Ueberlauf T9 noch offen?
         zeit_high++;               // nur, wenn capture-int + overflow-int gleichzeitig !
       mess_status = AUSWERTEN;     // Daten fertig fuer Auswertung
    }
  }
  if(TIM9->SR & TIM_SR_UIF) {       // nur bei überläufen von T9
     TIM9->SR = ~TIM_SR_UIF;
     ueberlauf++;
     mess_dauer++;
  }
}

void init_t9(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM9, ENABLE);  // Timer9 aktivieren
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); // PortE aktivieren

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_Init(GPIOE, &GPIO_InitStructure);

  // PE5 und PE6 als capture-input zu T9
  GPIO_PinAFConfig(GPIOE, GPIO_PinSource5, GPIO_AF_TIM9);
  GPIO_PinAFConfig(GPIOE, GPIO_PinSource6, GPIO_AF_TIM9);

  NVIC_EnableIRQ(TIM1_BRK_TIM9_IRQn);       // Int-T9 aktivieren
  NVIC_SetPriority(TIM1_BRK_TIM9_IRQn,0x2);

  TIM9->CCMR1 = TIM_CCMR1_CC1S_0 + TIM_CCMR1_IC1F_1;     // capture zuordnung IC1 -> TI1
  TIM9->CCER = TIM_CCER_CC1E;         // capture freigeben

#ifdef MIT_VORTEILER
//  TIM9->CCMR1 |= TIM_CCMR1_IC1F_1;    // Eingangssignal auf 20MHz begrenzen
  TIM9->CCMR1 |= TIM_ICPSC_DIV8;      // Vorteiler /8 aktivieren
#else
//  TIM9->CCMR1 |= TIM_CCMR1_IC1F_0 ; // Eingangssignal max. 2,5MHz
#endif

  TIM9->DIER |= TIM_DIER_CC1IE + TIM_DIER_UIE; // mit interrupts
  TIM9->CR1 |= TIM_CR1_CEN;           // T9 starten
}



void f_mess(void)
{
char s[50];
uint8_t neue_werte = 1;             // Platzhalter anzeigen
char format[] = " Nin:%7d  Nref:%9d ";
  init_t9();                        // T9 freilaufend mit 168MHz im capture-modus starten
  mess_status = MESSEN;
  messwert_vorhanden = 0;           // 1. ergebnis verwerfen
  while(1) {
    if(neue_werte) {
      sprintf(s,"eff. Messzeit:%7.3f sek.",(float)mess_zeit/F_CLOCK);
      tft_str_einmitten(s,210,HELLGRAU,2);
      sprintf(s,format,mess_ereignisse, mess_zeit);
      tft_str_einmitten(s,240,BLAU,2);
      neue_werte = 0;
    }
    if(mess_status==MESSEN  && mess_dauer >= MESSZEIT) {
      mess_status=AUSLESEN;         // Auswertung starten
    }

    if(mess_status==AUSWERTEN) {
      end_zeit = zeit_high*0x10000 + zeit_low;
      mess_zeit = end_zeit - start_zeit;                // Zeit-Differenz bilden
      start_zeit = end_zeit;        // neue startzeit merken
      mess_ereignisse = end_ereignis - start_ereignis;  // Impuls-Differenz
#ifdef MIT_VORTEILER
      mess_ereignisse *= 8;
#endif
      start_ereignis = end_ereignis;// fuers naechste Intervall
      mess_status = MESSEN;         // neu starten
      mess_dauer = 0;               // und Messzeit abwarten
      if(messwert_vorhanden) {
        frequenz = ((double)mess_ereignisse * F_CLOCK) / mess_zeit;  // Frequenz berechnen
        zeige_x(frequenz, 0, s);
        tft_str_einmitten(s,120,SCHWARZ,3);
        zeige_x(frequenz, 1, s);
        tft_str_einmitten(s,165,SCHWARZ,3);
        neue_werte = 1;             // Details der Messung anzeigen
      }
      else messwert_vorhanden = 1;  // sperre wieder aufheben
    }
  }
}
