/*
   _===_ _           _                  ______ _     _             _ _ _
  |  ___| |         | |                 | ___ (_)   | |           (_) | |
  | |__ | | ___  ___| |_ _ __ ___ ______| |_/ /_  __| | ___  _   _ _| | | ___ _   _ _ __
  |  __|| |/ _ \/ __| __| '__/ _ \______| ___ \ |/ _` |/ _ \| | | | | | |/ _ \ | | | '__|
  | |___| |  __/ (__| |_| | | (_) |     | |_/ / | (_| | (_) | |_| | | | |  __/ |_| | |
  \____/|_|\___|\___|\__|_|  \___/      \____/|_|\__,_|\___/ \__,_|_|_|_|\___|\__,_|_|

  Contrôle de référence 10 MHz par GPS: Essai de "capture" par compteur de STM32 Blue Pill
  Électro-Bidouilleur, Février 2022. https://www.youtube.com/Électro-Bidouilleur
  Licence de droits: Attribution 4.0 International (CC BY 4.0)  Lisez https://creativecommons.org/licenses/by/4.0/deed.fr  
*/

// DIRECTIVES AU COMPILATEUR
#include <timer.h>

#define BROCHE_PPS            A1                      // Timer2, entrée de capture 0
#define BROCHE_REF_IN         A0                      // Timer2, entrée d'incrémentation du compteur.
#define NOMB_PPS_PAR_ECHANT   10                      // Le nombre d'impulsions PPS dans un échantillon
#define vrai true
#define faux false
#define oui true
#define non false
#define vt_cls                  "\x1B[2J"            // Code VT-100 clear screen
#define vt_back_default_colour  "\x1B[49m"
#define vt_reverse              "\x1B[7m"
#define vt_normal               "\x1B[m"


HardwareTimer RefTimer(TIM2);                         // Instanciation de la minuterie TIM2

// VARIABLES GLOBALES
unsigned short int  compte_nominal;                   // Le résultat de l'échantillon de TIM2 si la fréquence est d'exactement 10 MHz
unsigned short int  nouv_val_capture;                 // Nouvelle valeur de l'échantillon de TIM2
unsigned short int  preced_val_Capture;               // Ancienne valeur de l'échantillon de TIM2
unsigned short      compteur_captures = 0;            // Le compteur du nombre de PPS durant l'échantillonnage
unsigned int        compte_10M;                       // L'échantillon de TIM2 à analyser
short int           delta_compte_10M;                 // La différence entre l'échantillon et le compte nominal
short int           somme_delta_compte_10M = 0;       // La somme des delta_compte_10M pour fins de calcul
unsigned short int  compteur_échantillons = 0;        // Le compteur du nombre d'échantillons 
unsigned short int  nomb_echantillons = 10;          // Le nombre d'échantillons dans la période d'analyse     
float               moyenne_delta_compte_10M;         // La moyenne accumulée des différences échantillon-nominal
float               moyenne_decalage_hz = 0;          // La moyenne du décalage des échantillons, exprimé en Hz 
double              frequence_mesuree_hz;             // La fréquence 10 MHz mesurée
bool                echantillon_pret = non;           // Signal annonçant qu'un échantillon est prêt
bool                sauter_echantillon;               // Signal annonçant de sauter l'échantillon courant
bool                detection_pps = non;              // Signal indiquant qu'une pulsation PPS a été détectée


// Fonction gérant l'interruption de la minuterie T2 générée par le signal PPS
void Interrupt_TIM2_PPS()
{
  if (++compteur_captures >= NOMB_PPS_PAR_ECHANT)
  {
    preced_val_Capture = nouv_val_capture;            // Conserver la précédente valeur capturée de TIM2 
    nouv_val_capture = TIM2->CCR2;                    // Lire la nouvelle valeur capturée de TIM2
    if (nouv_val_capture > preced_val_Capture) compte_10M = nouv_val_capture - preced_val_Capture;    // Gérer le débordement des valeurs nouvelle et précédente
    else compte_10M = 65536 + nouv_val_capture - preced_val_Capture;                                  //     "
    compteur_captures = 0;                            // Ré-initialiser le compteur des captures
    echantillon_pret = oui;                           // Aviser qu'un nouvel échantillon est prêt
  }
  detection_pps = oui;                                // Aviser de la détection d'une impulsion PPS
}

