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

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

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 Circuit avec C = 270 pF

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

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

13 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!

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

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

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

Capture d'écran de l'afficheur 320x240

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

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

18 Wobulation de ce filtre en Pi - 2.4GHz

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

1300 MHz affiché en haut c'est la fréquence centrale du balayage (trait vertical au centre). 1328 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 !

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

20 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)')

21 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 !

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

22 Les modules DDS AD9850 & ADF4351

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

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

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

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

27 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

28 -

Liens...


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

701