Générateur UHF bande s - avec un ADF4351 Synthesizer

Cet appareil génère des fréquences de 35 MHz à ... 4400MHz. Affichage TFT 480x360
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

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::impulse_clk()
  50. {
  51. _delay_us(5); // temps necessaire à la stabilisation du niveau envoyé
  52. port_ADF4351 |= pin_CLK;
  53. _delay_us(5);
  54. port_ADF4351 &= ~pin_CLK; // l'operateur "~" donne le complement (inverse des bits). ne pas confondre avec l'opérateur "!"
  55. _delay_us(5);
  56. }
  57.  
  58.  
  59. void ADF4351::impulse_LE()
  60. {
  61. // _delay_us(10);
  62. port_ADF4351 |= pin_LE;
  63. _delay_us(10);
  64.  
  65. port_ADF4351 &= ~pin_LE;
  66. _delay_us(10);
  67. }
  68.  
  69.  
  70. void ADF4351::set_LE()
  71. {
  72. port_ADF4351 |= pin_LE;
  73. }
  74.  
  75.  
  76. void ADF4351::reset_LE()
  77. {
  78. port_ADF4351 &= ~pin_LE;
  79. }
  80.  
  81.  
  82. void ADF4351::set_DATA()
  83. {
  84. port_ADF4351 |= pin_DATA;
  85. }
  86.  
  87.  
  88. void ADF4351::reset_DATA()
  89. {
  90. port_ADF4351 &= ~pin_DATA;
  91. }
  92.  
  93.  
  94.  
  95. void ADF4351::out1octet(uint8_t octet_i)
  96. {
  97. // MSB first (= mode par defaut)
  98. uint8_t i;
  99. uint8_t masque;
  100.  
  101. for (i=0; i < 8; i++)
  102. {
  103. masque = 0b10000000 >> i; // le '1' se deplace de gauche a droite
  104. if ( (octet_i & masque) != 0) { port_ADF4351 |= pin_DATA; } else { port_ADF4351 &= ~pin_DATA; }
  105. impulse_clk();
  106. }
  107. }
  108.  
  109.  
  110.  
  111. void ADF4351::Write_Register32(const uint32_t value) //Programme un registre 32bits
  112. {
  113. uint8_t octet_i;
  114. reset_LE();
  115. for (int i = 3; i >= 0; i--) // boucle sur 4 x 8bits
  116. {
  117. octet_i=(value >> 8 * i) & 0xFF; // décalage et masquage de l'octet
  118. out1octet(octet_i);
  119. }
  120. impulse_LE();
  121. }
  122.  
  123.  
  124. void ADF4351::Write_All_Register() // Programme tous les registres de l'ADF4351
  125. {
  126. for (int i = 5; i >= 0; i--) // programmation ADF4351 en commencant par R5
  127. {
  128. Write_Register32(registers[i]);
  129. }
  130. }
  131.  
  132.  
  133. void ADF4351::setFreq(double frequence)
  134. {
  135. RFout = frequence;
  136.  
  137. registers[4] = 0x8C803C; // cette valeur n'est pas arbitraire. En particulier les bits 20,21,22 doivent être =0.
  138. // ce qui est le cas : 0x8C803C = 0b 1000 1100 1000 0000 0011 1100
  139. if (RFout >= 2200) { DIV = 1; }
  140. 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)
  141. else if (RFout >= 550) { DIV = 4; registers[4] += (2UL<<20); }
  142. else if (RFout >= 275) { DIV = 8; registers[4] += (3UL<<20); }
  143. else if (RFout >= 137.5) { DIV = 16; registers[4] += (4UL<<20); }
  144. else if (RFout >= 68.75) { DIV = 32; registers[4] += (5UL<<20); }
  145. else { DIV = 64; registers[4] += (6UL<<20); }
  146.  
  147. INTA = (RFout * DIV) / PFDRFout;
  148. MOD = (PFDRFout / OutputChannelSpacing);
  149. FRACF = (((RFout * DIV) / PFDRFout) - INTA) * MOD;
  150. FRAC = round(FRACF); // On arrondit le résultat
  151.  
  152. registers[0] = 0;
  153. registers[0] = INTA << 15; // décalage de 15 rangs pour ranger cette valeur (16 bits) à sa place dans R0 ; voir p:15 du pdf
  154. registers[0] = registers[0] + (FRAC << 3); // decalage de 3 rangs vers la gauche pour le rentrer dans les bits [DB3..DB14]
  155.  
  156. registers[1] = 0;
  157. registers[1] = MOD << 3; // decalage de 3 pour le rentrer dans les bits [DB3..DB14]
  158. registers[1] = registers[1] + 1 ; // ajout de l'adresse "001"
  159. bitSet (registers[1], 27); // PR1-> Prescaler = 8/9 ; voir p:16 du pdf
  160.  
  161. bitSet (registers[2], 28); // Digital lock == "110" sur b28 b27 b26
  162. bitSet (registers[2], 27); // digital lock
  163. bitClear (registers[2], 26); // digital lock
  164.  
  165. // bitSet (registers[4], 10); //MTLD: Supply current to the RF output stage to be shut down until the part achieves lock
  166.  
  167. Write_All_Register(); // Programme tous les registres de l'ADF4351
  168. }
  169.  
  170.  
  171.  
  172.  



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.  
  23. class ADF4351
  24. {
  25.  
  26. public:
  27.  
  28. uint32_t registers[6] = {0x4580A8, 0x80080C9, 0x4E42, 0x4B3, 0xBC803C, 0x580005};
  29.  
  30. uint32_t RFint, RFintold, INTA, RFcalc, PDRFout, MOD, FRAC;
  31.  
  32. double RFout, REFin, PFDRFout, OutputChannelSpacing, FRACF;
  33.  
  34. double RFoutMin = 35; // 35 MHz
  35. double RFoutMax = 4400; // 4400MHz = 4.4 GHz
  36. double REFinMax = 250;
  37. double PDFMax = 32;
  38.  
  39. uint8_t DIV;
  40. uint8_t lock=2;
  41.  
  42. ADF4351();
  43.  
  44. void init();
  45.  
  46. void reset_LE();
  47. void set_LE();
  48. void reset_DATA();
  49. void set_DATA();
  50. void impulse_clk();
  51. void impulse_LE();
  52. void out1octet(uint8_t octet_i);
  53. void Write_Register32(const uint32_t value); //Programme un registre 32bits
  54. void Write_All_Register(); // Programme tous les registres de l'ADF4351
  55. void setFreq(double frequence); // en MHz (avec decimales)
  56.  
  57. private:
  58.  
  59. bool _powerdown, _auxEnabled, _rfEnabled, _feedbackType;
  60.  
  61. };
  62.  
  63. #endif
  64.  

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...

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 Documents techniques :

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

14 -



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...
63