Générateur 4.4GHz Wobulé - avec un ADF4351

Cet appareil génère des fréquences de 35 MHz à ... 4400MHz. Affichage TFT 480x360

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 (<1mW c.a.d < 0 dBm), permettant d'expérimenter et de régler des circuits VHF et UHF 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 SMA. Un filtre HF sera intercalé sur l'alimentation. Voir les liens en bas de page concernant l'attribution des fréquences radio. Toute émission dans les bandes VHF et UHF est interdite (hors bande radio-amateur) et serait rapidement repérée ce qui peut vous amener directement en prison. Les radio-amateurs pourront adapter cette réalisation pour leurs besoins propres.
Cliquez sur une image ci-dessous pour visualiser l'album. Certaines sont commentées

1 Le géné en fonctionnement :

2 Le schéma

Une liaison série sur trois fils suffit à piloter l'ADF4351 :
  • Data
  • Clock
  • LE
Ces trois signaux sont générés par l'ATmega sur son port F avec un niveau logique de 0..5V
Mais l'ADF4351 travaille sous 3V3, d'où la présence des trois diviseurs de tension à résistances (560 ohm - 1k).

Le quatrième signal (MUX OUT) est une sortie logique de l'ADF4351 indiquant la bonne synchronisation.

3 La carte ADF4351

Cette carte que l'on trouve toute faite sur le net pour à peine le double du prix du composant seul est la bienvenue sachant que l'ADF4351 se présente en boîtier microscopique LFCSP au pas de 0.5mm.

4 Le module afficheur TFT 3.0 pouces 480x320 pixels

Ce module est prévu pour interfacer directement un Arduino Mega2560. Sa résolution de 480x360 pixels pour une taille de 3 pouces procure une excellent finesse de l'image (en couleur). La librairie "UTFT" disponible pour les Arduino lui convient très bien.

5 La carte Arduino Mega2560

Cette carte n'est plus à présenter. Un grand classique dont le cœur est un ATmega 2560, un des plus puissants avant de passer aux ARM (ces derniers sont utilisés sur les Raspberry Pi en particulier).

L'ATmega 2560 c'est :
  • 256 kB de mémoire flash
  • 8KB RAM
  • 4KB EEprom
  • Toute la gamme des périphériques (USART, SPI, Compteurs, Timers... )
  • 86 lignes de ports E/S
  • etc...
Je l'ai choisi afin d'interfacer facilement l'afficheur. Quant à l'ADF4351, certains le pilotent avec un simple et minuscle Arduino Mini.

6 Les encodeurs rotatifs code gray

J'ai consacré un article à cet encodeur : ICI .

Ils permettent d'incrémenter / décrémenter la fréquence de sortie, et de choisir le digit à modifier.

7 Le datasheet de l'ADF4351 : détail

Ce diagramme fonctionnel m'a laissé perplexe. Ce n'est qu'en lisant et relisant le texte du pdf que j'ai compris le caractère profondément transcendant du rectangle grisé en bas à gauche ! Dès lors la formule magique qui suit devient lumineuse... ouf !

D'autre part la lecture du pdf laisse supposer que ce circuit a été conçu pour équiper les appareils de téléphonie mobile GSM et autres smartphones opérant en UHF (1800 MHz) et en bande s.

