Wobulateur UHF 4GHz ADF4351 + AD9850 + ESP32 + ILI9341

J'avais déjà réalisé, il y a plusieurs années, un tel géné UHF utilisant un ADF4351, mais sur une base arduino Mega2560. Le temps passant les library utilisées, surtout la lib UTFT sont devenues obsolètes, et celles qui leurs ont succédé posent des problèmes de dépendances au point que je ne peux plus moi-même assurer les mises à jour. Les encodeurs rotatifs pas à pas eux aussi posent problème, ils vieillissent très mal !

J'ai donc décidé de faire un nouvel appareil basé cette fois sur un ESP32 (beaucoup plus puissant et rapide que le Mega2560) et un afficheur ILI9341 avec écran tactile permettant de se passer des encodeurs mécaniques.
1 - Première vidéo : la saisie de la fréquence au stylet
2 - Le schéma de ma carte ESP32 + ILI9341 2.8
3 - Le schéma complet du géné HF - UHF wobulé
4 - Le code source (fichier main.cpp)
5 - VIDEO : Wobulation 300MHz
6 - Le signal 300MHz wobulé vu à l'oscilloscope
7 - Signal 4MHz généré par le module AD9850
8 - PREMIERE LUEUR !
9 - Wobulations avec l'AD9850
10 - Formule de Thomson
11 - Premier test en très basse fréquence (80kHz)
12 - Test de transfos FI 455kHz et 10.6MHz
13 - Test d'un filtre céramique 10.7MHz
14 - Circuit avec C = 270 pF et une self inconnue :
15 - Circuit avec C = 82 pF
16 - Circuit avec C = 47 pF
17 - Circuit avec C = 18 pF
18 - Circuit avec C = 10 pF
19 - Wobulation avec l'ADF4351 - circuit LC - 170MHz
20 - Filtre en Pi 2.4GHz
21 - Wobulation de ce filtre en Pi - 2.4GHz
22 - Test d'un autre filtre 2.4GHz
23 - petite capture d'écran...
24 - Le détecteur d'amplitude AM
25 - Un autre détecteur AM
26 - Quelques boucles d'induction et diverses selfs enrobées
27 - Les modules DDS AD9850 & ADF4351
28 - L'afficheur 2.8 et le module ESP32
29 - Le board (carte perso reliant l'afficheur à l'ESP32 )
30 - Cet appareil dans son boîtier
31 - Disposition des modules dans la boite
32 - Affichage bande passante et facteur de qualité
33 - Facteur de qualité d'un filtre blindé FI 455MHz
34 - Facteur de qualité d'un filtre céramique 10.7MHz
35 - Facteur de qualité d'un quartz 16MHz
36 - Test par GPS de la précision de la fréquence de l'AD9850 et de celle du quartz 16MHz
37 - On va dorloter le quartz !
38 - documents

1 Première vidéo : la saisie de la fréquence au stylet

Fréquence sur 10 chiffres, jusqu'à 4.4GHz
Remarque : l'image de l'oscillo c'est juste pour faire joli et pour montrer que ma carte est capable de lire rapidement des données sur la SDcard et d'afficher des images. Elle permet aussi les copies d'écran vers la SDcard.

Voir pour patienter mon article :

qui utilise déjà cette même carte.




2 Le schéma de ma carte ESP32 + ILI9341 2.8

Cette carte perso remplace la célèbre "carte jaune CYD" avec des fonctions inutiles en moins et beaucoup de ports GPIO libres en plus. J'ai utilisé cette carte une première fois pour le petit récepteur radio SW-AM, FM, AIRband présenté ICI :

3 Le schéma complet du géné HF - UHF wobulé

Le seul circuit imprimé c'est celui de la carte qui relie l'ESP32 devKit à l'afficheur 2.8" ILI9341 + lecteur SDcard (encadré par les pointillés bleus).

4 Le code source (fichier main.cpp)

