#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
}