Le principe général de l'ADF4351 est le suivant :
Un ensemble de trois VFO (commutables) couvre la bande des fréquences de 2 GHz à 4 GHz. Les fréquences plus basses (jusqu'à 35 MHz) sont obtenues par division par /2, /4, /8, /16, /32, /64 comme on peut le voir dans mon fichier de la class ADF4351 plus bas sur cette page.

8 La formule magique :

La formule qui permet de calculer la fréquence de sortie en fonction des paramètres appliqués au circuit se trouve à la page 12/30 du datasheet.

On en déduit facilement les valeurs à donner aux paramètres pour obtenir la fréquence désirée.

9 Le programme en C++ pour l'Arduino Mega2560

CODE SOURCE en c
  1. /*
  2. Firmware pour carte générateur de signaux ADF4351
  3. et carte Uno Mega2560 + afficheur 480x320 non tactile.
  4. par Silicium628.
  5.  
  6. Ce fichier source "GeneUHF_ADF4351.cpp" est libre, vous pouvez le redistribuer et/ou le modifier selon
  7. les termes de la Licence Publique Générale GNU.
  8.  
  9. En ce qui concerne certaines bibliothèques incluses, issues du domaine "Arduino" (en particulier UTFT), il faut voir au cas par cas.
  10. */
  11.  
  12. /**
  13. REMARQUES :
  14. -les ports utilisés doivent être définis dans les fichiers "AD9850.h" et "AD9951.h"
  15. -il faut copier le dossier "UTFT" dans le dossier des librairies /usr/share/arduino/libraries ET définir les permissions qui vont bien
  16.  
  17. **/
  18.  
  19.  
  20. //================================
  21. #define version "v4.0"
  22. //================================
  23.  
  24. #include "GeneUHF_ADF4351.h"
  25.  
  26.  
  27. #include <avr/io.h>
  28. #include <stdint.h>
  29. #include <stdlib.h>
  30. #include <util/delay.h>
  31. #include <math.h>
  32.  
  33. #include "UTFT.cpp"
  34. #include "uart2560_628.c"
  35. #include "ADF4351-628v2.h";
  36.  
  37. #define portPIN_switch PINH
  38.  
  39. #define pin_switch1 0b00000001
  40. #define pin_switch2 0b00000010
  41.  
  42.  
  43.  
  44. // Declare which fonts we will be using
  45. extern uint8_t SmallFont[];
  46. extern uint8_t BigFont[];
  47. extern uint8_t SevenSegNumFont[];
  48.  
  49. /****************************************************************************************************************************************************
  50. //choisir le bon driver suivant, en fonction du type d'afficheur (tous deux des 480x320 qui se ressemblent, achetés au même fournisseur) ******
  51. //la library correcte doit être installée dans le bon dossier ARDUINO, avec les permissions d'accès correctes !
  52.  
  53. UTFT TFT480(CTE32HR,38,39,40,41);
  54. UTFT TFT480(HX8357C,38,39,40,41);
  55. ****************************************************************************************************************************************************/
  56.  
  57. UTFT TFT480(HX8357C,38,39,40,41);
  58.  
  59. ADF4351 CarteADF4351;
  60.  
  61. AffiNombre pannel_1;
  62. AffiNombre pannel_2;
  63. AffiNombre pannel_3;
  64. AffiNombre pannel_4;
  65. AffiNombre pannel_5;
  66. AffiNombre pannel_6;
  67. AffiNombre pannel_7;
  68. AffiNombre pannel_8;
  69. AffiNombre pannel_9;
  70.  
  71. AffiNombre pannel_R0;
  72. AffiNombre pannel_R1;
  73. AffiNombre pannel_R2;
  74. AffiNombre pannel_R3;
  75. AffiNombre pannel_R4;
  76. AffiNombre pannel_R5;
  77.  
  78. LED LED1;
  79. LED LED2;
  80.  
  81. uint8_t mode = 0;
  82.  
  83. uint16_t x0, y0;
  84. uint16_t x_7seg, y_7seg;
  85.  
  86. uint32_t frequence , memo_frequence; // fréquence du signal de sortie ; en multiples de 10 kHz ; ex: 240000 représente 2400.00MHz
  87. uint32_t frq_min;
  88. uint32_t frq_max;
  89. uint32_t delta_F;
  90. uint32_t lambda; // longueur d'onde en mm
  91.  
  92. uint8_t pos; // position du multiplicateur
  93. uint8_t pos_curseur; // position du curseur (pour affichage)
  94. uint8_t switches, memo_switches; // 8 bits permettant de mémoriser l'état de 8 inverseurs
  95.  
  96. uint32_t pas;
  97. uint8_t etat;
  98. uint16_t compteur1;
  99.  
  100. uint8_t envoyer_data;
  101. float position;
  102.  
  103. String txt_hexa[33];
  104.  
  105.  
  106. /**
  107. RAPPEL variables avr-gcc (vérifiable avec le .map)
  108.  
  109. char 1 -128 .. 127 ou caractères
  110. unsigned char 1 0 .. 255
  111. uint8_t 1 (c'est la même chose que l'affreux 'unsigned char')
  112. char toto[n] n
  113. int 2 -32768 .. 32767
  114. int16_t 2 idem 'int'
  115. short int 2 pareil que int (?)
  116. unsigned int 2 0 .. 65535
  117. uint16_t 2 idem 'unsigned int'
  118. long int 4 octets -2 147 483 648 à 2 147 483 647
  119. int32_t 4 octets -> 32 bits ; idem long int
  120. long long int 8 octets -> 64 bits
  121. unsigned long int 4 octets -> 32 bits ; 0 .. 4 294 967 295 (4,2 x 10^9)
  122. uint32_t 4 32 bits ; idem 'unsigned long int'
  123. float 4
  124. double ATTENTION ! 4 octets (oui, 32 bits ! et pas 64 bits (8 octets) comme en C standard)
  125.  
  126. La déclaration char JOUR[7][9];
  127. réserve l'espace en mémoire pour 7 mots contenant 9 caractères (dont 8 caractères significatifs).
  128. **/
  129.  
  130. void init_ports (void) // ports perso
  131. {
  132. // 0 = entree, 1=sortie ; les 1 sur les pins en entrees activent les R de Pull Up (tirage à VCC)
  133.  
  134. DDRD = 0b11110000;
  135. PORTD = 0b00001111;
  136.  
  137. DDRF = 0b11101111;
  138. PORTF = 0b00000000;
  139.  
  140. DDRH = 0b11111100;
  141. PORTH = 0b00000011;
  142.  
  143. // DDRJ = 0b11111110; // PJ0 = RXD3 - PJ1 = TXD3 (attention c'est PJ1 qui est au bord du connecteur sur la carte Arduino mega2560)
  144. // PORTJ = 0b00000001;
  145.  
  146.  
  147. // REMARQUE : les ports utilisés doivent être définis dans les fichiers "AD4351.h" et "AD4351.h"
  148. }
  149.  
  150.  
  151.  
  152. void int_EXT_setup()
  153. {
  154. // voir 15. External Interrupts (ATmega2560.pdf p:109)
  155.  
  156. EICRA |= 0b00001010; // bits[1,0] = 10 -> int0 sur front descendant; bits[3,2] = 10 -> int1 sur front descendant; - p:110
  157. EIMSK |= 0b00000011; // bit[0]=1 -> INT0 enable ; bit[1]=1 -> INT1 enable - parag 15.2.3 EIMSK - p:111
  158. }
  159.  
  160.  
  161.  
  162. void init_variables(void)
  163. {
  164. envoyer_data = 0;
  165. frequence = 4000; // 40.00 MHz
  166. frq_min = 3200; // 32.00 MHz - toutefois la PLL décroche au dessous de 32.4MHz (voir led LOCK)
  167. frq_max = 440000; // 4400.00 MHz = 4.4 GHz
  168. pos_curseur = 3;
  169.  
  170. pas = 1000;
  171. switches =0;
  172. memo_switches =0;
  173.  
  174. compteur1 =0;
  175.  
  176. uint8_t x0=10;
  177. uint8_t y0=20;
  178.  
  179. delta_F = 100; // 1.00 MHz
  180. }
  181.  
  182.  
  183.  
  184.  
  185. ISR (INT0_vect)
  186. {
  187. //interruption sur front descendant sur l'entree Int0
  188. // declenchee par la rotation du codeur_ROT (1) -> +/-frequence
  189.  
  190. etat = PIND & 0b00000100;
  191.  
  192. {
  193. if (etat == 0b00000100)
  194. {
  195. if (frequence >= (frq_min + pas)) {frequence -= pas;}
  196. }
  197. else
  198. {
  199. if ( (frequence+pas) <= frq_max) {frequence += pas;}
  200. if ( (frequence+pas) > frq_max) {frequence = frq_max;}
  201. }
  202. envoyer_data = 1;
  203. }
  204. }
  205.  
  206.  
  207.  
  208. ISR (INT1_vect)
  209. {
  210. //interruption sur front descendant sur l'entree Int1
  211. // declenchee par la rotation du codeur_ROT (2) -> pas
  212. uint8_t n;
  213. etat = PIND & 0b00001000;
  214.  
  215. if (etat == 0)
  216. {
  217. if (pos_curseur > 1) {pos_curseur--;}
  218. }
  219. else
  220. {
  221. if (pos_curseur < 6) {pos_curseur++ ;}
  222. }
  223. pas=1;
  224. for (n=1; n<pos_curseur; n++) { pas *=10; }
  225. envoyer_data = 1;
  226. }
  227.  
  228.  
  229. ISR(TIMER3_COMPA_vect)
  230. { }
  231.  
  232.  
  233. void trace_ecran()
  234. {
  235. TFT480.setColor(10, 10, 5);
  236. TFT480.fillRect(0, 14, 479, 309);
  237. trace_entete();
  238.  
  239. TFT480.setBackColor(0, 0, 0);
  240. TFT480.setColor(180,180,180); // couleur de l'étiquette uniquement
  241. TFT480.setFont(BigFont);
  242.  
  243. if(mode==0)
  244. {
  245. pannel_1.init(10, 20, 210, 80,"FREQUENCE");
  246.  
  247. pannel_2.init(5, 120, 130, 50,"Lambda ");
  248. pannel_3.init(5, 180, 130, 50,"Lambda/2");
  249. pannel_4.init(5, 240, 130, 50,"Lambda/4");
  250.  
  251.  
  252. uint16_t xp1 = 145;
  253. uint16_t yp1 = 120;
  254. uint8_t dy1 = 60;
  255. pannel_5.init(xp1, yp1, 75, 50,"Div");
  256. pannel_6.init(xp1, yp1+dy1, 75, 50,"FRAC");
  257. pannel_7.init(xp1, yp1+dy1*2, 75, 50,"MOD");
  258.  
  259. pannel_8.init(xp1+80, yp1, 85, 50,"INTA");
  260.  
  261. uint16_t xp2 = 385;
  262. uint16_t yp2 = 40;
  263. uint8_t dy2 = 40;
  264. pannel_R0.init(xp2, yp2, 80, 35,"R0");
  265. pannel_R1.init(xp2, yp2+dy2, 80, 35,"R1");
  266. pannel_R2.init(xp2, yp2+dy2*2, 80, 35,"R2");
  267. pannel_R3.init(xp2, yp2+dy2*3, 80, 35,"R3");
  268. pannel_R4.init(xp2, yp2+dy2*4, 80, 35,"R4");
  269. pannel_R5.init(xp2, yp2+dy2*5, 80, 35,"R5");
  270. }
  271. else
  272. {
  273. pannel_1.init(10, 40, 210, 80,"F centrale");
  274. pannel_9.init(10, 130, 210, 35,"delta F");
  275. }
  276. }
  277.  
  278.  
  279.  
  280.  
  281. void trace_entete()
  282. {
  283. TFT480.setColor(64, 64, 64);
  284. TFT480.fillRect(0, 0, 479, 13); // bandeau en haut
  285.  
  286. TFT480.setColor(30, 30, 30);
  287. TFT480.fillRect(0, 300, 479, 319); // bandeau en bas
  288.  
  289.  
  290. // TFT480.setBackColor(64, 64, 64);
  291. TFT480.setColor(255,255,255);
  292. TFT480.print("GENE 35MHz .. 4400MHz - ADF4351", CENTER, 1);
  293.  
  294. // TFT480.setBackColor(64, 64, 64);
  295. TFT480.setColor(255,255,255);
  296. TFT480.print(version, 5, 300);
  297. TFT480.setColor(255,255,255);
  298. TFT480.print("ATmega2560 - ", 100, 300);
  299. TFT480.setColor(255,255,255);
  300. TFT480.print("Silicium628", 210, 300);
  301.  
  302. }
  303.  
  304.  
  305. void TFT_aff_ICI()
  306. {
  307. TFT480.setFont(BigFont);
  308. TFT480.setColor(255,255,255);
  309. TFT480.print("ICI", 0, 220);
  310. }
  311.  
  312.  
  313.  
  314.  
  315. void TFT_affiche_chiffre_7seg(uint8_t num)
  316. {
  317. TFT480.setFont(SevenSegNumFont);
  318. TFT480.printNumI(num, x_7seg, y_7seg);
  319. }
  320.  
  321.  
  322.  
  323. void TFT_aff_nb_form3 (uint32_t valeur, uint8_t nb_chiffres, uint8_t curseur, uint8_t R, uint8_t G, uint8_t B)
  324. {
  325. //affiche un nombre en representation decimale en separant les groupes de 3 chiffres
  326. unsigned char r ;
  327. char tbl[7];
  328. uint8_t i;
  329.  
  330. curseur=nb_chiffres +1 -curseur;
  331.  
  332. for (i=1; i<=nb_chiffres; i++)
  333. {
  334. r=valeur % 10; // modulo (reste de la division)
  335. valeur /= 10; // quotient
  336. tbl[i]=r;
  337. }
  338. for (i=1; i<= nb_chiffres; i++)
  339. {
  340. TFT480.setColor(R,G,B);
  341.  
  342. if (i==curseur)
  343. {
  344. TFT480.setColor(0,250,250);
  345. }
  346. TFT_affiche_chiffre_7seg(tbl[nb_chiffres +1 -i]);
  347. x_7seg+=30;
  348.  
  349. uint8_t m, k, u;
  350. m =nb_chiffres-6;
  351. k =nb_chiffres-3;
  352. u =nb_chiffres;
  353.  
  354. //if (i== 3)
  355. if (i== m)
  356. {
  357. TFT480.setFont(BigFont);
  358. TFT480.setColor(100,100,100);
  359. TFT480.print("M",x_7seg, y_7seg+30);
  360. x_7seg+=15;
  361. }
  362. //if (i== 6)
  363. if (i== k)
  364. {
  365. TFT480.setFont(BigFont);
  366. TFT480.setColor(100,100,100);
  367. TFT480.print("k",x_7seg, y_7seg+30);
  368. x_7seg+=15;
  369. }
  370. //if (i== 9)
  371. if (i== u)
  372. {
  373. TFT480.setFont(BigFont);
  374. TFT480.setColor(100,100,100);
  375. TFT480.print("Hz",x_7seg, y_7seg+30);
  376. }
  377. }
  378. }
  379.  
  380.  
  381. void TFT_aff_nb_form3b (uint32_t valeur, uint8_t nb_chiffres, uint8_t curseur, uint8_t R, uint8_t G, uint8_t B)
  382. {
  383. //affiche un nombre en representation decimale avec separateur 'M' à gauche des 2 chiffres de droite
  384.  
  385. unsigned char r ;
  386. char tbl[7];
  387. uint8_t i;
  388.  
  389. curseur=nb_chiffres +1 -curseur;
  390.  
  391. for (i=1; i<=nb_chiffres; i++)
  392. {
  393. r=valeur % 10; // modulo (reste de la division)
  394. valeur /= 10; // quotient
  395. tbl[i]=r;
  396. }
  397. for (i=1; i<= nb_chiffres; i++)
  398. {
  399. TFT480.setColor(R,G,B);
  400.  
  401. if (i==curseur)
  402. {
  403. TFT480.setColor(0,250,250);
  404. }
  405. TFT_affiche_chiffre_7seg(tbl[nb_chiffres +1 -i]);
  406. x_7seg+=30;
  407.  
  408. uint8_t m = nb_chiffres-2;
  409.  
  410. if (i==m)
  411. {
  412. TFT480.setFont(BigFont);
  413. TFT480.setColor(200,200,200);
  414. TFT480.print("M",x_7seg, y_7seg+30); // affichage du separateur 'M' (M comme Megahertz)
  415. x_7seg+=15;
  416. }
  417. }
  418. }
  419.  
  420.  
  421.  
  422.  
  423.  
  424. // ===================================================================================================================================================
  425.  
  426. void setup()
  427. {
  428. init_ports();
  429. init_variables();
  430.  
  431. // USART_Init();
  432.  
  433. TFT480.InitLCD();
  434. // TFT480.rotate_screen_180();
  435. TFT480.setFont(SmallFont);
  436. TFT480.clrScr();
  437.  
  438. int_EXT_setup();
  439.  
  440. CarteADF4351.init();
  441.  
  442. trace_ecran();
  443. affiche_leds();
  444.  
  445. sei(); // enable interruptions
  446. envoyer_data = 1;
  447. }
  448.  
  449.  
  450.  
  451. void affiche_leds()
  452. {
  453. LED1.init(245, 80, 0,255,0, "LOCK" );
  454. LED2.init(245, 40, 255,0,0, "ON" );
  455. }
  456.  
  457.  
  458. void calcul_lambda()
  459. {
  460. lambda = 3e7/frequence; // en mm
  461. }
  462.  
  463.  
  464.  
  465. void loop()
  466. {
  467. uint8_t r1;
  468.  
  469. while(1)
  470. {
  471. // memo_mode = mode;
  472. // memo_switches = switches;
  473.  
  474. // mode = portPIN_switch & pin_switch1; // mode = 0 ou 1 suivant la position du switch1
  475. // if ((portPIN_switch & pin_switch2) == 0) mode = 3; // =2
  476.  
  477. if ((portPIN_switch & pin_switch1) == pin_switch1) { switches |= 0b00000001; } else { switches &= 0b11111110; }
  478. if ((portPIN_switch & pin_switch2) == pin_switch2) { switches |= 0b00000010; } else { switches &= 0b11111101; }
  479. if (memo_switches != switches) { }
  480.  
  481. if ((port_PIN_ADF4351 & pin_MUXOUT) == pin_MUXOUT) { LED1.setEtat(1); } else { LED1.setEtat(0);}
  482. LED2.setEtat(1); // pour faire joli !
  483.  
  484. if(envoyer_data > 0)
  485. {
  486. envoyer_data = 0;
  487.  
  488. calcul_lambda();
  489. _delay_ms(1);
  490. if ( (switches & 0b00000010) == 0) { affiche_leds(); }
  491.  
  492. double F2;
  493. F2 = frequence / 100.0; // F2 en MHz (avec 2 décimales)
  494.  
  495. CarteADF4351.setFreq(F2);
  496.  
  497. pannel_1.affiche_frequence(frequence, 0, 100, 255); // (avec de grands chiffres 7 segments)
  498.  
  499. if (mode == 0)
  500. {
  501. uint8_t DIV = CarteADF4351.DIV;
  502. pannel_5.affiche_valeur(DIV, 4, " ");
  503.  
  504. uint16_t FRAC = CarteADF4351.FRAC;
  505. pannel_6.affiche_valeur(FRAC, 4, " ");
  506.  
  507. uint32_t MOD = CarteADF4351.MOD;
  508. pannel_7.affiche_valeur(MOD, 4, " ");
  509.  
  510. uint32_t INTA = CarteADF4351.INTA;
  511. pannel_8.affiche_valeur(INTA, 5, " ");
  512.  
  513. _delay_ms(1);
  514.  
  515.  
  516. //unite="mm";
  517. pannel_2.affiche_valeur(lambda, 4, "mm");
  518. pannel_3.affiche_valeur(lambda/2, 4, "mm");
  519. pannel_4.affiche_valeur(lambda/4, 4, "mm");
  520.  
  521. pannel_R0.affiche_HEXA(CarteADF4351.registers[0]);
  522. pannel_R1.affiche_HEXA(CarteADF4351.registers[1]);
  523. pannel_R2.affiche_HEXA(CarteADF4351.registers[2]);
  524. pannel_R3.affiche_HEXA(CarteADF4351.registers[3]);
  525. pannel_R4.affiche_HEXA(CarteADF4351.registers[4]);
  526. pannel_R5.affiche_HEXA(CarteADF4351.registers[5]);
  527. }
  528. else
  529. {
  530. pannel_9.affiche_valeur(delta_F, 4, "M ");
  531. }
  532. _delay_ms(1);
  533. }
  534.  
  535. /**
  536. if((portPIN_RAZ & pin_RAZ) == 0) // RAZ de la fréquence sur appui de l'encodeurs rotatif du haut (qui comprend un switch)
  537. {
  538. if ( (switches & 0b00000001) == 0) {frequence =0; envoyer_data += 1; }
  539.  
  540.  
  541. if ((switches & 0b00000010) == 0)
  542. { // soft reset
  543. switches == 0;
  544. TFT480.clrScr();
  545. TFT480.setColor(30, 30, 30);
  546. TFT480.fillRect(0, 150, 479, 200); // bandeau
  547. TFT480.setColor(255,0,0);
  548. TFT480.print("SOFT RESET", CENTER, 160);
  549. _delay_ms(1000);
  550. TFT480.clrScr();
  551. setup();
  552. }
  553.  
  554. }
  555. **/
  556.  
  557. _delay_ms(1);
  558.  
  559. }
  560.  
  561. }
  562.  
  563. /** ***********************************************************************************
  564. CLASS AffiNombre
  565. ***************************************************************************************/
  566.  
  567. // Constructeur
  568. AffiNombre::AffiNombre()
  569. {
  570.  
  571. }
  572.  
  573.  
  574. void AffiNombre::init(uint16_t x, uint16_t y, uint16_t dx, uint16_t dy, char txt_etiquette_i[10])
  575. {
  576. x0 = x;
  577. y0 = y;
  578.  
  579. for (int i=0; i<10; i++) {txt_etiquette[i]=txt_etiquette_i[i];}
  580. txt_etiquette[10]='\0'; // zero terminal
  581.  
  582. TFT480.setColor(10, 10, 5);
  583. TFT480.fillRect(x0, y0, x0+dx, y0+dy);
  584. TFT480.setColor(100, 100, 100);
  585. TFT480.drawRect(x0, y0, x0+dx, y0+dy);
  586. TFT480.setBackColor(0, 0, 0);
  587. TFT480.setColor(180,180,180); // couleur de l'étiquette
  588. TFT480.setFont(BigFont);
  589. TFT480.print(txt_etiquette, x0, y0); // Etiquette
  590. }
  591.  
  592.  
  593. void AffiNombre::affiche_frequence(uint32_t F_i, uint8_t R, uint8_t G, uint8_t B)
  594. {
  595. x_7seg = x0+10;
  596. y_7seg = y0+20;
  597. uint8_t p2;
  598. p2 = pos_curseur;
  599. TFT_aff_nb_form3b (F_i, 6, p2, R, G, B); // 6 chiffres significatifs
  600. }
  601.  
  602.  
  603.  
  604. void AffiNombre::affiche_valeur(uint16_t valeur, uint8_t nb_chiffres, char txt_unite_i[3])
  605. {
  606. for (int i=0; i<2; i++) {txt_unite[i]=txt_unite_i[i];}
  607. txt_unite[2]='\0'; // zero terminal
  608.  
  609. TFT480.setColor(220,220,0);
  610. TFT480.setFont(BigFont);
  611. TFT480.printNumI(valeur, x0+5,y0+20, nb_chiffres, ' ');
  612. TFT480.setFont(BigFont);
  613. TFT480.print(txt_unite, x0+80,y0+20); // ex : mm, kHz, etc...
  614. // TFT480.print("ab", x0+80,y0+20); // ex : mm, kHz, etc...
  615. }
  616.  
  617.  
  618. void AffiNombre::affiche_HEXA(uint32_t valeur)
  619. {
  620. // affiche un nombre en representation hexadécimale
  621. // 16 nb_signes hexa max
  622.  
  623. uint8_t r;
  624. uint8_t i;
  625.  
  626. char tbl[9];
  627. char signes[17] = "0123456789ABCDEF";
  628.  
  629. for (i=0; i<8; i++)
  630. {
  631. r= valeur % 16; // modulo (reste de la division)
  632. valeur /= 16; // quotient
  633. tbl[7-i]=signes[r];
  634. };
  635. tbl[8]='\0';
  636.  
  637. TFT480.setColor(0,255,255);
  638. TFT480.setFont(SmallFont);
  639. TFT480.print(tbl, x0+10,y0+15);
  640. }
  641.  
  642.  
  643.  
  644. /** ***********************************************************************************
  645. CLASS LED
  646. ***************************************************************************************/
  647. // Constructeur
  648. LED::LED()
  649. {
  650.  
  651. }
  652.  
  653.  
  654. void LED::init(uint16_t x_i, uint16_t y_i, uint8_t R_i, uint8_t G_i, uint8_t B_i, char txt_etiquette[5])
  655. {
  656. x=x_i;
  657. y=y_i;
  658.  
  659. R=R_i;
  660. G=G_i;
  661. B=B_i;
  662.  
  663. TFT480.setColor(40,40,40); // gris
  664. TFT480.fillCircle(x, y, 10); // dessine la led éteinte (cercle plein grisé)
  665.  
  666. TFT480.setColor(180,180,180);
  667. TFT480.setFont(BigFont);
  668. TFT480.print(txt_etiquette, x+15, y-8); // Etiquette
  669. }
  670.  
  671.  
  672.  
  673. void LED::setEtat(uint8_t etat_i)
  674. {
  675. if(etat_i == 0)
  676. {
  677. TFT480.setColor(40,40,40); // gris
  678. TFT480.fillCircle(x, y, 10);
  679. }
  680. if(etat_i == 1)
  681. {
  682. TFT480.setColor(R,G,B); // couleur choisie
  683. TFT480.fillCircle(x, y, 10);
  684. }
  685. }
  686.  



Le fichier GeneUHF_ADF4351.h

CODE SOURCE en c
  1.  
  2. #ifndef GENEUHF_ADF4351_H
  3. #define GENEUHF_ADF4351_H
  4.  
  5. #include <stdint.h>
  6.  
  7.  
  8. class AffiNombre
  9. {
  10. public:
  11.  
  12. uint16_t x0;
  13. uint16_t y0;
  14.  
  15. AffiNombre();
  16.  
  17. void init(uint16_t x, uint16_t y, uint16_t dx, uint16_t dy,char txt_etiquette[10]);
  18. // void setTexte(char[3]);
  19. void affiche_frequence(uint32_t F_i, uint8_t R, uint8_t G, uint8_t B);
  20. void affiche_valeur(uint16_t valeur, uint8_t nb_chiffres, char txt_unite_i[3]);
  21. void affiche_HEXA(uint32_t valeur);
  22.  
  23.  
  24. private:
  25. char txt_etiquette[10];
  26. char txt_unite[3];
  27.  
  28. };
  29.  
  30.  
  31. class LED
  32. {
  33. public:
  34.  
  35. uint16_t x;
  36. uint16_t y;
  37.  
  38. uint8_t R;
  39. uint8_t G;
  40. uint8_t B;
  41.  
  42. LED();
  43.  
  44. void init(uint16_t x_i, uint16_t y_i, uint8_t R_i, uint8_t G_i, uint8_t B_i, char txt_etiquette[5]);
  45. void setEtat(uint8_t etat_i); // = 0 ou 1
  46.  
  47. private:
  48. char txt_etiquette[5];
  49.  
  50. };
  51.  
  52.  
  53. /***** autres declarations ******/
  54.  
  55. void init_ports (void);
  56. void init_variables(void);
  57. void trace_ecran();
  58. void trace_entete();
  59. void TFT_affiche_chiffre_7seg(uint8_t num);
  60. void TFT_aff_nb_form3 (uint32_t valeur, uint8_t nb_chiffres, uint8_t curseur, uint8_t R, uint8_t G, uint8_t B);
  61. void TFT_aff_nb_form3b (uint32_t valeur, uint8_t nb_chiffres, uint8_t curseur, uint8_t R, uint8_t G, uint8_t B);
  62. void TFT_aff_nb_form3c (uint32_t valeur, uint8_t nb_chiffres, uint8_t curseur, uint8_t R, uint8_t G, uint8_t B);
  63.  
  64.  
  65. void calcul_lambda();
  66. void affiche_leds();
  67.  
  68.  
  69. #endif
  70.  

10 La class ADF4351 version 1

CODE SOURCE en c
  1. /**
  2. ADF4351-628v2.cpp
  3. Firmware pour piloter un circuit DDS : ADF4351
  4. avec un AVR atmega2560
  5. **/
  6.  
  7. /*
  8. Ce fichier source "GeneUHF_ADF4351.cpp" est libre, vous pouvez le redistribuer et/ou le modifier selon
  9. les termes de la Licence Publique Générale GNU.
  10.  
  11. Un grand merci à Alain Fort F1CJN feb 2,2016
  12. pour son travail :
  13.  
  14. "ADF4251 and Arduino
  15. update march 7, 2016 (ROBOT V1.1 and V1.0)"
  16. -http://f6kbf.free.fr/html/ADF4351%20and%20Arduino_Fr_Gb.htm
  17. -http://f6kbf.free.fr/html/ADF4351_LCD_07032016.zip
  18.  
  19. dont je me suis servi pour créer cette objet (class) en C++
  20. J'ai aussi utilisé mon propre travail effectué pour les DDS AD9850 et AD9951
  21.  
  22. Silicium628 - Juillet 2018
  23.  
  24. */
  25.  
  26. #include <avr/io.h>
  27. #include <util/delay.h>
  28. #include "ADF4351-628v2.h"
  29.  
  30. #define bitSet(value, bit) ((value) |= (1UL << (bit))) // 1UL signifie la valeur 1 au format Unsigned Long
  31. #define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
  32. #define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))
  33.  
  34. // Constructeur
  35. ADF4351::ADF4351() { }
  36.  
  37.  
  38. void ADF4351::init()
  39. {
  40. set_LE();
  41. Write_All_Register();
  42. PFDRFout=25;
  43. RFint=7000;
  44. RFout = RFint/100 ; // fréquence de sortie
  45. OutputChannelSpacing = 0.01; // Pas de fréquence = 10kHz
  46. }
  47.  
  48.  
  49. void ADF4351::mute(uint8_t valeur)
  50. {
  51. if (valeur == 1) {port_ADF4351 &= ~pin_CE;} else {port_ADF4351 |= pin_CE;}
  52. }
  53.  
  54.  
  55.  
  56. void ADF4351::impulse_clk()
  57. {
  58. //_delay_us(1); // temps necessaire à la stabilisation du niveau envoyé
  59. port_ADF4351 |= pin_CLK;
  60. _delay_us(1);
  61. port_ADF4351 &= ~pin_CLK; // l'operateur "~" donne le complement (inverse des bits). ne pas confondre avec l'opérateur "!"
  62. _delay_us(1);
  63. }
  64.  
  65.  
  66. void ADF4351::impulse_LE()
  67. {
  68. // _delay_us(10);
  69. port_ADF4351 |= pin_LE;
  70. _delay_us(1);
  71. port_ADF4351 &= ~pin_LE;
  72. _delay_us(1);
  73. }
  74.  
  75.  
  76. void ADF4351::set_LE()
  77. {
  78. port_ADF4351 |= pin_LE;
  79. }
  80.  
  81.  
  82. void ADF4351::reset_LE()
  83. {
  84. port_ADF4351 &= ~pin_LE;
  85. }
  86.  
  87.  
  88. void ADF4351::set_DATA()
  89. {
  90. port_ADF4351 |= pin_DATA;
  91. }
  92.  
  93.  
  94. void ADF4351::reset_DATA()
  95. {
  96. port_ADF4351 &= ~pin_DATA;
  97. }
  98.  
  99.  
  100.  
  101. void ADF4351::out1octet(uint8_t octet_i)
  102. {
  103. // MSB first (= mode par defaut)
  104. uint8_t i;
  105. uint8_t masque;
  106.  
  107. for (i=0; i < 8; i++)
  108. {
  109. masque = 0b10000000 >> i; // le '1' se deplace de gauche a droite
  110. if ( (octet_i & masque) != 0) { port_ADF4351 |= pin_DATA; } else { port_ADF4351 &= ~pin_DATA; }
  111. impulse_clk();
  112. }
  113. }
  114.  
  115.  
  116.  
  117. void ADF4351::Write_Register32(const uint32_t data) //Programme un registre 32bits
  118. {
  119. uint8_t n; // numéro du registre (0..5) codé par les trois bits de poids faible
  120. uint8_t octet_i;
  121.  
  122. n = data && 0b00000111;
  123. if (data != memo_registers[n]) // on ne programme le registre que si son contenu doit changer. (cela accelère le programme)
  124. {
  125. memo_registers[n] = data;
  126. reset_LE();
  127. for (int i = 3; i >= 0; i--) // boucle sur 4 x 8bits
  128. {
  129. octet_i=(data >> 8 * i) & 0xFF; // décalage et masquage de l'octet
  130. out1octet(octet_i);
  131. }
  132. impulse_LE();
  133. // LE = Load Enable. When LE goes high, the data stored in the 32-bit shift register is loaded
  134. // into the register that is selected by the three control bits (ce sont les trois bits de poids faible)
  135. }
  136. }
  137.  
  138.  
  139. void ADF4351::Write_All_Register() // Programme tous les registres de l'ADF4351
  140. {
  141. for (int i = 5; i >= 0; i--) // programmation ADF4351 en commencant par R5
  142. {
  143. Write_Register32(registers[i]);
  144. }
  145. }
  146.  
  147.  
  148. void ADF4351::setFreq(double frequence)
  149. {
  150. RFout = frequence;
  151.  
  152. registers[4] = 0x8C803C; // cette valeur n'est pas arbitraire. En particulier les bits 20,21,22 doivent être =0.
  153. // ce qui est le cas : 0x8C803C = 0b 1000 1100 1000 0000 0011 1100
  154. if (RFout >= 2200) { DIV = 1; }
  155. else if (RFout >= 1100) { DIV = 2; registers[4] += (1UL<<20); } // 1UL signifie la valeur 1 au format Unsigned Long en memoire (uint32_t -> 4octets = 32 bits)
  156. else if (RFout >= 550) { DIV = 4; registers[4] += (2UL<<20); }
  157. else if (RFout >= 275) { DIV = 8; registers[4] += (3UL<<20); }
  158. else if (RFout >= 137.5) { DIV = 16; registers[4] += (4UL<<20); }
  159. else if (RFout >= 68.75) { DIV = 32; registers[4] += (5UL<<20); }
  160. else { DIV = 64; registers[4] += (6UL<<20); }
  161.  
  162. INTA = (RFout * DIV) / PFDRFout;
  163. MOD = (PFDRFout / OutputChannelSpacing);
  164. FRACF = (((RFout * DIV) / PFDRFout) - INTA) * MOD;
  165. FRAC = round(FRACF); // On arrondit le résultat
  166.  
  167. registers[0] = 0;
  168. registers[0] = INTA << 15; // décalage de 15 rangs pour ranger cette valeur (16 bits) à sa place dans R0 ; voir p:15 du pdf
  169. registers[0] = registers[0] + (FRAC << 3); // decalage de 3 rangs vers la gauche pour le rentrer dans les bits [DB3..DB14]
  170.  
  171. registers[1] = 0;
  172. registers[1] = MOD << 3; // decalage de 3 pour le rentrer dans les bits [DB3..DB14]
  173. registers[1] = registers[1] + 1 ; // ajout de l'adresse "001"
  174. bitSet (registers[1], 27); // PR1-> Prescaler = 8/9 ; voir p:16 du pdf
  175.  
  176. bitSet (registers[2], 28); // Digital lock == "110" sur b28 b27 b26
  177. bitSet (registers[2], 27); // digital lock
  178. bitClear (registers[2], 26); // digital lock
  179.  
  180. // bitSet (registers[4], 10); //MTLD: Supply current to the RF output stage to be shut down until the part achieves lock
  181.  
  182. Write_All_Register(); // Programme tous les registres de l'ADF4351
  183. }
  184.  
  185.  
  186.  
  187.  



Le fichier ADF4351-628v2.h

CODE SOURCE en c
  1. /**
  2. ADF4351-628v2.h
  3.  
  4. Firmware pour piloter un circuit DDS : ADF4351
  5. avec un AVR atmega2560
  6.  
  7. **/
  8.  
  9.  
  10. #ifndef ADF4351_628V2_H
  11. #define ADF4351_628V2_H
  12.  
  13. #pragma once
  14.  
  15. #define port_ADF4351 PORTF // sur portF
  16. #define port_PIN_ADF4351 PINF
  17.  
  18. #define pin_CLK 0b10000000 // (sortie) sur portF
  19. #define pin_DATA 0b01000000 // (sortie) sur portF
  20. #define pin_LE 0b00100000 // (sortie) sur portF
  21. #define pin_MUXOUT 0b00010000 // (*ENTREE*) sur PINF
  22. #define pin_CE 0b00001000 // (sortie) sur portF
  23.  
  24. class ADF4351
  25. {
  26.  
  27. public:
  28.  
  29. uint32_t registers[6] = {0x4580A8, 0x80080C9, 0x4E42, 0x4B3, 0xBC803C, 0x580005};
  30. uint32_t memo_registers[6] = {0, 0, 0, 0, 0, 0};
  31.  
  32. uint32_t RFint, RFintold, INTA, RFcalc, PDRFout, MOD, FRAC;
  33.  
  34. double RFout, REFin, PFDRFout, OutputChannelSpacing, FRACF;
  35.  
  36. double RFoutMin = 35; // 35 MHz
  37. double RFoutMax = 4400; // 4400MHz = 4.4 GHz
  38. double REFinMax = 250;
  39. double PDFMax = 32;
  40.  
  41. uint8_t DIV;
  42. uint8_t lock=2;
  43.  
  44. ADF4351();
  45.  
  46. void init();
  47.  
  48. void reset_LE();
  49. void set_LE();
  50. void reset_DATA();
  51. void set_DATA();
  52. void mute(uint8_t valeur);
  53. void impulse_clk();
  54. void impulse_LE();
  55. void out1octet(uint8_t octet_i);
  56. void Write_Register32(const uint32_t data); //Programme un registre 32bits
  57. void Write_All_Register(); // Programme tous les registres de l'ADF4351
  58. void setFreq(double frequence); // en MHz (avec decimales)
  59.  
  60. private:
  61.  
  62. bool _powerdown, _auxEnabled, _rfEnabled, _feedbackType;
  63.  
  64. };
  65.  
  66. #endif
  67.  

Un grand merci à Alain Fort F1CJN feb 2,2016
pour son travail :

"ADF4251 and Arduino
update march 7, 2016 (ROBOT V1.1 and V1.0)"
-http://f6kbf.free.fr/html/ADF4351%20and%20Arduino_Fr_Gb.htm
-http://f6kbf.free.fr/html/ADF4351_LCD_07032016.zip

dont je me suis servi pour créer cette objet (class) en C++
J'ai aussi utilisé mon propre travail effectué pour les DDS AD9850 et AD9951

Silicium628 - Juillet 2018

11 Détail de l'affichage

Sont affichés :
  • La fréquence
  • La longueur d'onde lambda correspondante
  • Lambda/2 et lambda/4
  • Les principaux paramètres envoyés à l'ADF4351 (DIV, FRAC, MOD, INTA)
  • Le contenu des six registres de l'ADF4351 affichés en hexadécimal
Le tout mis à jour en temps réel bien entendu.
Le chiffre en surbrillance (pour la fréquence) est celui qui sera incrémenté/décrémenté par l'encodeur incrémental du haut.
l'encodeur du bas permet de choisir le chiffre à modifier.

12 ce que je compte faire ensuite...

19 juillet 2018: Nous voici en possession d'un générateur de fréquence couvrant une gamme très étendue et ayant son affichage propre en haute résolution et en couleur. Il est donc possible d'en faire un wobuloscope autonome, c'est à dire un wobulateur possédant son propre afficheur pour tracer la courbe de réponse en fréquence d'un filtre, UHF qui plus est.

Il faut toutefois prendre en considération que le circuit ADF4351 ne fournit un signal sinusoïdal en mode fondamental que pour les fréquences s'étendant de 2,2GHz à 4,4GHz. Les fréquences plus basses sont obtenues par des diviseurs logiques et ne sont donc pas sinusoïdales pures mais de forme d'onde plutôt "carrée" (ou triangulaire d'après ce que j'ai constaté sur un oscillo 100MHz et au vu de la répartition des harmoniques).
En conséquence ce qui sera réalisable simplement sera un wobuloscope pour la gamme 2GHz->4GHz, ce qui n'est pas si mal et bien pratique pour expérimenter avec le 2.4GHz par exemple pour lequel les applications ne manquent pas (WiFi, Bluetooth, télécommande RC, radioamateurs...)

13 Ce que J'AI FAIT ensuite : Le WOBULATEUR

20 juillet 2018: Il s'agit du même appareil : un simple inverseur (celui de gauche) permet de passer du mode générateur UHF simple au mode wobulateur (c'est à dire générateur à fréquence glissante (par petit sauts) et affichage de la courbe de réponse obtenue , en temps réel). Le tout autonome (pas besoin de PC ou de portable ni de smartphone...)