CODE SOURCE en C++
  1. /* ************************************************************************************
  2. Gene UHF et Wobulateur AD9850 + ADF4351
  3. par Silicium628
  4. pour ma carte (fab : JLCPCB) ESP32 Wroom + afficheur 2.8" TFT 240x320 + SDcard
  5.  
  6. Cette version utilise la SDcard
  7.  
  8. ************************************************************************************ */
  9.  
  10. // REMARQUES:
  11. // 1) lorsque la carte est connectée sur un bus USB de l'ordinateur,
  12. // un terminal série tel que CuteCom affichera beaucoup de choses en temps réel.
  13. // 2) Pour s'y retrouver dans le code, le plus simple est de partir de la fonction 'loop()' qui appelle les autres
  14. // 3) Le fichier User_Setup dans le dossier 'lib/TFT_eSPI' est spécifique à cette configuration
  15. // pour ce qui concerne les connexions mais aussi les limitations de fréquence du bus SPI
  16.  
  17.  
  18. #include <Arduino.h>
  19. #include "main.h"
  20. #include "ADF4351-ESP32.h"
  21. #include "AD9850-ESP32.h"
  22.  
  23. String version = "3.1.1";
  24.  
  25. #include "FS.h"
  26. #include "SD.h"
  27. #include "Wire.h"
  28. #include <stdint.h>
  29.  
  30. #include <Free_Fonts.h>
  31. #include "TFT_eSPI.h" // Hardware-specific library
  32. #include "SPI.h"
  33. // #include "Digit_Font.h"
  34. #include <XPT2046_Touchscreen.h>
  35.  
  36. #include "constantes2_628.h"
  37. #include "Couleurs_AEC.h"
  38.  
  39. int style = 0; // 0 bleu, 1 vert, 3 jaune
  40.  
  41. ADF4351 CarteADF4351;
  42. AD9850 CarteAD9850;
  43.  
  44.  
  45. #define SPI_READ_FREQUENCY 16000000
  46.  
  47. #define XPT2046_IRQ 36
  48. #define XPT2046_MOSI 32 //T_DI 32
  49. #define XPT2046_MISO 39 //T_DO 39
  50. #define XPT2046_CLK 25
  51. #define XPT2046_CS 33
  52.  
  53. const int GPIO_SDA = 21;
  54. const int GPIO_SCL = 22;
  55.  
  56. const int analogPin = 35;
  57. const int boutonPin1 = 4; // GPIO4
  58.  
  59. //const int GPIO_BL = 4; // pour LED backlight ILI9341- INUTILISE POUR CETTE APPLICATION - relier backlight à +3v3
  60. // donc le GPIO4 est libre...
  61.  
  62. const int _DX = 320;
  63. const int _DY = 240;
  64.  
  65. SPIClass mySpi = SPIClass(VSPI);
  66. XPT2046_Touchscreen ts(XPT2046_CS, XPT2046_IRQ);
  67.  
  68. SPIClass mySpi2 = SPIClass(HSPI);
  69.  
  70. TFT_eSPI TFT = TFT_eSPI(); // Configurer le fichier User_Setup.h de la bibliothèque TFT_eSPI au préalable
  71.  
  72. TFT_eSprite sprite_frq = TFT_eSprite(&TFT); // grands chiffres en haut
  73. TFT_eSprite sprite_freq_W_min = TFT_eSprite(&TFT);
  74. TFT_eSprite sprite_freq_W_max = TFT_eSprite(&TFT);
  75.  
  76. uint16_t FRQ_x0;
  77. uint16_t FRQ_y0;
  78.  
  79. const uint64_t F_min_AD9850 = 1;
  80. const uint64_t F_max_AD9850 = 52000000; // 52 MHz , au delà le signal est pourri !
  81.  
  82. const uint64_t F_min_ADF4351 = 33000000;
  83. const uint64_t F_max_ADF4351 = 4400000000;
  84.  
  85.  
  86. struct ETALON_TS // etalon touch screen
  87. {
  88. int16_t x0;
  89. int16_t dx;
  90. int16_t y0;
  91. int16_t dy;
  92. };
  93.  
  94. ETALON_TS eTS;
  95.  
  96. struct POINT
  97. {
  98. uint16_t x;
  99. uint16_t y;
  100. };
  101.  
  102. // coordonnées des points de calibrage de l'écran tactile
  103. // (calibration ? étalonnage ?? ajustage ??? boaf, m'enfin !!)
  104. POINT A, B;
  105.  
  106. uint8_t SDcardOk=0;
  107.  
  108. uint16_t x_touch, y_touch;
  109. uint16_t memo_x_touch, memo_y_touch;
  110.  
  111. uint16_t compte1=0;
  112. uint16_t compte2=0;
  113.  
  114. uint8_t num_capture = 0;
  115.  
  116. uint64_t frequence=10000;
  117. uint32_t pas_W = 0; // pas (de wobulation)
  118. //uint64_t freq_W_min;
  119. uint64_t freq_W_max;
  120. uint64_t Fmin, Fmax;
  121.  
  122.  
  123. double F_9850; // fréquence à envoyer à l'AD9850 (en 1/10 Hz)
  124. double F_4351; // fréquence à envoyer à l'ADF4351 (en MHz avec décimales)
  125.  
  126. //boolean le_pas_est_choisi = false;
  127. boolean soft_setup =false;
  128. boolean do_capt_screen = false;
  129.  
  130. boolean stop1=false;
  131. boolean en_boucle = false;
  132.  
  133. uint16_t JAUNE_chiffres = 65504;
  134. uint16_t VERT_chiffres = 2016;
  135.  
  136. TOUCH_BOUTON bt_RAZ;
  137.  
  138. //fréquences presets
  139. TOUCH_BOUTON bt_455k;
  140. TOUCH_BOUTON bt_4M;
  141. TOUCH_BOUTON bt_10_7M;
  142. TOUCH_BOUTON bt_27M;
  143. TOUCH_BOUTON bt_35M;
  144. TOUCH_BOUTON bt_41M;
  145. TOUCH_BOUTON bt_144M;
  146. TOUCH_BOUTON bt_432M;
  147. TOUCH_BOUTON bt_2400M;
  148.  
  149.  
  150. TOUCH_BOUTON bt_AD9850;
  151. TOUCH_BOUTON bt_ADF4351;
  152.  
  153. TOUCH_BOUTON bt_mode_GENE;
  154. TOUCH_BOUTON bt_mode_WOBU;
  155.  
  156. TOUCH_BOUTON bt_STOP_1; // du balayage fréquenciel
  157. //TOUCH_BOUTON bt_STOP_2; // de la boucle de déplacement du curseur
  158.  
  159. TOUCH_BOUTON bt_polarite;
  160.  
  161. TOUCH_BOUTON bt_gain_P; // gain +
  162. TOUCH_BOUTON bt_gain_M; // gain -
  163.  
  164. TOUCH_BOUTON bt_pas_P; // incrémente le pas de wobulation suivant la suite 1,2,5...
  165. TOUCH_BOUTON bt_pas_M;
  166. uint8_t num_pas=0;
  167.  
  168. TOUCH_BOUTON bt_boucle;
  169.  
  170. TOUCH_BOUTON bt_calibrate;
  171. TOUCH_BOUTON bt_TEST;
  172.  
  173. CHF_PAD chf_pad1;
  174.  
  175. enum OUTPUT_MODE {MODE_9850, MODE_4351};
  176. OUTPUT_MODE out_mode1;
  177. OUTPUT_MODE memo_out_mode1;
  178.  
  179. enum FONCTION_MODE {MODE_ACCUEIL, MODE_GENE, MODE_WOBU};
  180. FONCTION_MODE f_mode1;
  181. //FONCTION_MODE memo_f_mode1;
  182.  
  183. enum POLARITE {_DIR, _INV};
  184. POLARITE polarite1 = _DIR;
  185.  
  186. uint32_t liste_pas_W_a[3] = {1, 2, 5}; // num 0 à 3
  187. uint64_t liste_pas_W_b[7] = {1, 10, 100, 1000, 10000, 100000, 1000000}; // numéros 0 à 6
  188.  
  189. uint16_t memo_img[200*32];
  190. float tableau_acq[320];
  191.  
  192.  
  193. void init_sprites()
  194. {
  195. sprite_frq.createSprite(315, 45);
  196. sprite_frq.setTextDatum(MR_DATUM); // alignement du texte
  197.  
  198. sprite_freq_W_min.createSprite(80, 8);
  199. sprite_freq_W_max.createSprite(80, 8);
  200. }
  201.  
  202.  
  203. void init_variables_globales()
  204. {
  205. FRQ_x0 = 2;
  206. FRQ_y0 = 30;
  207.  
  208. //frequence = 4123456789; // 4.12 GHz
  209. frequence = 60000000; // 60MHz
  210. }
  211.  
  212.  
  213. void init_1_bouton(uint8_t n_font, uint16_t xi, uint16_t yi, uint8_t dx, uint8_t dy, String si, TOUCH_BOUTON *bouton_i)
  214. {
  215. bouton_i->init(xi, yi, dx, dy, 3, GRIS_5);
  216. bouton_i->cliked = false;
  217. bouton_i->selected = false;
  218. bouton_i->label = si;
  219. bouton_i->affiche(BLANC, VERT, n_font);
  220. }
  221.  
  222.  
  223. void popup(uint16_t x, uint16_t y, String s1, String s2)
  224. {
  225. TFT.readRect(x, y, 200, 32, memo_img); // memorise image avant de tracer le texte
  226.  
  227. TFT.fillRect(x, y, 200, 32, NOIR);
  228. TFT.drawRect(x, y, 200, 32, VERT);
  229. TFT.setTextColor(BLANC, NOIR);
  230. TFT.setFreeFont(FF1); // 1ere ligne en 'gros' caractères
  231. TFT.drawString(s1, x+2, y+2);
  232. TFT.setFreeFont(FF0); // 2eme ligne en plus petits caractères (mais elle peut donc être plus longue)
  233. TFT.drawString(s2, x+2, y+20);
  234. delay(1000);
  235. TFT.pushRect(x, y, 200, 32, memo_img);
  236. }
  237.  
  238.  
  239.  
  240. void test_bouton_physique1()
  241. {
  242. int v = digitalRead(boutonPin1);
  243. if (v == 0)
  244. {
  245. capture_ecran();
  246. }
  247. }
  248.  
  249.  
  250.  
  251. // Fonction optimisée pour l'afficheur ILI9341 320x240 avec la library 'TFT_eSPI'
  252. // ne convient PAS pour les ESP32 Wroom + afficheur 3.5" TFT 480x320
  253. void capture_ecran() // enregistre image bmp 320x240 RGB565 (5+6+5 = 16bits/px)
  254. {
  255. Serial.println("capture_ecran()");
  256.  
  257. if (SDcardOk==0)
  258. {
  259. Serial.println("SDcard absente, capture ecran impossible");
  260. return;
  261. }
  262.  
  263. int32_t x, y;
  264. uint16_t color565;
  265.  
  266. uint8_t lineBuffer8[(320*2)];
  267. uint16_t lineBuffer16[320];
  268. uint8_t octet_A, octet_B;
  269. String s1, s2;
  270.  
  271. s1 ="/bmp565/capture";
  272. s1 += String(num_capture);
  273. s1 += ".bmp" ;
  274.  
  275. Serial.print("s1= "); Serial.println(s1);
  276.  
  277. while(SD.exists(s1) && num_capture <= 50)
  278. {
  279. num_capture ++;
  280. s1 ="/bmp565/capture";
  281. s1 += String(num_capture);
  282. s1 += ".bmp" ;
  283. }
  284. if (num_capture >= 50)
  285. {
  286. popup(40, 150, "Nb de CPT > 50 !", "Voir sur la SDcard"+s1);
  287. num_capture =0;
  288. }
  289.  
  290. File file1 = SD.open(s1, FILE_WRITE); // crée le fichier si pas présent
  291. delay(20);
  292. if (file1)
  293. {
  294. // création entête bmp565 - 138 octets; voir bmp565_header[] dans le fichier main.h
  295. for(uint8_t i=0; i<138; i++) { file1.write(bmp565_header[i]); }
  296. TFT.setTextColor(VERT, NOIR);
  297. for (int16_t y=239; y>=0; y--)
  298. {
  299. TFT.readRect(0, y, 320, 1, lineBuffer16); // lit une ligne
  300. s2=String(y/24);
  301. TFT.drawString(s2, 2, 2); // pour terminer par une écriture
  302.  
  303. uint16_t i=0;
  304. for (int16_t x=0; x<320; x++) //320
  305. {
  306. uint16_t color565px = lineBuffer16[x]; // BBBBBrrr rrrVVVVV
  307. octet_A = (color565px & 0b1111111100000000) >> 8;
  308. octet_B = (color565px & 0b0000000011111111);
  309.  
  310. lineBuffer8[i] = octet_A;
  311. lineBuffer8[i+1] = octet_B;
  312. i+=2;
  313. }
  314. file1.write(lineBuffer8, sizeof(lineBuffer8));
  315. }
  316. file1.close();
  317. num_capture ++;
  318.  
  319. // TFT.fillScreen(NOIR);
  320.  
  321. popup(40, 150, " Capture OK", " "+s1);
  322.  
  323.  
  324. delay(2000);
  325. }
  326. }
  327.  
  328.  
  329.  
  330. void printTouchToDisplay() // pour TEST positions lors de la conception de l'interface graphique
  331. {
  332. // dessine une petite croix jaune au points d'appuis du stylet
  333.  
  334. init_1_bouton(1, 235, 114, 30, 15, "TEST", &bt_TEST);
  335.  
  336. uint16_t memo_seg_H[22];
  337. uint16_t memo_seg_V[22];
  338. uint16_t memo_x, memo_y;
  339.  
  340. TFT.setFreeFont(FM9);
  341. TFT.setTextColor(BLEU_CLAIR, NOIR);
  342. //TFT.drawString("TEST TOUCH screen", 80, 120);
  343.  
  344. TFT.readRect(x_touch-10, y_touch, 20, 1, memo_seg_H);
  345. TFT.readRect(x_touch, y_touch-10, 1, 20, memo_seg_V);
  346. while(1)
  347. {
  348. if (ts.tirqTouched() && ts.touched())
  349. {
  350. get_XY_touch();
  351. TFT.readRect(x_touch-10, y_touch, 20, 1, memo_seg_H); // memorise image avant de tracer le trait
  352. delay(10);
  353. TFT.pushRect(memo_x_touch-10, memo_y_touch, 20, 1, memo_seg_H); // efface avec l'image enregistrée
  354. delay(10);
  355. TFT.drawFastHLine(x_touch-10, y_touch, 20, JAUNE);
  356. delay(10);
  357.  
  358. TFT.readRect(x_touch, y_touch-10, 1, 20, memo_seg_V); // memorise image avant de tracer le trait
  359. delay(10);
  360. TFT.pushRect(memo_x_touch, memo_y_touch-10, 1, 20, memo_seg_V); // efface avec l'image enregistrée
  361. delay(10);
  362. TFT.drawFastVLine(x_touch, y_touch-10, 20, JAUNE);
  363.  
  364. String s1;
  365. s1 = String(x_touch)+ " ";
  366. TFT.drawString(s1, 120, 130);
  367. s1 = String(y_touch)+ " ";
  368. TFT.drawString(s1, 120, 154);
  369. delay(10);
  370.  
  371. test_clic_bouton_TEST();
  372. }
  373. }
  374. }
  375.  
  376.  
  377.  
  378. void affiche_box_FRQ(uint16_t couleur) // autour de la fréquence (gros chiffres)
  379. {
  380. TFT.drawRect(0, FRQ_y0 -17, 319, 48, couleur);
  381. }
  382.  
  383.  
  384.  
  385. void acquisition_reponse()
  386. {
  387. uint16_t valeurLue = analogRead(analogPin); // = 0..4095
  388.  
  389. }
  390.  
  391.  
  392. void out_frequence()
  393. {
  394. if (out_mode1 == MODE_9850)
  395. {
  396. F_9850 = 10.0 * frequence;
  397. CarteAD9850.out_F(F_9850);
  398. }
  399.  
  400. if (out_mode1 == MODE_4351)
  401. {
  402. F_4351 = (double) frequence / 1e6;
  403. CarteADF4351.setFreq(F_4351);
  404. CarteADF4351.Write_All_Register();
  405. }
  406. }
  407.  
  408.  
  409. void affiche_frequence(uint64_t frq)
  410. {
  411. uint16_t couleur_chiffres;
  412. uint16_t couleur_fond;
  413.  
  414. if(style == 0) { couleur_chiffres = VERT_chiffres; couleur_fond = NOIR; }
  415. if(style == 1) { couleur_chiffres = BLANC; couleur_fond = BLEU; }
  416. if(style == 2) { couleur_chiffres = JAUNE_chiffres; couleur_fond = NOIR; }
  417.  
  418. sprite_frq.fillRect(0, 0, 320, 45, couleur_fond); // couleur_fond efface
  419.  
  420. sprite_frq.setTextColor(couleur_chiffres, couleur_fond);
  421. String Str_frq = formatage_frq(frq); // ajoute les points (.) séparateurs des milliers
  422. sprite_frq.drawString(Str_frq, 320, 30, 6); //320, 32, 6
  423. sprite_frq.pushSprite(FRQ_x0, 14);
  424.  
  425. affi_mode();
  426. out_frequence();
  427.  
  428. }
  429.  
  430.  
  431. void encadre_chiffre(uint16_t x)
  432. {
  433. TFT.drawRect(x, 18, 28, 44, BLANC);
  434. }
  435.  
  436.  
  437. void efface_bande_boutons()
  438. {
  439. TFT.fillRect(0, 61, 319, 19, NOIR);
  440. TFT.drawRect(0, 61, 319, 19, BLANC);
  441. }
  442.  
  443.  
  444. void affi_boutons_gene() // boutons presets de fréquences
  445. {
  446. efface_bande_boutons();
  447.  
  448. TFT.setFreeFont(FF0);
  449. TFT.setTextColor(BLANC, NOIR);
  450. TFT.drawString("presets->", 5, 65);
  451.  
  452. if (out_mode1 == MODE_9850)
  453. {
  454. uint16_t x = 60;
  455. init_1_bouton(1, x, 65, 30, 10, "455k", &bt_455k); x+=35;
  456. init_1_bouton(1, x, 65, 20, 10, "4M", &bt_4M); x+=25;
  457. init_1_bouton(1, x, 65, 30, 10, "10.7", &bt_10_7M); x+=35;
  458. init_1_bouton(1, x, 65, 25, 10, "27M", &bt_27M); x+=30;
  459.  
  460. }
  461.  
  462. if (out_mode1 == MODE_4351)
  463. {
  464. uint16_t x = 60;
  465. init_1_bouton(1, x, 65, 25, 10, "35M", &bt_35M); x+=30;
  466. init_1_bouton(1, x, 65, 25, 10, "41M", &bt_41M); x+=30;
  467. init_1_bouton(1, x, 65, 30, 10, "144M", & bt_144M); x+=35;
  468. init_1_bouton(1, x, 65, 30, 10, "432M", & bt_432M); x+=35;
  469. init_1_bouton(1, x, 65, 30, 10, "2.4G", & bt_2400M);
  470. }
  471.  
  472. TFT.drawRect(1, 61, 319, 19, BLANC);
  473. }
  474.  
  475.  
  476.  
  477. void affi_select_PAS()
  478. {
  479. efface_bande_boutons();
  480. init_1_bouton(1, 3, 63, 35, 15, "PAS-", &bt_pas_P);
  481. init_1_bouton(1, 43, 63, 35, 15, "PAS+", &bt_pas_M);
  482.  
  483. init_1_bouton(1, 83, 63, 40, 15, "BOUCLE", &bt_boucle);
  484. }
  485.  
  486.  
  487. uint64_t calcul_pas_w(uint8_t n) // progression 1,2,5,10,20...[5e6]re
  488. {
  489. if (n>21) {return 0;}
  490. int b=0;
  491. for(int i=0; i<7; i++)
  492. {
  493. if(n<3) {return liste_pas_W_a[n] * liste_pas_W_b[b];}
  494. b++; n-=3;
  495. }
  496. return 0;
  497. }
  498.  
  499.  
  500.  
  501. void init_boutons_choix_MODULE()
  502. {
  503. init_1_bouton(2, 45, 90, 90, 25, " AD9850", &bt_AD9850);
  504. init_1_bouton(2, 175, 90, 90, 25, " ADF4351", &bt_ADF4351);
  505. init_1_bouton(1, 5, 210, 60, 18, "calibrate", &bt_calibrate);
  506.  
  507. }
  508.  
  509.  
  510. void init_boutons_choix_MODE()
  511. {
  512. init_1_bouton(1, 80, 0, 60, 15, "GENE", &bt_mode_GENE);
  513. init_1_bouton(1, 160, 0, 60, 15, "WOBU", &bt_mode_WOBU);
  514. }
  515.  
  516.  
  517.  
  518. void init_affichages()
  519. {
  520. Serial.println("init_affichages()");
  521. TFT.fillRect(0, 0, 319, 239, NOIR);
  522.  
  523. TFT.setTextColor(BLANC, NOIR);
  524.  
  525. TFT.drawRect(0, 0, 320, 240, BLANC); // cadre principal pourtour de l'écran
  526. //TFT.setFreeFont(FF0);
  527. //TFT.setTextColor(BLANC, BLEU);
  528. //String s1 = "v:" + version;
  529. //TFT.drawString(s1, 2, 2);
  530.  
  531. while (!Serial && (millis() <= 1000));
  532.  
  533. TFT.setFreeFont(FF0);
  534.  
  535. init_1_bouton(1, 285, 0, 30, 12, "RAZ", &bt_RAZ);
  536.  
  537. affi_boutons_gene();
  538.  
  539. /*
  540. TFT.fillRect(3, 80, 115, 3, ORANGE); // barres horizontales colorées
  541. TFT.fillRect(135, 80, 105, 3, VERT);
  542. TFT.setFreeFont(FF0);
  543. TFT.setTextColor(ORANGE, NOIR);
  544. TFT.drawString("AD9850", 45, 80);
  545. TFT.setTextColor(VERT, NOIR);
  546. TFT.drawString("ADF4351", 165, 80);
  547. */
  548. affiche_box_FRQ(GRIS_3); // autour de la fréquence (gros chiffres JAUNE ou VERT)
  549.  
  550. bt_RAZ.affiche(BLANC, VERT, 1);
  551. bt_TEST.affiche(BLANC, VERT, 1);
  552.  
  553. TFT.drawRect(0, 0, 319, 240, BLANC); // cadre principal pourtour de l'écran
  554.  
  555. }
  556.  
  557.  
  558. void affi_page_info()
  559. {
  560. // affiche une page d'information:
  561.  
  562. TFT.fillScreen(NOIR);
  563. TFT.setTextColor(BLANC, NOIR);
  564. TFT.setFreeFont(FF1);
  565. uint16_t y=0;
  566. TFT.drawString("DOUBLE GENE UHF - WOBULATEUR", 0, y); y+=40;
  567.  
  568. TFT.setTextColor(ORANGE, NOIR);
  569. TFT.drawString("AD9850 -> 0 to 35Hz MHz", 0, y); y+=20;
  570. TFT.setTextColor(VERT, NOIR);
  571. TFT.drawString("ADF4351 -> 35 MHz to 4.4 GHz", 0, y); y+=40;
  572.  
  573. TFT.setTextColor(BLANC, NOIR);
  574. TFT.setFreeFont(FF1);
  575. String s1="version " + version;
  576. TFT.drawString(s1, 0, y); y+=20;
  577.  
  578. TFT.setTextColor(CYAN, NOIR);
  579. TFT.setFreeFont(FF1);
  580. TFT.drawString("Silicium628", 0, y); y+=40;
  581.  
  582. delay(1000);
  583. }
  584.  
  585.  
  586. void init_SDcard()
  587. {
  588. Serial.println("---------------------");
  589. Serial.println("init_SDcard()");
  590. String s1;
  591.  
  592. TFT.fillRect(0, 0, 480, 320, NOIR); // efface tout l'écran
  593. TFT.setTextColor(BLANC, NOIR);
  594. TFT.setFreeFont(FF1);
  595.  
  596. if(!SD.begin(5, mySpi2))
  597. {
  598. Serial.println("Card Mount Failed");
  599. SDcardOk=0;
  600. }
  601. else
  602. {
  603. Serial.println("SDcard OK");
  604. SDcardOk=1;
  605. TFT.fillRect(0, 0, 480, 320, VERT);
  606. delay(100);
  607. }
  608.  
  609. uint8_t cardType = SD.cardType();
  610.  
  611. if(cardType == CARD_NONE)
  612. {
  613. Serial.println("NO SDcard");
  614. SDcardOk=0;
  615. return;
  616. }
  617.  
  618. Serial.print("SDcard Type: ");
  619. if(cardType == CARD_SD) {Serial.print("SDSC");}
  620. else if(cardType == CARD_SDHC) {Serial.println("SDHC");}
  621.  
  622. uint32_t cardSize = SD.cardSize() / (1024 * 1024);
  623. s1=(String)cardSize + " GB";
  624. Serial.println(s1); Serial.println();
  625.  
  626. delay (100);
  627. TFT.fillRect(0, 0, 480, 320, NOIR); // efface
  628. }
  629.  
  630.  
  631. uint16_t read_16(File fp)
  632. {
  633. uint8_t low;
  634. uint16_t high;
  635. low = fp.read();
  636. high = fp.read();
  637. return (high<<8)|low;
  638. }
  639.  
  640.  
  641. uint32_t read_32(File fp)
  642. {
  643. uint16_t low;
  644. uint32_t high;
  645. low = read_16(fp);
  646. high = read_16(fp);
  647. return (high<<8)|low;
  648. }
  649.  
  650.  
  651. void affi_image_from_SD(String filename, uint16_t x0, uint16_t y0)
  652. {
  653. Serial.println("----------------------------");
  654. Serial.println("affi_image_from_SD()");
  655.  
  656. uint8_t bmp_data[2]={0,0};
  657. uint8_t a, b;
  658. uint16_t x=0;
  659. uint16_t y=0;
  660. int16_t xmax;
  661.  
  662. uint32_t seekOffset;
  663. uint16_t w, h, row, col;
  664.  
  665.  
  666. File fp = SD.open(filename, "r");
  667. if (!fp) { Serial.println("ERREUR open file on SD"); return; }
  668.  
  669. uint16_t etiq1 = read_16(fp);
  670.  
  671. // test bmp_header
  672. if (etiq1 == 0x4D42)
  673. {
  674. read_32(fp);
  675. read_32(fp);
  676. seekOffset = read_32(fp);
  677. Serial.print("seekOffset ="); Serial.println(seekOffset); // 138
  678.  
  679. Serial.print("seekOffset= "); Serial.println(seekOffset);
  680. read_32(fp);
  681. w = read_32(fp);
  682. h = read_32(fp);
  683. Serial.print("w= "); Serial.println(w); // 75
  684. Serial.print("h= "); Serial.println(h); // 50
  685. Serial.println(read_16(fp)); // 1
  686. Serial.println(read_16(fp)); // 16
  687. Serial.println(read_16(fp)); // 3
  688. fp.seek(seekOffset);
  689. }
  690.  
  691. y += h;
  692. xmax = w;
  693. if(xmax%2 == 1) {xmax +=1;}
  694.  
  695. uint16_t padding = (4 - ((w * 2) & 2)) & 2;
  696. uint8_t lineBuffer[(w*2) + padding];
  697.  
  698. for (row = 0; row < h; row++)
  699. {
  700. fp.read(lineBuffer, sizeof(lineBuffer));
  701. uint8_t* bptr = lineBuffer;
  702. uint16_t* tptr = (uint16_t*)lineBuffer;
  703.  
  704. for (uint16_t col = 0; col < w; col++)
  705. {
  706. a = *bptr++;
  707. b = *bptr++;
  708. *tptr++ = (a <<8 | b);
  709. }
  710. TFT.pushImage(x0 + x, y0 + y, w, 1, (uint16_t*)lineBuffer);
  711. y--;
  712. }
  713. fp.close();
  714. }
  715.  
  716.  
  717. void write_fichier_params() // sur la SDcard
  718. {
  719. Serial.println("write fichier '/params.txt'");
  720.  
  721. File file1 = SD.open("/params.txt", FILE_WRITE);
  722. String s1;
  723.  
  724. /*
  725. s1 ="[couleur_fond]";
  726. s1 += "<";
  727. s1 += String(couleur_fond_ecran);
  728. s1 +=">";
  729. file1.println(s1);
  730. */
  731.  
  732. s1 ="[frequence]";
  733. s1 += "<";
  734. s1 += String(frequence);
  735. s1 +=">";
  736. file1.println(s1);
  737.  
  738. s1 ="[Ax]";
  739. s1 += "<";
  740. s1 += String(A.x);
  741. s1 +=">";
  742. file1.println(s1);
  743.  
  744. s1 ="[Ay]";
  745. s1 += "<";
  746. s1 += String(A.y);
  747. s1 +=">";
  748. file1.println(s1);
  749.  
  750. s1 ="[Bx]";
  751. s1 += "<";
  752. s1 += String(B.x);
  753. s1 +=">";
  754. file1.println(s1);
  755.  
  756. s1 ="[By]";
  757. s1 += "<";
  758. s1 += String(B.y);
  759. s1 +=">";
  760. file1.println(s1);
  761.  
  762.  
  763. file1.close();
  764. delay(100);
  765. }
  766.  
  767.  
  768. void TS_calibrate() // calibrage de l'écran tactile
  769. {
  770. uint8_t L = 20;
  771. boolean ok;
  772. String s1;
  773.  
  774. TFT.setTextColor(BLANC, NOIR);
  775. TFT.setFreeFont(FF1);
  776.  
  777. // traitement point A
  778. TFT.fillScreen(NOIR);
  779. TFT.drawString("Cliquez sur le point A", 20, 50);
  780. delay(300);
  781. TFT.drawFastVLine(10, 0, L, JAUNE);
  782. TFT.drawFastHLine(0, 10, L, JAUNE);
  783. ok = false;
  784. while (! ok)
  785. {
  786. if (ts.tirqTouched() && ts.touched())
  787. {
  788. memo_y_touch = y_touch;
  789. TS_Point p = ts.getPoint();
  790. s1 = "Ax=" + String(p.x) + " "; TFT.drawString(s1, 50, 100);
  791. s1 = "Ay=" + String(p.y) + " "; TFT.drawString(s1, 50, 120);
  792. delay(100);
  793.  
  794. if( (p.x<600) && (p.y < 600)) // évite de valider une position irréaliste
  795. {
  796. A.x = p.x; // point A : variable globale
  797. A.y = p.y;
  798. ok = true;
  799. }
  800. }
  801. }
  802.  
  803. // traitement point B
  804. TFT.fillScreen(NOIR);
  805. delay(300);
  806. TFT.drawString("Cliquez sur le point B", 20, 70);
  807. TFT.drawFastVLine(310, 230-L/2, L, JAUNE);
  808. TFT.drawFastHLine(310-L/2, 230, L, JAUNE);
  809. ok = false;
  810. while (! ok)
  811. {
  812. if (ts.tirqTouched() && ts.touched())
  813. {
  814. memo_y_touch = y_touch;
  815. TS_Point p = ts.getPoint();
  816. s1 = "Bx=" + String(p.x) + " "; TFT.drawString(s1, 50, 150);
  817. s1 = "By=" + String(p.y) + " "; TFT.drawString(s1, 50, 170);
  818. delay(100);
  819.  
  820. if( (p.x>3600) && (p.y > 3400)) // évite de valider une position irréaliste
  821. {
  822. B.x = p.x;
  823. B.y = p.y;
  824. ok = true;
  825. }
  826. }
  827. }
  828.  
  829. if(SDcardOk==1)
  830. {
  831. TFT.fillScreen(NOIR);
  832. TFT.drawString("Ok", 20, 60);
  833. TFT.setFreeFont(FF0);
  834. TFT.drawString("Write fichier params sur la SD", 10, 90);
  835. write_fichier_params();
  836. delay(1000);
  837. }
  838.  
  839. TFT.fillScreen(NOIR);
  840. init_affichages();
  841.  
  842. //TS_verif();
  843. //printTouchToDisplay(); // POUR TESTER LA CALIBRATION DE L'ECRAN TACTILE
  844. }
  845.  
  846.  
  847. String read_line_params(uint16_t line_num)
  848. {
  849. int i = 1;
  850. char buffer[64];
  851. String s;
  852.  
  853. File file = SD.open("/params.txt", "r");
  854.  
  855. while (file.available())
  856. {
  857. int l = file.readBytesUntil('\n', buffer, sizeof(buffer));
  858. buffer[l] = 0;
  859. if (line_num == i)
  860. {
  861. s = buffer;
  862. file.close();
  863. return(s);
  864. }
  865. i++;
  866. }
  867. return "";
  868. }
  869.  
  870.  
  871. int32_t extract_params(String ligne, String label)
  872. {
  873. String s2;
  874. uint32_t valeur = 0;
  875. int p1, p2;
  876.  
  877. p1 = ligne.indexOf('['); p2 = ligne.indexOf(']');
  878. s2 = ligne.substring(p1+1, p2);
  879.  
  880. if (s2 == label)
  881. {
  882. p1 = ligne.indexOf('<'); p2 = ligne.indexOf('>');
  883. s2 = ligne.substring(p1+1, p2);
  884. valeur = s2.toInt();
  885. return valeur;
  886. }
  887. return -1;
  888. }
  889.  
  890.  
  891. void read_params() // lecture du fichier '/params.txt' en SD
  892. {
  893. Serial.println("lecture du fichier '/params.txt' en SD");
  894. String s1;
  895. int valeur;
  896.  
  897. s1 = "Z";
  898. uint8_t n=1;
  899. while (s1 !="")
  900. {
  901. s1 = read_line_params(n); // retourne(par exemple): [couleur_fond]<267>
  902.  
  903. //valeur = extract_params(s1, "frequence");
  904. //if(valeur != -1) {frequence = valeur;}
  905.  
  906. valeur = extract_params(s1, "Ax");
  907. if(valeur != -1) {A.x = valeur;}
  908.  
  909. valeur = extract_params(s1, "Ay");
  910. if(valeur != -1) {A.y = valeur;}
  911.  
  912. valeur = extract_params(s1, "Bx");
  913. if(valeur != -1) {B.x = valeur;}
  914.  
  915. valeur = extract_params(s1, "By");
  916. if(valeur != -1) {B.y = valeur;}
  917.  
  918. n++;
  919. }
  920. }
  921.  
  922.  
  923. void affi_mode()
  924. {
  925. TFT.setFreeFont(FF0);
  926. if (out_mode1 == MODE_9850)
  927. {
  928. TFT.setTextColor(VERT, NOIR);
  929. TFT.drawString("AD9850 ", 20, 3);
  930. }
  931. if (out_mode1 == MODE_4351)
  932. {
  933. TFT.setTextColor(CYAN, NOIR);
  934. TFT.drawString("ADF4351", 20, 3);
  935. }
  936.  
  937. TFT.setFreeFont(FF1);
  938.  
  939. if (out_mode1 != memo_out_mode1)
  940. {
  941. if(f_mode1 == MODE_GENE) { affi_boutons_gene(); }
  942. if(f_mode1 == MODE_WOBU) { affi_select_PAS(); }
  943. }
  944. memo_out_mode1 = out_mode1;
  945. }
  946.  
  947.  
  948. void efface_grand_cadre() // en épargnant la bande des boutons (1M..100k..10k..1HZ)
  949. {
  950. TFT.fillRect(1, 80, 317, 159, NOIR);
  951. }
  952.  
  953.  
  954. void trace_sinusoide()
  955. {
  956. float y;
  957. float v;
  958. for (uint16_t n=0; n<320; n++)
  959. {
  960. v = (float)n/20.0;
  961. y = 180-50*sin(v);
  962. TFT.drawPixel(n, (int)y, VERT);
  963. }
  964. }
  965.  
  966.  
  967. void setup()
  968. {
  969. if (soft_setup == false)
  970. {
  971. soft_setup = true;
  972. // étalonnage touch screen; ces valeurs peuvent varier d'un afficheur à l'autre
  973. // pour l'instant il faut les fixer ici à la main...
  974. eTS.x0 = -30; eTS.y0 = -30;
  975. eTS.dx = 11; eTS.dy = 14;
  976.  
  977. Serial.begin(115200);
  978. delay(20);
  979. Serial.println("setup A");
  980.  
  981. pinMode(boutonPin1, INPUT_PULLUP); // bouton physique
  982.  
  983. pinMode(ADF_CLK, OUTPUT);
  984. pinMode(ADF_DATA, OUTPUT);
  985. pinMode(ADF_LE, OUTPUT);
  986. pinMode(ADF_CE, OUTPUT);
  987.  
  988. pinMode(AD_WCL, OUTPUT);
  989. pinMode(AD_D7, OUTPUT);
  990. pinMode(AD_FQU, OUTPUT);
  991. pinMode(AD_RESET, OUTPUT);
  992.  
  993. //pinMode(GPIO_BL, OUTPUT);
  994. //digitalWrite(GPIO_BL, HIGH);
  995.  
  996. init_variables_globales();
  997.  
  998. Wire.begin(GPIO_SDA, GPIO_SCL, 2000000);
  999. //Tuner_Init(tuner_init_tab9216);
  1000. //affiche_frequence(frequence);
  1001.  
  1002. // Start the SPI for the touch screen and init the TS library
  1003. mySpi.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
  1004. ts.begin(mySpi);
  1005. ts.setRotation(1);// 1 ou 3 à voir, suivant disposition de l'afficheur
  1006.  
  1007.  
  1008. // mySpi2 -> pour le lecteur de SDcard
  1009. // mySpi2 -> partage du bus SPI - mêmes valeurs de GPIO (sck=14, miso=12)
  1010. // que pour l'afficheur ILI9341 , sauf CS=5. Voir sur le schéma ainsi que le fichier User_Setup.h
  1011. mySpi2.begin(14, 12, 13, 5);
  1012.  
  1013. init_SDcard();
  1014. delay(20);
  1015.  
  1016. if (SDcardOk == true)
  1017. {
  1018. read_params(); // sur la SDcard
  1019. }
  1020.  
  1021. TFT.init();
  1022. TFT.setRotation(1);// 1 ou 3 à voir, suivant disposition de l'afficheur
  1023. TFT.fillScreen(VERT);
  1024.  
  1025. init_sprites();
  1026.  
  1027. affi_page_info();
  1028. delay(2000);
  1029.  
  1030. // TS_calibrate();
  1031.  
  1032. // si les coordonnées des points de calibrage sont aux fraises...
  1033. if ( (A.x <300) || (A.x >700) || (A.y <200) || (A.y >600) ||
  1034. (B.x < 3500) || (B.x > 3900) || (B.y <3400) || (A.y >3800) )
  1035. {
  1036. TS_calibrate();
  1037. }
  1038.  
  1039. out_mode1 = MODE_9850;
  1040. style = 0;
  1041. frequence = 4000000;
  1042. }
  1043.  
  1044. Serial.println("setup B");
  1045.  
  1046. init_affichages();
  1047. affi_image_from_SD("/scope2.bmp", 0, 90);
  1048. affiche_frequence(frequence); // et met à jour Fd0
  1049. delay(500);
  1050.  
  1051. //f_mode1 = MODE_ACCUEIL;
  1052. efface_grand_cadre(); // grand cadre en partie basse
  1053.  
  1054. chf_pad1.init();
  1055.  
  1056. CarteADF4351.init();
  1057. CarteADF4351.mute(0);
  1058.  
  1059. CarteAD9850.reset();
  1060. delay(10);
  1061. CarteAD9850.out_F(10000.0);
  1062.  
  1063. init_boutons_choix_MODE();
  1064. bt_mode_GENE.selected = true;
  1065. bt_mode_GENE.affiche(NOIR, JAUNE, 2);
  1066. f_mode1 = MODE_GENE;
  1067.  
  1068. bt_mode_WOBU.selected = false; // l'autre bouton
  1069. bt_mode_WOBU.affiche(BLANC, GRIS_3, 2);
  1070.  
  1071.  
  1072. affiche_frequence(frequence);
  1073.  
  1074. efface_grand_cadre();
  1075.  
  1076. affi_mode();
  1077. trace_sinusoide();
  1078. init_boutons_choix_MODULE();
  1079.  
  1080. /*
  1081. for(int n=0; n<21; n++)
  1082. {
  1083. uint64_t v = calcul_pas_w(n);
  1084. Serial.println(v); // pour test
  1085. }
  1086. */
  1087.  
  1088. // les 2 lignes suivantes me servent à afficher une image destinée au site web.
  1089. //affi_image_from_SD ("/bmp565/capture0.bmp", 0, 0);
  1090. //while(1);
  1091.  
  1092.  
  1093. Serial.println("Fin du Setup");
  1094. Serial.println("-----------------------");
  1095.  
  1096. }
  1097.  
  1098.  
  1099. void get_XY_touch() // pour affi 320 x 240px
  1100. {
  1101. TS_Point p = ts.getPoint();
  1102. float dx = B.x - A.x;
  1103. float ech_x = 290.0 / dx;//300
  1104. float x0 = A.x;
  1105.  
  1106. float dy = B.y - A.y;
  1107. float ech_y = 210.0 / dy; //230
  1108. float y0 = A.y;
  1109.  
  1110. memo_x_touch = x_touch;
  1111. memo_y_touch = y_touch;
  1112.  
  1113. x_touch = 12 + uint16_t( (p.x - x0) * ech_x); //x_touch = 10 + uint16_t( (p.x - x0) * ech_x);
  1114. if(x_touch > 319){x_touch = 0;}
  1115. y_touch = 15 + uint16_t( (p.y - y0) * ech_y); //y_touch = 10 + uint16_t( (p.y - y0) * ech_y);
  1116. if(y_touch > 239){y_touch = 0;}
  1117. }
  1118.  
  1119.  
  1120. void test_clic_boutons(TOUCH_BOUTON *bouton_i)
  1121. {
  1122. uint16_t c1 = NOIR;
  1123. uint16_t c2 = VERT;
  1124.  
  1125. if ((x_touch > bouton_i->x0) && (x_touch < (bouton_i->x0 )+ bouton_i->read_dx())
  1126. && ( y_touch > ((bouton_i->y0)) ) && (y_touch < ((bouton_i->y0) + (bouton_i->read_dy())) ) )
  1127. {
  1128. bouton_i->cliked = true;
  1129. bouton_i->selected = true;
  1130. //bouton_i->affiche(BLANC, c2, 1);
  1131. delay(100);
  1132. }
  1133. }
  1134.  
  1135.  
  1136. int test_clic_boutons_inc_dec() // boutons placés derrière les chiffres de la fréquence
  1137. {
  1138. int8_t num_bouton = -1;
  1139. uint64_t facteur;
  1140.  
  1141. TOUCH_BOUTON bt_Hi, bt_Li;
  1142. for(uint8_t n=0; n<10; n++)
  1143. {
  1144. bt_Hi = chf_pad1.bt_pad_H[n];
  1145. test_clic_boutons(&bt_Hi);
  1146. if(bt_Hi.cliked) { num_bouton = n; }
  1147.  
  1148. bt_Li = chf_pad1.bt_pad_L[n];
  1149. test_clic_boutons(&bt_Li);
  1150. if(bt_Li.cliked) { num_bouton = n+10; }
  1151. }
  1152.  
  1153. if(num_bouton != -1)
  1154. {
  1155. if (num_bouton<10)
  1156. {
  1157. facteur = pow(10, 9-num_bouton);
  1158. frequence += facteur;
  1159. }
  1160. if ((num_bouton>=10) && (num_bouton<20))
  1161. {
  1162. facteur = pow(10, 19-num_bouton);
  1163. if (frequence > facteur) {frequence -= facteur;}
  1164. else {frequence = 1;}
  1165. }
  1166.  
  1167. if (frequence == 0) {frequence = 1;}
  1168. //if (frequence > 4400000000) {frequence = 4400000000;}
  1169.  
  1170. if (out_mode1 == MODE_9850)
  1171. {
  1172. if (frequence < F_min_AD9850) {frequence = F_min_AD9850;}
  1173. if (frequence > F_max_AD9850) {frequence = F_max_AD9850;} // au delà le signal est possible, mais très dégradé !
  1174. }
  1175.  
  1176. if (out_mode1 == MODE_4351)
  1177. {
  1178. if (frequence < F_min_ADF4351) {frequence = F_min_ADF4351;}
  1179. if (frequence > F_max_ADF4351) {frequence = F_max_ADF4351;}
  1180. }
  1181.  
  1182. affiche_frequence(frequence);
  1183. delay(100);
  1184. }
  1185. return num_bouton;
  1186. }
  1187.  
  1188.  
  1189.  
  1190. void draw_AEC(uint16_t x0, uint16_t y0, uint16_t L, uint8_t sens)
  1191. {
  1192. // ligne arc-en-ciel
  1193. // affiche une ligne de pixels colorés à partir de la variable 'couleurs_aec' mémorisée en PROGMEM (voir fichier Couleurs_AEC.h)
  1194. // L = longueur de la ligne
  1195.  
  1196. //Serial.println("draw_draw_AEC()");
  1197. uint16_t x, i, j;
  1198. uint16_t y1;
  1199. uint16_t couleur_i;
  1200.  
  1201. for (int16_t i=0; i<L; i++)
  1202. {
  1203. float f = 470.0/L * i; // pour balayer toute l'échelle des couleurs disponibles
  1204. j=uint16_t(f);
  1205.  
  1206. couleur_i = couleurs_aec[2*j] | couleurs_aec[2*j+1]<<8;
  1207.  
  1208. if (sens==0){x=i;} else {x=L-i;}
  1209.  
  1210. TFT.drawFastVLine(x0+x, y0, 20, couleur_i);
  1211.  
  1212. }
  1213. }
  1214.  
  1215. void test_clic_bts_choix_module() // AD9850 - ADF4351
  1216. {
  1217. test_clic_boutons(&bt_AD9850);
  1218. if (bt_AD9850.cliked)
  1219. {
  1220. bt_AD9850.cliked = false;
  1221. bt_AD9850.affiche(BLANC, VERT_2, 2);
  1222.  
  1223. bt_ADF4351.selected = false; // l'autre bouton
  1224. bt_ADF4351.affiche(BLANC, VERT_2, 2);
  1225.  
  1226. out_mode1 = MODE_9850;
  1227. style = 0;
  1228. frequence = 4000000;
  1229. affiche_frequence(frequence);
  1230.  
  1231. affi_boutons_gene();
  1232. f_mode1 = MODE_GENE;
  1233. }
  1234.  
  1235. test_clic_boutons(&bt_ADF4351);
  1236. if (bt_ADF4351.cliked)
  1237. {
  1238. bt_ADF4351.cliked = false;
  1239. bt_ADF4351.affiche(BLANC, BLEU, 2);
  1240.  
  1241. bt_AD9850.selected = false; // l'autre bouton
  1242. bt_AD9850.affiche(BLANC, VERT_2, 2);
  1243.  
  1244. out_mode1 = MODE_4351;
  1245. style = 1;
  1246. frequence = 100000000;
  1247. affiche_frequence(frequence);
  1248.  
  1249. affi_boutons_gene();
  1250. f_mode1 = MODE_GENE;
  1251. }
  1252.  
  1253. }
  1254.  
  1255.  
  1256. void test_clic_bts_MODE() // GENE - WOBU
  1257. {
  1258. test_clic_boutons(&bt_mode_GENE);
  1259. if (bt_mode_GENE.cliked)
  1260. {
  1261. bt_mode_GENE.cliked = false;
  1262. bt_mode_GENE.affiche(NOIR, JAUNE, 2);
  1263.  
  1264. bt_mode_WOBU.selected = false; // l'autre bouton
  1265. bt_mode_WOBU.affiche(NOIR, GRIS_4, 2);
  1266.  
  1267. f_mode1 = MODE_GENE;
  1268. out_mode1 = MODE_9850;
  1269. style = 0;
  1270. frequence = 4000000;
  1271. affiche_frequence(frequence);
  1272.  
  1273. bt_mode_GENE.selected = true;
  1274. bt_mode_GENE.affiche(NOIR, JAUNE, 2);
  1275. affi_boutons_gene();
  1276. efface_grand_cadre();
  1277. init_boutons_choix_MODULE();
  1278. TFT.drawRect(0, 0, 319, 240, BLANC); // cadre principal pourtour de l'écran
  1279. bt_AD9850.selected=true;
  1280. bt_AD9850.affiche(BLANC, VERT_2, 2);
  1281. }
  1282.  
  1283.  
  1284. test_clic_boutons(&bt_mode_WOBU);
  1285. if (bt_mode_WOBU.cliked)
  1286. {
  1287. bt_mode_WOBU.cliked = false;
  1288. bt_mode_WOBU.affiche(NOIR, JAUNE, 2);
  1289.  
  1290. bt_mode_GENE.selected = false; // l'autre bouton
  1291. bt_mode_GENE.affiche(NOIR, GRIS_4, 2);
  1292.  
  1293. f_mode1 = MODE_WOBU;
  1294. num_pas = 15;
  1295. pas_W = calcul_pas_w(num_pas);
  1296.  
  1297. affi_select_PAS();
  1298. affi_grille();
  1299.  
  1300. GO();
  1301. setup();
  1302. }
  1303. }
  1304.  
  1305.  
  1306. void test_clic_bt_calibrate()
  1307. {
  1308. test_clic_boutons(&bt_calibrate );
  1309. if (bt_calibrate.cliked)
  1310. {
  1311. bt_calibrate.cliked = false;
  1312. TS_calibrate();
  1313. soft_setup = false;
  1314. setup();
  1315. }
  1316. }
  1317.  
  1318.  
  1319. void test_clic_bouton_RAZ()
  1320. {
  1321. test_clic_boutons(&bt_RAZ );
  1322.  
  1323. if (bt_RAZ.cliked)
  1324. {
  1325. bt_RAZ.affiche(BLANC, ROUGE, 1);
  1326. delay(50);
  1327. bt_RAZ.cliked = false;
  1328. bt_RAZ.selected = true;
  1329. bt_RAZ.affiche(BLANC, ROUGE, 1);
  1330.  
  1331. frequence =0;
  1332. affiche_frequence(frequence);
  1333.  
  1334. bt_RAZ.cliked = false;
  1335. bt_RAZ.selected = false;
  1336. bt_RAZ.affiche(BLANC, ROUGE, 1);
  1337.  
  1338. delay(300);
  1339. //init_affichages();
  1340. }
  1341. }
  1342.  
  1343.  
  1344.  
  1345. void deselecte_bts_preset()
  1346. {
  1347. if (out_mode1 == MODE_9850)
  1348. {
  1349. bt_455k.selected = false; bt_455k.affiche(BLANC, VERT, 1);
  1350. bt_4M.selected = false; bt_4M.affiche(BLANC, VERT, 1);
  1351. bt_10_7M.selected = false; bt_10_7M.affiche(BLANC, VERT, 1);
  1352. bt_27M.selected = false; bt_27M.affiche(BLANC, VERT, 1);
  1353. }
  1354.  
  1355. if (out_mode1 == MODE_4351)
  1356. {
  1357. bt_35M.selected = false; bt_35M.affiche(BLANC, VERT, 1);
  1358. bt_41M.selected = false; bt_41M.affiche(BLANC, VERT, 1);
  1359. bt_144M.selected = false; bt_144M.affiche(BLANC, VERT, 1);
  1360. bt_432M.selected = false; bt_432M.affiche(BLANC, VERT, 1);
  1361. bt_2400M.selected = false; bt_2400M.affiche(BLANC, VERT, 1);
  1362. }
  1363. }
  1364.  
  1365.  
  1366. void test_clic_bt_455k()
  1367. {
  1368. test_clic_boutons(&bt_455k);
  1369. if (bt_455k.cliked)
  1370. {
  1371. deselecte_bts_preset();
  1372. bt_455k.cliked = false;
  1373. bt_455k.selected=true;
  1374. bt_455k.affiche(BLANC, VERT, 1);
  1375. frequence =455000;
  1376. affiche_frequence(frequence);
  1377. delay(300);
  1378. }
  1379. }
  1380.  
  1381. void test_clic_bt_4M()
  1382. {
  1383. test_clic_boutons(&bt_4M);
  1384. if (bt_4M.cliked)
  1385. {
  1386. deselecte_bts_preset();
  1387. bt_4M.cliked = false;
  1388. bt_4M.selected=true;
  1389. bt_4M.affiche(BLANC, VERT, 1);
  1390. frequence =4000000;
  1391. affiche_frequence(frequence);
  1392. delay(300);
  1393. }
  1394. }
  1395.  
  1396. void test_clic_bt_10_7M()
  1397. {
  1398. test_clic_boutons(&bt_10_7M);
  1399. if (bt_10_7M.cliked)
  1400. {
  1401. deselecte_bts_preset();
  1402. bt_10_7M.cliked = false;
  1403. bt_10_7M.selected=true;
  1404. bt_10_7M.affiche(BLANC, VERT, 1);
  1405. frequence =10700000;
  1406. affiche_frequence(frequence);
  1407. delay(300);
  1408. }
  1409. }
  1410.  
  1411. void test_clic_bt_27M()
  1412. {
  1413. test_clic_boutons(&bt_27M);
  1414. if (bt_27M.cliked)
  1415. {
  1416. deselecte_bts_preset();
  1417. bt_27M.cliked = false;
  1418. bt_27M.selected=true;
  1419. bt_27M.affiche(BLANC, VERT, 1);
  1420. frequence =27000000;
  1421. affiche_frequence(frequence);
  1422. delay(300);
  1423. }
  1424. }
  1425.  
  1426.  
  1427. void test_clic_bt_35M()
  1428. {
  1429. test_clic_boutons(&bt_35M);
  1430. if (bt_35M.cliked)
  1431. {
  1432. deselecte_bts_preset();
  1433. bt_35M.cliked = false;
  1434. bt_35M.selected=true;
  1435. bt_35M.affiche(BLANC, VERT, 1);
  1436. frequence =35000000;
  1437. affiche_frequence(frequence);
  1438. delay(300);
  1439. }
  1440. }
  1441.  
  1442.  
  1443. void test_clic_bt_41M()
  1444. {
  1445. test_clic_boutons(&bt_41M);
  1446. if (bt_41M.cliked)
  1447. {
  1448. deselecte_bts_preset();
  1449. bt_41M.cliked = false;
  1450. bt_41M.selected=true;
  1451. bt_41M.affiche(BLANC, VERT, 1);
  1452. frequence =41000000;
  1453. affiche_frequence(frequence);
  1454. delay(300);
  1455. }
  1456. }
  1457.  
  1458.  
  1459. void test_clic_bt_144M()
  1460. {
  1461. test_clic_boutons(&bt_144M);
  1462. if (bt_144M.cliked)
  1463. {
  1464. deselecte_bts_preset();
  1465. bt_144M.cliked = false;
  1466. bt_144M.selected=true;
  1467. bt_144M.affiche(BLANC, VERT, 1);
  1468. frequence =144000000;
  1469. affiche_frequence(frequence);
  1470. delay(300);
  1471. }
  1472. }
  1473.  
  1474.  
  1475. void test_clic_bt_432M()
  1476. {
  1477. test_clic_boutons(&bt_432M);
  1478. if (bt_432M.cliked)
  1479. {
  1480. deselecte_bts_preset();
  1481. bt_432M.cliked = false;
  1482. bt_432M.selected=true;
  1483. bt_432M.affiche(BLANC, VERT, 1);
  1484. frequence =432000000;
  1485. affiche_frequence(frequence);
  1486. delay(300);
  1487. }
  1488. }
  1489.  
  1490. void test_clic_bt_2400M()
  1491. {
  1492. test_clic_boutons(&bt_2400M);
  1493. if (bt_2400M.cliked)
  1494. {
  1495. deselecte_bts_preset();
  1496. bt_2400M.cliked = false;
  1497. bt_2400M.selected=true;
  1498. bt_2400M.affiche(BLANC, VERT, 1);
  1499. frequence =2400000000;
  1500. affiche_frequence(frequence);
  1501. delay(200);
  1502. }
  1503. }
  1504.  
  1505.  
  1506.  
  1507. void affi_pasW()
  1508. {
  1509. String s1 = formatage_frq(pas_W); // ajoute les points (.) séparateurs des milliers
  1510. String s2 = supp_zeros(s1);
  1511.  
  1512. TFT.fillRect(130, 63, 120, 12, NOIR); // efface
  1513. TFT.setFreeFont(FF1);
  1514. TFT.setTextColor(JAUNE);
  1515. TFT.drawString("P=" + s2, 130, 63);
  1516. }
  1517.  
  1518. /*
  1519. void calcul_Fmin_Fmax()
  1520. {
  1521. Fmin = frequence - 160 * pas_W;
  1522. Fmax = frequence + 160 * pas_W;
  1523. }
  1524. */
  1525.  
  1526. void test_clic_bt_pas_P()
  1527. {
  1528. test_clic_boutons(&bt_pas_P);
  1529. if (bt_pas_P.cliked)
  1530. {
  1531. bt_pas_P.cliked = false;
  1532.  
  1533. if (num_pas>0) {num_pas --;}
  1534.  
  1535. pas_W = calcul_pas_w(num_pas);
  1536. affi_pasW();
  1537.  
  1538. calcul_Fmin_Fmax(frequence);
  1539. affi_freq_W_min_max();
  1540. delay(200);
  1541. }
  1542. }
  1543.  
  1544.  
  1545. void test_clic_bt_pas_M()
  1546. {
  1547. test_clic_boutons(&bt_pas_M);
  1548. if (bt_pas_M.cliked)
  1549. {
  1550. bt_pas_M.cliked = false;
  1551.  
  1552. num_pas ++;
  1553. if (num_pas>20) {num_pas = 20;}
  1554.  
  1555. pas_W = calcul_pas_w(num_pas);
  1556. affi_pasW();
  1557.  
  1558. calcul_Fmin_Fmax(frequence);
  1559. affi_freq_W_min_max();
  1560. delay(300);
  1561. }
  1562. }
  1563.  
  1564.  
  1565. void test_clic_bt_boucle()
  1566. {
  1567. test_clic_boutons(&bt_boucle);
  1568. if (bt_boucle.cliked)
  1569. {
  1570. bt_boucle.cliked = false;
  1571.  
  1572. en_boucle = ! en_boucle;
  1573. if (en_boucle == true) { bt_boucle.affiche(NOIR, VERT, 1);}
  1574. else {bt_boucle.affiche(BLANC, GRIS_4, 1);}
  1575.  
  1576. delay(300);
  1577. }
  1578. }
  1579.  
  1580.  
  1581. void affi_trace_passe_bas()
  1582. {
  1583. // (Filtre passe-bas de second ordre RLC) pour exemple...
  1584. // https://www.silicium628.fr/article_i.php?id=112
  1585. float d1, d2,d3, T;
  1586. float x = 5;
  1587. float m=0.25;
  1588.  
  1589. for(int n=0; n<800; n++)
  1590. {
  1591. x= n/100.0;
  1592. d1 = 1 - x*x;
  1593. d2 = 2*m*x;
  1594. d3 = d1*d1 + d2*d2;
  1595. T = 1/sqrt(d3);
  1596. uint16_t y = int(70.0 *T);
  1597. TFT.drawPixel(n, 240-y, BLEU);
  1598. }
  1599. }
  1600.  
  1601.  
  1602. String formatage_frq(uint64_t frq_i) // ajoute les points (.) séparateurs des milliers
  1603. {
  1604. String s1 = String(frq_i);
  1605. s1 = "000000000" + s1;
  1606. uint8_t L= s1.length();
  1607. String sU = s1.substring(L-3); // Hz
  1608. String sK = s1.substring(L-6, L-3); // kHz
  1609. String sM = s1.substring(L-10, L-6);// MHz
  1610. return sM + "." + sK + "." + sU + " ";
  1611. }
  1612.  
  1613.  
  1614. String supp_zeros(String s_i) // supprime les zéros et les points de séparation des milliers non significatifs
  1615. {
  1616. String s1;
  1617.  
  1618. while (s_i.substring(0, 1) == "0") {s_i = s_i.substring(1); }
  1619. if(s_i[0] == '.') {s_i = s_i.substring(1); }
  1620. while (s_i.substring(0, 1) == "0") {s_i = s_i.substring(1); }
  1621. if(s_i[0] == '.') {s_i = s_i.substring(1); }
  1622. while (s_i.substring(0, 1) == "0") {s_i = s_i.substring(1); }
  1623. return s_i;
  1624. }
  1625.  
  1626.  
  1627. void affi_freq_W_min_max() // et les affiche verticalement
  1628. {
  1629. // la réquence centrale est celle choisie et affichée en gros chiffres en haut
  1630. String Str_frq;
  1631.  
  1632. Str_frq = formatage_frq(Fmin);
  1633. TFT.fillRect(6, 150, 8, 89, NOIR); // efface
  1634. sprite_freq_W_min.drawString(Str_frq, 0, 0);
  1635. TFT.setPivot(10, 190);
  1636. sprite_freq_W_min.pushRotated(-90, NOIR);
  1637.  
  1638. TFT.fillRect(305, 150, 8, 89, NOIR); // efface
  1639. Str_frq = formatage_frq(Fmax);
  1640. sprite_freq_W_max.drawString(Str_frq, 0, 0);
  1641. TFT.setPivot(310, 190);
  1642. sprite_freq_W_max.pushRotated(-90, NOIR);
  1643. }
  1644.  
  1645.  
  1646.  
  1647.  
  1648. void boucle_affi_curseur()
  1649. {
  1650. // dessine un trait vertical au point cliqué par le stylet
  1651. if (pas_W==0) {return;}
  1652. if(f_mode1 != MODE_WOBU) {return;}
  1653.  
  1654. efface_bande_boutons();
  1655. TFT.setFreeFont(FF1);
  1656. //TFT.setTextColor(BLEU_CLAIR);
  1657. TFT.setTextColor(BLANC, NOIR);
  1658. TFT.drawString("parcourir au stylet <>", 2, 64);
  1659.  
  1660. /*
  1661. init_1_bouton(1, 300, 63, 15, 16, "X", &bt_STOP_2);
  1662. bt_STOP_2.selected = true;
  1663. bt_STOP_2.affiche(BLANC, ROUGE, 2);
  1664. */
  1665.  
  1666. uint16_t memo_seg_V[100]= {0};
  1667. uint16_t memo_x;
  1668. int64_t frq_i, memo_frq_i;
  1669.  
  1670. TFT.setFreeFont(FM9);
  1671. TFT.setTextColor(BLEU_CLAIR, NOIR);
  1672.  
  1673. uint16_t couleur1 = JAUNE;
  1674. boolean stop2 = false;
  1675. while (! stop2)
  1676. {
  1677. if (ts.tirqTouched() && ts.touched())
  1678. {
  1679. memo_x = x_touch;
  1680. get_XY_touch();
  1681.  
  1682. if ((x_touch>15)&&(x_touch<300) && (memo_x>15) && (memo_x<300) && (y_touch > 80))
  1683. {
  1684. TFT.pushRect(memo_x, 120, 1, 100, memo_seg_V); // efface avec l'image enregistrée
  1685. TFT.readRect(x_touch, 120, 1, 100, memo_seg_V); // memorise image avant de tracer le trait
  1686. if ((x_touch>18)&&(x_touch<296)) {TFT.drawFastVLine(x_touch, 120, 100, BLEU_CLAIR);}
  1687.  
  1688. String s1;
  1689. memo_frq_i = frq_i;
  1690.  
  1691. frq_i = frequence - 160*pas_W + x_touch * pas_W;
  1692.  
  1693. if (frq_i <0) {frq_i =0;}
  1694.  
  1695. if (frq_i != memo_frq_i)
  1696. {
  1697. if(frq_i==0) {couleur1 = ROUGE;} else couleur1 = JAUNE;
  1698. s1 = formatage_frq(frq_i) + " Hz ";
  1699. TFT.drawString(s1, 80, 82);
  1700. //delay(5);
  1701. }
  1702. }
  1703. /*
  1704. test_clic_boutons(&bt_STOP_2 );
  1705. if (bt_STOP_2.cliked)
  1706. {
  1707. bt_STOP_2.cliked = false;
  1708. stop2 = true;
  1709. efface_bande_boutons();
  1710. }
  1711. */
  1712. }
  1713. test_bouton_physique1();
  1714. }
  1715. TFT.setFreeFont(FF1);
  1716. TFT.setTextColor(VERT, GRIS_6);
  1717. TFT.drawString("Choisir GENE ou WOBU" ,50, 150);
  1718.  
  1719. //setup();
  1720. }
  1721.  
  1722.  
  1723. void affiche_gain(float gain)
  1724. {
  1725. TFT.fillRect(230, 110, 70, 15, NOIR); // efface 140
  1726. TFT.setFreeFont(FF1);
  1727. TFT.setTextColor(BLANC);
  1728. TFT.drawString("g=" +String(gain), 230, 110);
  1729. }
  1730.  
  1731.  
  1732. void calcul_Fmin_Fmax(uint64_t Fi)
  1733. {
  1734. if(Fi > 160*pas_W) {Fmin = Fi - 160*pas_W;} else Fmin = 0;
  1735. Fmax = Fi + 160 * pas_W;
  1736. }
  1737.  
  1738.  
  1739.  
  1740. void affi_bande_passante()
  1741. {
  1742. /*
  1743. rappels : B = bande passante à -3dB. en rapport de tensions (r=v0/v1)
  1744. 1 decibel = 10 Log(r) en rapport de puissances; Log est ici le logarithme décimal, et pas le log népèrien
  1745. 1 decibel = 20 Log(r) en rapport de tensions, c'est le cas ici.
  1746.  
  1747. -3 = 20 Log(r))
  1748. Log(r) = -3/20
  1749. r = 10^(-3/20) = 10^(-0.3) = 0.707 = 1/racine(2)
  1750. le Q est donc calculé en prenant comme bande passante le seuil de tension = tension max / racine(2)
  1751.  
  1752. Calculs simples :
  1753. en puissance : 10 Log(1/2) = -3dB
  1754. en tension : 20 Log(1/racine(2)) = -3dB (ce calcul donne en fait -3.01029995663981 dB) :-)
  1755.  
  1756. B = F0/Q
  1757. BxQ = F0
  1758. Q = F0/B
  1759.  
  1760. Pour connaitre les fréquences F1 et F2 (bornant la bande passante) il nous faut le tableau de toutes les valeurs mesurées.
  1761. soit 320 floats et les analyser une par une.
  1762.  
  1763. */
  1764. float v_max=0;
  1765. float v_min=4000;
  1766. float v_bp;
  1767. float vn;
  1768. uint16_t y1, y2;
  1769. uint16_t nA, nB;
  1770. String s1;
  1771.  
  1772. TFT.fillRect(2, 80, 237, 16, NOIR); // efface dans la bande d'affichage des résultats
  1773. //TFT.fillRect(230, 80, 79, 16, NOIR); // efface dans la bande d'affichage des résultats (partie droite)
  1774. TFT.setFreeFont(FF1);
  1775. TFT.setTextColor(BLANC, NOIR);
  1776.  
  1777. //init_1_bouton(1, 300, 63, 15, 16, "X", &bt_STOP_2);
  1778. //bt_STOP_2.selected = true;
  1779. //bt_STOP_2.affiche(BLANC, ROUGE, 2);
  1780.  
  1781. // recherche de la valeur min :
  1782. for(int n=0; n<320; n++)
  1783. {
  1784. vn = tableau_acq[n];
  1785. if (v_min > vn ) {v_min = vn;}
  1786. }
  1787.  
  1788.  
  1789. // recherche de la valeur max :
  1790. for(int n=0; n<320; n++)
  1791. {
  1792. vn = tableau_acq[n];
  1793. if (v_max < vn ) {v_max = vn;}
  1794. }
  1795.  
  1796. //float r = v_max / 1.414;
  1797. //v_bp = uint16_t(r);
  1798.  
  1799. v_bp = v_min + (v_max - v_min) / 1.414;
  1800.  
  1801. if (polarite1 == _DIR)
  1802. {
  1803. y1 = 230 - v_max;
  1804. y2 = 230 - v_bp;
  1805. }
  1806. else
  1807. {
  1808. y1 = 90 + v_max;
  1809. y2 = 90 + v_bp;
  1810. }
  1811.  
  1812. if(y1 < 100) {y1=100;}
  1813. if(y2 < 100) {y2=100;}
  1814.  
  1815. TFT.drawFastHLine(16, y1, 280, GRIS_4);
  1816. TFT.drawFastHLine(16, y2, 280, GRIS_4);
  1817.  
  1818. // affiche les Freq min et max de la bande passante :
  1819.  
  1820. // recherche les point de croisement de la courbe avec la ligne horizontale à -3dB
  1821. // en deux temps pour éviter les détections multiples intempestives ; la première est la bonne !
  1822.  
  1823. // find left match ; en partant de la gauche
  1824. for(int n=0; n<320; n++)
  1825. {
  1826. vn = tableau_acq[n];
  1827. if (vn > v_bp)
  1828. {
  1829. TFT.drawFastVLine(n, 100, 130, GRIS_4);
  1830. nA=n;
  1831. break;
  1832. }
  1833. }
  1834.  
  1835. // find right match ; en partant de la droite
  1836. for(int n=319; n>0; n--)
  1837. {
  1838. vn = tableau_acq[n];
  1839. if (vn > v_bp)
  1840. {
  1841. TFT.drawFastVLine(n, 100, 130, GRIS_4);
  1842. nB=n;
  1843. break;
  1844. }
  1845. }
  1846.  
  1847. TFT.setFreeFont(FF0);
  1848. s1 = String(v_max);
  1849. TFT.drawString("Vmax=" + s1, 3, 100);
  1850. s1 = String(v_min);
  1851. TFT.drawString("Vmin=" + s1, 3, 110);
  1852.  
  1853.  
  1854. // affiche (en texte) la valeur de la fréquence correspondant au maximum de la courbe :
  1855. TFT.setFreeFont(FF1);
  1856. uint16_t xM;
  1857. xM = (nA+nB) /2;
  1858. TFT.drawFastVLine(xM, 100, 130, GRIS_5);
  1859.  
  1860. uint64_t F0 = frequence - 160*pas_W + xM * pas_W;
  1861.  
  1862. s1 = formatage_frq(F0); // ajoute les points (.) séparateurs des milliers
  1863. s1 = supp_zeros(s1); // supprime les zéros et les points de séparation des milliers non significatifs
  1864. TFT.setTextColor(JAUNE, NOIR);
  1865. TFT.drawString("F0=" + s1, 100, 220); // valeur max
  1866.  
  1867. uint64_t Bp = (nB-nA)*pas_W;
  1868. s1 = formatage_frq(Bp);
  1869. s1 = supp_zeros(s1);
  1870. TFT.drawString("Bp=" + s1, 3, 80); // bande passante
  1871.  
  1872. float Q =(float)F0 / (float)Bp;
  1873.  
  1874. TFT.fillRect(210, 80, 108, 16, NOIR);
  1875. TFT.drawString("Q=" + String(Q, 2), 210, 80);
  1876. TFT.drawRect(0, 0, 319, 240, BLANC); // cadre principal pourtour de l'écran (le string a peut-être dépassé !)
  1877. }
  1878.  
  1879.  
  1880.  
  1881.  
  1882. void balayage_freq(uint64_t F0) // wobulation "autour" de la fréquence centrale F0
  1883. {
  1884. compte1++;
  1885. Serial.print("compte1="); Serial.println(compte1);
  1886.  
  1887. if ((pas_W == 0) || (f_mode1 != MODE_WOBU))
  1888. {
  1889. stop1 = true;
  1890. return;
  1891. }
  1892.  
  1893. uint16_t memo_seg_V[100]= {0};
  1894. int16_t memo_n;
  1895.  
  1896. TFT.setFreeFont(FM9);
  1897. TFT.setTextColor(BLEU_CLAIR, NOIR);
  1898.  
  1899. uint16_t couleur1;// = JAUNE;
  1900.  
  1901. uint16_t valeurLue1 = 230;
  1902. uint16_t valeurLue2 = 230;
  1903. uint16_t valeurLue3 = 230;
  1904.  
  1905. float gain=0.3;
  1906. float memo_gain;
  1907. float vf;
  1908.  
  1909. int64_t frq_i; // peut-être négatif
  1910.  
  1911. uint16_t v1;
  1912. uint16_t memo_v1;
  1913. uint16_t x;
  1914.  
  1915. boolean boucle_attente;
  1916.  
  1917.  
  1918. TFT.fillRect(80, 1, 150, 12, NOIR); // efface les boutons GENE & WOBU en haut
  1919. TFT.drawRect(0, 0, 319, 240, BLANC); // retrace le cadre principal pourtour de l'écran
  1920. TFT.drawRect(0, 79, 319, 18, BLANC);
  1921. TFT.drawFastHLine(1, 13, 318, GRIS_4);
  1922. TFT.drawFastHLine(1, 14, 318, GRIS_4);
  1923.  
  1924. TFT.setFreeFont(FF0);
  1925. TFT.setTextColor(BLANC, NOIR);
  1926. TFT.drawString("WOBULATION", 120, 3);
  1927.  
  1928. TFT.drawString("gain:" +String(gain), 190, 120);
  1929.  
  1930. stop1 = false;
  1931. while (! stop1)
  1932. {
  1933. calcul_Fmin_Fmax(F0);
  1934.  
  1935. compte2++;
  1936. Serial.print("compte2="); Serial.println(compte2);
  1937. //TFT.fillRect(1, 80, 317, 159, NOIR);
  1938. //calcul_Fmin_Fcenter(F0);
  1939.  
  1940. //TFT.setFreeFont(FF1);
  1941. //TFT.setTextColor(BLANC, NOIR);
  1942. //TFT.drawString("F0", 150, 220);
  1943.  
  1944. boucle_attente = ! en_boucle; // le boolean en_boucle est positionné par le bouton 'bt_boucle'
  1945. x=1;
  1946. for(uint16_t n=0; n<320; n++)
  1947. {
  1948. x++;
  1949. //Fmax = F0 + 160 * pas_W;
  1950. //frq_i = F0 + n * pas_W;
  1951.  
  1952. if(n<160){frq_i = F0 - (160-n) * pas_W;}
  1953. if(n>=160){frq_i = F0 + (n-160) * pas_W;}
  1954.  
  1955. if (frq_i >0)
  1956. {
  1957. frequence = frq_i;
  1958. couleur1 = JAUNE;
  1959. }
  1960. else
  1961. {
  1962. frequence = 0;
  1963. couleur1 = GRIS_1;
  1964. }
  1965.  
  1966. out_frequence();
  1967. delay(1);
  1968. memo_v1 = v1;
  1969. valeurLue1 = analogRead(analogPin); //acquisition_réponse 0..4095
  1970. delay(1);
  1971. valeurLue2 = analogRead(analogPin);
  1972. delay(1);
  1973. valeurLue3 = analogRead(analogPin);
  1974.  
  1975. vf = (float (valeurLue1) + float (valeurLue2) + float (valeurLue3))/3.0;
  1976. vf *= gain;
  1977. tableau_acq[n]= vf;
  1978.  
  1979. v1 = uint16_t(vf);
  1980. if (v1 > 230-100) // écrétage de l'affichage (partie haute)
  1981. {
  1982. v1 = 230-100;
  1983. gain -= 0.01; // ajuste le gain
  1984. boucle_attente = false; // pour ne pas boucler plus bas, ce qui permet de repasser auto et de réajuster
  1985. }
  1986.  
  1987. if ((x>15) && (x<300) )//&& (memo_n>15) && (memo_n<300)) à voir
  1988. {
  1989. TFT.drawFastVLine(x, 100, 131, NOIR); // efface
  1990. TFT.drawFastVLine(160, 200, 30, 10976); // trait vertical au centre (F0)
  1991. if (n%10 == 0) { TFT.drawFastVLine(n, 220, 19, 10976); }// petits traits verticaux en bas
  1992. uint16_t y1, y2;
  1993.  
  1994. if (frequence == 0) {v1=memo_v1;}
  1995.  
  1996. if (polarite1 == _DIR) {y1 = 230 - memo_v1; y2 = 230 - v1;}
  1997. else {y1 = 90 + memo_v1; y2 = 90 + v1;}
  1998.  
  1999. if(y1 < 100) {y1=100;}
  2000. if(y2 < 100) {y2=100;}
  2001.  
  2002. TFT.drawLine(x-1, y1, x, y2, couleur1); // trait entre [P(n-1)] et [P(n)] ...(signal)
  2003. }
  2004.  
  2005. memo_n=n;
  2006.  
  2007. if (ts.tirqTouched() && ts.touched())
  2008. {
  2009. memo_y_touch = y_touch;
  2010. get_XY_touch();
  2011.  
  2012. frequence = F0; // sinon le calcul des F min et max sera faux
  2013.  
  2014. int num_bouton = test_clic_boutons_inc_dec(); // on peut changer la Freq centrale pendant le balayage
  2015. if (num_bouton != -1) {F0 = frequence;}
  2016.  
  2017. //test_clic_bts_pas(); // on peut changer le pas pendant le balayage
  2018.  
  2019. bt_polarite.cliked = false;
  2020. test_clic_boutons(&bt_polarite );
  2021. if (bt_polarite.cliked)
  2022. {
  2023. bt_polarite.cliked = false;
  2024. if (polarite1 == _DIR) { polarite1 = _INV; bt_polarite.label = "INV"; bt_polarite.affiche(BLANC, ROUGE, 1); }
  2025. else { polarite1 = _DIR; bt_polarite.label = "DIR"; bt_polarite.affiche(NOIR, VERT, 1); }
  2026. delay(300);
  2027. }
  2028.  
  2029. bt_gain_P.cliked = false;
  2030. test_clic_boutons(&bt_gain_P ); // gain plus
  2031. if (bt_gain_P.cliked)
  2032. {
  2033. bt_gain_P.cliked = false;
  2034. gain +=0.01;
  2035. if (gain <0.1) {gain = 0.1;}
  2036. TFT.setFreeFont(FF0);
  2037. TFT.drawString("gain=" +String(gain), 130, 80);
  2038. affiche_gain(gain);
  2039. delay(100);
  2040. }
  2041.  
  2042. bt_gain_M.cliked = false;
  2043. test_clic_boutons(&bt_gain_M ); // gain moins
  2044. if (bt_gain_M.cliked)
  2045. {
  2046. bt_gain_M.cliked = false;
  2047. gain -= 0.01;
  2048. if (gain >1.0) {gain = 1.0;}
  2049. affiche_gain(gain);
  2050. delay(100);
  2051. }
  2052.  
  2053. test_clic_bt_pas_P(); // pas +
  2054. test_clic_bt_pas_M(); // pas -
  2055.  
  2056. bt_boucle.cliked = false;
  2057. test_clic_bt_boucle();
  2058. boucle_attente = ! en_boucle;
  2059.  
  2060. bt_STOP_1.cliked = false;
  2061. test_clic_boutons(&bt_STOP_1 );
  2062. if (bt_STOP_1.cliked)
  2063. {
  2064. bt_STOP_1.cliked = false;
  2065. stop1 = true;
  2066. popup(40, 150, "Fin Wobulation", "Restart...");
  2067. break;
  2068. }
  2069. }
  2070.  
  2071. if(gain != memo_gain) { affiche_gain(gain); memo_gain = gain;}
  2072. test_bouton_physique1(); // bouton poussoir câblé sur entrée analogique
  2073.  
  2074. } // de "for(uint16_t n=0; n<320; n++)"
  2075.  
  2076. frequence = F0; // sinon le calcul des F min et max sera faux
  2077. if (! stop1) // par le bouton "bt_STOP_1"
  2078. {
  2079. affi_bande_passante();
  2080. affiche_gain(gain);
  2081. if(boucle_attente == true)
  2082. {
  2083. while ( !(ts.tirqTouched() && ts.touched()) ) // BOUCLE attente clic
  2084. {
  2085. test_bouton_physique1(); // bouton poussoir câblé sur entrée analogique
  2086. }
  2087. }
  2088.  
  2089. }
  2090. //delay(300);
  2091.  
  2092. } // "de while (! stop1)"
  2093.  
  2094. //popup(40, 150, " ici 1", " ");
  2095. frequence = F0;
  2096. }
  2097.  
  2098.  
  2099.  
  2100. void GO()
  2101. {
  2102. //if (pas_W == 0) {return;}
  2103. bt_STOP_1.label = "X";
  2104. bt_STOP_1.affiche(BLANC, ROUGE, 2);
  2105. delay(100); // évite de redéclencher le bouton intempestivement
  2106. TFT.fillRect(6, 82, 150, 10, NOIR); // efface le texte "Choisir un pas"
  2107. affi_freq_W_min_max(); // affiche verticalement
  2108. balayage_freq(frequence); // wobulation "autour" de la fréquence centrale F0
  2109. //popup(40, 150, " ici 2", " ");
  2110. }
  2111.  
  2112.  
  2113. void affi_grille()
  2114. {
  2115. efface_grand_cadre();
  2116. TFT.drawFastVLine(160, 100, 139, 10976);
  2117. for (int n =1; n<32; n++)
  2118. {
  2119. TFT.drawFastVLine(10*n, 220, 19, 10976);
  2120. }
  2121.  
  2122. TFT.drawFastHLine(1, 231, 318, GRIS_6); // zero en bas
  2123. TFT.drawFastHLine(1, 98, 318, GRIS_6); // max en haut
  2124.  
  2125. init_1_bouton(1, 300, 62, 15, 15, "X", &bt_STOP_1);
  2126. bt_STOP_1.selected = true;
  2127. bt_STOP_1.affiche(BLANC, ROUGE, 2);
  2128.  
  2129. init_1_bouton(1, 255, 63, 30, 15, "DIR", &bt_polarite);
  2130. bt_polarite.selected = true;
  2131. bt_polarite.affiche(NOIR, GRIS_2, 1);
  2132.  
  2133. init_1_bouton(1, 300, 100, 15, 15, "+", &bt_gain_P);
  2134. bt_gain_P.selected = true;
  2135. bt_gain_P.affiche(NOIR, BLEU_CLAIR, 1);
  2136.  
  2137. init_1_bouton(1, 300, 120, 15, 15, "-", &bt_gain_M);
  2138. bt_gain_M.selected = true;
  2139. bt_gain_M.affiche(NOIR, BLEU_CLAIR, 1);
  2140.  
  2141. /*
  2142. if(le_pas_est_choisi == false)
  2143. {
  2144. TFT.setFreeFont(FF1);
  2145. TFT.setTextColor(BLANC, ROUGE);
  2146. TFT.drawString("Choisir un pas", 80, 150);
  2147. }
  2148. */
  2149. }
  2150.  
  2151.  
  2152.  
  2153. void test_clic_bouton_TEST()
  2154. {
  2155. test_clic_boutons(&bt_TEST );
  2156.  
  2157. if (bt_TEST.cliked)
  2158. {
  2159. bt_TEST.affiche(BLANC, ROUGE, 1);
  2160. delay(10);
  2161. Serial.println("-------------------------------");
  2162. Serial.println("bt_TEST.cliked");
  2163. bt_TEST.cliked = false;
  2164. bt_TEST.selected = true;
  2165. bt_TEST.affiche(BLANC, ROUGE, 1);
  2166.  
  2167. //affi_nuances_de_gris();
  2168. TS_calibrate();
  2169.  
  2170. //TFT.fillScreen(NOIR); // effface
  2171. //draw_AEC(0, 100, 320, 1);
  2172. //delay(300);
  2173.  
  2174. bt_TEST.cliked = false;
  2175. bt_TEST.selected = false;
  2176. bt_TEST.affiche(BLANC, ROUGE, 1);
  2177.  
  2178. //init_affichages();
  2179. //affiche_frequence(frequence);
  2180. //affiche_frequence(1);
  2181. }
  2182. }
  2183.  
  2184. double nb=0;
  2185.  
  2186.  
  2187. void test_clic_bts_preset()
  2188. {
  2189. if(f_mode1 == MODE_WOBU) {return;}
  2190.  
  2191. if (out_mode1 == MODE_9850)
  2192. {
  2193. test_clic_bt_455k();
  2194. test_clic_bt_4M();
  2195. test_clic_bt_10_7M();
  2196. test_clic_bt_27M();
  2197. }
  2198. if(out_mode1 == MODE_4351)
  2199. {
  2200. test_clic_bt_35M();
  2201. test_clic_bt_41M();
  2202. test_clic_bt_144M();
  2203. test_clic_bt_432M();
  2204. test_clic_bt_2400M();
  2205. }
  2206. }
  2207.  
  2208.  
  2209.  
  2210. void loop()
  2211. {
  2212. if (ts.tirqTouched() && ts.touched())
  2213. {
  2214. memo_y_touch = y_touch;
  2215. get_XY_touch();
  2216.  
  2217. test_clic_boutons_inc_dec(); // boutons cachés derrière les chiffres de la fréquence
  2218. test_clic_bouton_RAZ();
  2219.  
  2220. if((f_mode1 == MODE_ACCUEIL) || (f_mode1 == MODE_GENE))
  2221. {
  2222. test_clic_bts_choix_module();
  2223. }
  2224. if(f_mode1 == MODE_GENE) { test_clic_bts_preset(); }
  2225. if(f_mode1 == MODE_WOBU)
  2226. {
  2227. test_clic_bt_pas_M();
  2228. test_clic_bt_pas_P();
  2229. //test_clic_bt_boucle();
  2230. }
  2231.  
  2232. test_clic_bt_calibrate();
  2233.  
  2234. //if(f_mode1 == MODE_WOBU) { test_clic_bts_pas(); }
  2235. test_clic_bts_MODE();
  2236.  
  2237. /*
  2238. if(stop1 == true)
  2239. {
  2240. stop1= false;
  2241. affi_bande_passante();
  2242. //boucle_affi_curseur();
  2243. */
  2244. }
  2245.  
  2246. test_bouton_physique1();
  2247.  
  2248. delay(20); //20
  2249.  
  2250. // uint16_t valeurLue1 = analogRead(analogPin);
  2251. // frequence = valeurLue1;
  2252. // //Serial.println(valeurLue1);
  2253. // affiche_frequence(frequence);
  2254.  
  2255. }
  2256.  
  2257.  
  2258. /** ***************************************************************************************
  2259. CLASS TOUCH_BOUTON // affiche un nombre ou un petit texte dans un rectangle
  2260. ainsi que (en plus petit) deux valeurs supplémentaires, par ex: les valeurs mini et maxi
  2261. ********************************************************************************************/
  2262.  
  2263. // Constructeur
  2264. TOUCH_BOUTON::TOUCH_BOUTON()
  2265. {
  2266.  
  2267. }
  2268.  
  2269.  
  2270.  
  2271.  
  2272. void TOUCH_BOUTON::init(uint16_t x_i, uint16_t y_i, uint8_t dx_i, uint8_t dy_i, uint8_t dr_i, uint16_t couleur_i)
  2273. {
  2274. x0 = x_i;
  2275. y0 = y_i;
  2276. dx = dx_i;
  2277. dy = dy_i;
  2278. dr = dr_i;
  2279. couleur = couleur_i;
  2280.  
  2281. cliked = false;
  2282. selected = false;
  2283. }
  2284.  
  2285.  
  2286.  
  2287. void TOUCH_BOUTON::affiche(uint16_t coul_txt, uint16_t coul_fill_select, uint8_t n_font)
  2288. {
  2289. uint16_t couleur_contour = GRIS_5;
  2290.  
  2291. TFT.setTextColor(coul_txt);
  2292.  
  2293. if(selected)
  2294. {
  2295. TFT.fillRoundRect(x0, y0, dx, dy, dr, coul_fill_select);
  2296. }
  2297. else
  2298. {
  2299. TFT.fillRoundRect(x0, y0, dx, dy, dr, couleur); // efface
  2300. TFT.drawRoundRect(x0, y0, dx, dy, dr, couleur_contour); // retrace juste le contour
  2301. }
  2302.  
  2303. //FM9 FMB9 FSS9... voir le fichier FrSD_Fonts.h
  2304. if (n_font == 1) { TFT.setFreeFont(FF0);}
  2305. if (n_font == 2) { TFT.setFreeFont(FM9);}
  2306. if (n_font == 3) { TFT.setFreeFont(FMB9);}
  2307. if (n_font == 4) { TFT.setFreeFont(FSS9);}
  2308.  
  2309. TFT.drawString(label, x0+3, y0 - (2*n_font) + (dy-4)/2);
  2310. }
  2311.  
  2312.  
  2313. uint8_t TOUCH_BOUTON::read_dx()
  2314. {
  2315. return dx;
  2316. }
  2317.  
  2318.  
  2319. uint8_t TOUCH_BOUTON::read_dy()
  2320. {
  2321. return dy;
  2322. }
  2323.  
  2324.  
  2325. /** ***************************************************************************************
  2326. CLASS CHF_PAD // boutons invisibles pour incrémenter/décrémenter les chiffres de la fréquence
  2327. ********************************************************************************************/
  2328. // Constructeur
  2329. CHF_PAD::CHF_PAD()
  2330. {
  2331.  
  2332. }
  2333.  
  2334. void CHF_PAD::init()
  2335. {
  2336. for(uint8_t n=0; n<=9; n++) // 1 9
  2337. {
  2338. bt_pad_H[n].init(x_ch[n], 16, 25, 22, 3, GRIS_3); //18
  2339. //bt_pad_H[n].affiche(BLANC, VERT, 1); // pour visualiser la position des boutons
  2340.  
  2341. bt_pad_L[n].init(x_ch[n], 38, 25, 22, 3, GRIS_3); //40
  2342. //bt_pad_L[n].affiche(BLANC, ROUGE, 1);
  2343. }
  2344.  
  2345. }
  2346.  
  2347.  

