#include "driver/gpio.h"
#include "stdio.h"
#include "driver/ledc.h"
#include "driver/pcnt.h"
#include "soc/pcnt_struct.h"
#include <math.h>
#include <Keypad.h>  // Bibliothèque pour le clavier matriciel
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// Configuration de l'afficheur LCD
LiquidCrystal_I2C lcd(0x27, 16, 2);

#define LEDC_HS_CH1_GPIO GPIO_NUM_4 // GPIO 33 comme sortie pour le générateur de pulses (LEDC)
uint32_t RapportCycliqSortie = 0;   // Valeur calculée du cycle de travail
uint32_t ResolutionSortie = 0;      // Valeur calculée de la résolution
// GPIO pour le clavier matriciel
const byte ROWS = 4; // 4 lignes
const byte COLS = 4; // 4 colonnes
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

byte rowPins[ROWS] = {5, 18, 19, 12}; // Connexion des lignes du clavier aux GPIO
byte colPins[COLS] = {14, 23, 25, 26}; // Connexion des colonnes du clavier aux GPIO

Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

#define LEDC_HS_CH0_GPIO GPIO_NUM_33 // GPIO 33 comme sortie pour le générateur de pulses (LEDC)

#define PCNT_COUNT_UNIT       PCNT_UNIT_0                                 // Unidade 0 do Contador de pulso PCNT do ESP32
#define PCNT_COUNT_CHANNEL    PCNT_CHANNEL_0                              // Canal 0 do Contador de pulso PCNT do ESP32
#define PCNT_INPUT_SIG_IO     GPIO_NUM_34                                 // Entrada do Frequencimetro -  GPIO 34
#define LEDC_HS_CH0_GPIO      GPIO_NUM_33                                 // Saida do LEDC - gerador de pulsos - GPIO_33

#define PCNT_INPUT_CTRL_IO    GPIO_NUM_35                                 // Pino de controle do PCNT - HIGH = compte à rebours, LOW = compte à rebours 
#define OUTPUT_CONTROL_GPIO   GPIO_NUM_32                                 // Sortie de la minuterie - Contrôle le décompte - GPIO_32


#define PCNT_H_LIM_VAL        overflow                                    // Limite superior de contagem
#define IN_BOARD_LED          GPIO_NUM_2                                  // LED nativo ESP32 - GPIO 2
bool            flag          = true;                                     // Indicador de fim de contagem - libera impressão
uint32_t        overflow      = 20000;                                    // Valor maximo para overflow do contador PCNT
int16_t         pulses        = 0;                                        // Quantidade de pulsos contados
uint32_t        multPulses    = 0;                                        // Quantidade de overflows do contador PCNT
uint32_t        InterValTemps = 1000000;                                  // Tempo de amostragem  de 1 segundo para a contagem de pulsos 
uint32_t        FreqVoulue     = 12543;                                    // Frequencia inicial do oscilador - 12543 Hz
uint32_t        RapportCycliq         = 0;                                        // Valor calculado do ciclo de trabalho
uint32_t        Resolution     = 0;                                        // Valor calculado da resolucao
float           Frequence    = 0;                                        // Variavel para calculo de frequencia

String freqInput = "";        // Stocke la fréquence entrée par l'utilisateur
int inputInt = 1000;          // Valeur par défaut de la fréquence


uint32_t n = 2;

esp_timer_create_args_t create_args;                                      // Argumentos do ESP-Timer
esp_timer_handle_t timer_handle;                                          // Instancia de ESP-Timer
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;                     // variavel tipo portMUX_TYPE para sincronismo

//======================================================================
void setup() {
  Serial.begin(115200);                                                   // Inicializa a serial 115200 Bps
  Serial.println(" Digite uma frequencia - 1 a 40 MHz");                  // Print na console
  
  lcd.init();
  lcd.backlight();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Freq Diviseur");

    // Initialiser les broches du clavier
  for (int i = 0; i < ROWS; i++) {
    pinMode(rowPins[i], OUTPUT);
  }

  for (int i = 0; i < COLS; i++) {
    pinMode(colPins[i], INPUT_PULLUP);
  }

  GenerateurDeSignaux();                                                // Démarre la génération d'impulsions dans l'oscillateur
  Compteurd1Impulsion();

    // Configurer GPIO en mode OUTPUT pour le contrôle de la fréquence
  pinMode(OUTPUT_CONTROL_GPIO, OUTPUT);
  // Configurer GPIO en mode INPUT pour le signal d'entrée de fréquence
  pinMode(PCNT_INPUT_SIG_IO, INPUT);
  // Configurer GPIO en mode OUTPUT pour l'LED
  pinMode(IN_BOARD_LED, OUTPUT);
  // Créer un timer ESP32 pour contrôler la fréquence
  create_args.callback = ControleurDeTemps;                                  // Instances du temps de contrôle
  esp_timer_create(&create_args, &timer_handle);                          // Création des paramètres de la minuterie
  // Configurer l'LED intégrée pour afficher la fréquence
  gpio_set_direction(IN_BOARD_LED, GPIO_MODE_OUTPUT); 


  
}