La partie analogique (redresseur à diodes + ampli) se connecte au générateur par une simple micro-prise à trois contacts au pas de 1/10 de pouce (2.54mm) (GND, +5V et signal de réponse à très basse fréquence). Le signal UHF, lui, emprunte le connecteur SMA comme il se doit.

Comme on peut le voir sur la photo, la partie analogique n'est pas encore implantée sur un circuit imprimé, ça ne saurait tarder... Mais la programmation du soft est terminée, elle est 100% fonctionnelle.

14 Le nouveau schéma, géné UHF + wobulateur

La partie analogique, qui comprenait deux diodes UHF assurant la détection de l'amplitude du signal, se voit désormais dotée d'un détecteur d'enveloppe logarithmique qui peut fonctionner jusqu'à 8GHz. Une cellule RC lisse un peu le signal avant d'attaquer l'ADC de l'ATmega2560.

Le détecteur AD8318 est constitué de plusieurs ampli SHF en cascade. Il peut ainsi offrir une dynamique de 70dB avec une résolution de 1 dB (voir le datasheet). Mais attention, il ne s'agit pas de lui appliquer un signal de 70dBm sous peine de faire de la soudure à l'arc!!!
Son domaine de fonctionnement va de -60dBm (oui avec un signe - devant) jusqu'à 0dBm. Autant dire qu'il faut blinder soigneusement toute la partie UHF pour ne pas obtenir un récepteur TNT, GSM, 3G, 4G, WIFI, BlueTooth, adaptateurs secteurs non anti-parasités, alim de certaines lampes à LED en 230V) etc...

15 Réponse fréquentielle d'un quartz 100MHz

Le trait rouge au centre du réticule indique la fréquence de 100.00 MHz
Le "pas WOB = 10" signifie 10 kHz / carreau. (oui, kilohertz, pas mégahertz !)
La largeur totale du graphe représente 100kHz.
Toutes ces valeurs sont modifiables avec les encodeurs rotatifs (fréquence centrale et pas).

Remarque : La seconde résonance sur la droite est vraisemblablement une image fantôme provoquée par une harmonique indésirable proche de la porteuse, qui lors du balayage passe à la fréquence de 100.00MHz. J'avais dès le départ repéré ces harmoniques et conclu que le wobulateur ne serait fiable que pour la gamme 2GHz à 4GHz pour laquelle le signal de sortie est sinusoïdal, directement issu du VCO de l'ADF4351. Quoi qu'il en soit, il va falloir expérimenter un peu plus avant de conclure quoi que ce soit.