Le programme complet (optimisé pour PlatformIO sous VScode) comprenant les "lib" et "include"... se trouve dans le fichier "documents.zip" au bas de cet article.

21 avril 2026 : La partie ADF4351 (30MHZ..2.4GHz) fonctionne, la fréquence choisie et affichée est réellement synthétisés et sortie sur le connecteur SMA. Je vous monterai des photos de l'ensemble en fonctionnement connecté sur un petit analyseur de spectre (tinySA).

25 avril 2026 : Les deux DDS (AD9850 et ADF4351) fonctionnent ! Le schéma a été mis à jour (libération du GPIO21) et le programme a été enrichi en conséquence. Nous avons là un double générateur de fréquence couvrant de 0 à 4.4 GHz sans trou. Reste à ajouter la fonction de wobulation.

26 avril 2026 : La programmation de la partie wobulation avance. On peut choisir la fréquence centrale et le pas de wobulation (avec 7 petits boutons sur l'afficheur). Reste à écrire la boucle de wobulation.

29 avril 2026 : Cette fois la boucle de wobulation est écrite. Il faut maintenant écrire la fonction d'acquisition de la réponse du circuit testé (actuellement j'affiche la réponse d'un filtre passe-bas du second ordre, par calcul dans le programme).
voir la fonction "void affi_trace_passe_bas()" dans le main.cpp

9 mai 2026 : L'acquisition de la réponse du circuit à tester est maintenant active, le résultat se dessine (rapidement) en temps réel sur l'afficheur. Le fonctionnement est correct tant pour les "basses" fréquences avec le module AD9850 que pour la VHF et l'UHF avec le module ADF4351. J'ai testé en particulier un fonctionnement OK pour 1350MHz.

5 VIDEO : Wobulation 300MHz






Le côté saccadé du signal est dû au temps d'analyse du "tinySA Spectrum Analyser" (dans cette configuration). Le signal de sortie de la carte ADF4351 est parfaitement fluide (vu à l'oscilloscope Hantek DSO2D50 - 500MHz).

