Générateur HF
40 à 90 MHz sortie sinus
synthétisé par PLL
piloté par un ATmega8
pas de 10kHz

Le but de cette étude n'est pas de réaliser un émetteur de radio mais un générateur de laboratoire de très faible puissance, permettant d'expérimenter des circuits VHF comme des filtres par exemple, afin de confronter la théorie à l'expérience, sans rayonner. L'appareil doit être enfermé dans un boîtier métallique relié à la masse. La sortie du signal se fera sur une prise BNC. Un filtre VHF sera intercalé sur l'alimentation. Voir les liens en bas de page concernant l'attribution des fréquences radio. Plutôt que de vous livrer en bloc le schéma définitif assorti de quelques explications approximatives, je préfère pour cette réalisation vous décrire pas à pas les étapes (et les péripéties...) de sa conception.

1 Etape1 : L'oscillateur sinusoïdal

C'est le coeur de la partie analogique de l'appareil.

Le but étant d'obtenir un signal haute fréquence de 90MHz sinusoïdal, j'ai opté pour un oscillateur de type Colpitts dans un étage à transistor UHF NPN en collecteur commun. La self L1 et le condensateur C4 constituent le circuit oscillant. C2 réinjecte le signal sur l'entrée haute impédance de cet étage collecteur commun (base du transistor). La sortie à basse impédance se fait sur l'émetteur. Le signal est réinjecté en phase dans le circuit oscillant à travers le diviseur d'impédance (je devrais dire ici multiplieur...) constitué par les condensateurs C3 et C5.
R1 et R2 déterminent le point de fonctionnement du transistor.

2 Etape2 : Faire varier la fréquence

Pour faire varier la fréquence à volonté on remplace le condensateur C4 par une diode varicap. (Cette diode polarisée en inverse ne laisse pas passer le courant mais offre une capacitance qui diminue lorsque la tension continue appliquée augmente). La résistance R1 de 1Mohm bloque la HF.
Le courant dans R1 et D6 est quasiment nul.

La variation de capacité obtenue  est de l'ordre de 2/1 pour une tension variant de 0 à +24V.

3 Etape3 : Réguler l'amplitude pour limiter les harmoniques:

4 -

Pour obtenir un signal de sortie exempt d'harmoniques,  il est bon de commencer par ne pas en produire dès le départ. (Une autre solution consiste à générer un signal rectangulaire, triangulaire ou autre puis à supprimer les harmoniques par de jolis filtres en T... Moi j'ai opté ici pour la première solution.)

L'amplitude d'un oscillateur comme celui décrit plus haut n'est pas déterminée à priori. Si le gain en boucle ouverte est inférieure à l'unité, l'oscillation, si on la provoque par un 'Dirac' par exemple, sera d'amplitude décroissant et s'annulera. Si le gain est strictement égale à 1 pour la fréquence de résonance, une amplitude d'oscillation constante serait possible en théorie, mais un gain strictement égal à 1 est impossible à réaliser et à maintenir en pratique sans asservissement. Un gain supérieur à 1 donnera lieu à une oscillation dont l'amplitude va croître jusqu'à être limitée par la tension d'alimentation. On obtient une sinusoïde déformée qui sera très riches en harmoniques impaires.
Le plus simple pour réguler l'amplitude "en douceur" est d'utiliser deux diodes câblées "tête bêche"  en parallèle sur la self. Mais l'amplitude obtenue est alors faible et le taux d'harmoniques encore trop élevé.

D'où l'asservissement d'amplitude ci-dessus.

Le signal sinusoïdal présent sur l'émetteur de T2 est redressé par les diodes D4 et D2. le condensateur C11 se charge à la valeur crête. L'étage AOP amplifie la tension obtenue et pilote le géné. de courant T1.  Lorsque l' amplitude  de l'oscillation augmente, la tension aux bornes de C11 augmente, la tension à la sortie de IC2 diminue, T1 qui est un transistor PNP conduit davantage,  le courant dans la diode PIN (D1) augmente et cette dernière voit son impédance dynamique chuter. (C'est la principale caractéristiques d'une diode PIN). D1 et R8 agissent comme un diviseur de tension vis à vis du signal réinjecté via C7 sur la base de T2. Ainsi le gain diminue ce qui tend à maintenir une amplitude constante, dont la valeur peut être modifiée par le potentiomètre P1.

5 Les alimentations 5V et 24V

Une tension relativement élevée (24V est souhaitable pour alimenter la diode varicap en obtenant une bonne variation de capacité)

Les tensions sont obtenues à partir d'une alimentation 12V:
  • par un classique régulateur intégré 7805 pour le 5V
  • et par un doubleur de tension en CMOS pour le 24V 