//================================================================================
void loop() {

  ClaviMatricielle () ; // Fonction pour le clavier matricielle


  if (flag == true)                                                       // Si le décompte est terminé
  {
    flag = false;                                                         // Empêche la réimpression
    Frequence = ((pulses + (multPulses * overflow)) / 2) / n  ;          // Calcule la somme des impulsions comptées dans le PCNT
    Serial.print("Frequencia : ");
    Serial.print(Frequence);
    Serial.println(" Hz ");                                                     // Imprimer l'unité en Hz

    lcd.setCursor(0, 1);
    lcd.print("SORTIE: ");
    lcd.print(Frequence);
    lcd.print(" Hz");
    


    multPulses = 0;                                                       // Remise à zéro du compteur de débordement
    // Espaco para qualquer função
    delay (100);                                                          // Délai 100 ms
    // Espaco para qualquer função
    pcnt_counter_clear(PCNT_COUNT_UNIT);                                  // Remise à zéro du compteur PCNT
    esp_timer_start_once(timer_handle, InterValTemps);                           // Lance le compteur de temps d'une seconde
    gpio_set_level(OUTPUT_CONTROL_GPIO, 1);                               // Porte de contrôle - permet le comptage des impulsions
  }
}

//==============================Clavier Matricielle======================================
void ClaviMatricielle () {
  char key = keypad.getKey();
  
  // Si une touche est appuyée
  if (key) {
    Serial.println(key);

    switch (key) {
      case '0' ... '9':    // Pour les touches numériques
        freqInput += key;
        Serial.print("Fréquence en entrée: ");
        Serial.println(freqInput);

        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("ENTREE: ");
        lcd.print(freqInput);
        lcd.print(" "); // Effacer les anciens caractères
        break;
      FreqVoulue = 0;

      case '#':  // Valider la fréquence entrée
        if (freqInput.length() > 0) {
          inputInt = freqInput.toInt();  // Convertir l'entrée en entier
          //freqInput = "";
          FreqVoulue = inputInt;  // Mettre à jour la fréquence souhaitée
          freqInput = "";
          if (FreqVoulue != 0)                                                     // Si une valeur a été introduite
          {
             GenerateurDeSignaux ();                                              // Reconfigure la fréquence de l'oscillateur
          }
          //GenerateurDeSignaux();
          Serial.print("Nouvelle fréquence: ");
          Serial.println(FreqVoulue);

          // Réinitialiser et appliquer la nouvelle fréquence
          
        }
        break;

      case '*':  // Effacer l'entrée actuelle
        freqInput = "";
        Serial.println("Effacement de l'entrée.");

        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("ENTREE:  "); // Effacer l'affichage de l'entrée
        break;

      default:
        break;
    }
  }
}