6 Le signal 300MHz wobulé vu à l'oscilloscope






On voit que la variation de fréquence se fait d'une manière fluide (bien que, ici, par pas de 1MHz, 320 pas en tout, soit 160 pas de part et d'autre de la fréquence centrale égale à 300MHz, 1 pas de 1MHz par pixel horizontal sur l'afficheur ILI9341).

7 Signal 4MHz généré par le module AD9850






8 PREMIERE LUEUR !

En astronomie, la première lumière est la première utilisation pratique d'un nouvel instrument, généralement un télescope pour prendre une image. Ici il s'agit du premier résultat d'une wobulation complète, à savoir :
  • Génération en boucle du signal wobulé (ici 16MHz)
  • Attaque du circuit à tester (ici un quartz 16.000000 MHz)
  • détection de la réponse du circuit (redressement par deux diodes UHF polarisées afin de se débarrasser du seuil de conduction)
  • Acquisition temps réel du résultat
  • Affichage en temps réel sur l'afficheur ILI9341
On peut voir la justesse de l'affichage par comparaison avec l'oscilloscope connecté en parallèle.

Il me reste à mettre à jour le schéma (ajout du détecteur d'amplitude polarisé) mais aussi de finaliser le programme principal (ajout d'un réticule gradué et correction de quelque bugs lors du changement de mode [géné <-> wobu] ).
Le programme complet (optimisé pour PlatformIO sous VScode) comprenant les "lib" et "include"... se trouve dans le fichier "documents.zip" au bas de cet article.