Le doubleur de tension est constitue par un oscillateur RC à inverseur à hystérésis (IC2A,  CD40106) suivi d'un étage de puissance (IC2B) et d'un doubleur de tension à diodes Schottky (faible perte de tension directe = 100mV) . Cette configuration est rendue possible du fait de la consommation nulle dans la varicap, et très faible dans le potentiomètre (de forte valeur).

Toutefois ce type de montage est générateur de signaux parasites difficilement conciliables avec la pureté souhaitée du signal de sortie HF du générateur. En conséquence je ne l'utiliserai pas, au détriment de l'amplitude permise pour la variation de fréquence.

Un AOP  TL082 alimenté sur le  +12V permettra d'augmenter l'amplitude du signal sur la varicap entre +1,5V et +10V compte tenu des pertes "drop out" de l'AOP.

6 Le circuit de PLL: Le MC145170 (Motorola)

Le but de cette réalisation étant d'obtenir un signal de fréquence très précise, sélectionnable par pas de 10kHz, on utilisera une boucle d'asservissement de fréquence (PLL) et un oscillateur à quartz comme référence.

J'ai choisi comme PLL le MC145170 qui, bien que déjà considéré comme obsolète, correspond exactement à cet usage et à cette gamme de fréquence.

Ci-contre un extrait  du data sheet du MC145170.

Une autre version du générateur utilisera un LM7001.

Où trouver le MC145170 ?

7 Le schéma d'ensemble de la partie HF:

8 -

Le signal de référence (présent sur le pin 9) est fixé = 10kHz afin d'obtenir un pas de 10kHz. (on pourrait obtenir également  5kHz) Il est obtenu par division par R=400 de la fréquence d'un oscillateur à quartz (oscillateur intégré compensé en température). En effet  4MHz / 400 = 10kHz. Cette valeur 400 est simplement enregistrée dans le registre R du MC145170 par l'ATmega8.

Le signal du VCO est injecté via T12 sur l'entrée F-IN du MC145170 afin d'être divisé par une valeur N choisie de façon à obtenir également 10kHz.

Si on veut obtenir un signal de sortie de 80,00 MHz, l'ATmega8 devra écrire N = 8000 dans le registre N de la PLL. (80MHz / 8000 = 10kHz)
de même:
Si on veut obtenir un signal de sortie de 80,01 MHz, l'ATmega8 devra écrire N = 8001 dans le registre N de la PLL.
etc...

Les deux signaux de 10kHz (quartz/R ,  VCO / N) sont comparés au sein du MC145170 et le signal d'erreur  est disponible sur le pin13. Ce signal d'erreur sert à piloter la varicap (D3) après filtrage passe bas (R23 - C15 - R24 - C16) , amplification (x 2) par IC4A.  La variation possible de fréquence obtenue avec une seule self est de l'ordre de 1 / 1,2 .
(par exemple 72 à 85MHz avec L= 3spires non jointives air diamètre 4mm) .
Ce n'est pas beaucoup,  d'où l'utilisation de plusieurs selfs commutées par des transistors, pilotés par directement par l' ATmega8 .  Les selfs peuvent être connectées en parallèle par l'ATmega.
La solution des transistors de commutation n'est pas élégante, mais je constate que ça fonctionne. Des circuits à diodes PIN seraient préférables .

Cette configuration permet de générer des fréquences entre 30 et 90 MHz, voire plus. En deçà de 30MHz, le MC145170 alimenté en 5V ne voit plus le signal d'entrée (voir son datasheet...)

9 Le circuit imprimé:

Des pistes courtes pour les signaux VHF.

10 -

Seuls les BFR93 et les trois transistors BC847 de commutation des selfs sont en boîtier CMS (côté pistes donc non visibles sur cette photo).

Les 2 selfs (à gauche sur la photo) qui comportent un noyau ne sont pas celle de la version opérationnelle, qui elles sont au nombre de trois, et "à air".

11 L'ensemble des cartes


L'ensemble de la carte HF et de la (future) carte ATmega8 en cours de test.

Pas de mauvaises surprises, tout fonctionne correctement.

Le pas de synthèse peut être choisi à volonté et à tout moment égal à 10MHz, 1MHz, 100kHz ou 10kHz et ce à partir de n'importe quelle fréquence en cours. (On peut donc générer 81,230MHz ou 67,890 MHz par exemple, exemples pris au hasard)

12 Fréquences obtenues

Nous avons vu sur le schéma que les trois selfs L1, L2, L3 sont commutées par de simples transistors CMS type BC847. Les transistors étant commandés directement par un port du microcontrôleur ATmega8, il est possible de sélectionner plusieurs selfs à la fois, ce qui revient à les connecter en parallèle, ce qui permet d'obtenir 7 plages de fréquences avec seulement 3 selfs.

Il reste un trou entre 38 et 48 MHz, je compte bien le supprimer.

Les selfs sont bobinées à spires non jointives sur des mandrins isolants de 5mm de diamètre, sans noyau.