//===============================LEDC=================================================
void GenerateurDeSignaux ()                                              // Inicializa gerador de pulsos
{
  Resolution = (log (80000000 / FreqVoulue)  / log(2)) / 2 ;                // Calculo da resolucao para o oscilador
  if (Resolution < 1) Resolution = 1;                                       // Resoluçao mínima 
  Serial.println(Resolution);                                           // Print
  RapportCycliq = (pow(2, Resolution)) / 2;                                        // Calculo do ciclo de trabalho 50% do pulso
  Serial.println(RapportCycliq);                                               // Print
  ledc_timer_config_t ledc_timer = {};                                    // Instancia a configuracao do timer do LEDC
  ledc_timer.duty_resolution =  ledc_timer_bit_t(Resolution);              // Configura resolucao
  ledc_timer.freq_hz    = FreqVoulue;                                      // Configura a frequencia do oscilador
  ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE;                           // Modo de operacao em alta velocidade
  ledc_timer.timer_num = LEDC_TIMER_0;                                    // Usar timer0 do LEDC
  ledc_timer_config(&ledc_timer);                                         // Configura o timer do LEDC
  ledc_channel_config_t ledc_channel = {};                                // Instancia a configuracao canal do LEDC
  ledc_channel.channel    = LEDC_CHANNEL_0;                               // Configura canal 0 
  ledc_channel.duty       = RapportCycliq;                                        // Configura o ciclo de trabalho
  ledc_channel.gpio_num   = LEDC_HS_CH0_GPIO;                             // Configura GPIO da saida do LEDC - oscilador
  ledc_channel.intr_type  = LEDC_INTR_DISABLE;                            // Desabilita interrupção do LEDC
  ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE;                         // Modo de operacao do canal em alta velocidade
  ledc_channel.timer_sel  = LEDC_TIMER_0;                                 // Seleciona timer 0 do LEDC
  ledc_channel_config(&ledc_channel);                                     // Configura o canal do LEDC
}
//===============================Fonction d'interruption=====================================
static void IRAM_ATTR pcnt_intr_handler(void *arg)                        // Contagem do contador de Overflow
{
  portENTER_CRITICAL_ISR(&timerMux);                                      // Bloqueia nova interrupção
  multPulses++;                                                           // Incrementa contador de overflow
  PCNT.int_clr.val = BIT(PCNT_COUNT_UNIT);                                // Limpa indicador de interrupção
  portEXIT_CRITICAL_ISR(&timerMux);                                       // Libera nova interrupção
}
//----------------------------------------------------------------------------------
void Compteurd1Impulsion (void)                                            // Inicializacao do contador de pulsos
{
  pcnt_config_t pcnt_config = { };                                        // Instancia PCNT config
  pcnt_config.pulse_gpio_num = PCNT_INPUT_SIG_IO;                         // Configura GPIO para entrada dos pulsos
  pcnt_config.ctrl_gpio_num = PCNT_INPUT_CTRL_IO;                         // Configura GPIO para controle da contagem
  pcnt_config.unit = PCNT_COUNT_UNIT;                                     // Unidade de contagem PCNT - 0
  pcnt_config.channel = PCNT_COUNT_CHANNEL;                               // Canal de contagem PCNT - 0
  pcnt_config.counter_h_lim = PCNT_H_LIM_VAL;                             // Limite maximo de contagem - 20000
  pcnt_config.pos_mode = PCNT_COUNT_INC;                                  // Incrementa contagem na subida do pulso
  pcnt_config.neg_mode = PCNT_COUNT_INC;                                  // Incrementa contagem na descida do pulso
  pcnt_config.lctrl_mode = PCNT_MODE_DISABLE;                             // PCNT - modo lctrl desabilitado
  pcnt_config.hctrl_mode = PCNT_MODE_KEEP;                                // PCNT - modo hctrl - se HIGH conta incrementando
  pcnt_unit_config(&pcnt_config);                                         // Configura o contador PCNT
  pcnt_counter_pause(PCNT_COUNT_UNIT);                                    // Pausa o contador PCNT
  pcnt_counter_clear(PCNT_COUNT_UNIT);                                    // Zera o contador PCNT
  pcnt_event_enable(PCNT_COUNT_UNIT, PCNT_EVT_H_LIM);                     // Configura limite superior de contagem
  pcnt_isr_register(pcnt_intr_handler, NULL, 0, NULL);                    // Conigura rotina de interrupção do PCNT
  pcnt_intr_enable(PCNT_COUNT_UNIT);                                      // Habilita interrupções do PCNT
  pcnt_counter_resume(PCNT_COUNT_UNIT);                                   // Reinicia a contagem no contador PCNT
}
//----------------------------------------------------------------------------------
void ControleurDeTemps (void *p)                                              // Fin du temps de lecture de l'impulsion
{
  gpio_set_level(OUTPUT_CONTROL_GPIO, 0);                                 // Contrôle du PCNT - pour le comptable
  pcnt_get_counter_value(PCNT_COUNT_UNIT, &pulses);                       // Obtient la valeur comptée dans le PCNT
  flag = true;                                                            // Informe qu'il y a eu une interruption du contrôle
}













D0D1D2D3D4D5D6D7GNDLOGIC