21 avril 2026 : La partie ADF4351 (30MHZ..2.4GHz) fonctionne, la fréquence choisie et affichée est réellement synthétisés et sortie sur le connecteur SMA. Je vous monterai des photos de l'ensemble en fonctionnement connecté sur un petit analyseur de spectre (tinySA).

25 avril 2026 : Les deux DDS (AD9850 et ADF4351) fonctionnent ! Le schéma a été mis à jour (libération du GPIO21) et le programme a été enrichi en conséquence. Nous avons là un double générateur de fréquence couvrant de 0 à 4.4 GHz sans trou. Reste à ajouter la fonction de wobulation.

26 avril 2026 : La programmation de la partie wobulation avance. On peut choisir la fréquence centrale et le pas de wobulation (avec 7 petits boutons sur l'afficheur). Reste à écrire la boucle de wobulation.

29 avril 2026 : Cette fois la boucle de wobulation est écrite. Il faut maintenant écrire la fonction d'acquisition de la réponse du circuit testé (actuellement j'affiche la réponse d'un filtre passe-bas du second ordre, par calcul dans le programme).
voir la fonction "void affi_trace_passe_bas()" dans le main.cpp

9 mai 2026 : L'acquisition de la réponse du circuit à tester est maintenant active, le résultat se dessine (rapidement) en temps réel sur l'afficheur. Le fonctionnement est correct tant pour les "basses" fréquences avec le module AD9850 que pour la VHF et l'UHF avec le module ADF4351. J'ai testé en particulier un fonctionnement OK pour 1350MHz.

9 Wobulations avec l'AD9850

Nous allons expérimenter avec le circuit LC en // ci-dessus.

  • Le signal d’excitation issu du DDS AD9850 sera appliqué au circuit résonnant LC // au moyen d'une boucle d'induction (5 spires diamètre 10mm) visible en haut de la photo.
  • Le circuit LC à tester (monté sur une mini platine à mini connecteurs permettant de changer facilement les composants) est relié au détecteur d'amplitude à diodes Schottky (le petit module blindé) par un câble coaxial diamètre 2,8mm ayant (nous indique le fabriquant) une capacité de 100 pF/m (à ne pas confondre avec l'impédance caractéristique de 50 ohm) Ce câble auquel on peut ajouter les deux connecteurs SMA aux extrémités totalise une longueur de 13mm. Ce qui nous permet d'estimer la capacité totale de l'ensemble à 0.13x100pF = 13pF. Ces 13 pF viennent s'ajouter à la valeur des condensateurs que nous connecterons en parallèle sur l'inductance L.