// La fonction d'initialisation, style Arduino
void setup()
{
  Serial.begin(115200);                                 // Le port série pour lire les rapports et résultats

  pinMode(BROCHE_PPS, INPUT_PULLUP);                  // Broche PPS en entrée avec pullup
  pinMode(BROCHE_REF_IN, INPUT_PULLUP);               // Broche de la référence 10 MHz en entrée avec pullup
  pinMode(LED_BUILTIN, OUTPUT);                       // Broche de la LED intégrée en sortie

  // Calcul du compte nominal d'un échantillon pour une référence d'exactement 10 MHz
  compte_nominal = round((NOMB_PPS_PAR_ECHANT * 10e6 / 65536 - trunc(NOMB_PPS_PAR_ECHANT * 10e6 / 65536)) * 65536); 

  RefTimer.attachInterrupt(2,Interrupt_TIM2_PPS);     // Associer la fonction d'interruption au compteur TIM2

  // Configuration du compteur TIM2 pour faire la "capture" de la référence 10 MHz
  TIM2->CR1 =   0b0000'0000'0000'0000;                // TIM2 désactivé au départ
  TIM2->CR2 =   0b0000'0000'0000'0000;                // TIM2 Canal 1 est l'entrée 
  TIM2->SMCR =  0b0100'0000'1111'0111;                // ETR non-inversé, External Mode 2 sactivé, Prescaler éteint, pas de filtre. master-slave synchro,  entrée ETR va incrémenter TIM2
  TIM2->DIER =  0b0000'0000'0000'0100;                // Capture/Compare 2 interrupt activé, 
  TIM2->EGR =   0b0000'0000'0000'0100;                // Capture/compare 2 generation activé
  TIM2->CCMR1 = 0b0000'0001'0000'0000;                // Canal 2 en entrée pour le déclenchement PPS, pas de filtre, pas de préselecteur
  TIM2->CCMR2 = 0b0000'0000'0000'0000;
  TIM2->CCER =  0b0000'0000'0001'0000;                // Front montant de CC2P, Capture activée
  TIM2->PSC =   0b0000'0000'0000'0000;                // Prescaler à 1
  TIM2->ARR =   0b1111'1111'1111'1111;                // Valeur Auto-Reload au maximum.
  TIM2->CR1 =   0b0000'0000'0000'0001;                // TIM2 activé et incrémente

  sauter_echantillon = oui;                           // Aviser de sauter le premier échantillon après la mise sous tension
  delay(1000);                                        // Faire une pause pour permettre au port série de s'ouvrir sur l'ordi
  Serial.println("Essai de ""capture"" par compteur de STM32, Version 1");    // Message d'accueuil
  Serial.println("Electro-Bidouilleur, Fevrier 2022");   
}

// La boucle principale, style Arduino
void loop()
{
  if (echantillon_pret)                               // Est-ce qu'un échantilon est prêt?                                           
  {  // Oui 
    if (!sauter_echantillon)                          // Doit-on sauter le nouvel échantillon ?
    {   // Non
      delta_compte_10M = compte_10M - compte_nominal; // Calculer la différence entre le compte de l'échantillon et le compte nominal
      somme_delta_compte_10M += delta_compte_10M;     // Accumuler ces différences pour fins de calcul
      moyenne_delta_compte_10M = float(somme_delta_compte_10M) / ++compteur_échantillons;   // Faire la moyenne de ces différences
      moyenne_decalage_hz = float(moyenne_delta_compte_10M) / NOMB_PPS_PAR_ECHANT;          // Exprimer la moyenne des différences en Hz
      frequence_mesuree_hz = 10000000.0000 + moyenne_decalage_hz;
 
      // Imprimer les différents résultats de calculs.
      //Serial.println(compte_10M);
      Serial.print(vt_cls);                           // Un effacement de l'écran en mode terminal VT-100
      Serial.println("\n--------------------------------------");              
      Serial.print("Echantillon: ");
      Serial.print(compteur_échantillons);
      Serial.print(" / ");
      Serial.println(nomb_echantillons);
      Serial.print("Compteur de l'oscillateur: ");
        Serial.print(compte_10M);
      Serial.print("     Nominal: ");
      Serial.println(compte_nominal);      
      Serial.print("Ecart vs. compte nominal: ");
      Serial.println(delta_compte_10M);
      Serial.print("Moyenne des ecarts: ");
      Serial.println(moyenne_delta_compte_10M,4);
      Serial.print("Moyenne des ecarts (Hz): ");
      Serial.println(moyenne_decalage_hz,6);
      Serial.print("Frequence calculee de la reference (Hz): ");
      Serial.println(frequence_mesuree_hz,4);

      // Ici le traitement de la correction de l'OCXO est fait si le nombre d'échantillons accumulés est rencontré.
      if (compteur_échantillons >= nomb_echantillons) // Est-ce le temps de calculer la correction d'OCXO?
      {   // Oui
        Serial.println("CYCLE DE MOYENNE COMPLET");
        compteur_échantillons = 0;                    // Ré-initialiser les différentes valeurs pour démarrer un neauveau cycle d'échantillonnage.
        moyenne_delta_compte_10M = 0;                 //            "
        somme_delta_compte_10M = 0;
      }
     Serial.println("--------------------------------------");
    }  
    else sauter_echantillon = non;                    // Sinon il fait sauter cet échantillon.
    echantillon_pret = non;                           // Ré-initialiser le marqueur de nouvel échantillon prêt
  }
  if (detection_pps)                                  // Traitement des pulsations PPS (partie non-critique)?
  {   // Oui
      detection_pps = non;                            // Ré-initialiser le marqueur de PPS reçu   
      digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));   // Basculer l'état de la LED
      Serial.print("-");                              // Arricher le numéro de l'impulsion PPS durant l'échantillonnage
      Serial.print(compteur_captures + 1);            //            "
  }
}