Leurs valeurs (mesurées) des trois self sont:
  • L1 = 446 nH  (10,5 spires sur un mandrin de diamètre 5,5mm, air)
  • L2 = 229 nH  ( 6,5 spires sur un mandrin de diamètre 5,5mm, air)
  • L1 =   93 nH. ( 3,5 spires sur un mandrin de diamètre 5,5mm, air)

13 Valeurs des selfs en fonction du nombre de spires:

14 -

Les valeurs des selfs ont été déterminées par la mesure de la fréquence d'un oscillateur "Armstrong" utilisant la self + une capa 22pF comme circuit oscillant de collecteur d'un BFR93A.
Voir cette ma page:
(J'ai bien un multimetre avec la fonction inductancemètre, mais avec une précision de 1 micro henry sur la gamme 1mH, il ne convient absolument pas ici, autant mesurer l'épaisseur d'une feuille de papier avec un double-décimètre).
En abscisse le nombre de spires pour le graphique de gauche, et le carré du nombre de spires pour celui de droite.
On voit que, conformément à la théorie (voir liens externes), la valeur du coefficient de self-induction d'une bobine est proportionnel au carré du nombre de spires.  (Enfin presque dans le cas de bobines courtes comme c'est le cas ici)

Voir également mon grip-dip décrit plus bas.

15 Le schéma de la carte logique (à microcontrôleur ATmega)

16 La carte logique

Sous l'ATmega8 on voit le connecteur de programmation "en-circuit". Le composant noir rectangulaire à droite est le récepteur infra-rouge dédié à la liaison RC5 par télécommande universelle TV.

Où trouver l'afficheur 4 lignes 20 caractères?

sur eBay, recherchez LCD (par exemple) On peut tout à fait se contenter d'un afficheur 2 lignes 16 caractères, en modifiant légèrement le soft.

17 Le commutateur rotatif

Ce bouton rotatif est un commutateur à résistances CMS (19 résistances de 3k3 câblées en série), commutateur récupéré sur un lave-linge à programmateur éléctronique (et servant sur cette machine à sélectionner les programmes de lavage). 

18 -