10 Formule de Thomson

Dans la suite de l'expérimentation nous utiliserons la Formule de Thomson (la 1ere ci-dessus), et celles directement dérivées donnant les valeurs de L en fonction de la fréquence et de C ainsi que la valeur de C en fonction de la fréquence et de L.

11 Premier test en très basse fréquence (80kHz)


C'est un circuit LC résonnant parallèle. La self est un modèle enrobé (au tout premier plan, coiffée par la boucle d'induction). Le condensateur est un 1nF céramique.

Le calcul 1/(2pi.sqrt(L.C)) donne une fréquence de résonance de 73.41Hz. Ce qui correspond au résultat affiché par le wobulo. Erreur 10%, ce qui colle bien avec la précision donnée pour ces composants.

Remarque : J'ai ajouté une capa de 100nF sur la sortie du détecteur (qui est l'entrée du convertisseur A/N de l'ESP32) pour lisser le signal, qui sans cela se trouve légèrement bruité, surtout pour les analyses à des fréquences inférieures à 1MHz.

12 Test de transfos FI 455kHz et 10.6MHz

Voilà qui devrait plaire aux nostalgiques des "postes à transistors" des années 70', juste après la disparition des dinosaures. La 'fréquence intermédiaire' 455kHz, différence entre celle reçue à l'antenne (PO et GO) et l'oscillateur local était utilisée pour la modulation d'amplitude. Celle de 10.6MHz (ou 10.7) MHz servait pour la gamme FM (88..108MHz). Et les ondes courtes ?? me rappelle plus ! On utilisait aussi des filtres à résonateur céramique...