21 juillet 2018: Après examen du signal 100MHz à l'analyseur de spectre, je n'ai pas détecté le moindre signal parasite à 40kHz de la porteuse. Doit-on conclure que nous observons une réelle résonance secondaire du quartz à 100.040 MHz ? Il faut toutefois noter qu'on côtoie la limite de résolution spectrale de l'analyseur analogique HM5010... C'est un peu comme apercevoir une planète extra-solaire près de son étoile... Comme quoi notre wobulateur fait beaucoup mieux qu'un "vrai" analyseur de spectre ! (un peu has-been du point de vue des caractéristiques le HM5010, c'est vrai).
23 juillet 2018: Si l'on pousse les investigations le long d'une plus grande plage de fréquences, on s'aperçoit que le quartz de 100MHz présente des résonnances pour les fréquences suivantes :
  • 60 MHz
  • 100 MHz
  • 140 MHz
  • 180 MHz
d'amplitudes décroissantes à mesure que la fréquence augmente. Il s'agit donc d'un quartz "overtone" concernant la fréquence marquée dessus. Les fréquences de résonance sont espacées de 20MHz et sont tout simplement les harmoniques (multiples) impaires de la fréquence fondamentale = 20MHz.
  • 60 MHz = 20 x 3
  • 100 MHz = 20 x 5
  • 140 MHz = 20 x 7
  • 180 MHz = 20 x 9
Tout cela il fallait s'en douter, les quartz de fréquences > 30MHz sont en principe des overtones, au contraire des résonateur à onde de surface (SAW pour Surface Acoustic Wave).

Remarque :

Pourquoi un quartz résonne électriquement sur des multiples impairs de sa fréquence fondamentale, et pas (aussi) sur des multiples pairs ?
Lorsqu'un quartz est sollicité électriquement un régime d'ondes mécaniques stationnaires s'établit, il vibre. Il apparaît des ventres et des nœuds de vibrations. De par sa constitution les molécules qui constituent le quartz sont piézoélectriques c'est à dire qu'elles font apparaître une dissymétrie spatiale de leurs charges électriques internes lorsqu'elle subissent une contrainte mécanique (pression). Toutes ces charges s'additionnent suivant l'axe d'oscillation de sorte que la résultante entre le potentiel électrique correspondant à un ventre et celui correspondant à un nœud est fondamentalement dissymétrique. La tension aux bornes, c'est à dire entre les faces est maximale lorsque la distance entre ces faces est un multiple de celle séparant un nœud d'un ventre. Deux nœuds ou deux ventres sont distant d'une demi longueur d'onde. Et celle séparant un ventre d'un nœud vaut lambda/4. ( Voir mon exposé ICI.).

La résonance électrique (tension alternative maximale aux bornes) se produit donc lorsque l'épaisseur du quartz est égale à lambda/4 ou à un multiple de lambda/4. Ce qui s'écrit :

E = (2n+1) * lambda/4
(2n+1) étant un entier impair (1, 3, 5, 7...)
d'où :
lambda = 4E / (2n+1)
F = v/lambda = (2n+1)v / 4E
avec v : vitesse de propagation de l'onde "acoustique" dans le quartz
F=v/4E...3v/4E...5v/4E...
en appelant F0 = v/4E la plus basse de ces fréquences, dite fréquence fondamentale, nous obtenons la suite des harmoniques :
F= Fo...3Fo...5Fo...7Fo... etc...
Des harmoniques de rang pair ne feraient tout simplement pas apparaître de différence de potentiel entre les faces (la répartition des charges étant alors symétrique), donc pas d'effet piézoélectrique détectable.

Oui mais cela n'explique toujours pas la (les) petite(s) résonance(s) secondaire(s) à 40kHz de la principale.

16 Le programme version géné + wobulateur

CODE SOURCE en c
  1. /*
  2. Firmware pour piloter une carte générateur de signaux ADF4351
  3. et une carte Uno Mega2560 + afficheur 480x320 non tactile.
  4. par Silicium628.
  5.  
  6. CE fichier source "Gene_Wobul_double_ADF4351.cpp" est libre, vous pouvez le redistribuer et/ou le modifier selon
  7. les termes de la Licence Publique Générale GNU.
  8.  
  9. En ce qui concerne certaines bibliothèques "incluses" (par la directive "#include"), issues du domaine "Arduino", il faut voir au cas par cas.
  10. En particulier la library "UTFT" n'est pas libre, il y a un copyright dans l'entête, que je cite:
  11. "UTFT.h - Arduino/chipKit library support for Color TFT LCD Boards Copyright (C)2010-2014 Henning Karlsen. All right reserved"
  12. Fin de citation.
  13. */
  14.  
  15.  
  16. //================================
  17. #define version "v9.1"
  18. //================================
  19.  
  20. #include "Gene_Wobul_ADF4351_v9_1.h"
  21.  
  22.  
  23. #include <avr/io.h>
  24. #include <stdint.h>
  25. #include <stdlib.h>
  26. #include <util/delay.h>
  27. #include <math.h>
  28.  
  29. #include "UTFT.cpp"
  30. #include "uart2560_628.c"
  31. #include "ADF4351-628v5.h";
  32. #include <EEPROM.h>
  33.  
  34. #define portPIN_switch0 PINH
  35. #define portPIN_switch1 PINH
  36. #define portPIN_switch2 PINJ
  37. #define portPIN_switch3 PINH
  38. #define portPIN_switch4 PINH
  39.  
  40. #define pin_switch0 0b00000001
  41. #define pin_switch1 0b00000010
  42. #define pin_switch2 0b00000001
  43. #define pin_switch3 0b00001000
  44. #define pin_switch4 0b00010000
  45.  
  46. extern uint8_t SmallFont[];
  47. extern uint8_t BigFont[];
  48. extern uint8_t SevenSegNumFont[];
  49.  
  50.  
  51. UTFT TFT480(HX8357C,38,39,40,41);
  52.  
  53. int polarite = -1;
  54.  
  55. ADF4351 CarteADF4351_A;
  56.  
  57. AffiRect Affi_1a;
  58. AffiRect Affi_1b;
  59. AffiRect Affi_2;
  60. AffiRect Affi_3;
  61. AffiRect Affi_4;
  62. AffiRect Affi_5;
  63. AffiRect Affi_6;
  64. AffiRect Affi_7;
  65. AffiRect Affi_8;
  66. AffiRect Affi_9;
  67. AffiRect Affi_10;
  68. AffiRect Affi_11;
  69. AffiRect Affi_12;
  70. AffiRect Affi_13;
  71. AffiRect Affi_14;
  72.  
  73. AffiRect Affi_R0;
  74. AffiRect Affi_R1;
  75. AffiRect Affi_R2;
  76. AffiRect Affi_R3;
  77. AffiRect Affi_R4;
  78. AffiRect Affi_R5;
  79.  
  80. AffiV AffiV_01;
  81. AffiV AffiV_02;
  82.  
  83. LED LED_a;
  84. LED LED_b;
  85.  
  86. LED LED0;
  87. LED LED1;
  88. LED LED2;
  89. LED LED3;
  90. LED LED4;
  91.  
  92. uint8_t couleur[3];
  93. uint8_t rouge[3]={255, 0, 0};
  94. uint8_t orange[3]={245, 121, 0};
  95. uint8_t jaune[3]={255, 255, 0};
  96. uint8_t jaune_sombre[3]={220, 220, 0};
  97. uint8_t jaune_tres_sombre[3]={100, 100, 0};
  98. uint8_t vert[3]={0, 255, 0};
  99. uint8_t cyan[3]={0, 255, 255};
  100. uint8_t cyan_sombre[3]={0, 200, 200};
  101. uint8_t bleu_pale[3]={160, 240, 255};
  102. uint8_t bleu_clair[3]={0, 100, 255};
  103. uint8_t bleu[3]={0, 0, 255};
  104. uint8_t violet[3]={200, 0, 255};
  105. uint8_t noir[3]={0, 0, 0};
  106. uint8_t gris[3]={150, 150, 150};
  107. uint8_t blanc[3]={255, 255, 255};
  108.  
  109. uint8_t couleur_gain[3];
  110.  
  111. uint8_t switches;
  112. uint8_t memo_switches;
  113.  
  114. uint8_t SW_0;
  115. uint8_t SW_1;
  116. uint8_t SW_2;
  117. uint8_t SW_3;
  118. uint8_t SW_4;
  119.  
  120. parametres params1;
  121. int addr_params1=0; // en EEprom
  122.  
  123. uint8_t mode_GENE = 1;
  124. uint8_t mode_WOB0 = 0;
  125. uint8_t mode_WOB1 = 0;
  126. uint8_t mode_saisie_freq_WOB = 1;
  127. uint8_t mode_saisie_offset = 0 ;
  128. uint8_t mode_rapide = 0;
  129.  
  130. uint16_t x0, y0;
  131. uint16_t x_7seg, y_7seg;
  132.  
  133. uint32_t memo_frequence; // fréquence du signal de sortie ; en multiples de 10 kHz
  134. uint32_t frq_min;
  135. uint32_t frq_max;
  136. float F_mark;
  137. float lambda; // longueur d'onde en cm
  138. float F_affi_min, F_affi_max;
  139.  
  140. uint8_t DIV;
  141. uint16_t FRAC;
  142. uint32_t MOD;
  143. uint32_t INTA;
  144.  
  145. uint32_t pas_frq; // pas de saisie de la fréquence principale
  146. uint32_t kHz_div; // pas de wobulation
  147. uint32_t facteurs_mult[13] = {1, 2 , 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000};
  148.  
  149. uint8_t dB_div;
  150.  
  151. uint8_t etat;
  152. uint16_t compteur1;
  153. uint8_t raffraichir;
  154.  
  155. uint8_t envoyer_data;
  156. uint8_t stop =0;
  157. float position;
  158.  
  159. uint16_t valeur_lue; // par le convertisseur ADC
  160. uint8_t depassement;
  161.  
  162. uint32_t EE_adrs_freq0;
  163.  
  164. uint16_t tableau_acq[470];
  165.  
  166.  
  167. /**
  168. RAPPEL variables avr-gcc
  169.  
  170. TYPE nb octets
  171. char 1 -128 .. 127 (ou caractères)
  172. unsigned char 1 0 .. 255
  173. uint8_t 1 (c'est la même chose que l'affreux 'unsigned char')
  174. char toto[n] n
  175. int 2 -32768 .. 32767
  176. int16_t 2 idem 'int'
  177. short int 2 pareil que int (?)
  178. unsigned int 2 0 .. 65535
  179. uint16_t 2 idem 'unsigned int'
  180. long int 4 octets -2 147 483 648 à 2 147 483 647
  181. int32_t 4 octets -> 32 bits ; idem long int
  182. long long int 8 octets -> 64 bits
  183. unsigned long int 4 octets -> 32 bits ; 0 .. 4 294 967 295 (4,2 x 10^9)
  184. uint32_t 4 32 bits ; idem 'unsigned long int'
  185. float 4
  186. double ATTENTION ! 4 octets (oui, 32 bits ! et pas 64 bits (8 octets) comme en C standard)
  187.  
  188. La déclaration char JOUR[7][9];
  189. réserve l'espace en mémoire pour 7 mots contenant 9 caractères (dont 8 caractères significatifs).
  190. **/
  191.  
  192. void init_ports (void)
  193. {
  194. // 0 = entree, 1=sortie ; les 1 sur les pins en entrees activent les R de Pull Up (tirage à VCC)
  195.  
  196. DDRD = 0b11110000;
  197. PORTD = 0b00001111;
  198.  
  199. DDRF = 0b11101110;
  200. PORTF = 0b00000000;
  201.  
  202. DDRJ = 0b11111100;
  203. PORTJ = 0b00000011;
  204.  
  205. DDRH = 0b11100100;
  206. PORTH = 0b00011011;
  207.  
  208. // REMARQUE : les ports et pins utilisés par le circuit ADF4351 sont définis dans les fichiers "ADF4351-628v{x}.h"
  209. }
  210.  
  211.  
  212. /**
  213. The ADC module contains a prescaler, which generates an acceptable ADC clock fre-
  214. quency from any CPU frequency above 100 kHz. The prescaling is set by the ADPS bits
  215. in ADCSRA
  216. **/
  217.  
  218. void InitADC()
  219. {
  220. //SFIOR = 0b10000000; // ADTS2,1,0 = 100 -> ADC declenché par Timer0 Overflow - p:218
  221.  
  222. /** ========= REGITRE ADMUX ======= **/
  223.  
  224. ADMUX = 0b11000000;
  225. //Bit 7:6 = ADC Reference Selection = 11 -> Internal 2.56V Voltage Reference with external capacitor at AREF pin - p:281 du datasheet ATmega2560
  226. //Bits 0:5 - Analog Channel Selection Bits et gain éventuel - p282
  227. // ici: Select pin ADC0 using MUX avec ref tension 2.56V interne - pas de gain.
  228.  
  229. /** ========= REGITRE ADCSRA ======= **/
  230. // voir p:285
  231. ADCSRA = 0b10000101; // Activate ADC; Prescaler=f/32; bit5=0 -> Auto-Trigger desable
  232.  
  233. // bits [0..2] = ADPS -> Prescaler
  234. // ADPS = 010; Prescaler=f/4 gamme 8-90 kHz
  235. // ADPS = 011; Prescaler=f/8 gamme 4-56 kHz
  236. // ADPS = 100; Prescaler=f/16 gamme 2-31 kHz
  237. // ADPS = 101; Prescaler=f/32 gamme 1-15 kHz
  238. // ADPS = 110; Prescaler=f/64 gamme 300Hz- 8kHz
  239. // ADPS = 111; Prescaler=f/128 gamme 200Hz- 4300 Hz
  240.  
  241. //bit5 = ADATE (ADC Auto Trigger Enable)
  242. //Bit7 – ADEN: ADC Enable Writing this bit to one enables the ADC. By writing it to zero, the ADC is turned off.
  243.  
  244. }
  245.  
  246. template <class T> void EEPROM_write_Struct(int ee, const T& value)
  247. {
  248. const byte* p = (const byte*)(const void*)&value;
  249. for (int i = 0; i < sizeof(value); i++) { EEPROM.write(ee++, *p++); }
  250. }
  251.  
  252.  
  253. template <class T> void EEPROM_read_Struct(int ee, T& value)
  254. {
  255. byte* p = (byte*)(void*)&value;
  256. for (int i = 0; i < sizeof(value); i++) { *p++ = EEPROM.read(ee++); }
  257. }
  258.  
  259.  
  260.  
  261. void save_params_to_EEPROM()
  262. {
  263. EEPROM_write_Struct(addr_params1, params1);
  264.  
  265. }
  266.  
  267.  
  268. void load_params_from_EEPROM()
  269. {
  270. EEPROM_read_Struct(addr_params1, params1);
  271. }
  272.  
  273.  
  274. int freeRam()
  275. {
  276. extern int __heap_start, *__brkval;
  277. int v;
  278. return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
  279. }
  280.  
  281.  
  282. void int_EXT_setup()
  283. {
  284. // voir 15. External Interrupts (ATmega2560.pdf p:109)
  285.  
  286. EICRA |= 0b00001010; // bits[1,0] = 10 -> int0 sur front descendant; bits[3,2] = 10 -> int1 sur front descendant; - p:110
  287. EIMSK |= 0b00000011; // bit[0]=1 -> INT0 enable ; bit[1]=1 -> INT1 enable - parag 15.2.3 EIMSK - p:111
  288. }
  289.  
  290.  
  291. void setCouleur(uint8_t *couleur_i, uint8_t R, uint8_t V,uint8_t B)
  292. {
  293. couleur_i[0]=R;
  294. couleur_i[1]=V;
  295. couleur_i[2]=B;
  296. }
  297.  
  298.  
  299. void init_variables(void)
  300. {
  301. load_params_from_EEPROM();
  302. if (params1.cle != 187) // à la première programmation la clé ne sera pas correcte => reset bas niveau
  303. {
  304. params1.offset_y =0;
  305. params1.gain=1;
  306. params1.pos_curseur_frq = 4; // valeur de départ; on peut la modifier (détermine le digit en surbrillance pour la fréquence)
  307. params1.pos_curseur_wob = 10; // valeur de départ; on peut la modifier (détermine le facteur kHz/div au départ)
  308. params1.pos_mark = 300;
  309. params1.frequence = 30000; // 300.00 MHz
  310. params1.cle = 187; // cle ok pour les fois suivantes (ainsi on ne repassera plus pas cette condition)
  311. }
  312.  
  313. pas_frq=1; for (int n=1; n<params1.pos_curseur_frq; n++) { pas_frq *=10; } // <- ne surtout pas toucher cette ligne de calcul auto
  314. kHz_div=10; kHz_div *= facteurs_mult[params1.pos_curseur_wob]; // <- ne surtout pas toucher cette ligne de calcul auto
  315. dB_div =1; dB_div *= facteurs_mult[params1.gain]; // <- ne surtout pas toucher cette ligne de calcul auto
  316.  
  317. setCouleur(couleur_gain, 0, 255, 0);
  318. envoyer_data = 0;
  319.  
  320. frq_min = 3300; // 33.00 MHz - La PLL décroche en dessous de 32MHz environ (voir led LOCK en mode GENE)
  321. frq_max = 440000; //4400.00 MHz = 4.4 GHz
  322.  
  323. compteur1 = 0;
  324. depassement = 0;
  325. raffraichir=0;
  326. }
  327.  
  328.  
  329.  
  330. void inc_FRQ()
  331. {
  332. if (params1.frequence >= (frq_min + pas_frq)) {params1.frequence -= pas_frq;}
  333. }
  334.  
  335. void dec_FRQ()
  336. {
  337. if ( (params1.frequence+pas_frq) <= frq_max) {params1.frequence += pas_frq;}
  338. if ( (params1.frequence+pas_frq) > frq_max) {params1.frequence = frq_max;}
  339. }
  340.  
  341. void inc_offset()
  342. {
  343. params1.offset_y += 10;
  344. if ( params1.offset_y > 2000) {params1.offset_y = 2000;}
  345. Affi_12.affiche_valeur(params1.offset_y, 5, "");
  346. }
  347.  
  348. void dec_offset()
  349. {
  350. params1.offset_y -= 10;
  351. if (params1.offset_y < -2000) {params1.offset_y = -2000;}
  352. Affi_12.affiche_valeur(params1.offset_y, 5, "");
  353. }
  354.  
  355.  
  356.  
  357. void inc_pos_curseur_frq()
  358. {
  359. if (params1.pos_curseur_frq < 6) {params1.pos_curseur_frq++ ;}
  360. }
  361.  
  362. void dec_pos_curseur_frq()
  363. {
  364. if (params1.pos_curseur_frq > 1) {params1.pos_curseur_frq--;}
  365. }
  366.  
  367.  
  368. void inc_pas_curseur_wob()
  369. {
  370. if (params1.pos_curseur_wob < 12) {params1.pos_curseur_wob++ ;}
  371. }
  372.  
  373. void dec_pas_curseur_wob()
  374. {
  375. if (params1.pos_curseur_wob > 0) {params1.pos_curseur_wob--;}
  376. }
  377.  
  378.  
  379. void affiche_freq_marqueur()
  380. {
  381. Affi_10.affiche_float(F_mark, 6, 2, "");
  382. TFT480.setColor(100, 100, 100);
  383. }
  384.  
  385.  
  386. void affi_marqueur()
  387. {
  388. uint16_t memo_pos_mark;
  389. uint16_t y_i, y2;
  390. memo_pos_mark = params1.pos_mark;
  391.  
  392. if((memo_pos_mark % 47)==0) { TFT480.setColor(120, 120,120); } else { TFT480.setColor(0, 0, 0); }
  393. if(memo_pos_mark == 235) { TFT480.setColor(255, 100,100); }
  394. TFT480.drawLine(params1.pos_mark, 81, params1.pos_mark, 318);// efface le marqueur avant de le deplacer (et retrace le réticule en jouant sur les couleurs)
  395.  
  396. //re-quadrillage H (juste les points qui ont été effacés)
  397. for (int i=1; i<10; i++)
  398. {
  399. y2= 81 + 24*i;
  400. TFT480.setColor(60, 60, 60);
  401. TFT480.drawPixel(memo_pos_mark, y2);
  402. }
  403.  
  404. if (etat == 1) { if (params1.pos_mark > 1) {params1.pos_mark --;} }
  405. else { if ( params1.pos_mark <= 469) {params1.pos_mark ++;} if ( params1.pos_mark > 470) {params1.pos_mark = 470;} }
  406. TFT480.setColor(200, 0, 255);
  407. TFT480.drawLine(params1.pos_mark, 81, params1.pos_mark, 318); // retrace le marqueur à sa nouvelle position
  408.  
  409. TFT480.setColor(0, 255, 0);
  410.  
  411. y_i=tableau_acq[memo_pos_mark];
  412. TFT480.drawPixel(memo_pos_mark, y_i); // <=== retrace le signal effacé
  413.  
  414.  
  415. affiche_freq_marqueur();
  416. }
  417.  
  418.  
  419. void affi_min_max()
  420. {
  421. TFT480.setFont(SmallFont);
  422. //RAPPEL: void printNumF(double num, byte dec, int x, int y, char divider='.', int length=0, char filler=' ');
  423. TFT480.setColor(0, 180, 255);
  424. TFT480.printNumF(F_affi_min/100.0, 2, 2, 304, '.', 8, ' ');
  425. TFT480.printNumF(F_affi_max/100.0, 2, 400, 304, '.', 8, ' ');
  426. }
  427.  
  428.  
  429. ISR (INT0_vect)
  430. {
  431. cli();
  432. //interruption sur front descendant sur l'entree Int0 declenchee par la rotation du codeur_ROT (1) -> FRQ
  433.  
  434. etat = (PIND & 0b00000100) >> 2; // donc etat = 0 ou 1
  435.  
  436. if ((mode_WOB0 == 1) || (mode_WOB1 == 1))
  437. {
  438. //WOBULATEUR"/
  439. if( mode_saisie_offset == 0)
  440. {
  441. if (mode_saisie_freq_WOB == 0)
  442. {
  443. //saisie PAS - marqueur
  444. affi_marqueur();
  445. }
  446. else
  447. {
  448. //saisie FREQ - FREQ
  449. setCouleur(couleur_gain, 0, 255, 0);
  450. if(etat == 1) {inc_FRQ();} else {dec_FRQ();}
  451. }
  452. }
  453. else
  454. {
  455. if (mode_WOB0 == 1)
  456. {
  457. //saisie OFFSET - gain dB/div
  458. setCouleur(couleur_gain, 0, 255, 255);
  459. if(etat == 0) {params1.gain += 1;} else {params1.gain -= 1;}
  460. if(params1.gain<0) {params1.gain=0;}
  461. if(params1.gain>3) {params1.gain=3;}
  462. dB_div =1;
  463. dB_div *= facteurs_mult[params1.gain];
  464. Affi_11.affiche_valeur(dB_div, 2, "");
  465. }
  466. if (mode_WOB1 == 1)
  467. {
  468. //saisie OFFSET - gain V/div
  469. setCouleur(couleur_gain, 0, 255, 255);
  470. if(etat == 0) {params1.gain += 1;} else {params1.gain -= 1;}
  471. if(params1.gain<-200) {params1.gain=-200;}
  472. if(params1.gain>200) {params1.gain=200;}
  473. Affi_11.affiche_valeur(params1.gain, 5, "");
  474. }
  475. }
  476. }
  477. else if (mode_GENE == 1)
  478. {
  479. //GENE - FREQ
  480. if(etat == 1) {inc_FRQ();} else {dec_FRQ();}
  481. envoyer_data = 1;
  482. }
  483.  
  484. envoyer_data = 1;
  485. stop =1;
  486. raffraichir=1;
  487. sei();
  488. }
  489.  
  490.  
  491.  
  492. ISR (INT1_vect)
  493. {
  494. cli();
  495. //interruption sur front descendant sur l'entree Int1 déclenchee par la rotation du codeur_ROT (2)
  496. uint8_t n;
  497. etat =(PIND & 0b00001000) >> 3; // donc etat = 0 ou 1
  498.  
  499. if ((mode_WOB0 == 1) || (mode_WOB1 == 1))
  500. //MODES WOBULATEUR"
  501. {
  502. if( mode_saisie_offset == 0)
  503. {
  504. if (mode_saisie_freq_WOB == 0)
  505. {
  506. //saisie PAS - kHz/div ->
  507. if(etat == 0) {inc_pas_curseur_wob();} else {dec_pas_curseur_wob();}
  508. kHz_div=10;
  509. kHz_div *= facteurs_mult[params1.pos_curseur_wob];
  510. if (params1.pos_curseur_wob == 0)
  511. {
  512. Affi_9.setCouleurTxt(bleu);
  513. Affi_9.affiche_texte("min");
  514. _delay_ms(300);
  515. }
  516. if (params1.pos_curseur_wob == 12)
  517. {
  518. Affi_9.setCouleurTxt(rouge);
  519. Affi_9.affiche_texte("MAX");
  520. _delay_ms(300);
  521. }
  522. }
  523. else
  524. {
  525. //saisie FREQ - DIGIT ->
  526. if(etat == 1) {inc_pos_curseur_frq(); } else {dec_pos_curseur_frq();}
  527. pas_frq=1;
  528. for (n=1; n<params1.pos_curseur_frq; n++) { pas_frq *=10; }
  529. envoyer_data = 1;
  530. }
  531. }
  532. else
  533. {
  534. //saisie OFFSET - pos ->
  535. if(etat == 0) { inc_offset();} else { dec_offset();}
  536. }
  537. }
  538. else if (mode_GENE == 1)
  539. // MODE GENE
  540. {
  541. // digit ->
  542. if(etat == 1) { inc_pos_curseur_frq(); } else { dec_pos_curseur_frq();}
  543. pas_frq=1;
  544. for (n=1; n<params1.pos_curseur_frq; n++) { pas_frq *=10; }
  545. envoyer_data = 1;
  546. }
  547. envoyer_data = 1;
  548. stop =1;
  549. raffraichir=1;
  550. sei();
  551.  
  552. }
  553.  
  554.  
  555. ISR(TIMER3_COMPA_vect)
  556. { }
  557.  
  558.  
  559.  
  560. void acquisition_data_WOB()
  561. {
  562. uint16_t acqH;
  563. uint16_t val = 0;
  564.  
  565. PORTF |= 0b00000010; // signal test
  566. ADCSRA |= _BV(ADSC); //Start conversion - resolution 10bits
  567. while (ADCSRA & _BV(ADSC) ) {;} // attend la fin de la converstion
  568. PORTF &= ~0b00000010;
  569. val = ADCL;
  570. acqH = ADCH; // passage à 16 bits pour pouvoir décaller
  571. val += (acqH << 8);
  572. valeur_lue = val;
  573.  
  574. /** pour afficher le résultat, pour test (en connectant une tension connue sur l'entrée ADC)
  575. TFT480.setFont(BigFont);
  576. TFT480.setColor(255, 255, 100);
  577. TFT480.print("ADC", 20, 180);
  578. TFT480.printNumI(valeur_lue, 145, 180, 4, '0');
  579. // résultat [0...1023] pour [0V .. 2.54V]
  580. **/
  581.  
  582. }
  583.  
  584.  
  585.  
  586. void trace_entete()
  587. {
  588. TFT480.setColor(64, 64, 64);
  589. TFT480.fillRect(0, 0, 479, 13); // bandeau en haut
  590.  
  591. TFT480.setColor(30, 30, 30);
  592. TFT480.fillRect(0, 300, 479, 319); // bandeau en bas
  593.  
  594.  
  595. // TFT480.setBackColor(64, 64, 64);
  596. TFT480.setColor(255,255,255);
  597. TFT480.setFont(SmallFont);
  598. TFT480.print("GENE 35MHz .. 4400MHz - ADF4351", CENTER, 1);
  599.  
  600. // TFT480.setBackColor(64, 64, 64);
  601. TFT480.setColor(255,255,255);
  602. TFT480.print(version, 5, 300);
  603. TFT480.setColor(255,255,255);
  604. TFT480.print("ATmega2560 - ", 100, 300);
  605. TFT480.setColor(255,255,255);
  606. TFT480.print("Silicium628", 210, 300);
  607. }
  608.  
  609.  
  610.  
  611. void init_afficheurs_wob()
  612. {
  613. Affi_9.init(220, 40, 120, 35," Hz/div"); Affi_9.setCouleurTxt(bleu_pale); //Affi_9.setCouleurCadre(gris);
  614. Affi_10.init(220, 0, 120, 35,"Mark"); Affi_10.setCouleurTxt(violet); //Affi_10.setCouleurCadre(gris);
  615.  
  616. if (mode_WOB0 == 1)
  617. {
  618. Affi_11.init(355, 0, 110, 35,"dB/div"); Affi_11.setCouleurTxt(jaune); //Affi_11.setCouleurCadre(gris);
  619. }
  620. if (mode_WOB1 == 1)
  621. {
  622. Affi_11.init(355, 0, 110, 35,"mV/div"); Affi_11.setCouleurTxt(jaune); //Affi_11.setCouleurCadre(gris);
  623. }
  624.  
  625. Affi_12.init(355, 40, 110, 35,"Offset"); Affi_12.setCouleurTxt(vert); //Affi_12.setCouleurCadre(gris);
  626. }
  627.  
  628.  
  629. void affiche_pas_DIV(uint8_t couleur)
  630. {
  631. if (kHz_div<1000)
  632. {
  633. Affi_9.init(220, 40, 120, 35,"kHz/div");
  634. if (couleur == 0) { Affi_9.affiche_valeur(kHz_div, 6, ""); }
  635. else { Affi_9.affiche_valeur(kHz_div, 6, ""); }
  636. }
  637. else
  638. {
  639. Affi_9.init(220, 40, 120, 35,"MHz/div");
  640. if (couleur == 0)
  641. {
  642. Affi_9.setCouleurTxt(blanc);
  643. Affi_9.affiche_valeur(kHz_div/1000, 6, "");
  644. }
  645. else
  646. {
  647. Affi_9.setCouleurTxt(jaune);
  648. Affi_9.affiche_valeur(kHz_div/1000, 6, "");
  649. }
  650. }
  651. }
  652.  
  653.  
  654. void trace_ecran()
  655. {
  656. if (mode_GENE == 1)
  657. {
  658. TFT480.setColor(0, 0, 0);
  659. TFT480.fillRect(0, 14, 479, 309); // efface
  660. trace_entete();
  661.  
  662. TFT480.setBackColor(0, 0, 0);
  663. TFT480.setColor(180,180,180);
  664. TFT480.setFont(BigFont);
  665.  
  666. Affi_1a.init(10, 20, 210, 80,"FREQUENCE"); Affi_1a.setCouleurTxt(bleu_pale); Affi_1a.setCouleurCadre(gris);
  667.  
  668. Affi_2.init(5, 120, 130, 50,"Lambda "); Affi_2.setCouleurTxt(jaune); Affi_2.setCouleurCadre(gris);
  669. Affi_3.init(5, 180, 130, 50,"Lambda/2"); Affi_3.setCouleurTxt(jaune); Affi_3.setCouleurCadre(gris);
  670. Affi_4.init(5, 240, 130, 50,"Lambda/4"); Affi_4.setCouleurTxt(jaune); Affi_4.setCouleurCadre(gris);
  671.  
  672.  
  673. uint16_t xp1 = 145;
  674. uint16_t yp1 = 120;
  675. uint8_t dy1 = 60;
  676. Affi_5.init(xp1, yp1, 75, 50,"Div"); Affi_5.setCouleurTxt(orange); Affi_5.setCouleurCadre(gris);
  677. Affi_6.init(xp1, yp1+dy1, 75, 50,"FRAC"); Affi_6.setCouleurTxt(orange); Affi_6.setCouleurCadre(gris);
  678. Affi_7.init(xp1, yp1+dy1*2, 75, 50,"MOD"); Affi_7.setCouleurTxt(orange); Affi_7.setCouleurCadre(gris);
  679.  
  680. Affi_8.init(xp1+80, yp1, 85, 50,"INTA"); Affi_8.setCouleurTxt(orange); Affi_8.setCouleurCadre(gris);
  681.  
  682. uint16_t xp2 = 385;
  683. uint16_t yp2 = 40;
  684. uint8_t dy2 = 40;
  685. Affi_R0.init(xp2, yp2, 80, 35,"R0"); Affi_R0.setCouleurTxt(jaune_sombre); Affi_R0.setCouleurCadre(gris);
  686. Affi_R1.init(xp2, yp2+dy2, 80, 35,"R1"); Affi_R1.setCouleurTxt(jaune_sombre); Affi_R1.setCouleurCadre(gris);
  687. Affi_R2.init(xp2, yp2+dy2*2, 80, 35,"R2"); Affi_R2.setCouleurTxt(jaune_sombre); Affi_R2.setCouleurCadre(gris);
  688. Affi_R3.init(xp2, yp2+dy2*3, 80, 35,"R3"); Affi_R3.setCouleurTxt(jaune_sombre); Affi_R3.setCouleurCadre(gris);
  689. Affi_R4.init(xp2, yp2+dy2*4, 80, 35,"R4"); Affi_R5.setCouleurTxt(jaune_sombre); Affi_R4.setCouleurCadre(gris);
  690. Affi_R5.init(xp2, yp2+dy2*5, 80, 35,"R5"); Affi_R5.setCouleurTxt(jaune_sombre); Affi_R5.setCouleurCadre(gris);
  691. }
  692. else
  693. {
  694. TFT480.setColor(10, 10, 5);
  695. TFT480.fillRect(0, 0, 479, 319); // efface tout
  696. Affi_1a.init(0, 0, 210, 78,"F centrale"); Affi_1a.setCouleurTxt(bleu_clair); Affi_1a.setCouleurCadre(gris);
  697. init_afficheurs_wob();
  698. trace_pannel_graph();
  699. }
  700. affi_helps();
  701. }
  702.  
  703.  
  704. void trace_pannel_graph()
  705. {
  706. TFT480.setColor(0, 0, 0);
  707. TFT480.fillRect(0, 80, 479, 319);
  708. TFT480.setColor(180, 180, 180);
  709. TFT480.drawRect(0, 80, 479, 319);
  710.  
  711. //quadrillage V
  712. uint16_t n;
  713. uint16_t x;
  714. for ( n=1; n<10; n++)
  715. {
  716. x= 47*n;
  717. if (n==5) { TFT480.setColor(120, 120,120); } else { TFT480.setColor(60, 60, 60); }
  718. TFT480.drawLine(x, 81, x, 318);
  719. }
  720.  
  721. //quadrillage H
  722. uint16_t y;
  723. for (n=1; n<10; n++)
  724. {
  725. y= 81 + 24*n;
  726. if (n==5) { TFT480.setColor(120, 120,120); } else { TFT480.setColor(60, 60, 60); }
  727. TFT480.drawLine(1, y, 478, y);
  728. }
  729. }
  730.  
  731.  
  732. void efface_centre_graph()
  733. {
  734. TFT480.setColor(0, 0, 0);
  735. TFT480.fillRect(101, 101, 349, 299);
  736. TFT480.setColor(0, 200, 200);
  737. TFT480.drawRect(100, 100, 350, 300);
  738.  
  739. TFT480.setColor(200, 0, 0);
  740. TFT480.drawLine(235, 101, 235, 299);
  741.  
  742. TFT480.setColor(200,200,200);
  743. TFT480.setFont(SmallFont);
  744. TFT480.print("Mode balayage rapide", 150, 306);
  745. }
  746.  
  747. /**
  748. // trace une sinusoide
  749. TFT480.setColor(0, 255, 0);
  750. for (uint16_t n=0; n<470; n++)
  751. {
  752. float x, y;
  753. x= (float)n/470.0*2.0*M_PI;
  754. y=190-80*sin(5*x);
  755. TFT480.drawPixel(n, y);
  756. }
  757. **/
  758.  
  759.  
  760.  
  761. void TFT_affiche_chiffre_7seg(uint8_t num, uint16_t x_i, uint16_t y_i )
  762. {
  763. TFT480.setFont(SevenSegNumFont);
  764. TFT480.printNumI(num, x_i, y_i);
  765. }
  766.  
  767.  
  768.  
  769. void TFT_aff_nb_form3 (uint32_t valeur, uint8_t nb_chiffres, uint8_t curseur, uint8_t R, uint8_t G, uint8_t B, uint16_t x_i, uint16_t y_i)
  770. {
  771. //affiche un nombre en représentation decimale en séparant les groupes de 3 chiffres
  772. unsigned char r ;
  773. char tbl[7];
  774. uint8_t i;
  775.  
  776. x_7seg = x_i;
  777. y_7seg = y_i;
  778.  
  779. curseur=nb_chiffres +1 -curseur;
  780.  
  781. for (i=1; i<=nb_chiffres; i++)
  782. {
  783. r=valeur % 10; // modulo (reste de la division)
  784. valeur /= 10; // quotient
  785. tbl[i]=r;
  786. }
  787. for (i=1; i<= nb_chiffres; i++)
  788. {
  789. TFT480.setColor(R,G,B);
  790.  
  791. if (i==curseur)
  792. {
  793. TFT480.setColor(0,250,250);
  794. }
  795. TFT_affiche_chiffre_7seg(tbl[nb_chiffres +1 -i], x_7seg, y_7seg);
  796. x_7seg+=30;
  797.  
  798. uint8_t m, k, u;
  799. m =nb_chiffres-6;
  800. k =nb_chiffres-3;
  801. u =nb_chiffres;
  802.  
  803. //if (i== 3)
  804. if (i== m)
  805. {
  806. TFT480.setFont(BigFont);
  807. TFT480.setColor(100,100,100);
  808. TFT480.print("M",x_7seg, y_7seg+30);
  809. x_7seg+=15;
  810. }
  811. //if (i== 6)
  812. if (i== k)
  813. {
  814. TFT480.setFont(BigFont);
  815. TFT480.setColor(100,100,100);
  816. TFT480.print("k",x_7seg, y_7seg+30);
  817. x_7seg+=15;
  818. }
  819. //if (i== 9)
  820. if (i== u)
  821. {
  822. TFT480.setFont(BigFont);
  823. TFT480.setColor(100,100,100);
  824. TFT480.print("Hz",x_7seg, y_7seg+30);
  825. }
  826. }
  827. }
  828.  
  829.  
  830. void TFT_aff_nb_form3b (uint32_t valeur, uint8_t nb_chiffres, uint8_t curseur, uint8_t R, uint8_t G, uint8_t B, uint8_t SB, uint16_t x_i, uint16_t y_i)
  831. {
  832. //affiche un nombre en représentation décimale avec séparateur 'M' à gauche des 2 chiffres de droite
  833.  
  834. unsigned char r ;
  835. char tbl[7];
  836. uint8_t i;
  837.  
  838. x_7seg = x_i;
  839. y_7seg = y_i;
  840.  
  841. curseur=nb_chiffres +1 -curseur;
  842.  
  843. for (i=1; i<=nb_chiffres; i++)
  844. {
  845. r=valeur % 10; // modulo (reste de la division)
  846. valeur /= 10; // quotient
  847. tbl[i]=r;
  848. }
  849. for (i=1; i<= nb_chiffres; i++)
  850. {
  851. TFT480.setColor(R,G,B);
  852.  
  853. if ((i==curseur) && (SB == 1) )
  854. {
  855. TFT480.setColor(0,250,250); // surbrillance du digit sélectionné
  856. }
  857. TFT_affiche_chiffre_7seg(tbl[nb_chiffres +1 -i], x_7seg, y_7seg);
  858. x_7seg+=30;
  859.  
  860. uint8_t m = nb_chiffres-2;
  861.  
  862. if (i==m)
  863. {
  864. TFT480.setFont(BigFont);
  865. TFT480.setColor(200,200,200);
  866. TFT480.print("M",x_7seg, y_7seg+30); // affichage du separateur 'M' (M comme Megahertz)
  867. x_7seg+=15;
  868. }
  869. }
  870. }
  871.  
  872.  
  873.  
  874.  
  875.  
  876. // ===================================================================================================================================================
  877.  
  878. void setup()
  879. {
  880. init_ports();
  881. init_variables();
  882. scrute_switches();
  883.  
  884. // USART_Init();
  885.  
  886. TFT480.InitLCD();
  887. TFT480.setFont(SmallFont);
  888. TFT480.clrScr();
  889.  
  890. int_EXT_setup();
  891. CarteADF4351_A.init();
  892. CarteADF4351_A.mute(0);
  893.  
  894. AffiV_01.init(469, 110); AffiV_01.setCouleur(noir);
  895. AffiV_02.init(469, 305); AffiV_02.setCouleur(noir);
  896.  
  897. /* POUR INFO - ces lignes peuvent être commentées */
  898. //int free_RAM;
  899. //free_RAM = freeRam();
  900. //TFT480.setFont(BigFont);
  901. //TFT480.setColor(255, 255, 100);
  902. //TFT480.print(version, 20, 160);
  903. //TFT480.setColor(0, 200, 200);
  904. //TFT480.print("free_RAM", 20, 180);
  905. //TFT480.printNumI(free_RAM, 145, 180, 6, ' ');
  906. //TFT480.print("octets", 250, 180);
  907. //_delay_ms(2000);
  908. /* ********************************************** */
  909.  
  910. trace_ecran();
  911. if (mode_rapide) { efface_centre_graph();}
  912.  
  913. init_leds_switches();
  914. InitADC();
  915.  
  916.  
  917. sei(); // enable interruptions
  918. envoyer_data = 1;
  919.  
  920. }
  921.  
  922.  
  923.  
  924. void affiche_valeurs_registres()
  925. {
  926. Affi_R0.affiche_HEXA(CarteADF4351_A.registers[0]);
  927. Affi_R1.affiche_HEXA(CarteADF4351_A.registers[1]);
  928. Affi_R2.affiche_HEXA(CarteADF4351_A.registers[2]);
  929. Affi_R3.affiche_HEXA(CarteADF4351_A.registers[3]);
  930. Affi_R4.affiche_HEXA(CarteADF4351_A.registers[4]);
  931. Affi_R5.affiche_HEXA(CarteADF4351_A.registers[5]);
  932. }
  933.  
  934.  
  935. void affiche_lambda()
  936. {
  937. Affi_2.affiche_float(lambda, 4, 1, "cm");
  938. Affi_3.affiche_float(lambda/2, 4, 1, "cm");
  939. Affi_4.affiche_float(lambda/4, 4, 1, "cm");
  940. }
  941.  
  942.  
  943. void affiche_params()
  944. {
  945. DIV = CarteADF4351_A.DIV; Affi_5.affiche_valeur(DIV, 4, " ");
  946. FRAC = CarteADF4351_A.FRAC; Affi_6.affiche_valeur(FRAC, 4, " ");
  947. MOD = CarteADF4351_A.MOD; Affi_7.affiche_valeur(MOD, 4, " ");
  948. INTA = CarteADF4351_A.INTA; Affi_8.affiche_valeur(INTA, 5, " ");
  949. }
  950.  
  951.  
  952. void affiche_leds()
  953. {
  954. LED_a.init(245, 80, 0,255,0,10, "LOCK" );
  955. LED_b.init(245, 40, 255,0,0,10, "ON" );
  956. }
  957.  
  958.  
  959. void init_leds_switches()
  960. {
  961. LED0.init(452, 8, 255,0,0,5, "" );
  962. LED1.init(452, 18, 255,255,0,5, "" );
  963. LED2.init(462, 8, 0,255,0,5, "" );
  964. LED3.init(472, 8, 0,255,255,5, "" );
  965. LED4.init(472, 18, 0,0,255,5, "" );
  966.  
  967. }
  968.  
  969.  
  970. void calcul_lambda()
  971. {
  972. lambda = 3e6/params1.frequence; // en cm
  973. }
  974.  
  975.  
  976. void affi_helps() // Ecriture verticales au bord droit de l'écran pour rappeler la fonction des boutons + coloration des afficheurs
  977. {
  978. if ((mode_WOB0 == 1) || (mode_WOB1 == 1))
  979. {
  980. //WOBULATEUR
  981.  
  982. if( mode_saisie_offset == 0)
  983. {
  984. setCouleur(couleur_gain, 0, 255, 0);
  985. if (mode_saisie_freq_WOB == 0)
  986. {
  987. //saisie PAS
  988. AffiV_01.affiche_texte_V(" MARQUEUR");
  989. AffiV_02.affiche_texte_V(" Hz/div");
  990. Affi_10.setCouleurCadre(jaune_sombre); Affi_10.flashFond(jaune_sombre);
  991. Affi_9.setCouleurCadre(jaune_sombre); Affi_9.flashFond(jaune_sombre);
  992. Affi_11.setCouleurCadre(gris);
  993. Affi_12.setCouleurCadre(gris);
  994. Affi_1a.setCouleurCadre(gris);
  995. }
  996. else
  997. {
  998. //saisie FREQ
  999. AffiV_01.affiche_texte_V(" FREQ");
  1000. AffiV_02.affiche_texte_V(" DIGIT");
  1001. Affi_1a.setCouleurCadre(jaune_sombre); Affi_1a.flashFond(jaune_tres_sombre);
  1002. Affi_9.setCouleurCadre(gris);
  1003. Affi_10.setCouleurCadre(gris);
  1004. Affi_11.setCouleurCadre(gris);
  1005. Affi_12.setCouleurCadre(gris);
  1006. }
  1007. }
  1008. else
  1009. {
  1010. //saisie OFFSET
  1011. setCouleur(couleur_gain, 0, 255, 255);
  1012. if (mode_WOB0 == 1) { AffiV_01.affiche_texte_V(" Gain");}
  1013. if (mode_WOB1 == 1) { AffiV_01.affiche_texte_V(" dB/div");}
  1014. AffiV_02.affiche_texte_V(" OFFSET");
  1015. Affi_11.setCouleurCadre(jaune_sombre); Affi_11.flashFond(jaune_sombre);
  1016. Affi_12.setCouleurCadre(jaune_sombre); Affi_12.flashFond(jaune_sombre);
  1017. Affi_9.setCouleurCadre(gris);
  1018. Affi_10.setCouleurCadre(gris);
  1019. Affi_1a.setCouleurCadre(gris);
  1020. }
  1021. }
  1022. else
  1023. {
  1024. //GENE
  1025. AffiV_01.affiche_texte_V(" FREQ");
  1026. AffiV_02.affiche_texte_V(" DIGIT");
  1027. Affi_1a.setCouleurCadre(jaune_sombre);
  1028. }
  1029.  
  1030.  
  1031. }
  1032.  
  1033.  
  1034. void scrute_switches()
  1035. {
  1036. SW_0 = portPIN_switch0 & pin_switch0; // le bit lu est à l'emplacement 0;
  1037. SW_1 = (portPIN_switch1 & pin_switch1) >>1; // décallage pour positionner correctement le bit lu vers l'emplacement 0;
  1038. SW_2 = portPIN_switch2 & pin_switch2;
  1039. SW_3 = (portPIN_switch3 & pin_switch3) >>3;
  1040. SW_4 = (portPIN_switch4 & pin_switch4) >>4;
  1041.  
  1042. SW_0 ^= 1; // ou exclusif;
  1043. SW_1 ^= 1;
  1044. SW_2 ^= 1;
  1045. SW_3 ^= 1;
  1046. SW_4 ^= 1;
  1047.  
  1048. LED0.setEtat(SW_0);
  1049. LED1.setEtat(SW_1);
  1050. LED2.setEtat(SW_2);
  1051. LED3.setEtat(SW_3);
  1052. LED4.setEtat(SW_4);
  1053.  
  1054. memo_switches = switches;
  1055. switches = 0;
  1056. switches |= SW_0;
  1057. switches |= SW_1 << 1;
  1058. switches |= SW_2 << 2;
  1059. switches |= SW_3 << 3;
  1060. switches |= SW_4 << 4;
  1061.  
  1062. mode_GENE = SW_0;
  1063. mode_WOB0 = (SW_0 || SW_1)^1;
  1064. mode_WOB1 = SW_1;
  1065. mode_saisie_freq_WOB = SW_4;
  1066. mode_saisie_offset = SW_3;
  1067. mode_rapide = SW_2;
  1068.  
  1069. if ((switches & 0b00011111) != (memo_switches & 0b00011111))
  1070. {
  1071. trace_ecran();
  1072. envoyer_data = 1;
  1073. }
  1074.  
  1075. }
  1076.  
  1077.  
  1078. double recadre(double valeur_i)
  1079. {
  1080. double y2;
  1081. double y_min, y_max;
  1082. if (mode_WOB0 == 1)
  1083. {
  1084. /**
  1085. RAPPELS:
  1086. - la réponse du détecteur log AD8318 = 25mV/dB
  1087. - l'ADC de l'ATmega convertit 2.56V -> 1024 pas (0..1023) soit 1024/2560mV = 0.4pas/mV (voir fonction InitADC() -> Internal 2.56V Voltage Reference)
  1088. - 1dB => 25mV => 25x0.4pas => 10pas
  1089. - echelle de la valeur lue = Ech = 10pas/dB
  1090. - le panel graphique fait 320-80 = 240px en Y
  1091. - ce panel graphique est divisé en 10 divisions qui font chacune 240/10 = 24 pixels
  1092.  
  1093. - en position 1dB/div le gain doit être 1dB => 10pasADC => 1divY => 24px
  1094. - gain(1dB/div) = G1 = 24/10 px/pasADC = 2.4
  1095. **/
  1096. if (mode_rapide==0) { y_min=83; y_max =317;} else { y_min=101; y_max =299;}
  1097. y2 = 200 + (-1.0) * (params1.offset_y + (2.4 / dB_div) * polarite * valeur_i );
  1098. if (y2<y_min) {y2=y_min;}
  1099. if (y2>y_max) {y2=y_max;}
  1100. return y2;
  1101. }
  1102. if (mode_WOB1 == 1)
  1103. {
  1104. if (mode_rapide==0) { y_min=83; y_max =317;} else { y_min=101; y_max =299;}
  1105. y2 = 200.0 + params1.gain / 10.0 * (valeur_i + params1.offset_y * 5 - 200) ;
  1106. if (y2<y_min) {y2=y_min;}
  1107. if (y2>y_max) {y2=y_max;}
  1108. return y2;
  1109. }
  1110. }
  1111.  
  1112.  
  1113.  
  1114. void loop()
  1115. {
  1116. double Fd1, Fcentrale, Fmin, Fmax, increment_F;
  1117. double y_float;
  1118. uint16_t y, memo_y, y_repos, y2;
  1119. uint8_t i;
  1120.  
  1121.  
  1122. while(1)
  1123. {
  1124. scrute_switches();
  1125.  
  1126. // TFT480.setFont(BigFont);
  1127. // TFT480.setColor(0, 200, 200);
  1128. // TFT480.printNumI(compteur1, 20, 200, 3, ' ');
  1129.  
  1130. //------------------------------------------------------------------------------------------------------------------
  1131. // GENERATEUR SIMPLE
  1132. //------------------------------------------------------------------------------------------------------------------
  1133. if (mode_GENE == 1)
  1134. {
  1135. if(envoyer_data > 0)
  1136. {
  1137. envoyer_data = 0;
  1138. calcul_lambda();
  1139. Fd1 = params1.frequence / 100.0; // F2 en MHz (avec 2 décimales)
  1140.  
  1141. CarteADF4351_A.setFreq(Fd1);
  1142. CarteADF4351_A.Write_All_Register();
  1143. _delay_us(10);
  1144.  
  1145. Affi_1a.setCouleurTxt(bleu_clair); Affi_1a.setCouleurCadre(jaune_sombre);
  1146. Affi_1a.affiche_frequence(params1.frequence, 1); // (avec de grands chiffres 7 segments)
  1147.  
  1148. affiche_leds();
  1149. if ((PIN_ADF4351_A & pin_MUXOUT) == pin_MUXOUT) { LED_a.setEtat(1); } else { LED_a.setEtat(0);}
  1150. LED_b.setEtat(1);
  1151.  
  1152. affiche_params();
  1153. affiche_lambda();
  1154. affiche_valeurs_registres();
  1155. save_params_to_EEPROM();
  1156. }
  1157. }
  1158. //------------------------------------------------------------------------------------------------------------------
  1159. // WOBULATEUR
  1160. //------------------------------------------------------------------------------------------------------------------
  1161. else if ((mode_WOB0 == 1) || (mode_WOB1 == 1))
  1162. {
  1163.  
  1164. if (raffraichir == 1)
  1165. {
  1166. //TFT480.setColor(0, 0, 0);
  1167. //TFT480.fillRect(0, 0, 460, 79);
  1168. Affi_1a.init(0, 0, 210, 78,"F centrale");
  1169. init_afficheurs_wob();
  1170. raffraichir=0;
  1171. save_params_to_EEPROM();
  1172. }
  1173.  
  1174. if (mode_rapide) { efface_centre_graph();}
  1175.  
  1176. LED0.setEtat(SW_0);
  1177. LED1.setEtat(SW_1);
  1178. LED2.setEtat(SW_2);
  1179. LED3.setEtat(SW_3);
  1180. LED4.setEtat(SW_4);
  1181.  
  1182. //trace_afficheurs_wob();
  1183. if (mode_saisie_freq_WOB == 1)
  1184. {
  1185. Affi_1a.setCouleurTxt(bleu); //Affi_1a.setCouleurCadre(jaune_sombre);
  1186. Affi_1a.affiche_frequence(params1.frequence, 1);
  1187. }
  1188. else
  1189. {
  1190. Affi_1a.setCouleurTxt(bleu); Affi_1a.setCouleurCadre(gris);
  1191. Affi_1a.affiche_frequence(params1.frequence, 0);
  1192. }
  1193. affiche_pas_DIV(0);
  1194.  
  1195. Fcentrale = params1.frequence / 100.0;
  1196. increment_F = kHz_div / 470.0;
  1197.  
  1198. Fmin = params1.frequence-235*increment_F;
  1199. Fmax = params1.frequence+235*increment_F;
  1200.  
  1201. F_mark =(Fmin + ((double) params1.pos_mark * increment_F)) / 100.0;
  1202. affiche_freq_marqueur();
  1203.  
  1204. F_affi_min=Fmin;
  1205. if (F_affi_min < frq_min) {F_affi_min = frq_min;}
  1206.  
  1207. F_affi_max=Fmax;
  1208. if (F_affi_max > frq_max) {F_affi_max = frq_max;}
  1209.  
  1210.  
  1211. // **** hors boucle pour ne pas ralentir ****
  1212. acquisition_data_WOB();
  1213. y=valeur_lue;
  1214.  
  1215. // ******************************************
  1216. affiche_freq_marqueur();
  1217. if (mode_WOB0 == 1) {Affi_11.affiche_valeur(dB_div, 2, "");}
  1218. if (mode_WOB1 == 1) {Affi_11.affiche_valeur(params1.gain, 5, "");}
  1219. Affi_12.affiche_valeur(params1.offset_y, 5, "");
  1220.  
  1221. // BOUCLE DE BALAYAGE - incrémente la fréquence et affiche le graphique en temps réel
  1222.  
  1223. // for (int n=0; n<470; n++)
  1224.  
  1225. uint8_t dn=1; if (mode_rapide==1) {dn =5;}
  1226. int n=0; if (mode_rapide==1) {n =100;}
  1227. uint16_t n_max =470; if (mode_rapide==1) {n_max =350;}
  1228. stop=0;
  1229. while ((n<n_max) && (stop ==0))
  1230. {
  1231.  
  1232. if(raffraichir==1)
  1233. {
  1234. affiche_pas_DIV(0);
  1235. Affi_1a.setCouleurTxt(bleu_clair);
  1236. Affi_1a.affiche_frequence(params1.frequence, 1);
  1237.  
  1238. if (mode_saisie_freq_WOB == 1)
  1239. {
  1240. Affi_1a.affiche_frequence(params1.frequence, 1); // (grands chiffres + surbrillance 1 digit)
  1241. }
  1242. else
  1243. {
  1244. Affi_1a.affiche_frequence(params1.frequence, 0); // (grands chiffres, pas de surbrillance digit )
  1245. }
  1246. affiche_pas_DIV(0);
  1247.  
  1248. F_affi_min=Fmin;
  1249. if (F_affi_min < frq_min) {F_affi_min = frq_min;}
  1250.  
  1251. F_affi_max=Fmax;
  1252. if (F_affi_max > frq_max) {F_affi_max = frq_max;}
  1253.  
  1254. affiche_freq_marqueur();
  1255. affi_min_max();
  1256. raffraichir=0;
  1257. }
  1258.  
  1259. depassement =0;
  1260. Fd1 = (Fmin + n * increment_F);
  1261. if (Fd1 < frq_min) {Fd1 = frq_min; depassement =1;}
  1262. if (Fd1 > frq_max) {Fd1 = frq_max; depassement =1;}
  1263. Fd1 = Fd1 / 100.0; // Fd1 en MHz (avec 2 décimales)
  1264.  
  1265. CarteADF4351_A.setFreq(Fd1);
  1266. _delay_ms(1);
  1267.  
  1268. acquisition_data_WOB();
  1269.  
  1270. memo_y=y;
  1271. y_float = recadre((float)valeur_lue); // règlage de offset et amplitude pour entrer dans le cadre d'affichage
  1272. y = (uint16_t)y_float;
  1273. if (y>317) {y=317;}
  1274.  
  1275. tableau_acq[n]=y; // mémorisation de la courbe (recadrée) en SRAM
  1276.  
  1277. if(mode_rapide ==0)
  1278. {
  1279. TFT480.setColor(255, 255, 255);
  1280. if(n<465) {TFT480.drawLine(n+2, y-3, n+2, y+3);} // petit tiret vertical pour faire joli (spot) et montrer l'avancement
  1281. if((n % 47)==0) { TFT480.setColor(120, 120,120); } else { TFT480.setColor(0, 0, 0); }
  1282. if(n == 235) { TFT480.setColor(255, 100,100); }
  1283. if (n== params1.pos_mark) { TFT480.setColor(200, 0, 255); }
  1284.  
  1285. TFT480.drawLine(n, 81, n, 318); // efface toute une ligne verticale tout en redessinant le reticule V et le marqueur (en jouant sur les couleurs)
  1286. TFT480.setColor(100, 100, 100);
  1287.  
  1288. //quadrillage H
  1289. for (int i=1; i<10; i++)
  1290. {
  1291. y2= 81 + 24*i;
  1292. if (n==5) { TFT480.setColor(120, 120,120); } else { TFT480.setColor(60, 60, 60); }
  1293. TFT480.drawPixel(n, y2);
  1294. }
  1295. }
  1296.  
  1297. TFT480.setColor(0, 255, 0);
  1298. if (depassement == 0) // en fréquence
  1299. {
  1300. if(n>(20*dn)) {TFT480.drawLine(n-dn, memo_y, n, y);} // <=== trace le signal normal *****
  1301. }
  1302. else
  1303. {
  1304. TFT480.setColor(255, 0, 0);
  1305. TFT480.drawLine(n-1, 318, n, 318); // trace une ligne rouge H
  1306. TFT480.drawLine(n-1, 310, n, 310);
  1307. }
  1308. if ((n==80) || (n==460)) { affi_min_max(); }
  1309.  
  1310. n+=dn;
  1311.  
  1312. scrute_switches();
  1313. if ((switches & 0b00001111) != (memo_switches & 0b00001111)) { stop=1; }
  1314. }
  1315. // _delay_ms(1000); // pause pour pouvoir prendre des photos facilement;
  1316. }
  1317. }
  1318. }
  1319.  
  1320. /** ***********************************************************************************
  1321. CLASS AffiRect // affiche un nombre ou un petit texte dans un rectangle
  1322. ***************************************************************************************/
  1323.  
  1324. // Constructeur
  1325. AffiRect::AffiRect()
  1326. {
  1327.  
  1328. }
  1329.  
  1330.  
  1331. void AffiRect::init(uint16_t x, uint16_t y, uint16_t dx, uint16_t dy, char txt_etiquette_i[10])
  1332. {
  1333. x0 = x;
  1334. y0 = y;
  1335. dx0 = dx;
  1336. dy0 = dy;
  1337.  
  1338. for (int i=0; i<10; i++) {txt_etiquette[i]=txt_etiquette_i[i];}
  1339. txt_etiquette[10]='\0'; // zero terminal
  1340.  
  1341. TFT480.setBackColor(0, 0, 0);
  1342. TFT480.setColor(10, 10, 5);
  1343.  
  1344. TFT480.setColor(Rcadre, Vcadre, Bcadre);
  1345. TFT480.drawRect(x0, y0+5, x0+dx0, y0+dy0);
  1346.  
  1347. traceCadre();
  1348. affi_etiquette();
  1349.  
  1350. }
  1351.  
  1352. void AffiRect::traceCadre()
  1353. {
  1354. TFT480.setColor(Rcadre, Vcadre, Bcadre);
  1355. TFT480.drawRect(x0, y0+5, x0+dx0, y0+dy0);
  1356. }
  1357.  
  1358.  
  1359. void AffiRect::affi_etiquette()
  1360. {
  1361. TFT480.setBackColor(0, 0, 0);
  1362. TFT480.setFont(BigFont);
  1363. TFT480.setColor(180,180,180); // couleur de l'étiquette
  1364. TFT480.print(txt_etiquette, x0, y0); // Etiquette
  1365. }
  1366.  
  1367.  
  1368. void AffiRect::setCouleurTxt(uint8_t *couleur_i)
  1369. {
  1370. Rtxt=couleur_i[0];
  1371. Vtxt=couleur_i[1];
  1372. Btxt=couleur_i[2];
  1373. }
  1374.  
  1375. void AffiRect::setCouleurCadre(uint8_t *couleur_i)
  1376. {
  1377. Rcadre=couleur_i[0];
  1378. Vcadre=couleur_i[1];
  1379. Bcadre=couleur_i[2];
  1380.  
  1381. TFT480.setColor(Rcadre, Vcadre, Bcadre);
  1382. TFT480.drawRect(x0, y0+5, x0+dx0, y0+dy0);
  1383. TFT480.setBackColor(0, 0, 0);
  1384. TFT480.setFont(BigFont);
  1385. TFT480.setColor(180,180,180); // couleur de l'étiquette
  1386. TFT480.print(txt_etiquette, x0, y0); // Etiquette (pour écrire volaontairement par dessus le cadre))
  1387. }
  1388.  
  1389. void AffiRect::setCouleurFond(uint8_t *couleur_i)
  1390. {
  1391. Rfond=couleur_i[0];
  1392. Vfond=couleur_i[1];
  1393. Bfond=couleur_i[2];
  1394. }
  1395.  
  1396. void AffiRect::flashFond(uint8_t *couleur_i)
  1397. {
  1398. Rfond=couleur_i[0];
  1399. Vfond=couleur_i[1];
  1400. Bfond=couleur_i[2];
  1401. TFT480.setColor(Rfond, Vfond, Bfond);
  1402. TFT480.fillRect(x0, y0, x0+dx0, y0+dy0);
  1403. _delay_ms(10);
  1404. TFT480.setColor(10, 10, 5);
  1405. TFT480.fillRect(x0, y0, x0+dx0, y0+dy0);
  1406. traceCadre();
  1407. affi_etiquette();
  1408. }
  1409.  
  1410.  
  1411. void AffiRect::affiche_frequence(uint32_t F_i, uint8_t SB_i) // SB = surbrillance 1 digit
  1412. {
  1413. uint16_t x_7seg;
  1414. uint16_t y_7seg;
  1415. uint8_t p2;
  1416.  
  1417. x_7seg = x0+10;
  1418. y_7seg = y0+20;
  1419. p2 = params1.pos_curseur_frq;
  1420. TFT480.setBackColor(0, 0, 0);
  1421. TFT_aff_nb_form3b (F_i, 6, p2, Rtxt, Vtxt, Btxt, SB_i, x_7seg, y_7seg); // 6 chiffres significatifs
  1422. }
  1423.  
  1424.  
  1425.  
  1426. void AffiRect::affiche_valeur(uint32_t valeur, uint8_t nb_chiffres, char txt_unite_i[3])
  1427. {
  1428. for (int i=0; i<3; i++) {txt_unite[i]=txt_unite_i[i];}
  1429. txt_unite[3]='\0'; // zero terminal
  1430.  
  1431. TFT480.setBackColor(0, 0, 0);
  1432. TFT480.setColor(Rtxt, Vtxt, Btxt);
  1433. TFT480.setFont(BigFont);
  1434. TFT480.printNumI(valeur, x0+5,y0+18, nb_chiffres, ' ');
  1435. TFT480.setFont(BigFont);
  1436. TFT480.print(txt_unite, x0+80,y0+18); // ex : mm, kHz, etc...
  1437. }
  1438.  
  1439.  
  1440.  
  1441. void AffiRect::affiche_float(float valeur, uint8_t nb_chiffres, uint8_t nb_dec, char txt_unite_i[3])
  1442. {
  1443. for (int i=0; i<3; i++) {txt_unite[i]=txt_unite_i[i];}
  1444. txt_unite[3]='\0'; // zero terminal
  1445.  
  1446. TFT480.setBackColor(0, 0, 0);
  1447. TFT480.setColor(Rtxt, Vtxt, Btxt);
  1448. TFT480.setFont(BigFont);
  1449.  
  1450. //RAPPEL: void printNumF(double num, byte dec, int x, int y, char divider='.', int length=0, char filler=' ');
  1451. TFT480.printNumF(valeur, nb_dec, x0+5,y0+18, ',', nb_chiffres, ' ');
  1452.  
  1453. TFT480.setFont(BigFont);
  1454. TFT480.print(txt_unite, x0+80,y0+18); // ex : mm, kHz, etc...
  1455. }
  1456.  
  1457.  
  1458. void AffiRect::affiche_HEXA(uint32_t valeur)
  1459. {
  1460. // affiche un nombre en representation hexadécimale
  1461. // 16 nb_signes hexa max
  1462.  
  1463. uint8_t r;
  1464. uint8_t i;
  1465.  
  1466. char tbl[9];
  1467. char signes[17] = "0123456789ABCDEF";
  1468.  
  1469. for (i=0; i<8; i++)
  1470. {
  1471. r= valeur % 16; // modulo (reste de la division)
  1472. valeur /= 16; // quotient
  1473. tbl[7-i]=signes[r];
  1474. };
  1475. tbl[8]='\0';
  1476.  
  1477. TFT480.setBackColor(0, 0, 0);
  1478. TFT480.setColor(0,255,255);
  1479. TFT480.setFont(SmallFont);
  1480. TFT480.print(tbl, x0+10,y0+15);
  1481. }
  1482.  
  1483.  
  1484. void AffiRect::affiche_texte(char txt_i[10])
  1485. {
  1486. for (int i=0; i<10; i++) {texte[i]=txt_i[i];}
  1487. texte[10]='\0'; // zero terminal
  1488.  
  1489. TFT480.setBackColor(0, 0, 0);
  1490. TFT480.setColor(Rtxt, Vtxt, Btxt);
  1491. TFT480.setFont(BigFont);
  1492. TFT480.print(texte, x0+5,y0+18);
  1493. }
  1494.  
  1495.  
  1496. /** ***********************************************************************************
  1497. CLASS AffiV // affiche verticalement sur fond gris
  1498. ***************************************************************************************/
  1499.  
  1500. // Constructeur
  1501. AffiV::AffiV()
  1502. {
  1503.  
  1504. }
  1505.  
  1506. void AffiV::init(uint16_t x, uint16_t y)
  1507. {
  1508. x0 = x;
  1509. y0 = y;
  1510. }
  1511.  
  1512. void AffiV::setCouleur(uint8_t *couleur_i)
  1513. {
  1514. R=couleur_i[0];
  1515. V=couleur_i[1];
  1516. B=couleur_i[2];
  1517. }
  1518.  
  1519. void AffiV::affiche_texte_V(char txt_i[10])
  1520. {
  1521.  
  1522. for (int i=0; i<10; i++) {texte[i]=txt_i[i];}
  1523. texte[10]='\0'; // zero terminal
  1524.  
  1525. //TFT480.setBackColor(100,100,100);
  1526. TFT480.setBackColor(200,200,200);
  1527. TFT480.setColor(R, V, B);
  1528. TFT480.setFont(SmallFont);
  1529. TFT480.print(texte, x0,y0, -90);
  1530. }
  1531.  
  1532.  
  1533.  
  1534. /** ***********************************************************************************
  1535. CLASS LED
  1536. ***************************************************************************************/
  1537. // Constructeur
  1538. LED::LED()
  1539. {
  1540.  
  1541. }
  1542.  
  1543.  
  1544. void LED::init(uint16_t x_i, uint16_t y_i, uint8_t R_i, uint8_t G_i, uint8_t B_i, uint_fast8_t taille_i, char txt_etiquette[5])
  1545. {
  1546. x=x_i;
  1547. y=y_i;
  1548.  
  1549. R=R_i;
  1550. G=G_i;
  1551. B=B_i;
  1552.  
  1553. taille = taille_i;
  1554.  
  1555. TFT480.setColor(40,40,40); // gris
  1556. TFT480.fillCircle(x, y, taille); // dessine la led éteinte (cercle plein grisé)
  1557.  
  1558. TFT480.setColor(180,180,180);
  1559. TFT480.setFont(BigFont);
  1560. if (txt_etiquette[0] != '\0') {TFT480.print(txt_etiquette, x+15, y-8);} // Etiquette
  1561. }
  1562.  
  1563.  
  1564.  
  1565. void LED::setEtat(uint8_t etat_i)
  1566. {
  1567. if(etat_i == 0)
  1568. {
  1569. TFT480.setColor(40,40,40); // gris
  1570. TFT480.fillCircle(x, y, taille);
  1571. }
  1572. if(etat_i == 1)
  1573. {
  1574. TFT480.setColor(R,G,B); // couleur choisie
  1575. TFT480.fillCircle(x, y, taille);
  1576. }
  1577.  
  1578. }
  1579.  



Le fichier Gene_Wobul_ADF4351_v9_1.h

CODE SOURCE en c
  1. //================================
  2. // version "v9.1"
  3. //================================
  4.  
  5. #ifndef GENE_WOBUL_ADF4351_V9_1_H
  6. #define GENE_WOBUL_ADF4351_V9_1_H
  7.  
  8. #include <stdint.h>
  9. #include "Arduino.h"
  10.  
  11.  
  12. /** ***********************************************************************************
  13. CLASS AffiRect // affiche un nombre ou un petit texte dans un rectangle
  14. ***************************************************************************************/
  15.  
  16. class AffiRect
  17. {
  18. public:
  19.  
  20. uint16_t x0; //position
  21. uint16_t y0;
  22. uint16_t dx0; //dimension
  23. uint16_t dy0;
  24.  
  25. //couleurs
  26. uint8_t Rfond, Vfond, Bfond;
  27. uint8_t Rcadre, Vcadre, Bcadre;
  28. uint8_t Rtxt, Vtxt, Btxt;
  29.  
  30. AffiRect();
  31.  
  32. void init(uint16_t x, uint16_t y, uint16_t dx, uint16_t dy,char txt_etiquette[10]);
  33. void setCouleurCadre(uint8_t *couleur_i);
  34. void setCouleurTxt(uint8_t *couleur_i);
  35. void setCouleurFond(uint8_t *couleur_i);
  36. void flashFond(uint8_t *couleur_i);
  37. void affiche_frequence(uint32_t F_i, uint8_t SB_i); // SB = surbrillance 1 digit
  38. void affiche_valeur(uint32_t valeur, uint8_t nb_chiffres, char txt_unite_i[3]);
  39. void affiche_float(float valeur, uint8_t nb_chiffres, uint8_t nb_dec, char txt_unite_i[3]);
  40. void affiche_HEXA(uint32_t valeur);
  41. void affiche_texte(char txt_i[5]);
  42.  
  43.  
  44. private:
  45. char txt_etiquette[11];
  46. char txt_unite[4];
  47. char texte[11];
  48.  
  49. void traceCadre();
  50. void affi_etiquette();
  51.  
  52. };
  53.  
  54. /** ***********************************************************************************
  55. CLASS AffiV // affiche verticalement sur fond gris
  56. ***************************************************************************************/
  57. class AffiV
  58. {
  59. public:
  60.  
  61. uint16_t x0;
  62. uint16_t y0;
  63. uint8_t R, V, B;
  64.  
  65. AffiV();
  66.  
  67. void init(uint16_t x, uint16_t y);
  68. void setCouleur(uint8_t *couleur_i);
  69. void affiche_texte_V(char txt_i[10]);
  70.  
  71.  
  72. private:
  73. char texte[11];
  74. };
  75.  
  76.  
  77. /** ***********************************************************************************
  78. CLASS LED
  79. ***************************************************************************************/
  80.  
  81. class LED
  82. {
  83. public:
  84.  
  85. uint16_t x;
  86. uint16_t y;
  87.  
  88. uint8_t R;
  89. uint8_t G;
  90. uint8_t B;
  91.  
  92. uint8_t taille; // = dimensions x et y
  93.  
  94. LED();
  95.  
  96. void init(uint16_t x_i, uint16_t y_i, uint8_t R_i, uint8_t G_i, uint8_t B_i, uint_fast8_t taille, char txt_etiquette[5]);
  97. void setEtat(uint8_t etat_i); // = 0 ou 1
  98.  
  99. private:
  100. char txt_etiquette[5];
  101.  
  102. };
  103.  
  104.  
  105. /***** autres declarations ******/
  106.  
  107.  
  108. #define PIN_ADF4351_A PINF
  109. #define PIN_ADF4351_B PINK
  110.  
  111.  
  112. // types
  113. struct parametres
  114. {
  115. uint8_t cle;
  116. uint32_t frequence;
  117. uint8_t pos_curseur_frq; // position du curseur (pour affichage freq)
  118. uint8_t pos_curseur_wob; // position du curseur (pour wobulation)
  119. uint16_t pos_mark;
  120. int32_t offset_y;
  121. int16_t gain;
  122. };
  123.  
  124. // fonctions
  125.  
  126. void InitADC();
  127. void init_ports ();
  128. void init_variables();
  129. void init_leds_switches();
  130. void init_afficheurs_wob();
  131. void init_leds_switches();
  132.  
  133. void save_params_to_EEPROM();
  134. void load_params_from_EEPROM();
  135. int freeRam();
  136. void int_EXT_setup();
  137. void setCouleur(uint8_t *couleur_i, uint8_t R, uint8_t V,uint8_t B);
  138. void inc_FRQ();
  139. void dec_FRQ();
  140. void inc_offset();
  141. void dec_offset();
  142. void inc_pos_curseur_frq();
  143. void dec_pos_curseur_frq();
  144. void inc_pas_curseur_wob();
  145. void dec_pas_curseur_wob();
  146. void affiche_freq_marqueur();
  147. void affi_marqueur();
  148. void affiche_leds();
  149. void affi_min_max();
  150.  
  151. void acquisition_data_WOB();
  152. void scrute_switches();
  153. void calcul_lambda();
  154. double recadre(double valeur_i);
  155.  
  156. void trace_ecran();
  157. void trace_entete();
  158. void trace_pannel_graph();
  159. void trace_afficheurs_wob();
  160. void efface_centre_graph();
  161.  
  162. void affiche_valeurs_registres();
  163. void affi_helps();
  164. void affiche_lambda();
  165. void affiche_params();
  166. void affiche_freq_marqueur();
  167. void affiche_leds();
  168. void affiche_pas_DIV(uint8_t couleur);
  169. void TFT_affiche_chiffre_7seg(uint8_t num, uint16_t x_i, uint16_t y_i );
  170. void TFT_aff_nb_form3 (uint32_t valeur, uint8_t nb_chiffres, uint8_t curseur, uint8_t R, uint8_t G, uint8_t B, uint16_t x_i, uint16_t y_i);
  171. void TFT_aff_nb_form3b (uint32_t valeur, uint8_t nb_chiffres, uint8_t curseur, uint8_t R, uint8_t G, uint8_t B, uint16_t x_i, uint16_t y_i);
  172.  
  173.  
  174.  
  175. void setup();
  176.  
  177.  
  178. #endif
  179.  

17 La class ADF4351 version 5

CODE SOURCE en c
  1. /**
  2. ADF4351-628v5.cpp
  3. Firmware pour piloter un circuit DDS : ADF4351
  4. avec un AVR atmega2560
  5. **/
  6.  
  7. /*
  8. Ce fichier source "ADF4351-628v5.cpp" est libre, vous pouvez le redistribuer et/ou le modifier selon
  9. les termes de la Licence Publique Générale GNU.
  10.  
  11. Un grand merci à Alain Fort F1CJN feb 2,2016
  12. pour son travail :
  13.  
  14. "ADF4251 and Arduino
  15. update march 7, 2016 (ROBOT V1.1 and V1.0)"
  16. -http://f6kbf.free.fr/html/ADF4351%20and%20Arduino_Fr_Gb.htm
  17. -http://f6kbf.free.fr/html/ADF4351_LCD_07032016.zip
  18.  
  19. dont je me suis inspiré pour créer cette objet (class) en C++
  20. J'ai aussi utilisé mon propre travail effectué pour les DDS AD9850 et AD9951
  21.  
  22. Silicium628 - Juillet 2018
  23. */
  24.  
  25. #include <avr/io.h>
  26. #include <util/delay.h>
  27. #include "ADF4351-628v5.h"
  28.  
  29. #define bitSet(value, bit) ((value) |= (1UL << (bit))) // 1UL signifie la valeur 1 au format Unsigned Long (32 bits)
  30. #define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
  31. #define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))
  32.  
  33. // Constructeur
  34. ADF4351::ADF4351() { }
  35.  
  36.  
  37. void ADF4351::PORT_ADF_set_bit( uint8_t masque_bit) // masque_bit doit être du type 0b00001000 (un seul bit à 1)
  38. {
  39. PORT_ADF4351 |= masque_bit;
  40. }
  41.  
  42.  
  43. void ADF4351::PORT_ADF_clear_bit( uint8_t masque_bit) // masque_bit doit être du type 0b00001000 (un seul bit à 1)
  44. {
  45. PORT_ADF4351 &= ~masque_bit;
  46. }
  47.  
  48.  
  49.  
  50. void ADF4351::init()
  51. {
  52. PORT_ADF_set_bit(pin_LE);
  53. Write_All_Register();
  54. PFDRFout=25;
  55. RFint=7000;
  56. RFout = RFint/100 ; // fréquence de sortie
  57. OutputChannelSpacing = 0.01; // Pas de fréquence = 10kHz
  58. }
  59.  
  60.  
  61. void ADF4351::mute(uint8_t valeur)
  62. {
  63. if (valeur == 1) {PORT_ADF_clear_bit(pin_CE);} else {PORT_ADF_set_bit(pin_CE);}
  64. }
  65.  
  66.  
  67. void ADF4351::impulse_CLK()
  68. {
  69. PORT_ADF_set_bit(pin_CLK);
  70. //_delay_us(1); // le fait de supprimer ces tempos augmente grandement la réactivité du circuit.
  71. PORT_ADF_clear_bit(pin_CLK);
  72. //_delay_us(1);
  73. }
  74.  
  75.  
  76. void ADF4351::impulse_LE()
  77. {
  78. PORT_ADF_set_bit(pin_LE);
  79. _delay_us(1);
  80. PORT_ADF_clear_bit(pin_LE);
  81. //_delay_us(1);
  82. }
  83.  
  84.  
  85. void ADF4351::out1octet(uint8_t octet_i)
  86. {
  87. // MSB first (= mode par defaut)
  88. uint8_t i;
  89. uint8_t masque;
  90.  
  91. for (i=0; i < 8; i++)
  92. {
  93. masque = 0b10000000 >> i; // le '1' se déplace de gauche a droite
  94. if ( (octet_i & masque) != 0) {PORT_ADF_set_bit(pin_DATA); } else {PORT_ADF_clear_bit(pin_DATA); }
  95. impulse_CLK();
  96. }
  97. }
  98.  
  99.  
  100. void ADF4351::Write_Register32(uint8_t reg_num) //Programme un registre 32bits
  101. {
  102. uint8_t n; // numéro du registre (0..5) codé par les trois bits de poids faible
  103. uint8_t octet_i;
  104. uint32_t data;
  105.  
  106. data = registers[reg_num];
  107. n = data && 0b00000111;
  108. if (data != memo_registers[n]) // on ne programme le registre que si son contenu doit changer. (cela accelère l'execution du programme)
  109. {
  110. memo_registers[n] = data;
  111. PORT_ADF_clear_bit(pin_LE);
  112.  
  113. for (int i = 3; i >= 0; i--) // boucle sur 4 x 8bits
  114. {
  115. octet_i=(data >> 8 * i) & 0xFF; // décalage et masquage de l'octet
  116. out1octet(octet_i);
  117. }
  118. impulse_LE();
  119. // LE = Load Enable. When LE goes high, the data stored in the 32-bit shift register is loaded
  120. // into the register that is selected by the three control bits (ce sont les trois bits de poids faible)
  121. }
  122. }
  123.  
  124.  
  125. void ADF4351::Write_All_Register() // Programme tous les registres de l'ADF4351
  126. {
  127. for (int i = 5; i >= 0; i--) // programmation ADF4351 en commencant par R5
  128. {
  129. Write_Register32(i);
  130. }
  131. }
  132.  
  133.  
  134. void ADF4351::setFreq(double frequence) // calcule la valeur des registres mais n'envoie rien au circuit ADF4351
  135. {
  136. RFout = frequence;
  137.  
  138. registers[4] = 0x8C803C; // cette valeur n'est pas arbitraire. En particulier les bits 20,21,22 doivent être =0.
  139. // ce qui est le cas : 0x8C803C = 0b 1000 1100 1000 0000 0011 1100
  140. if (RFout >= 2200) { DIV = 1; }
  141. else if (RFout >= 1100) { DIV = 2; registers[4] += (1UL<<20); } // 1UL signifie la valeur 1 au format Unsigned Long en memoire (uint32_t -> 4octets = 32 bits)
  142. else if (RFout >= 550) { DIV = 4; registers[4] += (2UL<<20); }
  143. else if (RFout >= 275) { DIV = 8; registers[4] += (3UL<<20); }
  144. else if (RFout >= 137.5) { DIV = 16; registers[4] += (4UL<<20); }
  145. else if (RFout >= 68.75) { DIV = 32; registers[4] += (5UL<<20); }
  146. else { DIV = 64; registers[4] += (6UL<<20); }
  147.  
  148. INTA = (RFout * DIV) / PFDRFout;
  149. MOD = (PFDRFout / OutputChannelSpacing);
  150. FRACF = (((RFout * DIV) / PFDRFout) - INTA) * MOD;
  151. FRAC = round(FRACF); // On arrondit le résultat
  152.  
  153. registers[0] = 0;
  154. registers[0] = INTA << 15; // décalage de 15 rangs pour ranger cette valeur (16 bits) à sa place dans R0 ; voir p:15 du pdf
  155. registers[0] = registers[0] + (FRAC << 3); // decalage de 3 rangs vers la gauche pour le rentrer dans les bits [DB3..DB14]
  156.  
  157. registers[1] = 0;
  158. registers[1] = MOD << 3; // decalage de 3 pour le rentrer dans les bits [DB3..DB14]
  159. registers[1] = registers[1] + 1 ; // ajout de l'adresse "001"
  160. bitSet (registers[1], 27); // PR1-> Prescaler = 8/9 ; voir p:16 du pdf
  161.  
  162. bitSet (registers[2], 28); // Digital lock == "110" sur b28 b27 b26
  163. bitSet (registers[2], 27); // digital lock
  164. bitClear (registers[2], 26); // digital lock
  165.  
  166. // bitSet (registers[4], 10); //MTLD: Supply current to the RF output stage to be shut down until the part achieves lock
  167.  
  168. Write_All_Register();
  169. }
  170.  
  171.  
  172.  
  173.  



Le fichier ADF4351-628v5.h

CODE SOURCE en c
  1. /**
  2. ADF4351-628v5.h
  3.  
  4. Firmware pour piloter un circuit DDS : ADF4351
  5. avec un AVR atmega2560
  6.  
  7. **/
  8.  
  9.  
  10. #ifndef ADF4351_628V5_H
  11. #define ADF4351_628V5_H
  12.  
  13. #pragma once
  14.  
  15. #define PORT_ADF4351 PORTF
  16.  
  17. #define pin_CLK 0b10000000 // (sortie)
  18. #define pin_DATA 0b01000000 // (sortie)
  19. #define pin_LE 0b00100000 // (sortie)
  20. #define pin_MUXOUT 0b00010000 // (*ENTREE*)
  21. #define pin_CE 0b00001000 // (sortie)
  22.  
  23. class ADF4351
  24. {
  25.  
  26. public:
  27.  
  28. uint32_t registers[6] = {0x4580A8, 0x80080C9, 0x4E42, 0x4B3, 0xBC803C, 0x580005};
  29. uint32_t memo_registers[6] = {0, 0, 0, 0, 0, 0};
  30.  
  31. uint32_t RFint, RFintold, INTA, RFcalc, PDRFout, MOD, FRAC;
  32.  
  33. double RFout, REFin, PFDRFout, OutputChannelSpacing, FRACF;
  34.  
  35. double RFoutMin = 35; // 35 MHz
  36. double RFoutMax = 4400; // 4400MHz = 4.4 GHz
  37. double REFinMax = 250;
  38.  
  39. uint8_t DIV;
  40.  
  41. ADF4351();
  42.  
  43. void init();
  44. void mute(uint8_t valeur);
  45. void setFreq(double frequence); // en MHz (avec decimales) -> calcule la valeur des registres mais n'envoie rien au circuit ADF4351
  46. void Write_Register32(uint8_t reg_num); //Programme physiquement un registre 32bits. SetFreq() doit avoir été appelé avant.
  47. void Write_All_Register(); // Programme physiquement tous les registres de l'ADF4351
  48.  
  49. private:
  50.  
  51. bool _powerdown, _auxEnabled, _rfEnabled, _feedbackType;
  52. void PORT_ADF_set_bit( uint8_t masque_bit);
  53. void PORT_ADF_clear_bit( uint8_t masque_bit);
  54. void impulse_CLK();
  55. void impulse_LE();
  56. void out1octet(uint8_t octet_i);
  57.  
  58. };
  59.  
  60. #endif
  61.  

Un grand merci à Alain Fort F1CJN feb 2,2016
pour son travail :

"ADF4251 and Arduino
update march 7, 2016 (ROBOT V1.1 and V1.0)"
-http://f6kbf.free.fr/html/ADF4351%20and%20Arduino_Fr_Gb.htm
-http://f6kbf.free.fr/html/ADF4351_LCD_07032016.zip

dont je me suis servi pour créer cette objet (class) en C++
J'ai aussi utilisé mon propre travail effectué pour les DDS AD9850 et AD9951

Silicium628 - Juillet 2018

18 Evolution

23 juillet 2018: Je fais évoluer cet appareil chaque jour, et je publie les mises à jour du programme pluri-quotidiennement...
J'ai ainsi inversé l'affichage de la courbe (elle était "tête en bas") et j'ai ajouté un fin marqueur de fréquence, en surimpression, déplaçable et très précis (100Hz).
Il faut noter aussi que pour l'instant je ne garanti pas la valeur des composants de la partie analogique, je publierai un nouveau schéma prochainement lorsque ces valeurs seront définitives.

19 Réponse fréquentielle d'un circuit LC

.

29 juillet 2018:
Il s'agit d'un circuit LC (une inductance en // sur un condensateur) dit "circuit bouchon" en réalité RLC puisque les composants parfaits n’existent pas et que le circuit est attaqué au travers d'une résistance de 1K...
Ce circuit est dit du second ordre, c'est à dire qu'il est décrit par une équation différentielle du second ordre. Nous avions étudié ce circuit : CIRCUIT RLC

Sur la photo nous remarquons :
  • une ligne bleue verticale (à 58MHz), c'est le marqueur
  • l'affichage de la fréquence du marqueur (en petits caractères bleus)
  • un trait horizontal rouge en bas indiquant une zone de fréquences non couvertes (<33MHz)
  • L'étiquette "Pas WOB" a été remplacée par "MHz /div" ce qui est plus parlant.
Nous explorons donc ici le domaine des fréquences les plus basses accessibles à l'appareil. Reste à voir ce qui se passe dans les UHF au delà du GHz.

20 Tests en UHF

Comme vous pouvez le voir sur le diaporama en haut de cet article, j'ai poussé les tests de circuits LC jusqu'à près de 700MHz (avec le détecteur à diodes 1N6263) Passé 500MHz la courbe de réponse s'élargit et visiblement s'éloigne de plus en plus de la courbe en cloche attendue. Il faut dire que pour ces fréquences, les inductances et capacités réparties deviennent prépondérantes par rapport à celles des composants. (J'ai utilisé des selfs cms 0802 mais des condensateurs céramiques petits mais non cms. Et le fait de couper plus courts les pattes du condensateur a fait varier la fréquence de près de 100MHz !). Et puis on se rapproche de la fréquence limite des diodes de détection utilisées (1GHz). J'étudie une solution pour atteindre les 4GHz.

.

21 suite...

01 août 2018:
J'ai ajouté de nouvelles fonctionnalités

Un OM que certains d'entre vous connaissent certainement, Laurent de F4FDW, que je remercie ici, voyant ce site m'a contacté et depuis m'aide à améliorer cet appareil, à le rendre utile pour les radioamateurs et à trouver des solutions au problème de détection en SHF. Mieux que ça, de son côté il a expérimenté à partir de la présente réalisation et m'a fait parvenir des images très prometteuses montrant la réponse de filtres jusqu'à plus de 2GHz. Laurent, avait de son côté déjà utilisé l'ADF4351 comme vous pouvez le voir sur son site :

J'ai commandé certains composants dont il m'a parlé et je vous tiendrai au courant lorsque j'obtiendrai des résultats tangibles.
à suivre...

22 Les résultats obtenus par Laurent de F4FDW

.

26 août 2018:
Voici le résultat avec un filtre de bande 23cm (1300MHz)

D'autres résultats tout aussi prometteurs ont été obtenus par Laurent, par exemple dans la bande 13cm (2.3GHz)

Mais je vous invite à découvrir tout ça plus en détails sur la page dédié de son site :

23 Version (v7_3) - Couleurs

28 août 2018:
J'ai ajouté la possibilité de régler le gain et l'offset y pendant le fonctionnement, par action sur les encodeurs rotatifs.

On y accède en basculant un nouveau (un troisième donc) switch.

Je parle de réglage du gain, je devrais plutôt dire réglage du coefficient d'amplification logiciel, parce qu'il agit suivant une échelle linéaire et non logarithmique. Et on peut en diminuant la valeur absolue, atteindre zéro et le dépasser pour obtenir des valeurs négatives (-1, -2,...) ce qui est voulu et a pour effet d'inverser le graphe (symétrie suivant l'axe Y) ce qui devenait nécessaire du fait que nous testons actuellement F4FDW et moi, divers détecteurs, linéaires ou logarithmiques, dont les réponses sont de diverses polarités.
De nouvelles indications sont affichées :
  • Trois petites "leds" colorées indiquant la positions des 3 switches
  • Des rappels, en écriture verticale en bord droit de l'écran, de la fonction des encodeurs rotatifs compte tenu de la position actuelles de switches. On sait de cette façon ce qu'on va modifier AVANT de tourner les boutons
  • Les petits cadres d'affichage de valeurs numériques changent de couleur en fonction de la position des switches, et flashes brièvement lors du changement de mode, de façon à repérer facilement ceux qui sont en cours d'édition.

J'en ai profité pour ré-écrire entièrement certaines fonctions, en particulier les traitements des interruptions matérielles.

J'ai aussi reçu de nouveaux composants pour la détection micro-onde (détecteur logarithmique AD8318) que j'ai fait figurer sur le schéma, et qui commence à me procurer des résultats intéressants.


24 Résultats obtenus avec le détecteur logarithmique AD8318

4 septembre 2018:
Voici à gauche la courbe tracée lors de l'analyse d'un filtre "sérieux" (de type "Comb-Line Filter" de marque RLC ELECTRONICS) après que j'ai joué pendant une heure avec ses 7 petites vis de réglage pour le faire fonctionner dans la bade amateur des 23cm, sans vraiment y parvenir, il ne semble se cantonner obstinément juste après la fin de ladite bande. Notre wobulateur est bien adapté pour ce genre d'opération. Je vais toutefois chercher à augmenter la vitesse de balayage afin de rendre son utilisation plus confortable et de gagner du temps lors d'une telle opération de réglage.

La photo de droite montre le même filtre réglé dans sa bande d'origine (1800 à 1900 MHz). Ce filtre de récupération dont on trouve ses frères jumeaux sur eBay a donc sans doute servi dans un relais GSM.

25 Version (v8_3) - Mode d'analyse rapide - 4eme switch - Enregistrement automatique des paramètres en mémoire EEprom.

06 septembre 2018:
Une nouvelle version (v8_3) est en ligne avec sur un mode d'analyse hyper rapide (deux balayages par seconde environ) ce qui permet de voir les réglages de l'accord d'un filtre "en temps réel". Ainsi, rendre la réponse de ce filtre plus "carrée" ne m'a pris qu'une minute, contre une heure le coup précédent (La fréquence obtenue ne correspond à aucune utilisation particulière, je fais juste des essais du vobulateur pour le moment).

26 Version (v8_4) - Switch trois positions en remplacement de deux switches

12 septembre 2018:
Comme vous pouvez le voir sur le schéma, j'ai remplacé les deux switches servant à choisir le mode de saisie par un seul à 3 pos. Ces deux switches à deux positions pouvaient logiquement prendre 4 états distincts alors qu'il n'y a que trois modes de saisie :
  • saisie fréquence
  • saisie positions marqueur & MHz/div
  • saisie Gain & Offset
D'où rapidement prise de tête !!
Le switch à trois positions mécaniques stables convient bien mieux, et judicieusement disposé reflète bien l'affichage du mode choisi.

C'est la modification principale de cette nouvelle version v8_4.

Mais bien des améliorations restent à faire, comme :
  • l'étalonnage et la graduation en dB de l'écran de wobulation (pour une utilisation avec un détecteur logarithmique d'enveloppe du signal tel que l'AD8318)
  • L'affichage de l'intégralité du marqueur lors de son déplacement, sans effacer le tracé de la courbe (cela nécessite la mémorisation de la courbe en RAM entre deux balayages, je vais voir si les 8 K octets de SRAM de l'ATmega 2560 suffisent (en plus des variables du programme), à priori oui, largement.)

27 Version (v8_5) - Mémorisation de la courbe en SRAM - Marqueur

Comme prévu, l'espace en SRAM de l'AT2560 a été suffisamment important pour y enregistrer la totalité des points formant un balayage du wobulateur, permettant ainsi de placer (et déplacer) des tracés par dessus la courbe sans l’abîmer (ce qu'on appelle parfois des "sprites", ou superposition de calques). En fait de superposition, cela consiste à redessiner l'arrière plan lorsque l'objet au premier plan s'est déplacé.

Première mise à profit de la chose : le trait vertical violet symbolisant le marqueur de fréquence peut traverser verticalement tout l'écran sans effacer des portions de la courbe lorsqu'on le déplace latéralement.

Accessoirement cette mise en mémoire permettra :
  • de rappeler une courbe de réponse mémorisée pour comparaison avec l'actuelle.
  • d'envoyer les données d'un balayage vers un PC (ou un gadget tournant sous Androïd) via une liaison série USB rapide pour visu, analyse, impression...
Voilà, y'a plus qu'à...

28 Utilisation d'un détecteur linéaire à diode schottky

14 sept 2018:
J'ai testé le wobulateur avec un détecteur linéaire passif à diodes schottky acheté sur eBay. Il fonctionne correctement jusqu'à plus de 3GHz. Son signal de sortie n'est donc pas logarithmique, il est par conséquent bien moins sensible que l'AD8318. En contre partie il ne nécessite aucune alimentation. Quant à la sensibilité moins grande, ce peut être un avantage lors de tests de circuits expérimentaux pas ou insuffisamment blindés tel ce filtre maison en pi, réalisé sur un PCB double face avec des composants cms. Dans ce cas de figure l'AD8318 "reniflait" beaucoup de choses de niveau très faible qui rendait la mise au point extrêmement délicate. Toutefois un détecteur logarithmique utilisé avec du matériel pro montrera toute supériorité.

29 Le filtre shf en composants cms

Ce filtre à refusé de fonctionner correctement tant que je n'ai pas ajouté les via connectant les pistes GND au plan de masse de l'autre face. A ces fréquences ce qui peut sembler n'être qu'un détail est en fait nécessaire et incontournable.

30 Le filtre connecté au wobu et sa réponse :

On atteint là sans doute la limite de ce qu'on peut faire avec des composants discrets, fussent-ils cms, l'inductance étant de l'ordre du nanohenry et le condensateur de l'ordre du pF. C'était juste "pour voir" ! Bien entendu les composants intégrés sur une puce peuvent monter un ordre de grandeur plus haut comme on peut le constater facilement (par exemple systèmes vidéo en 5.8 GHz, ou radar de proximité en 10 GHz, LNB pour réception satellite, sans oublier nos petits synthétiseurs de fréquences...)

31 Version (v9_0) - échelle logarithmique en dB/div

Voici donc une fonctionnalité promise de longue date : Le réglage du "gain" devient un réglage du nombre de dB/division.

La progression du réglage est la suivante :
-1 dB/div
-2 dB/div
-5 dB/div
-10 dB/div

Cet affichage en dB n'a de sens qu'avec l'utilisation d'un détecteur de puissance shf tel que l'AD8318 (25mV/dB). Avec un autre détecteur ayant une sensibilité différente, il faudra retoucher une valeur dans le programme principal (dans la fonction "recadre()")

D'autre part j'ai dû étendre la plage de réglage de l'offset de -2000 à +2000 afin que l'on puisse zoomer sur un détail en étant sur une forte sensibilité (par exemple en 1dB/div). On peut ainsi voir le petit cailloux au somment de la grande montagne, où encore le petit brin d'herbe à son pied.

Une variable ("polatite") en tout début du programme principal permet (avant compilation) d'inverser la courbe suivant la polarité du signal de sortie du détecteur.

Dans le cas de l'utilisation d'un détecteur linéaire (diodes schottky), les signal s'affichera correctement, simplement l'unité "dB/div" ne sera pas pertinente.

Que dire de plus ? eh bien qu'il faut tester tout ça ! (en particulier la justesse de la graduation, un truc pour les OM ça... moi il me manque pour l'instant un atténuateur 10dB fiable en shf, il est en commande)
Je pense que la première chose que je ferai lorsque je vais le recevoir, ce sera de transformer celui du bas (0 dB... merci bien) en 10dB ou 20dB.

32 Version (v9_1) - Deux types de détecteurs


Les deux types de détecteurs (linéaire et logarithmique) avec le traitement numérique et l'affichage qui leurs sont associés sont maintenant pris en compte. Ci dessus la réponse d'un filtre de bande centré sur 2.4GHz de type microbande (microstrip) connecté à un détecteur passif linéaire.

33 Documents techniques :

Pour ma part, j'ai écrit et compilé tout ça avec l'éditeur Geany sous Linux Mint 18.3 Sylvia 64-bit.

34 Liens :

Haute fréquence :

Règlementation :

  • Décision n° 00-1364 de l'Autorité de régulation des télécommunications (ART) précisant les conditions d'utilisation des installations de radioamateur (en particulier l'annexe 3: Caractéristiques techniques à respecter lors de l'utilisation d'une installation radioamateur). C'est très restrictif. ATTENTION: ceci concerne les Radioamateurs, pour les autres personnes (particuliers) toute émission, tout rayonnement en VHF sont purement et simplement interdits.

Filtres :

Filtres de type microstrip (microbande) :

Logiciels pour le calcul et la simulation de filtres :

-RF SIM 99 (sous Linux avec wine) : -Qucs (sous Linux directement) :

Bobinages - selfs :

Théorie des diviseurs de fréquence - bascules RS, T, D, JK, circuiterie interne des portes logiques :

Multiplication de fréquences - convertisseurs de fréquences :


35 -



Me contacter à propos de cet article :

Pseudo :
Question mathématique :
Click to reload image
=
cliquez sur l'image pour un faire autre calcul.
Réponses à certaines de vos questions...

le: 04-09-2018

Bonjour
Super votre projet.
Puis-je me permettre de vous demander où avez-vous acheté la carte avec l’AD8318?
Merci
Cordialement
Patrice


-> Bonjour Patrice

Merci. voici un lien :

https://www.ebay.fr/itm/1Mhz-8000Mhz-RF-Power-Meter-RF-Logarithmic-Detector-AD8318-65dBm-5dBm/362423255928?hash=item54621c0378:g:OfkAAOSwSB1aoRxC

Sinon il suffit de chercher sur eBay "AD8318" dans "monde entier" et "achat immédiat"; prix environ 14€
(moi je ne vends rien...)
1016