Trouver une station
Chercher l'aiguille
Le signal carré issu du montage précédent à 555 dont la fréquence est asservie à la position du condensateur variable est appliqué sur la broche D5 d'un module Arduino pro, nano, uno ou autre.
La mesure de fréquence par l'Arduino.
En réalité on ne va pas mesurer la fréquence du signal mais se contenter de compter le nombre de fronts hauts du signal pendant un intervalle de temps bien défini. La fréquence exacte n'a pas d'intérêt pour ce montage.
J'ai trouvé une documentation très complète sur la mesure de fréquence par Arduino ici : http://www.f-legrand.fr/scidoc/docmml/sciphys/arduino/oscillateur/oscillateur.html
Merci beaucoup à M Frédéric Legrand pour la mise à disposition de ce remarquable travail.
Voici le code de la mesure :
// NE555 oscillator freq-capacitance conversion: F = 1.44 / ( (R1+2R2) * C ) with R1=R2=1Mohm #define CONVERTFACTOR 480000 // (1.44/3)*10e6 #define INPUTPIN 5 // oscillator signal to pulse counter (Timer1) volatile uint16_t count_high,count_low; volatile uint32_t count; volatile uint16_t ic; // interrupts counter uint16_t nbPerSample=10; // number of periodic interrupts for a sample void start_count() { // Timer2 : periodic interrupts TCCR2A |= (1 << WGM21); // CTC mode 'clear timer on compare), top = OCR2A OCR2A = 0xFF; // periodic interrupt frequency 16 or 8 MHz /1024/256 TIMSK2 = 1 << TOIE2; // overflow interrupt enable TCNT2 = 0; ic = 0; // Timer1 : pulse counter (on rising edge) TCCR1A = 0; TCCR1B = 0; TCNT1 = 0; TIMSK1 = 1<<TOIE1; // overflow interrupt enable count = 0; sei(); // enable interrupts TCCR2B |= (1 << CS12) | (1 << CS11) | (1 << CS10); // prescaler = 1024 TCCR1B |= (1 << CS12) | (1 << CS11) | (1 << CS10); // external clock on rising edge } ISR(TIMER2_OVF_vect) { // Overflow interrupt ic++; if (ic==nbPerSample) { ic = 0; count_low = TCNT1; TCNT1 = 0; count = ((uint32_t)count_high)<<16 | count_low; count_high = 0; } } ISR(TIMER1_OVF_vect) { count_high++; } void setup() { // lancement du processus de mesure de la frequence pinMode(INPUTPIN,INPUT); start_count(); }
Pour exploiter la mesure, on utilisera dans le code principal la variable 'count' qui contient le nombre d'impulsions comptées dans l'intervalle de temps de la mesure.
void loop() { ... int impuls=round(count); ... }
Calculer la fréquence FM qui correspond à la position de l'aiguille.
A partir du nombre d'impulsions il est facile de calculer une fréquence équivalente par une simple règle de trois à condition de connaître deux types de bornes :
- Les fréquences minimum et maximum que l'on souhaite recevoir;
- Les nombres minimum et maximum d'impulsions entre les butées du condensateur variable.
Les fréquences sont celles de la bande FM. 87,5 à 108 Mhz en France. Les limites des impulsions sont établies en site après l'installation du module dans la radio en les faisant afficher dans le moniteur série en debug. Il suffit, le module installé, de relever les valeurs de impuls avec l'aiguille en bout de course de chaque coté du cadran. Il est possible également de prévoir une procédure d'étalonnage avec bouton poussoir mais comme chaque montage est dédié à une seule radio la procédure in situ est suffisante et ne consomme pas de code ni de mémoire.
Code du calcul de la fréquence.
int ind; // indice de balayage du tableau des frequences float logcount=log((double)count)*1000; int impuls=round(logcount); #if DEBUG Serial.print("\nImpuls="); Serial.println(impuls); #endif chan=map(impuls,5800,8480,865,1080); // translation de la valeur impulsion du 555 en frequence
Pourquoi utiliser le logarithme de count ?
La courbe de variation d'un condensateur de TSF n'est pas linéaire ce qui entraîne un tassement des fréquences sur la course de l'aiguille. Après quelques essais, la conversion de la valeur de count en Log permet de rétablir une certaine linéarité comme le montre le graphique ci-dessous :
![]() |
- En jaune : variation linéaire;
- En bleu : variation mesurée directement depuis le CV;
- En rouge : variation après passage en Log.
La correction n'est pas parfaite, mais c'est mieux que rien :-) .
Le calcul de la fréquence sélectionnée.
C'est l'instruction map qui va faire directement la règle de trois pour charger dans chan une fréquence entre 865 et 1080 Mhz en fonction de la valeur de count relativement aux bornes, ici 5800 et 8480.
Trouver la station qui correspond à la position de l'aiguille.
Le calcul pourrait s'arrêter là mais pour améliorer la stabilité du réglage j'ai ajouté une fonction qui va récupérer dans la mémoire EEProm de l'Arduino la station locale la plus proche de la fréquence calculée.
En effet, s'il est amusant de rechercher des stations en écoute des ondes courtes par exemple, c'est totalement inutile en FM où les stations sont localement bien déterminées. Sur le site du CSA : https://www.csa.fr/maradiofm/formulaire il est très simple de récupérer la liste des stations locales. Même la qualité de réception est précisée. Cela permet aussi de ne rentrer éventuellement que les stations que l'on souhaite réellement écouter ou - après mesure au debug des impulsions générées - de replacer en quelque sorte les stations des Grandes Ondes à leur place sur le cadran.
Les fréquences des stations locales sont stockées dans l'EEProm soit par programmation directe à l'aide d'un petit programme spécifique, soit par une fonction de mise à jour incluse dans le code.
Je n'ai pas utilisé les fonctions Seek-Up et Seek-Down des modules radio car elles ne sont pas toujours très convaincantes. Enfin, la mise à jour - surtout sur un poste fixe - ne se fait pas tous les jours !
Principe :
A chaque passage dans la fonction loop le programme calcule la fréquence souhaitée et si cette dernière diffère de plus de 200 Khz (valeur de séparation standard) en valeur absolue de la fréquence actuelle une recherche est lancée dans la liste des fréquences en mémoire pour retrouver la plus proche.
Recherche de la fréquence la plus proche : Code exemple
chanprec=chan; for(ind=0;ind<nbFreq;ind++) { // tant que la frequence lue n'est pas assez proche de la frequence calculée on cherche en EEPROM la plus proche freqlue=lisFreqEeprom(ind); if (chan < freqlue-2) { // On teste < car on demarre toujours de la frequence la plus basse if (indCour != ind) // si la frequence calculée est différente de celle courante, on change { // Talonnage sur la derniere frequence. freqMAX est valorisée à la derniere frequence locale de l'EEPROM avant le EOF. Comme freqMAX est independante // du calcul de chan, on ne pars pas dans une boucle sans fin sur la recherche de frequence. freqlue en EOF de EEPROM (1083) est toujours > chan+2 if (freqlue>1080) freqlue=freqMAX; radio.setChannel(freqlue); delay(50); // afficheFreq(freqlue); } indCour=ind; // frequence courante=nouvelle frequence break; // on sort de la boucle } } } delay (50); // pause! }
Explications :
Si la liste des stations en mémoire est 878 884 888 895 901 906 1090 1090 et que la fréquence calculée chan est 898 alors la boucle va s'arrêter sur la valeur 901 car 898 est < 901-2 . Pour être sûr de ne pas sortir des limites, une valeur plancher est définie à 865 dans la fonction map ainsi qu'une valeur plafond à 1080. Et pour ne pas dépasser les limites de la recherche en mémoire, deux valeurs supplémentaires supérieures à 1080 sont écrites dans l'EEPRom et vont provoquer la sortie de la recherche en cas de dépassement. Cette fonction est également prévue pour mémoriser la plus grande fréquence locale sans avoir à lire le tableau des fréquences au préalable (économie de mémoire toujours).
Pour complèter, les fréquences sont stockées en EEPRom modulo 860 pour économiser là aussi de précieux octets. Les 860 d'offset sont ajoutés ou retranchés là où c'est nécessaire dans le programme.
Lecture des fréquences en EEPRom : code exemple.
int lisFreqEeprom(int indice) { // recuperation d'une frequence en EEPROM selon son indice // frequence lue sur un octet = frequence reelle - 860 int freqEEPROM; freqEEPROM=EEPROM.read(indice)+860; delay(3); // pour ne pas avoir gerer le nombre de frequence en EEPROM (j'y arrive pas !), on positionne la frequence lue en borne sup // sauf quand on passe hors borne de l'EEPROM. Cette freqMAX n'est utilisee que dans la boucle principale if(freqEEPROM<1081) freqMAX=freqEEPROM; return (freqEEPROM); }
Explications :
Le programme lit à chaque passage une fréquence à la position indice de l'EEPRom et la retourne à la fonction appelante. Si la fréquence lue est inférieure à 1081, la variable freqMAX est chargée avec cette valeur lue sinon, au retour dans la fonction appelante, un test plafonnera la fréquence lue en EEPRom à celle de freqMAX qui contiendra automatiquement la plus grande fréquence locale.