13 Test d'un filtre céramique 10.7MHz

Comme quoi le wobulateur s’accommode tout aussi bien de ces composants. Toutefois les petits transfos FI et ce filtre sont testé isolés de leur utilisation normale. C'est à dire que lorsqu'ils sont en place dans un récepteur de radio et que vous ne voulez pas les dessouder, ou simplement que vous voulez connaître leur comportement in situ, la manip risque d'être un peu plus délicate.

14 Circuit avec C = 270 pF et une self inconnue :

Appliquons (avec un tableur LibreOffice Calc, je vous donne la feuille de calcul dans le fichier "documents.zip" au bas de l'article) la formule qui donne la valeur de L en fonction de F et de C: (en ajoutant les 13pF du coax à la valeur de C).

Nous obtenons L = 937,70nH

15 Circuit avec C = 82 pF

Remplaçons le condensateur par un 82pF sans changer la bobine L et calculons la fréquence que nous devrions obtenir.

Le calcul donne 16,863 MHz

L'expérience ci-contre donne une fréquence de 16,500 MHz. L'erreur est de 2%... Sachant que la précision des condensateurs céramiques "ordinaires" est plutôt de 20%... C'est pas mal.

REMARQUES :
C'est la racine carrée de C qui intervient dans la formule...
Vous pouvez aussi trouver des céramiques avec une précision de 2%.

16 Circuit avec C = 47 pF

Remplaçons le condensateur par un 47pF sans changer la bobine L et calculons la fréquence que nous devrions obtenir.

Le calcul donne 21,218 MHz

L'expérience ci-contre donne une fréquence de 21,100 MHz. L'erreur est de 0,6% (21218 / 21100=1.006). Waouh!

17 Circuit avec C = 18 pF

Remplaçons le condensateur par un 18pF sans changer la bobine L et calculons la fréquence que nous devrions obtenir.

Le calcul donne 29,519 MHz

L'expérience ci-contre donne une fréquence de 28,800 MHz (vu de près, un peu plus ! )
L'erreur est de 2%.

18 Circuit avec C = 10 pF

L'erreur et de 4%.

On constate donc que le wobulateur ne fait pas "n'importe quoi" ! Mais il faut éviter les erreurs expérimentales, comme par exemple négliger l'influence des capacités parasites.

19 Wobulation avec l'ADF4351 - circuit LC - 170MHz

Capture d'écran de l'afficheur 320x240

Le filtre 170MHz expérimental (!) en composants classiques

20 Filtre en Pi 2.4GHz

Vu la valeur des composants il est clair qu'on ne va pas pouvoir aller beaucoup plus haut en fréquence avec la technologie SMD (surface mounted component = en français CMS composant monté en surface).

C'est un double face, l'autre face est entièrement métallisée = GND. Détail important : plusieurs vias non visibles ici relient les GND des deux faces.

21 Wobulation de ce filtre en Pi - 2.4GHz

La réponse du filtre LC (capture d'écran de l'afficheur ILI9341 2.8" + touchScreen).

2300 MHz affiché en haut c'est la fréquence centrale du balayage (trait vertical au centre). 2510 MHz c'est la fréquence qui correspond au curseur (trait vertical plus petit à droite) positionné ici manuellement (au stylet) sur la fréquence de résonnance.

Le filtre perso 2400 MHz en question, réalisé en composants CMS.
A ces fréqences on ne travaille plus avec des composants classiques !

22 Test d'un autre filtre 2.4GHz

La face arrière est totalement métallisée. Comme on peut le voir j'avais bricolé ce filtre pour je ne sais plus quelle raison. A ces fréquences et surtout au delà, on utilise plutôt des technologies "Microstrip Bandpass Filter" comme ici, voire des résonateurs à cavités résonnantes qui ont un Q très élevé (>30 000) et donc une bande passante extrêmement étroite.

23 petite capture d'écran...

Où l'on voit de nouvelles fonctions:
  • bouton 'INV' d'inversion (haut-bas) de l'affichage
  • Bouton 'STOP' du balayage, fait entrer dans le mode 'déplacement curseur' (latéralement, au stylet)
  • boutons '+' et '-' pour régler le gain
  • affichage de la valeur du gain. (voir fonction 'void balayage_freq(uint64_t F0)')

24 Le détecteur d'amplitude AM

Il est annoncé fonctionner de 0.1MHz à 3.3 GHz. La sérigraphie est particulièrement discrète. A part ce numéro...
J'en avais deux dont un HS, j'ai ouvert son blindage pour voir sa constitution: Il y a une double diode Schottky dans un boîtier SMD "type transistor" + 2 condensateurs et 2 résistances qui se battent en duel. Il n'est pas alimenté. On peut le réaliser avec deux diodes Schottky 1N6263 (pour des fréquences <1GHz). Les signaux à analyser étant de faible amplitude (400mV) et descendant jusqu'à 0V, j'ai dû polariser sa sortie avec R=220k au +3V3. Attention: ces composants sont fragiles. Pas de surtensions, pas de décharges électrostatiques !

Quelques précisions concernant le petit schéma d'interconnexion ci-dessus :
R1=220k sert à faire passer un faible courant dans le détecteur afin de polariser ses diodes Schottky (dans le sens passant) juste à leur seuil de conduction pour ne pas manquer les signaux faibles.
R2=15k parcourue elle aussi par ce courant de repos sert à décaler le potentiel envoyé au CAN de l'ESP32 parce que ce dernier ne démarre pas la conversion depuis zéro V, mais depuis une faible tension positive.
Les condensateurs évitent de récupérer du bruit sur l'alim.

J'ai également utilisé des détecteurs logarithmiques AD8302 et AD8318. Fragiles eux aussi !

25 Un autre détecteur AM

Ce modèle non-blindé (et moins cher) fonctionne tout aussi bien que le précédent. Il a l'avantage de montrer ses composants.

L'espèce de transistor SMD (qui n'en est pas un) est marqué XH8 sur celui que j'utilise, on le retrouve sous l'appellation "SCHOTTKY BARRIER RECTIFIER".


Certains fournisseurs montrent une photo sur laquelle on peut lire 'P2A'... qui peut correspondre à plein de choses ! Voir par exemple : https://smd.yooneed.one/code5032.html#code5032
Le marquage des composants SMD c'est, à mes yeux, un peu le bazar !

26 Quelques boucles d'induction et diverses selfs enrobées

Ces boucles s'utilisent pour coiffer l'inductance d'un circuit LC à tester (qui doit être électriquement relié au détecteur).

La plus petite destinée aux fréquences VHF compte deux spires, la plus grande qui donne satisfaction pour des fréquences de l'ordre du MHz voire bien moins (je l'ai testée à 80 kHz, comme vu plus haut) compte une cinquantaine de spires.

