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

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

33 -

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

827