Détail du commutateur. On peut le remplacer par un commutateur rotatif classique et câbler des résistances classiques (d'ailleurs pour cette application, il est nul besoin de 19 positions, 3 suffiraient pour discriminer le sens de rotation, mais une dizaine ou plus permettent d'obtenir un plus grand confort d'utilisation) en modifiant le soft en conséquence. L'avantage des résistances commutées consiste en l'utilisation d'un seul bit de port en entrée de l'ATmega8 (en utilisant son convertisseur analogique-numérique).

19 -

De nombreuses informations complémentaires peuvent être trouvées dans les commentaires du programme source pour l'ATmega.

20 Le code source du programme

CODE SOURCE en langage PASCAL
  1. program GeneHF_90MHz;
  2. {===============================================================================
  3. par Silicium628
  4. versions: voir plus bas dans la partie "const" - derniere mise à jour 22 aout 2008
  5. ================================================================================
  6. PRINCIPE:
  7. --------------------------------------------------------------------------------
  8.  
  9. ================================================================================
  10. La carte electronique répond aux signaux d'une télécommande TV infrarouge universelle
  11. Beaucoup de modèles conviennent, il faudra adapter les codes des touches dans ce
  12. programme le cas écheant (voir les 'case octet_IR of') dans la partie "main"
  13. Pour ma part, j'utilise une PHILIPS type 'UNIVERSAL SBC RU 252 II'
  14.  
  15. ================================================================================
  16. }
  17.  
  18. { $W+ Warnings} {Warnings off}
  19.  
  20. Device = mega8, VCC = 5;
  21.  
  22. Import SysTick, LCDport, {RTclock,} {TickTimer,} RC5Rxport, { FreqCount,} ADCPort;
  23.  
  24. // From RTclock Import RTCtimer;
  25.  
  26. From System Import Int64, float;
  27.  
  28. Define
  29. ProcClock = 16000000; {Hertz}
  30. SysTick = 10, Timer0; {msec} // ATTENTION: necessaire pour l'horloge RTC et LCD
  31.  
  32. // RTclock = iData, DateTime; {Time, DateTime}
  33. // RTCsource = SysTick {, adj}; { adj = +/-100}
  34. // RTCtimer = 4; // 4 cannaux
  35.  
  36. // FreqTimer = Timer1; // (used 16bit Timer}
  37.  
  38. StackSize = 256, iData; // (voir affi en runtime)
  39. FrameSize = 128, iData;
  40.  
  41. LCDport = PortB, 0, PortC, 0; // control port, bit, Data port, bit ; voir le docuDriver.pdf (3.4.3 LCD-Split)
  42. //The first parameter defines the control port with its first used bit. The order of the bits are fixed but
  43. //different to the non-split driver: RS, RW, E (, Enable2 optional).
  44. LCDtype = 44780;
  45. LCDrows = 4; {rows}
  46. LCDcolumns = 20; {columns per line}
  47.  
  48. RC5Rxport = PinC, 4, negative; {Port, Pin#, polarity}
  49. RC5mode = rc_6bit; {command bit count}
  50.  
  51. ADCchans = [6], iData; {use only 1 Channel, ADC5}
  52. ADCpresc = 16;
  53.  
  54.  
  55.  
  56. Implementation
  57.  
  58. //==============================================================================
  59. {$IDATA}
  60.  
  61. type
  62.  
  63.  
  64. const
  65. version : string = '4.0';
  66.  
  67. { Type Declarations }
  68.  
  69.  
  70. //==============================================================================
  71. {$IDATA}
  72.  
  73. var
  74. // delay : SysTimer8;
  75. ticked : boolean;
  76. // timeout1 : boolean; // pour RTCtimer
  77. // timeout2 : boolean; // pour RTCtimer
  78. // timeout3 : boolean; // pour RTCtimer
  79. // timeout4 : boolean; // pour RTCtimer
  80. stk1 : word;
  81. itps : word;
  82.  
  83. OCR2_real : float;
  84.  
  85. rxAdr : byte;
  86. rxCmd : byte;
  87. adr1 : integer;
  88. cmd1 : integer;
  89. octet_IR : byte; //octet recu par le recepteur IR
  90. bitsix : byte;
  91. memobitsix : byte;
  92. nouveau : boolean; // pour l'anti rebond signaux IR
  93. acqui_ADC : word;
  94. pos_bt : byte;
  95. memo_pos_bt : byte;
  96. digit_x : byte;
  97. increment : word;
  98. puiss10 : array[1..4] of word;
  99. pos_cur : array[1..4] of byte;
  100.  
  101. // Frequence : longword;
  102. // F_affi : longword;
  103. F_base1 : word;
  104. consigne_F : longword;
  105. gamme : byte;
  106. memo_gamme : byte;
  107. manuel : boolean;
  108.  
  109. {--------------------------------------------------------------}
  110. {functions }
  111.  
  112. procedure init_ports; // ports perso
  113. // 0 = entree, 1=sortie ; les 1 sur les pins en entrees activent les R de Pull Up (tirage à VCC)
  114. begin
  115. PortB:= %00000000;
  116. DDRB:= DDRB or %00001000; // portB[3] = sortie (OC2)
  117.  
  118. DDRC:= DDRC and %001111; //PC4 en entree (IR)
  119.  
  120. portD:= %00100001;
  121. DDRD:= %11111111;
  122. end;
  123.  
  124.  
  125. procedure interroge_IR;
  126. begin
  127. if RecvRC5(rxAdr, rxCmd) then // interroge IR
  128. adr1:= integer(rxAdr);
  129. cmd1:= {%00111111 and} integer(rxCmd);
  130. octet_IR:= byte(cmd1); // le bit6 (=64 decimal) est=1 un appui de touche de la zapette sur deux
  131. memobitsix:= bitsix;
  132. bitsix:= octet_IR and %01000000;
  133. if bitsix <> memobitsix then
  134. nouveau:= true;
  135. else nouveau:= false;
  136. endif;
  137. octet_IR:= octet_IR and %00111111; // on supprime l'info de repetition de l'octet
  138. // portx:= portx or ......; // allume LED
  139. else
  140. octet_IR:= $FF;
  141. // portx:= portx and %......; // eteint LED
  142. endif;
  143. if nouveau = false then octet_IR:= $FF; endif; // pas de repetition auto dans cette application
  144. end;
  145.  
  146.  
  147. procedure InitINTs; // A ADAPTER au ATmega8
  148. begin
  149. { TCCR2:
  150. wgm21,20 =11 ->Fast PMW
  151. com21,com20=11 ->Set OC2 on Compare Match, clear OC2 at TOP ;
  152. bits2,1,0: prescaler = 1/256
  153.  
  154.   TCCR2:= %01111110;
  155. }
  156.  
  157. TCCR2:= %00000000; // Timer2 non utilisé. OC2 = sortie (portB,3) normale
  158.  
  159. TIMSK := TIMSK or %00000000; // INT Timer2 comp disable; INT Timer2 overflow disable;
  160. GICR := GICR or %00000000; // gere les INTs voir page 67 du pdf
  161. MCUCR := MCUCR or %00000010; // The falling edge of INT0 generates an interrupt request. p:67 du pdf
  162. end;
  163.  
  164.  
  165.  
  166. procedure Affiche_STACK_free; // attention: ne pas appeller depuis une INT sinon fait planter
  167. begin
  168. LCDxy(16, 3);
  169. Write(LCDout, IntToStr(stk1 : 3 : '0') );
  170. // LCDxy(16, 3);
  171. // Write(LCDout, IntToStr(fram_free : 3 : '0') ); fonction bugguée? chaque lecture consomme 1 octet!
  172. end;
  173.  
  174.  
  175. (*
  176. procedure RTCtickSecond; // CallBack from RTClock
  177. begin
  178.  
  179.   LCDxy(8, 0);
  180.   LCDclrEOL;
  181.   Write(LCDout, IntToStr(F_base1));
  182.  
  183.   LCDxy(0, 0);
  184. // LCDclrEOL;
  185.   Write(LCDout, ByteToStr(pos_bt : 2));
  186.  
  187. end;
  188. *)
  189.  
  190. procedure active_ENB;
  191. begin
  192. portD:= portD and %11011111; // ENB/=0 (active le transfert entre le uC et le MC145170)
  193. udelay(1); // 10us
  194. end;
  195.  
  196.  
  197. procedure desactive_ENB;
  198. begin
  199. portD:= portD or %00100000; // ENB/=1 (desactive ENB/ ce qui effectue le transfert dans les registres internes)
  200. mdelay(1);
  201. end;
  202.  
  203.  
  204. procedure init_variables;
  205. begin
  206. puiss10[1]:= 1;
  207. puiss10[2]:= 10;
  208. puiss10[3]:= 100;
  209. puiss10[4]:= 1000;
  210.  
  211. pos_cur[1]:= 0;
  212. pos_cur[2]:= 1;
  213. pos_cur[3]:= 3;
  214. pos_cur[4]:= 4;
  215.  
  216. gamme:= 0;
  217. manuel:= false;
  218. end;
  219.  
  220.  
  221. procedure clk_PLL;
  222. begin
  223. portD:= portD or %01000000;
  224. udelay(1); // 10us
  225. portD:= portD and %10111111;
  226. udelay(1); // 10us
  227. end;
  228.  
  229.  
  230. procedure out_registre_C(C_data : byte);
  231. //c'est le nombre de clocks qui determine le registre de destination
  232. // en envoie le bit de poids fort en premier
  233. var
  234. n, p : byte;
  235. begin
  236. active_ENB;
  237. for n:= 7 downto 0 do // 8 bits
  238. // p:= 1 shl (n - 1);
  239. // if (C_data and p) <> 0 then
  240. if bit(C_data, n) then
  241. portD:= portD or %10000000;
  242. else
  243. portD:= portD and %01111111;
  244. endif;
  245. clk_PLL;
  246. endfor;
  247. desactive_ENB;
  248. end;
  249.  
  250.  
  251. procedure out_registre_N(N_data : word); // vers la PLL MC145170
  252. //c'est le nombre de clocks qui determine le registre de destination
  253. // en envoie le bit de poids fort en premier
  254. var
  255. n : byte;
  256. p : word;
  257. begin
  258. active_ENB;
  259. for n:= 15 downto 0 do // 16 bits
  260. if bit(N_data, n) then
  261. portD:= portD or %10000000;
  262. else
  263. portD:= portD and %01111111;
  264. endif;
  265. clk_PLL;
  266. endfor;
  267. desactive_ENB;
  268. end;
  269.  
  270.  
  271. procedure out_registre_R(R_data : word); // vers la PLL MC145170
  272. //c'est le nombre de clocks qui determine le registre de destination
  273. // en envoie le bit de poids fort en premier
  274. var
  275. n : byte;
  276. p : word;
  277. begin
  278. active_ENB;
  279. for n:= 14 downto 0 do // 15 bits
  280. if bit(R_data, n) then
  281. portD:= portD or %10000000;
  282. else
  283. portD:= portD and %01111111;
  284. endif;
  285. clk_PLL;
  286. endfor;
  287. desactive_ENB;
  288. end;
  289.  
  290.  
  291. procedure init_registre_C; // de la PLL
  292. {-------------------------------------------------------------------------------------------------------------
  293. RAPPEL: programmation du registre C (8 bits) de la PLL MC145170:
  294. C7 - POL:Select the output polarity of the phase/frequency detectors.
  295. When set high, this bit inverts PDout and interchanges the fR function with fV as depicted in Figure 19.
  296. Also see the phase detector output pin descriptions for more information. This bit is cleared low at power up.
  297.  
  298. C6 - PDA/B:Selects which phase/frequency detector is to be used.
  299. When set high, enables the output of phase/frequency detector A (PDout) and disables phase/frequency detector B
  300. by forcing fR and fV to the static high state.
  301. When cleared low, phase/frequency detector B is enabled (fR and fV) and phase/frequency detector A is disabled
  302. with PDout forced to the high-impedance state. This bit is cleared low at power up.
  303.  
  304. C5 - LDE:Enables the lock detector output when set high.
  305. When the bit is cleared low, the LD output is forced to a static low level.
  306. This bit is cleared low at power up.
  307.  
  308. C4 - C2, OSC2 - OSC0: Reference output controls which determines the REFout characteristics as shown below.
  309.  
  310. C4 C3 C2 REFout Frequency ; Upon power up, the bits are initialized such that OSCin/8 is selected.
  311. 000dc (Static Low)
  312. 001 OSCin
  313. 010 OSCin /2
  314. 011 OSCin /4
  315. 100 OSCin /8 (par Defaut)
  316. 101 OSCin /16
  317. 110 OSCin /8
  318. 111 OSCin /16
  319.  
  320. C1 - fVE:Enables the fV output when set high.
  321. When cleared low, the fV output is forced to a static low level.
  322. The bit is cleared low upon power up.
  323.  
  324. C0 - fRE:Enables the fR output when set high. When cleared low, the fR output is forced to a static low level.
  325. The bit is cleared low upon power up.
  326. -------------------------------------------------------------------------------------------------------------}
  327. begin
  328. // bits 76543210
  329. out_registre_C(%01110011);
  330. end;
  331.  
  332.  
  333. procedure test_IR;
  334. var
  335. chr1 : char;
  336. begin
  337. LCDclr;
  338. LCDxy(0, 0);
  339. Write(LCDout, 'GeneHF 90MHz ' + version );
  340. LCDxy(0, 1);
  341. Write(LCDout, 'Test IR ok = out' );
  342.  
  343. repeat
  344. interroge_IR;
  345. if nouveau = true then
  346. chr1:= '*';
  347. else chr1:= ' ';
  348. endif;
  349. if octet_IR <> 255 then
  350. LCDxy(0, 0);
  351. LCDclrEOL;
  352. Write(LCDout, ByteToStr(octet_IR : 3 : ' ') + ' ' + chr1);
  353. mdelay(200);
  354. endif;
  355. until (octet_IR = 0) or (octet_IR = 23);
  356. end;
  357.  
  358.  
  359. procedure acqui_pos_btn;
  360. // lit la position d'un potentiometre à resistances CMS discretes (63k au total)
  361. begin
  362. // LCDclr;
  363.  
  364. acqui_ADC:= GetADC; // resolution 10bits
  365.  
  366. case acqui_ADC of
  367.  
  368. 881..900 : pos_bt:= 0; // 884
  369. |
  370. 875..880 : pos_bt:= 1; // 878
  371. |
  372. 869..874 : pos_bt:= 2; // 871
  373. |
  374. 860..868 : pos_bt:= 3; // 862
  375. |
  376. 851..859 : pos_bt:= 4; // 854
  377. |
  378. 839..850 : pos_bt:= 5; // 844
  379. |
  380. 826..838 : pos_bt:= 6; // 831
  381. |
  382. 811..825 : pos_bt:= 7; // 819
  383. |
  384. 791..810 : pos_bt:= 8; // 804
  385. |
  386. 776..790 : pos_bt:= 9; // 787
  387. |
  388. 756..775 : pos_bt:= 10; // 768
  389. |
  390. 736..755 : pos_bt:= 11; // 744
  391. |
  392. 701..735 : pos_bt:= 12; // 717
  393. |
  394. 671..700 : pos_bt:= 13; // 683
  395. |
  396. 631..670 : pos_bt:= 14; // 639
  397. |
  398. 551..630 : pos_bt:= 15; // 585
  399. |
  400. 451..550 : pos_bt:= 16; // 512
  401. |
  402. 350..450 : pos_bt:= 17; // 409
  403. |
  404. 200..300 : pos_bt:= 18; // 255
  405. |
  406. 0..100 : pos_bt:= 19; // 0
  407. |
  408. endcase;
  409. // LCDxy(0, 1);
  410. // Write(LCDout, ByteToStr(n : 3));
  411. end;
  412.  
  413.  
  414. procedure commute_selfs(gamme1 : byte);
  415. // gamme = 1..7
  416. // chaque bit commute une des trois selfs. Les selfs peuvent donc etre utilisees en //
  417. begin
  418. portD:= portD and %11111000;
  419. portD:= portD or (gamme1 and %00000111);
  420. end;
  421.  
  422.  
  423. procedure select_gamme;
  424. // a finaliser, sans trous, apres avoir ajuste toutes les selfs
  425. begin
  426. memo_gamme:= gamme;
  427. case F_base1 of
  428. 8001..9000 : gamme:= 7;
  429. |
  430. 7301..8000 : gamme:= 6;
  431. |
  432. 6801..7300 : gamme:= 5;
  433. |
  434. 6301..6800 : gamme:= 4;
  435. |
  436. 5501..6300 : gamme:= 3;
  437. |
  438. 4800..5500 : gamme:= 2;
  439. |
  440. 3200..3800 : gamme:= 1;
  441. |
  442. endcase;
  443.  
  444. // if gamme <> memo_gamme then
  445. commute_selfs(gamme);
  446.  
  447.  
  448. LCDxy(7, 2);
  449. Write(LCDout, ByteToStr(gamme));
  450. // endif;
  451. end;
  452.  
  453.  
  454. procedure increment_F;
  455. begin
  456. F_base1:= F_base1 + increment;
  457. LCDxy(4, 0);
  458. LCDclrEOL;
  459. Write(LCDout, IntToStr(F_base1 : 3 : 2) + ' MHz');
  460. LCDxy(8 - digit_x, 0);
  461. out_registre_N(F_base1);
  462. if not(manuel) then
  463. select_gamme;
  464. endif;
  465. end;
  466.  
  467.  
  468. procedure decrement_F;
  469. begin
  470. F_base1:= F_base1 - increment;
  471. LCDxy(4, 0);
  472. LCDclrEOL;
  473. Write(LCDout, IntToStr(F_base1 : 3 : 2) + ' MHz');
  474. LCDxy(8 - digit_x, 0);
  475. out_registre_N(F_base1);
  476. if not(manuel) then
  477. select_gamme;
  478. endif;
  479. end;
  480.  
  481.  
  482. procedure affiche_increment;
  483. begin
  484. case digit_x of
  485. 1 : LCDxy(4, 1);
  486. Write(LCDout, ' 10 kHz');
  487. |
  488. 2 : LCDxy(4, 1);
  489. Write(LCDout, '100 kHz');
  490. |
  491. 3 : LCDxy(4, 1);
  492. Write(LCDout, ' 1 MHz');
  493. |
  494. 4 : LCDxy(4, 1);
  495. Write(LCDout, ' 10 MHz');
  496. |
  497. endcase;
  498. end;
  499.  
  500.  
  501. //==============================================================================
  502. { Main Program }
  503. {$IDATA}
  504.  
  505. begin
  506. init_variables;
  507. init_ports;
  508. InitINTs;
  509. init_registre_C;
  510.  
  511. out_registre_R(400); // 4MHz/400 = 10kHz (ref)
  512. out_registre_N(7000);
  513.  
  514. ticked := true;
  515. LCDclr; { clear display }
  516. LCDcursor(false, false); { display on, cursor off & no blink }
  517. Write(LCDout, 'RESET');
  518.  
  519. mdelay(300);
  520. LCDclr;
  521. Write(LCDout, 'GeneHF 90MHz ' + version );
  522. mdelay(1000);
  523. LCDclr;
  524.  
  525. LCDxy(0, 0);
  526. Write(LCDout, 'Out');
  527. LCDxy(0, 1);
  528. Write(LCDout, 'pas');
  529. LCDxy(0, 2);
  530. Write(LCDout, 'gamme');
  531.  
  532. // SetFreqCountMode(TFreqBase1MHz); // mode frequencemetre TFreqBase1MHz Frequ = 100Hz...6.5535MHz gate time = 10msec
  533.  
  534. octet_IR:= 255;
  535.  
  536. F_base1:= 7000;
  537. gamme:= 1;
  538. commute_selfs(gamme);
  539. LCDxy(7, 2);
  540. Write(LCDout, ByteToStr(gamme));
  541.  
  542. EnableInts;
  543.  
  544. digit_x:= 3;
  545. increment := puiss10[digit_x];
  546. affiche_increment;
  547.  
  548. loop
  549. memo_pos_bt:= pos_bt;
  550. acqui_pos_btn;
  551. if (pos_bt < memo_pos_bt) and (pos_bt + memo_pos_bt <> 19) then
  552. decrement_F;
  553. elsif (pos_bt > memo_pos_bt)and (pos_bt + memo_pos_bt <> 19) then
  554. increment_F;
  555. elsif (( pos_bt = 0) and (memo_pos_bt = 19) ) then
  556. increment_F;
  557. elsif (( pos_bt = 19) and (memo_pos_bt = 0) ) then
  558. decrement_F;
  559. endif;
  560.  
  561. interroge_IR;
  562.  
  563. if octet_IR <> 255 then
  564. case octet_IR of
  565. 0 : manuel:= false;
  566. LCDxy(10, 2);
  567. Write(LCDout, '( auto )');
  568. |
  569. 1..8 : manuel:= true;
  570. gamme:= octet_IR;
  571. commute_selfs(gamme);
  572. LCDxy(7, 2);
  573. Write(LCDout, ByteToStr(gamme));
  574. LCDxy(10, 2);
  575. Write(LCDout, '(manuel)');
  576. |
  577. 9 : test_IR
  578. |
  579. 12 : system_reset;
  580. |
  581. 17 : LCDcursor(true, false);
  582. if digit_x < 4 then // (fleche <)
  583. inc(digit_x);
  584. LCDxy(8 - pos_cur[digit_x], 0);
  585. increment := puiss10[digit_x];
  586. affiche_increment;
  587. endif;
  588. |
  589. 16 : if digit_x > 1 then // (fleche >)
  590. dec(digit_x);
  591. LCDxy(8 - pos_cur[digit_x], 0);
  592. increment := puiss10[digit_x];
  593. affiche_increment;
  594. endif;
  595. |
  596. 32 : increment_F; // (fleche haut)
  597. |
  598. 33 : decrement_F; // (fleche bas)
  599. |
  600. 43 : nop; // (<< rouge)
  601. |
  602. 46 : nop; //(>> bleue)
  603. |
  604. 47 : nop; // (.)
  605. |
  606. 56 : nop; // (lecture >)
  607. |
  608. 23 : nop; // (Ok)
  609. |
  610. endcase;
  611. endif;
  612.  
  613.  
  614. // Frequence:= 16 * GetFreqCounterL;
  615.  
  616. mdelay(2);
  617.  
  618. endloop;
  619.  
  620. // RTCtimer_Stop(0);
  621. end GeneHF_90MHz.
  622.  
  623.  
  624.  
  625.  
  626.  

21 Documents techniques


22 Améliorations

2 octobre 2008:
Il reste à concevoir un circuit de sortie avec réglage et calibrage de l'amplitude (digital par diodes PIN) et éventuellement filtrage. La possibilité de moduler le signal serait aussi intéressante afin d'obtenir un générateur exploitable dans une large gamme d'applications.

22 octobre 2008:
Afin d'affiner la mesure de la valeur des selfs j'ai conçu et réalisé ce petit grip-dip: 

23 Le Grip-Dip

24 -

Note: Je n'ai pas réinventé la roue, le grip-dip était déjà connu au débuts de la radio et fonctionnait à l'époque avec une lampe triode. Voir liens externes.

PRINCIPE:
On constitue un circuit accordé (circuit bouchon) en câblant un condensateur de valeur connue (10pF) aux bornes de la self à mesurer, puis on approche simplement la self de celle du grip-dip (L1,L2).

Le principe consiste à détecter la diminution du courant de gate (qui traverse la résistance R3 de 470k) lorsque la fréquence de l'oscillateur Hartley correspond à la fréquence de résonance du circuit bouchon.

On recherche donc ce minimum en faisant varier la fréquence avec le potentiomètre P1. (le courant dans le vu-mètre augmente brusquement autour de la résonance). De la fréquence obtenue (mesurée au fréquencemètre numérique) et de la valeur du condensateur, on calcule la valeur de la self. (On peut aussi se contenter d'utiliser un large disque gradué (étalonné une fois pour toutes) sur l'axe de P1 si on ne recherche pas une grande précision).

L'avantage du grip-dip sur le montage direct de la self à mesurer au sein d'un oscillateur est justement l'absence de connexions électriques entre les éléments à mesurer et l'appareil de mesure, ce qui évite l'apport de capacité supplémentaire aux bornes de la self. Dans le cas de selfs de très faibles valeurs utilisées en VHF que l'on fait résonner avec des condensateurs eux-même de très faibles valeurs (facteur Q oblige, quelques pF), cet avantage est décisif afin d'obtenir une bonne précision.

Mais ce qui m'a paru le plus intéressant dans ce montage, c'est l'amplitude de la variation de fréquence obtenue avec une simple diode varicap sous 12V. Le fréquences maxi et mini sont un un rapport 1,7 /1 soit beaucoup mieux que le 1,2 / 1 de l'oscillateur Colpitts à BFR93 constituant le coeur du générateur HF.
Cette plus grande plage de réglage de la fréquence est due au fait que la varicap est le seul élément capacitif aux bornes de la self (la self à prise intermédiaire L1,L2) contrairement à ce qui se passe pour l'oscillateur Colpitts pour lequel la varicap se trouve en parallèle avec le pont diviseur capacitif fixe, ce qui diminue la variation de capacité résultante. 

D'où ma nouvelle réalisation: un géné HF 160MHz en utilisant ce VCO à transistor JFET 2N3819, associé à une PLL + diviseurs intégrés de type LM7001

Je connaissais bien évidemment tout cela depuis longtemps, alors pourquoi ne pas avoir choisi l'oscillateur Hartley dès le départ? A cause de la self à prise intermédiaire qui me semblait bien peu pratique à commuter électroniquement (pour changer de gamme). Mais je devrais pouvoir ne commuter qu'une des deux parties, celle reliée à la masse... et puis vu l'étendue des fréquences accessibles avec une seule self, la commutation de selfs est rendue facultative. à suivre.


24 octobre 2008:
Le projet du nouveau géné à 2N3819 et  LM7001 est en cours d'étude.
Il utilisera la même carte logique à ATmega8 que celle décrite ici. Je vais d'ailleurs renommer cette carte:
 "Carte ATmega Universelle", elle servira sans doute à d'autres réalisations, ce qui me fera gagner du temps.

24 octobre 2008:
Le géné géné HF 160MHz est en voie de finalisation. Il manque juste un filtre passe bas coupant à 160MHz et un étage de sortie.

25 Liens externes

26 -

Liens...



55038