Vue d'ensemble de tout cela. Les selfs enrobées ressemblent furieusement à des résistances à couche métallique (verdâtres elles aussi). Attention à ne pas tout confondre !

27 Les modules DDS AD9850 & ADF4351

28 L'afficheur 2.8 et le module ESP32

Il s'agit du modèle ILI9341 2.8" AVEC écran tactile (touch screen). Il est aussi proposé en version no-touch qui ne convient pas étant donné que tout se fait au stylet sur l'écran. Donc attention lors de l'achat.

Un grand classique.

29 Le board (carte perso reliant l'afficheur à l'ESP32 )






Je l'ai donné(e) à fabriquer par JLPCB. Je vous fournis tous les documents Kicad dans le fichier 'documents.zip' au bas de l'article.

30 Cet appareil dans son boîtier

Cette face avant est constituée par une feuille de papier imprimée à l'imprimante laser puis plastifiée avec une plastifieuse classique. Le tout est collé sur le dessus du couvercle après découpe de l'emplacement de l'afficheur. Quant aux boutons, il s'agit évidemment d'une image pour faire joli (!) sachant qu'aucun bouton n'est nécessaire pour faire fonctionner l'appareil, tout se fait au stylet sur l'écran tactile.

31 Disposition des modules dans la boite

L'afficheur ILI9341 est caché sous la carte d’interconnexion avec l'ESP32, disposés dans le couvercle. Les deux modules HF sont placés dans le fond de la boite.

32 Affichage bande passante et facteur de qualité

14 juin 2026 :
Ajout de nouvelles fonctions :
  • Calcul et affichage de la fréquence de résonance d'un circuit oscillant
  • Calcul et affichage de la bande passante à -3dB en tension, soit Vmax/sqrt(2)
  • Calcul et affichage du facteur de qualité 'Q'


Quelques précisions :

  • 0007.000.000 c'est 7 MHz, fréquence centrale de la vobulation, qui correspond horizontalement au centre de l'affichage à 160px du bord, l'affichage faisant 320px de large. On peut la modifier à tout moment, avec le stylet directement sur les gros chiffres en haut.
  • F0=6.020.000 (= 6020kHz = 6,020 MHz) c'est la fréquence de résonance du circuit LC testé.
  • P=20.000 (= 20kHz) est la valeur du pas de vobulation. Chaque pas correspond horizontalement à 1 pixel. L'affichage fait 320 px de large.
  • Bp=760.000 (= 760kHz) c'est la largeur bande passante mesurée.
  • g=0.15 c'est le gain vertical (qui se règle automatiquement en cas d’écrêtage du signal). Il peut aussi se modifier par les boutons [+] et [-]
  • Q=7.92 c'est le facteur de qualité. S'il semble trop faible on peut faire une réclamation à la poste :))

J'ai également retourné l'affichage (à 180 deg) pour pouvoir utiliser l'appareil avec l'écran à droite et non plus à gauche, ce qui permet de ne pas être géné par la connectique lorsqu'on utilise le stylet sur l'écran, du moins pour les droitiers, je m'excuse pour les gauchers... mais sachez qu'il n'y a qu'une ligne de code à modifier, cherchez :
TFT.setRotation(1);// 1 ou 3 à voir, suivant disposition de l'afficheur

33 Facteur de qualité d'un filtre blindé FI 455MHz

On mesure un Q=20.68. C'est déjà mieux que le circuit LC précédent.
Bande passante 22KHz (= +/- 11kHz) c'est ce qu'on attend d'un filtre FI modulé en amplitude des signaux BF = 0..9 kHz (les bandes latérales de part et d'autre de la porteuse étant = +/- 9kHz).

Tiens, tiens... C'est bizarre, la fréquence de résonance est exactement égale à 455kHz ! Ben non, j'ai juste tourné le noyau avec un tournevis pour obtenir ce résultat !

34 Facteur de qualité d'un filtre céramique 10.7MHz

Cette fois le Q n'est plus de 7.92 mais de 254.3.
Certes les courbes de résonance se ressemblent mais à y regarder de plus près le pas d'analyse n'est plus de 20kHz mais vaut P=2.000 (=2kHz)

35 Facteur de qualité d'un quartz 16MHz

Le quartz 16MHz est inséré en série entre la sortie de l'AD9850 et le détecteur d'amplitude.

Sur la première saisie d'écran on constate deux pics de transmission. Sur les captures suivante on zoome sur chacun d'eux.
Le premier se situe à la fréquence = 16.004MHz soit très proche des 16 MHz attendu. On peut se demander si le léger décalage fréquentiel provient de la précision du générateur ou de celle du quartz (ou les deux ?) Le Q monte à 2800 ce qui est bien supérieur à ce que donne un circuit LC.
Le second pic se produit à 16.109500 MHz, ce qui s'éloigne de la fréquence nominale, mais en contrepartie son Q s'élève à 18950 (Le pas d’analyse et respectivement de 1000 Hz, 200 Hz et 50Hz).

L'article 'Quartz (électronique)' sur Wikipédia dit :

Un quartz peut être modélisé comme un circuit électrique possédant deux fréquences de résonance proches l'une de l'autre, l'une à faible impédance (série), et l'autre à haute impédance (parallèle).

puis plus loin :

Pour que le quartz fonctionne à sa fréquence nominale en mode parallèle, il faut que la capacité équivalente du circuit actif présenté au quartz soit celle spécifiée par le constructeur.

Nous voilà prévenus !

Quant à la Loi de Murphy, elle nous rapelle :

"Un oscillateur à quartz oscille forcément sur une autre fréquence que celle prévue... s’il oscille ! "

36 Test par GPS de la précision de la fréquence de l'AD9850 et de celle du quartz 16MHz

Dans le paragraphe précédent nous avons constaté que le quartz 16 MHz (marqué 16.0000MHz sur son boîtier) présentait une fréquence de résonance de 16.004 MHz mesurée par le wobulateur. Je me devais donc d'expérimenter afin de savoir lequel des deux était le plus juste, le quartz ou l'AD9850 ?

Il se trouve que je suis en mesure de le faire, avec mon Géné VHF 1Hz à 100MHz à DDS AD9951 piloté par GPS.

J'ai donc généré un signal = 16MHz par le géné GPS et un autre signal de 16 MHZ également par le wobu en mode géné. J'ai alors connecté les deux signaux sur un oscilloscope en mode Lissajous et j'ai réglé très finement la fréquence du wobu pour obtenir une figure stable.

RESULTAT :
La synchronisation se produit lorsque le wobu affiche 16.000202 MHz. L'ADF9850 s'en sort donc très bien (200Hz d’écart) et le quartz avec son 16.004800 est mis KO par 24 à 1 ! L'erreur 4800Hz est en effet 24 fois supérieure à celle de 202 Hz. Toutefois nous avons vu plus haut que :
Pour que le quartz fonctionne à sa fréquence nominale en mode parallèle, il faut que la capacité équivalente du circuit actif présenté au quartz soit celle spécifiée par le constructeur.
Donc on peut penser que ce quartz utilisé correctement serait plus précis.

Et n'oublions pas que ce n'est pas pour rien qu'on a inventé les oscillateurs TCXO, les OCXO, les horloges atomiques au rubidium (j'en ai une d'occase qui m'a coûté environ 100€) et les horloges atomiques au césium (alors là c'est beaucoup beaucoup plus cher). Mais avec le GPS on dispose d'une horloge au césium gratos !

37 On va dorloter le quartz !

Fort des connaissances acquises précédemment nous ne pouvions pas en rester là ! Il fallait absolument tester ce quartz en le plaçant dans un environnement qui lui convient. J'ai donc ajouté un condensateur céramique de 12pF en parallèle. Et cette fois le résultat est excellent. Non seulement la fréquence est 100 fois plus précise mais en plus le Q est passé de 2860 à 9700.

On retiendra que lorsqu'on utilise un quartz il faut le faire sérieusement.

38 documents

Code source complet en C++
Version du code source adaptée à l’éditeur VScode/PlatformIO

Vous trouverez dans ce fichier 'documents.zip' le code source complet ainsi que des fichiers à placer sur la SDcard.

Remarque: En phase de développement, mises à jour quasi quotidiennes -> des fonctions en plus, une ergonomie améliorée, des bugs en moins ! Voir le n° de version au début du fichier main.cpp

39 -

Liens...
Pour me joindre : silicium628@free.fr
Un site complémentaire à celui-ci ; Electro & Robot

1303