Wobulateur digital à DDS AD9850
(résolution 1Hz)
piloté par un ATmega8

Cet article fait suite à celui décrivant un générateur HF 40MHz sinus basé sur ce même circuit DDS AD9850.

1 -

L'appareil tient sur un carte unique qui comprend le module AD9850, le microcontrôleur ATmega8 qui le pilote, fait les acquisitions analogiques grâce à son convertisseur A/N intégré, et assure la liaison RS232 avec le PC, et la partie analogique (ampli OP haute impédance à TL082).

2 Le circuit DDS AD9850

On peut se procurer le AD9850 seul en boîtier SMD de folie (j'en ai soudé un, je déconseille l'aventure!), ou bien comme ici monté sur un module au pas de 2.54mm ce qui facilite grandement la réalisation du circuit imprime.

Le AD9850 est un DDS (direct digital synthesis) qui synthétise directement un signal sinusoïdal à une fréquence précise. Dans le cas présent la fréquence peut être choisie entre 1Hz et plus de 40MHz avec une résolution de 0.0291 Hz lorsqu'il est piloté par une horloge (à quartz) de 125 MHz.
En interne le DDS génère une sinusoïde numérique qu'il convertit en un signal analogique avec un convertisseur N/A. La fréquence du signal est déterminée par un mot de 32 bits. La résolution de 0.0291 est égale à la fréquence du quartz divisée par 2 puissance 32. (125E6 / 4294967296 = 0.0291)

3 Le principe mis en oeuvre au sein du DDS (je vous traduis une toute petie partie du datasheet en anglais)

La circuiterie interne du DDS est un diviseur digital de fréquence dont la résolution incrémentale (la finesse du pas) est déterminée par la fréquence de l'horloge de référence (ici 125MHz) divisée la 2^N nombre de bits du mot de commande de fréquence (un grand (32 bits) nombre binaire qui détermine la fréquence de sortie).
L'accumulateur (la mémoire) de phase est un compteur à module variable qui incrémente (ajoute) le nombre enregistré à chaque fois qu'il reçoit une impulsion d'horloge.
Lorsque le compteur déborde, il reboucle sur lui-même de sorte que le signal est généré continuellement.
Le mot de commande de fréquence (il sera fourni par l'ATmega en fonction de la fréquence désirée) constitue la valeur du module du compteur, ce qui en fin de compte détermine la taille de l'incrément (delta phase) qui est ajouté dans l'accumulateur de phase lors de l'impulsion d'horloge suivante. Plus l'incrément est large et plus vite l'accumulateur se remplit et déborde ce qui produit une fréquence plus élevée. Le signal numérique de sortie est issu d'un calcul mathématique d'une fonction cosinus avec comme variable la valeur de la phase.

4 -

La fréquence de sortie est donnée par la formule ci-contre:

5 Le schéma du Wobulateur

Le premier étage redresse à large bande les signaux issus de la wobulation grâce à des diodes VHF polarisées afin de ne pas avoir de seuil de conduction. Le résultat est intégré par une cellule RC. On obtient une tension correspondant à l'enveloppe du signal, qui est alors amplifiée avec un AOP de type TL082. La diode redresse les alternances négatives ce qui permet de travailler au potentiel de masse côté gauche tout en la polarisant à partir d'une tension positive côté droit. Cet étage attaque l'entrée A/D du microcontrôleur ATmega8 qui numérise le signal obtenu avec son convertisseur analogique/numérique 10bits intégré. Puis les résultats des conversions sont envoyés au PC par une liaison RS232.

6 Liaison série RS232 avec le PC:

Les ports série se faisant rares sur les PC récents et les portables, on peut avantageusement utiliser un petit adaptateur USB-RS232 du commerce basé sur une puce Prolific Technology PL2303, compatible Linux (sans drivers supplémentaires) fournissant un port série RS232 virtuel sous la forme /dev/ttyUSB0 (ou USB1...)

Je l'utilise actuellement avec satisfaction. J'ai acheté ce convertisseur dans une "boutique" sur Ebay... Je vous laisse chercher.

7 -

Il y a une alternative à cette solution: c'est la possibilité d'utiliser un convertisseur USB-RS232 offrant directement des niveaux de sortie TTL (0V -5V) (enfin c'est ce que j'ai cru comprendre en lisant la description du produit...) au lieu de -15V , +15V de la norme RS232. Ce qui doit permettre de simplifier encore l'électronique... Je précise: la photo ci-contre est recopiée depuis le site d'un vendeur sur eBay, moi je ne vends rien. J'en ai commandé un. Je vous tiens au courant...

8 Partie logique

Le mot de commande peut être chargé en mode parallèle ou en mode série. J'ai choisi le mode série pour économiser des ports de l'ATmega8.
Les boutons sont lus sur un seul pin par le convertisseur analogique/numérique de l'ATmega. (on pourrait en théorie mettre jusqu'à 1024 boutons! en pratique disons une vingtaine pour garder une bonne marge de sécurité).

9 Le Firmware en langage C:

CODE SOURCE en langage C
  1. /*==============================================================================
  2. par Silicium628
  3. derniere mise à jour 26 octobre 2012
  4. ================================================================================
  5. Pilotage d'un circuit DDS AD9850 générateur de fréquence 0..40MHz sinus avec un quartz (pour le AD9850) de 125MHz
  6. -Affichage LCD 2 x 20 caractères ("splitté entre deux ports de l'ATmega)
  7. -Wobulateur (necessite la liaison RS232 avec le soft tournant sur le PC; les commandes sont effectuees depuis le PC)
  8. ================================================================================
  9. */
  10. // #include <math.h>
  11.  
  12.  
  13. #define F_CPU 16000000
  14.  
  15. #include <avr/io.h>
  16. #include <util/delay.h>
  17.  
  18. #include "uart_628.c"
  19.  
  20.  
  21.  
  22. #define pin_WCL 0b00000100
  23. #define pin_FQU 0b10000000 // sur port D
  24. #define pin_RESET 0b01000000
  25. #define pin_DATA 0b00001000
  26.  
  27.  
  28.  
  29. char * version = "2.1";
  30.  
  31. /*
  32. RAPPEL variables avr-gcc (vérifiable avec le .map)
  33.  
  34. char 1 -128 .. 127 ou caractères
  35. unsigned char 1 0 .. 255 (equiv à byte du pascal)
  36. uint8_t 1 (c'est la même chose que l'affreux 'unsigned char')
  37. char toto[n] n
  38. int 2 -32768 .. 32767
  39. int16_t 2 idem 'int'
  40. short int 2 pareil que int (?)
  41. unsigned int 2 0 .. 65535
  42. uint16_t 2 idem 'unsigned int'
  43. long int 4 -2 147 483 648 à 2 147 483 647
  44. int32_t 4 32 bits ; idem long int
  45. unsigned long long 64-bit unsigned type.
  46. uint64_t 8 64-bit unsigned type.
  47. long long int 8
  48. unsigned long int 4 32 bits ; 0 .. 4 294 967 295 (4,2 x 10^9)
  49. float 4
  50. double 4 // (?)
  51.  
  52. La déclaration char JOUR[7][9];
  53. réserve l'espace en mémoire pour 7 mots contenant 9 caractères (dont 8 caractères significatifs).
  54. */
  55.  
  56. uint64_t frequence;
  57. uint64_t pas_i;
  58. uint8_t pos;
  59. uint8_t digits_frq[8];
  60. uint8_t pas_wob = 2; // soit 10e2 = 100
  61. uint64_t Frq_W32; // valeur binaire destine a l'AD9850, vaut environ 68,72 x frequence en Hz.
  62. uint8_t phase;
  63. uint8_t nb_cycles = 0;
  64. uint8_t octet_recu;
  65. uint8_t buffer232[20];
  66. uint8_t compteur1; // pour tests
  67. // uint8_t flags = 0;
  68.  
  69.  
  70.  
  71. void init_ports (void) // ports perso
  72. // 0 = entree, 1=sortie ; les 1 sur les pins en entrees activent les R de Pull Up (tirage à VCC)
  73. {
  74. PORTB = 0b00000000;
  75. DDRB |= 0b00001000; // portB[3] = sortie (OC2)
  76.  
  77. DDRC = 0b11001111; //PC4 en entree (acquisition signal) ; PC5 en entree (ADC5 - boutons)
  78.  
  79. DDRD = 0b11111111; //PD1 = TXD ;
  80. PORTD = 0b00100000; //PD0 = RXD
  81. }
  82.  
  83.  
  84.  
  85. void InitADC (void)
  86. {
  87. ADCSRA = _BV(ADEN) | _BV(ADPS2); // Activate ADC with Prescaler 16 --> 1Mhz/16 = 62.5kHz
  88. ADMUX = 5; // Select pin ADC5 using MUX
  89. }
  90.  
  91.  
  92.  
  93. void InitINTs (void)
  94. /*
  95.  TCCR2:
  96. wgm21,20 =11 ->Fast PMW
  97. com21,com20=01 ->Set OC2 on Compare Match, clear OC2 at TOP (valable pour le mode Fast PWM); voir p:116
  98. bits2,1,0: prescaler (010 = 1/8)
  99. */
  100. {
  101. TCCR2= 0b00000000; // Timer2 non utilisé.
  102. TIMSK|= 0b00000000; // INT Timer2 comp disable; INT Timer2 overflow disable;
  103. GICR|= 0b00000000; // gere les INTs voir page 67 du pdf
  104. MCUCR|= 0b00000000; // The falling edge of INT0 generates an interrupt request. p:67 du pdf
  105. }
  106.  
  107.  
  108.  
  109.  
  110. void init_variables(void)
  111. {
  112. /*
  113. 1Hz -> 69
  114. 10Hz -> 687
  115. 100Hz -> 6872
  116. 1kHz -> 68719
  117. 10kHz -> 687195
  118. 100kHz -> 6871948
  119. 1MHz -> 68719477
  120. 10MHz -> 687 194 767
  121. */
  122. uint8_t n;
  123. for (n=0; n<=7; n++)
  124. {
  125. digits_frq[n]=0;
  126. }
  127. pos=3;
  128. digits_frq[pos]=1;
  129.  
  130. Frq_W32=68719;
  131.  
  132. phase = 0;
  133. }
  134.  
  135.  
  136.  
  137. void reset_DDS(void)
  138. {
  139. _delay_us(1);
  140. PORTD |= pin_RESET;
  141. _delay_us(1);
  142. PORTD &= ~pin_RESET; // l'operateur "~" donne le complement (inverse des bits). ne pas confondre avec l'opérateur "!"
  143. _delay_us(1);
  144. }
  145.  
  146.  
  147. void impulse_clk_W(void) // sur pin W_CL
  148. {
  149. _delay_us(1);
  150. PORTD |= pin_WCL;
  151. _delay_us(1);
  152. PORTD &= ~pin_WCL; // l'operateur "~" donne le complement (inverse des bits). ne pas confondre avec l'opérateur "!"
  153. _delay_us(1);
  154. }
  155.  
  156.  
  157. void impulse_FQ_U(void) // sur pin FQ_U
  158. {
  159. _delay_us(1);
  160. PORTD |= pin_FQU;
  161. _delay_us(1);
  162. PORTD &= ~pin_FQU; // l'operateur "~" donne le complement (inverse des bits). ne pas confondre avec l'opérateur "!"
  163. _delay_us(1);
  164. }
  165.  
  166.  
  167. void out_DDS(uint64_t freq_i) // 40 bits vers AD9850; voir son datasheet
  168. {
  169. uint8_t n;
  170.  
  171. //envoi de la frequence
  172. uint32_t masque;
  173.  
  174. masque = 1;
  175. for (n=0; n<= 31; n++) // on sort le LSB (W0) en premier
  176. {
  177. masque += masque; // revient à x2 le masque le '1' se deplacant de droite a gauche, 32 bits en tout
  178. if ( (freq_i & masque) != 0) {PORTD |= pin_DATA;} else { PORTD &= ~pin_DATA; }
  179.  
  180. impulse_clk_W();
  181. }
  182.  
  183. PORTD &= ~pin_DATA; // (W32 toujours = 0)
  184. impulse_clk_W();
  185.  
  186. PORTD &= ~pin_DATA; // (W33 toujours = 0)
  187. impulse_clk_W();
  188.  
  189. PORTD &= ~pin_DATA; // (W34 = Power-Down = 0)
  190. impulse_clk_W();
  191.  
  192. // envoi de la phase (5 bits)
  193.  
  194. for (n=0; n<=4; n++) // on sort le LSB (W35) en premier et le MSB en dernier.
  195. {
  196. masque = (1 << n);
  197. if (phase & masque) {PORTD |= pin_DATA;} else { PORTD &= ~pin_DATA; }
  198.  
  199. impulse_clk_W();
  200. }
  201. // envoi impulsion FQ_UD
  202. impulse_FQ_U();
  203. }
  204.  
  205.  
  206.  
  207. void calcul_Frq_W32(void)
  208. {
  209. // frequence = digits_frq[0]+digits_frq[1]*10+digits_frq[2]*100+digits_frq[3]*1000+digits_frq[4]*10000+digits_frq[5]*100000+digits_frq[6]*1000000+digits_frq[7]*10000000; // pour affichage
  210. // J'ai cessé de traiter des grands integers (64bits) pour la représentation de la frequence en decimal, donc commente la ligne precedente (pour gagner place en memoire)
  211.  
  212. Frq_W32 =
  213. (uint64_t)digits_frq[0]*(uint64_t)69
  214. +(uint64_t)digits_frq[1]*(uint64_t)687
  215. +(uint64_t)digits_frq[2]*(uint64_t)6872
  216. +(uint64_t)digits_frq[3]*(uint64_t)68719
  217. +(uint64_t)digits_frq[4]*(uint64_t)687195
  218. +(uint64_t)digits_frq[5]*(uint64_t)6871948
  219. +(uint64_t)digits_frq[6]*(uint64_t)68719477
  220. +(uint64_t)digits_frq[7]*(uint64_t)687194767;
  221. // pour DDS
  222.  
  223. /* REMARQUE:
  224. Les (uint64_t) forcent les calculs sur 64 bits et evitent le bug suivant mais consomment 1100 octets en memoire flash
  225. detail du bug sans les (uint64_t) -> les frequences entre 476 et 954 Hz ne sont pas synthetisees
  226. 476*68.72 = 32710 -> ok
  227. 477*68.72 = 32779 -> HS
  228. 954*68.72 = 65558 -> reprise mais HS -> qq Hz
  229. */
  230. }
  231.  
  232.  
  233.  
  234. void regle_Frq_00 (void)
  235. {
  236. // on regle la frequence sur 0 (absence de signal de sortie)
  237. digits_frq[7]=0; digits_frq[6]=0; digits_frq[5]=0; digits_frq[4]=0; digits_frq[3]=0; digits_frq[2]=0; digits_frq[1]=0; digits_frq[0]=0;
  238. calcul_Frq_W32();
  239. // affiche_frequence();
  240. }
  241.  
  242.  
  243.  
  244.  
  245. void reception_params()
  246. {
  247. uint8_t n;
  248. uint8_t ok = 0;
  249.  
  250. compteur1++;
  251.  
  252. for (n=0; n<10; n++)
  253. {
  254. octet_recu=USART_RxByte();
  255. buffer232[n]=octet_recu;
  256. }
  257.  
  258.  
  259. pas_wob= 10*(buffer232[0]-48) + buffer232[1]-48;
  260.  
  261. pas_i=1;
  262. for (n=1; n<= pas_wob; n++) {pas_i *= 2;}
  263.  
  264. for (n=0; n<8; n++)
  265. {
  266. digits_frq[n]=buffer232[n+2]-48;
  267. }
  268. calcul_Frq_W32();
  269. out_DDS(Frq_W32);
  270. }
  271.  
  272.  
  273.  
  274. void balayage_wob(void)
  275. {
  276. uint8_t octet_recu;
  277. uint32_t n;
  278. uint16_t valeur_lue;
  279. uint8_t n3H;
  280. uint8_t n3L;
  281. uint8_t compteur2;
  282. uint64_t freq_i;
  283.  
  284. ADMUX = 4; // Select pin ADC4 using MUX
  285.  
  286. // attend feu vert du PC
  287. octet_recu=USART_RxByte();
  288. while (octet_recu !='G') {octet_recu=USART_RxByte();}
  289. {
  290.  
  291. USART_TxStr("DAT");
  292. compteur2=0;
  293.  
  294. freq_i = Frq_W32;
  295. out_DDS(freq_i);
  296.  
  297. for (n=0; n<510; n++)
  298. {
  299. //acquisition
  300. _delay_ms(30); // bloqueur (de la valeur analogique mesuree) pendant l'acquisition
  301.  
  302. ADCSRA |= _BV(ADSC); //Start conversion - resolution 10bits
  303. while (ADCSRA & _BV(ADSC) ) {;} // attend la fin de la converstion
  304. valeur_lue = ADCW; // lit la value convertie
  305. valeur_lue = 1024 - valeur_lue; // inversion y
  306.  
  307. // valeur_lue=n; // pour test (=ok)
  308.  
  309. n3L=valeur_lue % 128; //codage sur 7 bits
  310. n3L &= 0b01111111; // 8eme bit de l'octet L toujours = 0 - (cette ligne est en fait inutile)
  311. n3H=valeur_lue/128; //codage sur 7 bits
  312. // on reserve le 8eme bit pour identifier l'octet (L ou H)
  313. n3H |= 0b10000000; // 8eme bit de l'octet H toujours = 1
  314.  
  315. // envoi donnee vers PC
  316. USART_TxByte(n3L);
  317. _delay_us(100);
  318. USART_TxByte(n3H);
  319. _delay_us(100);
  320.  
  321. compteur2++;
  322.  
  323. freq_i += pas_i;
  324. out_DDS(freq_i);
  325. }
  326. }
  327.  
  328. }
  329.  
  330.  
  331. void wobulateur (void)
  332. {
  333.  
  334. uint8_t compteur;
  335.  
  336.  
  337. while(1)
  338. {
  339. octet_recu=USART_RxByte();
  340.  
  341. while ( (octet_recu != 'P') && (octet_recu != 'G')) { octet_recu=USART_RxByte(); }
  342.  
  343. if (octet_recu == 'P') { reception_params(); }
  344. else if (octet_recu == 'G') { balayage_wob(); }
  345.  
  346. }
  347. }
  348.  
  349.  
  350.  
  351. void test (void)
  352. {
  353.  
  354. }
  355.  
  356.  
  357. int main (void)
  358. {
  359. init_variables();
  360. init_ports();
  361. InitADC();
  362. InitINTs();
  363. USART_Init();
  364.  
  365. reset_DDS();
  366.  
  367.  
  368. _delay_ms(2000);
  369.  
  370.  
  371. uint8_t compteur;
  372. compteur=20;
  373.  
  374. uint32_t f1;
  375. uint32_t f2;
  376. uint32_t f3;
  377.  
  378. uint8_t a;
  379. uint8_t b;
  380. uint8_t c;
  381.  
  382.  
  383. Frq_W32 = a*f1+b*f2+c*f3;
  384. calcul_Frq_W32();
  385.  
  386. out_DDS(Frq_W32);
  387. out_DDS(Frq_W32);
  388.  
  389.  
  390. wobulateur();
  391.  
  392.  
  393.  
  394. }
  395.  
  396.  

10 SOFT côté PC en C++ & Qt4

CODE SOURCE en langage C
  1. /*
  2. INTERFACE D'ACQUISITION POUR WOBULATEUR 0-40MHz
  3. auteur Silicium628
  4. http://www.silicium628.fr
  5. logiciel libre sous licence GNU
  6.  
  7. */
  8.  
  9.  
  10. #include "mainwindowimpl.h"
  11. #include <stdio.h>
  12. #include <unistd.h>
  13. #include "rs232.h"
  14. #include <math.h>
  15.  
  16. #include <QPainter>
  17. #include <QMessageBox>
  18. #include <QDir>
  19.  
  20.  
  21. QString version = "7.2";
  22.  
  23. // numport=16; /* /dev/ttyUSB0
  24. // numport=17; /* /dev/ttyUSB1
  25. int numport=16;
  26.  
  27. int bdrate=9600; /* 9600 baud */
  28. unsigned char buf[100];
  29.  
  30. QList<Acq_Rec> liste_mesures1, liste_mesures2, liste_mesures3, liste_mesures4, liste_mesures5; // 512 mesures par balayage
  31. unsigned char params[1030];
  32. unsigned char datas[1030];
  33. QString str_test;
  34.  
  35. int num_octet_recu;
  36. int num_ligne;
  37. int num_pas_recu;
  38. int num_pas_desire;
  39. int digits_F_min[8];
  40. int digits_F_centrale[8];
  41. float pas;
  42. long int Freq_min_desiree, Freq_centrale_desiree, Freq_max_desiree;
  43. // long int Fmin, Fmax;
  44. long int Fmarqueur;
  45. int pos_marqueur, memo_pos_marqueur;
  46. // long int graduation_x;
  47. long int pas_graduation=1000; //en Hz
  48. int num_pas_graduation;
  49. // int a_effacer=1;
  50.  
  51. int cnx232_ok =0;
  52. int memo_y;
  53.  
  54.  
  55. QColor couleur_ecran = QColor::fromRgb(1, 92, 65, 255);
  56. QColor couleur_ligne = QColor::fromRgb(165, 42, 42, 255);
  57. QColor couleur_trace = QColor::fromRgb(66, 249, 112, 255);
  58.  
  59. QColor couleur_memo1 = QColor::fromRgb(255, 85, 0, 255);
  60. QColor couleur_memo2 = QColor::fromRgb(255, 255, 0, 255);
  61. QColor couleur_memo3 = QColor::fromRgb(0, 255, 0, 255);
  62. QColor couleur_memo4 = QColor::fromRgb(0, 170, 255, 255);
  63.  
  64. QColor couleur_marqueur = QColor::fromRgb(255, 255, 0, 255);
  65. QColor couleur_texte = QColor::fromRgb(66, 249, 112, 255);
  66. QColor vert = QColor::fromRgb(0, 255, 0, 255);
  67. QColor jaune = QColor::fromRgb(255, 255, 0, 255);
  68.  
  69.  
  70. QPen pen_trace(couleur_trace, 1, Qt::SolidLine);
  71. QPen pen_memo1(couleur_memo1, 1, Qt::SolidLine);
  72. QPen pen_memo2(couleur_memo2, 1, Qt::SolidLine);
  73. QPen pen_memo3(couleur_memo3, 1, Qt::SolidLine);
  74. QPen pen_memo4(couleur_memo4, 1, Qt::SolidLine);
  75.  
  76. QPen pen_reticule(couleur_ligne, 1, Qt::SolidLine);
  77. QPen pen_marqueur(couleur_marqueur, 1, Qt::SolidLine);
  78.  
  79.  
  80.  
  81. MainWindowImpl::MainWindowImpl( QWidget * parent, Qt::WFlags f)
  82. : QMainWindow(parent, f)
  83. {
  84. setupUi(this);
  85. setWindowTitle("Wobulateur ATmega + AD9850 - Silicium628 - version " + version);
  86.  
  87. Led1 = new QLed(frame2);
  88. Led1->setGeometry(QRect(20, 40, 20, 20));
  89. Led1->setCouleur(QColor (0,255,0));
  90. Led1->setForme(1);
  91. Led1->setEtat(0);
  92.  
  93. Led2 = new QLed(frame3);
  94. Led2->setGeometry(QRect(140, 14, 20, 20));
  95. Led2->setCouleur(QColor (0,255,0));
  96. Led2->setForme(2);
  97. Led2->setEtat(0);
  98.  
  99. Led3 = new QLed(frame1);
  100. Led3->setGeometry(QRect(175, 243, 20, 20));
  101. Led3->setCouleur(QColor (0,255,0));
  102. Led3->setForme(2);
  103. Led3->setEtat(0);
  104.  
  105. scene = new QGraphicsScene(this);
  106. scene->setBackgroundBrush(couleur_ecran);
  107. graphicsView1->setScene(scene);
  108.  
  109. groupe_reticule = new QGraphicsItemGroup();
  110. groupe_trace = new QGraphicsItemGroup();
  111. scene->addItem(groupe_trace);
  112.  
  113. liste_mesures1.clear();
  114. timer1 = new QTimer(this);
  115.  
  116. connect(timer1, SIGNAL(timeout()), this, SLOT(reception_RS232() ));
  117.  
  118.  
  119. comboBox1->addItem("ttyS0 (com1)");
  120. comboBox1->addItem("ttyS1 (com2)");
  121. comboBox1->addItem("ttyUSB0");
  122. comboBox1->addItem("ttyUSB1");
  123.  
  124. comboBox1->setCurrentIndex(2);
  125.  
  126. BoutonLed1 = new QBoutonLed(frame4);
  127. BoutonLed1->setGeometry(QRect(20, 20, 40, 40));
  128. BoutonLed1->setCouleur(QColor (0,255,0));
  129. connect(BoutonLed1, SIGNAL(toggled(bool)), this, SLOT(on_BoutonLed1_toggled(bool)));
  130. BoutonLed1->click();
  131. pushButton->setVisible(false);
  132.  
  133.  
  134.  
  135. QPalette palette1 = frame1->palette();
  136. palette1.setColor( backgroundRole(), QColor( 220, 220, 220 ) );
  137. frame1->setPalette( palette1 );
  138. frame1->setAutoFillBackground( true );
  139.  
  140. QPalette palette2 = frame2->palette();
  141. palette2.setColor( backgroundRole(), QColor( 220, 220, 220 ) );
  142. frame2->setPalette( palette2 );
  143. frame2->setAutoFillBackground( true );
  144.  
  145. tracer_marqueur(1);
  146. spinBox_7->setValue(8);
  147. spinBox_PAS->setValue(12);
  148. spinBox2->setValue(6);
  149. compose_frequence();
  150. groupe_trace->setZValue(100);
  151.  
  152. }
  153. //
  154.  
  155. void vider_buffers()
  156. {
  157. int n;
  158. liste_mesures1.clear();
  159. for (n=0; n<1024; n++)
  160. {
  161. buf[n]=0;
  162. // mesures[n][0]=0;
  163. // mesures[n][1]=0;
  164. }
  165. }
  166.  
  167.  
  168. void efface_params()
  169. {
  170. int n;
  171. for(n=0; n<100; n++)
  172. {
  173. params[n]=0;
  174. }
  175. }
  176.  
  177.  
  178. void MainWindowImpl::tracer_1freq(int x_i, int y_i, QString sti)
  179. {
  180. // affichage 1 trait vertical
  181.  
  182. if((x_i>0) && (x_i<512))
  183. {
  184. ligne1 = new QGraphicsLineItem(x_i,0,x_i,512);
  185. ligne1->setPen(pen_reticule);
  186. groupe_reticule->addToGroup(ligne1);
  187.  
  188. texte_frq = new QGraphicsTextItem(sti);
  189. texte_frq->setDefaultTextColor(couleur_texte);
  190. texte_frq->setPos(x_i,y_i);
  191. if(x_i<450) // evite que l'ecriture ne deborde du cadre a droite
  192. {
  193. groupe_reticule->addToGroup(texte_frq);
  194. }
  195. }
  196. }
  197.  
  198.  
  199.  
  200. void MainWindowImpl::tracer_reticule()
  201. {
  202. int i,y;
  203. QString st1;
  204. //ATTENTION: ne pas remplacer les "float" par de "int" (aucuns) sous peine de calculs faux (erreurs d'arrondis)
  205. float nb_graduations;
  206. float largeur_graduations; // en pixels
  207. float deltaF; //en Hz
  208. float Fmilieu, Fi;
  209.  
  210.  
  211. deltaF=Freq_max_desiree-Freq_min_desiree;
  212. nb_graduations = deltaF/pas_graduation;
  213.  
  214.  
  215. if ((nb_graduations>12)&&(num_pas_graduation<20))
  216. {
  217. num_pas_graduation++;
  218. spinBox2->setValue(num_pas_graduation);
  219. calcul_freq_pas_graduation();
  220. nb_graduations = deltaF/pas_graduation;
  221. }
  222.  
  223. if ((nb_graduations<3)&&(num_pas_graduation>1))
  224. {
  225. num_pas_graduation--;
  226. spinBox2->setValue(num_pas_graduation);
  227. calcul_freq_pas_graduation();
  228. nb_graduations = deltaF/pas_graduation;
  229. }
  230. calcul_freq_pas_graduation();
  231.  
  232. // graguation centrale
  233. Fmilieu= (float)Freq_centrale_desiree;
  234. st1.setNum(Fmilieu);
  235. tracer_1freq(255, 490, st1);
  236.  
  237.  
  238. //graduations de part et d'autre de la fréquence centrale
  239. if ((nb_graduations>0) && (nb_graduations <20))
  240. {
  241. largeur_graduations = 512 / (nb_graduations);
  242.  
  243. for(i=1; i<=nb_graduations/2; i++)
  244. {
  245. if((i%2)==0){y=490;} else{y=20;}
  246. Fi=Fmilieu - i*pas_graduation;
  247. st1.setNum(Fi);
  248. tracer_1freq(255-i*largeur_graduations, y, st1);
  249.  
  250. Fi=Fmilieu + i*pas_graduation;
  251. st1.setNum(Fi);
  252. tracer_1freq(255+i*largeur_graduations,y, st1);
  253. }
  254. }
  255.  
  256. // lignes horizontales
  257. for (i=0; i<=10; i++)
  258. {
  259. y=50*i;
  260. // ligne1 = scene->addLine(0, y, 512, y, pen_reticule);
  261. ligne1 = new QGraphicsLineItem(0, y, 512, y);
  262. ligne1->setPen(pen_reticule);
  263. groupe_reticule->addToGroup(ligne1);
  264. }
  265. scene->addItem(groupe_reticule);
  266. }
  267.  
  268.  
  269.  
  270. void MainWindowImpl::effacer_reticule()
  271. {
  272. foreach( QGraphicsItem *item, scene->items( groupe_reticule->boundingRect() ) )
  273. {
  274. if( item->group() == groupe_reticule ) { delete item; }
  275. }
  276. }
  277.  
  278.  
  279.  
  280. void MainWindowImpl::effacer_trace()
  281. {
  282. foreach( QGraphicsItem *item, scene->items( groupe_trace->boundingRect() ) )
  283. {
  284. if( item->group() == groupe_trace ) { delete item; }
  285. }
  286. }
  287.  
  288.  
  289. void MainWindowImpl::effacer_marqueur()
  290. {
  291. scene->removeItem(ligne_marqueur);
  292. }
  293.  
  294.  
  295.  
  296. void MainWindowImpl::tracer_marqueur(int x)
  297. {
  298. ligne_marqueur = new QGraphicsLineItem(x,0,x,512);
  299. ligne_marqueur->setPen(pen_marqueur);
  300. scene->addItem(ligne_marqueur);
  301. }
  302.  
  303.  
  304. void MainWindowImpl::tracer_1point(int n, QList<Acq_Rec> *liste_mesures_i, QPen pen_i)
  305. {
  306. Acq_Rec mesure_i;
  307. int y1,y2;
  308. int n_max;
  309.  
  310. n_max=liste_mesures_i->size(); // =512 apres aquisition 1 balayage complet
  311. if ((n>1)&&(n<n_max))
  312. {
  313.  
  314. mesure_i=liste_mesures_i->at(n-1);
  315. y1=(128*mesure_i.Valeur[1]+mesure_i.Valeur[0])/2; if (y1>1024) { y1=1024;}
  316.  
  317. mesure_i=liste_mesures_i->at(n);
  318. y2=(128*mesure_i.Valeur[1]+mesure_i.Valeur[0])/2; if (y2>1024) { y2=1024;}
  319.  
  320. if ( (y1 != 0) && (y2 != 0))
  321. {
  322. segment_trace = new QGraphicsLineItem(n-1,y1,n,y2);
  323. segment_trace->setPen(pen_i);
  324. groupe_trace->addToGroup(segment_trace);
  325.  
  326. }
  327. }
  328. }
  329.  
  330.  
  331. void MainWindowImpl::save_image_ecran()
  332. {
  333. QString st1,nom1, filename;
  334. QImage image1 (512, 512, QImage::Format_RGB32 );
  335.  
  336. QPainter painter1(&image1);
  337. scene->render(&painter1);
  338. int n =0;
  339.  
  340. nom1=lineEdit13->text();
  341.  
  342. int ok=0;
  343. while (ok!=1)
  344. {
  345. st1.setNum(n); // conversion num -> txt
  346. filename = QDir::homePath()+"/"+nom1+st1+".png";
  347. QFile f( filename );
  348. if (!f.exists() ) { ok=1; } else { n++; }
  349. }
  350. image1.save(filename, "png", 50);
  351. QMessageBox::information(this, "copie d'ecran", "image cree: "+ filename);
  352. }
  353.  
  354.  
  355.  
  356. void formate_nombre(QString *str_i)
  357. {
  358. uint n;
  359. n=str_i->length();
  360. if (n>3) {str_i->insert(n-3, ' ');}
  361. if (n>6) {str_i->insert(n-6, ' ');}
  362. if (n>9) {str_i->insert(n-9, ' ');}
  363. *str_i+=" Hz";
  364. }
  365.  
  366.  
  367.  
  368.  
  369. void MainWindowImpl::reception_RS232()
  370. {
  371. Acq_Rec mesure_i;
  372. int n;
  373. int i;
  374. int data_i;
  375. float Fi;
  376.  
  377. QString st1;
  378. //rappel: int PollComport(int comport_number, unsigned char *buf, int size)
  379.  
  380. n = PollComport(numport, buf, 10);
  381. if(n >0)
  382. {
  383. for(i=0; i < n; i++)
  384. {
  385. datas[num_octet_recu] = buf[i];
  386. st1.setNum(buf[i]); // conversion num -> txt
  387. // str_test+=st1;
  388. num_octet_recu++;
  389. }
  390.  
  391. if (num_octet_recu>=10)
  392. {
  393. if ((datas[0]=='D')&&(datas[1]=='A')&&(datas[2]=='T'))
  394. {
  395. Led1->setCouleur(jaune);
  396. Led1->setEtat(1);
  397. for(i=0; i < n; i++)
  398. {
  399. data_i = buf[i];
  400.  
  401.  
  402. //les donnees sont transmises sur deux fois 7 bits
  403. // le 8eme bit (bit7) determine si c'est l'octet de poids faible ou celui de poids fort qui est recu
  404. if ((data_i & 128)==0)
  405. {
  406. st1.setNum(data_i); // conversion num -> txt
  407. mesure_i.Valeur[0]=data_i;
  408. }
  409. else
  410. {
  411. data_i &= 127; // elimine le bit d'identification de poids
  412.  
  413. Fi = Freq_min_desiree+num_ligne*pas;
  414. mesure_i.Freq=(long int)Fi;
  415. mesure_i.Valeur[1]=data_i;
  416. liste_mesures1<<mesure_i;
  417. tracer_1point(num_ligne, &liste_mesures1, pen_trace);
  418.  
  419. progressBar1->setValue(num_ligne);
  420.  
  421. num_ligne++;
  422. }
  423.  
  424. num_octet_recu++;
  425.  
  426.  
  427. if (num_ligne >= 506)
  428. {
  429. num_octet_recu=0;
  430. num_ligne=0;
  431. progressBar1->setValue(0);
  432. Led1->setCouleur(vert);
  433. Led1->setEtat(1);
  434.  
  435. }
  436. }
  437. }
  438. }
  439. }
  440. }
  441.  
  442.  
  443.  
  444. void MainWindowImpl::on_Btn_connect_pressed()
  445. {
  446. QString st1;
  447.  
  448. Led1->setEtat(0);
  449.  
  450. if (comboBox1->currentIndex()==0)
  451. {
  452. numport =0;
  453. st1="ttyS0";
  454. }
  455. if (comboBox1->currentIndex()==1)
  456. {
  457. numport =1;
  458. st1="ttyS1";
  459. }
  460. if (comboBox1->currentIndex()==2)
  461. {
  462. numport =16;
  463. st1="ttyUSB0";
  464. }
  465. if (comboBox1->currentIndex()==3)
  466. {
  467. numport =17;
  468. st1="ttyUSB1";
  469. }
  470. num_octet_recu=0;
  471. num_ligne=0;
  472. if(OpenComport(numport, bdrate))
  473. {
  474. cnx232_ok=0;
  475. lineEdit1->setText("Ne peut pas ouvrir "+st1);
  476. pushButton->setVisible(true);
  477. Led2->setCouleur(QColor (255,0,0));
  478. Led2->setEtat(1);
  479. }
  480. else
  481. {
  482. cnx232_ok=1;
  483. lineEdit1->setText("port "+st1+ " ouvert\n");
  484. pushButton->setVisible(false);
  485. Led2->setCouleur(QColor (0,255,0));
  486. Led2->setEtat(1);
  487.  
  488. timer1->start(2);
  489.  
  490. }
  491. }
  492.  
  493.  
  494.  
  495.  
  496.  
  497. void MainWindowImpl::MAJ_marqueur(int x_i)
  498. {
  499. QString st1;
  500. //int n;
  501. effacer_marqueur();
  502. pos_marqueur = x_i;
  503. tracer_marqueur(pos_marqueur);
  504.  
  505. Fmarqueur = Freq_min_desiree+pos_marqueur*pas;
  506. st1.setNum(Fmarqueur); // conversion num -> txt
  507. formate_nombre(&st1);
  508. lineEdit6->setText(st1);
  509. }
  510.  
  511.  
  512.  
  513. void MainWindowImpl::on_horizontalSlider_sliderMoved(int position)
  514. {
  515. MAJ_marqueur(position);
  516. }
  517.  
  518.  
  519. void MainWindowImpl::on_horizontalSlider_sliderReleased()
  520. {
  521.  
  522. }
  523.  
  524.  
  525. void MainWindowImpl::on_Btn_Go_pressed()
  526. {
  527. if(cnx232_ok ==1)
  528. {
  529. num_octet_recu=0;
  530. num_ligne=0;
  531. //clear_tableau();
  532. vider_buffers();
  533.  
  534. effacer_trace();
  535. Led1->setEtat(0);
  536.  
  537. SendByte(numport, 'G');
  538. SendByte(numport, 'G');
  539. }
  540. else { QMessageBox::information(this, "Erreur", "Pas de connexion RS232"); }
  541. }
  542.  
  543.  
  544.  
  545. void MainWindowImpl::on_Btn_envoi_clicked()
  546. {
  547. /*
  548. les valeurs numeriques sont transmises par RS232 digit par digit en clair (codage ascii des chiffres, codes 48..57 pour 0..9
  549. et non par leur valeur directe (15 sera envoyé comme 1 suivi de 5 et non par la valeur 15)
  550. ceci pour eviter des bugs lors de la transmisson de valeurs correspondant a des commandes RS232
  551. */
  552. int n;
  553.  
  554.  
  555. Led1->setEtat(0);
  556. Led3->setEtat(1);
  557.  
  558. if(cnx232_ok !=1) { on_Btn_connect_pressed(); }
  559.  
  560. effacer_trace();
  561. SendByte(numport, 'P'); //'P' comme 'Parametres' (pas sur 2 digits puis frequence sur 8 digits)
  562. // SendByte(numport, '=');
  563. //PAS sur 2 digits
  564. SendByte(numport, 48 + num_pas_desire /10); // unites (en chiffre 0..9)
  565. SendByte(numport, 48 + num_pas_desire %10); // dizaines
  566. //FREQUENCE sur 8 digits
  567. for(n=0; n<8; n++)
  568. {
  569. SendByte(numport, 48+ digits_F_min[n]); // (en chiffre 0..9)
  570. }
  571. // QTimer::singleShot(1000, this, SLOT(on_Btn_Go_pressed())); // laisse le temps de transmission puis de calcul pour l'ATmega
  572.  
  573. }
  574.  
  575.  
  576.  
  577. void MainWindowImpl::compose_frequence()
  578. {
  579. QString st1;
  580. int i;
  581. Freq_centrale_desiree=0;
  582. for(i=0; i<8; i++)
  583. {
  584. Freq_centrale_desiree+= digits_F_centrale[i] * pow(10,i);
  585. }
  586. Freq_min_desiree=Freq_centrale_desiree-256*pas;
  587. if(Freq_min_desiree<0)
  588. {
  589. QMessageBox::information(this, "Attention", "Frequence min < 0");
  590. Freq_min_desiree=0;
  591. }
  592.  
  593. Freq_max_desiree=Freq_centrale_desiree+256*pas;
  594.  
  595. if(Freq_max_desiree>49*1e6)
  596. {
  597. QMessageBox::information(this, "Attention", "Frequence max > 49MHz");
  598. Freq_max_desiree=49*1e6;
  599. }
  600.  
  601. digits_F_min[0]=(Freq_min_desiree/1) %10;
  602. digits_F_min[1]=(Freq_min_desiree/10) %10;
  603. digits_F_min[2]=(Freq_min_desiree/100) %10;
  604. digits_F_min[3]=(Freq_min_desiree/1000) %10;
  605. digits_F_min[4]=(Freq_min_desiree/10000) %10;
  606. digits_F_min[5]=(Freq_min_desiree/100000) %10;
  607. digits_F_min[6]=(Freq_min_desiree/1000000) %10;
  608. digits_F_min[7]=(Freq_min_desiree/10000000) %10;
  609.  
  610.  
  611. st1.setNum(Freq_min_desiree); // conversion num -> txt
  612. formate_nombre(&st1);
  613. lineEdit10->setText(st1);
  614.  
  615. st1.setNum(Freq_centrale_desiree); // conversion num -> txt
  616. formate_nombre(&st1);
  617. lineEdit8->setText(st1);
  618.  
  619. st1.setNum(Freq_max_desiree); // conversion num -> txt
  620. formate_nombre(&st1);
  621. lineEdit11->setText(st1);
  622.  
  623. effacer_reticule();
  624. tracer_reticule();
  625. MAJ_marqueur(pos_marqueur);
  626.  
  627. Led3->setEtat(0);
  628. }
  629.  
  630.  
  631.  
  632.  
  633. void MainWindowImpl::on_spinBox_8_valueChanged(int arg1)
  634. {
  635. digits_F_centrale[7]=arg1;
  636. compose_frequence();
  637. }
  638.  
  639. void MainWindowImpl::on_spinBox_7_valueChanged(int arg1)
  640. {
  641. digits_F_centrale[6]=arg1;
  642. compose_frequence();
  643.  
  644. }
  645.  
  646. void MainWindowImpl::on_spinBox_6_valueChanged(int arg1)
  647. {
  648. digits_F_centrale[5]=arg1;
  649. compose_frequence();
  650. }
  651.  
  652. void MainWindowImpl::on_spinBox_5_valueChanged(int arg1)
  653. {
  654. digits_F_centrale[4]=arg1;
  655. compose_frequence();
  656. }
  657.  
  658. void MainWindowImpl::on_spinBox_4_valueChanged(int arg1)
  659. {
  660. digits_F_centrale[3]=arg1;
  661. compose_frequence();
  662. }
  663.  
  664. void MainWindowImpl::on_spinBox_3_valueChanged(int arg1)
  665. {
  666. digits_F_centrale[2]=arg1;
  667. compose_frequence();
  668. }
  669.  
  670.  
  671. void MainWindowImpl::on_spinBox_2_valueChanged(int arg1)
  672. {
  673. digits_F_centrale[1]=arg1;
  674. compose_frequence();
  675. }
  676.  
  677.  
  678.  
  679.  
  680. void MainWindowImpl::on_spinBox_PAS_valueChanged(int arg1)
  681. {
  682. num_pas_desire=arg1;
  683. QString st1;
  684. pas = pow(2,num_pas_desire)/68.7194767;
  685.  
  686. st1.setNum(pas); // conversion num -> txt
  687. if (num_pas_desire>15) {formate_nombre(&st1);}
  688. lineEdit9->setText(st1);
  689.  
  690. compose_frequence(); // pour recalculer les freq min et max;
  691.  
  692. effacer_reticule();
  693. tracer_reticule();
  694. }
  695.  
  696.  
  697. void MainWindowImpl::on_spinBox_1_valueChanged(int arg1)
  698. {
  699. digits_F_centrale[0]=arg1;
  700. compose_frequence();
  701. }
  702.  
  703.  
  704. void MainWindowImpl::calcul_freq_pas_graduation()
  705. {
  706. QString st1;
  707. long int liste_pas[21]={
  708. 1,2,5,
  709. 10,20,50,
  710. 100,200,500,
  711. 1000,2000,5000,
  712. 10000,20000,50000,
  713. 100000,200000,500000,
  714. 1000000,2000000,5000000
  715. };
  716.  
  717. pas_graduation = liste_pas[num_pas_graduation];
  718. st1.setNum(pas_graduation); // conversion num -> txt
  719. formate_nombre(&st1);
  720. lineEdit12->setText(st1);
  721.  
  722. }
  723.  
  724.  
  725. void MainWindowImpl::on_spinBox2_valueChanged(int arg1)
  726. {
  727. num_pas_graduation=arg1;
  728. calcul_freq_pas_graduation();
  729. compose_frequence();
  730. }
  731.  
  732.  
  733. void MainWindowImpl::on_Btn1_clicked()
  734. {
  735. liste_mesures2=liste_mesures1;
  736. }
  737.  
  738.  
  739. void MainWindowImpl::on_Btn2_clicked()
  740. {
  741. int n;
  742. for (n=0; n<512; n++) { tracer_1point(n, &liste_mesures2, pen_memo1);}
  743. }
  744.  
  745.  
  746.  
  747. void MainWindowImpl::on_Btn_test_3_clicked()
  748. {
  749. effacer_trace();
  750. }
  751.  
  752.  
  753.  
  754.  
  755. void MainWindowImpl::on_BoutonLed1_toggled(bool checked)
  756. {
  757. if (checked)
  758. {
  759. tracer_reticule();
  760. scene->addItem(groupe_reticule);
  761. }
  762. else
  763. {
  764. foreach( QGraphicsItem *item, scene->items(groupe_reticule->boundingRect() ))
  765. {
  766. if( item->group() == groupe_reticule ) {delete item; }
  767. }
  768. }
  769. }
  770.  
  771.  
  772.  
  773.  
  774. void MainWindowImpl::on_Btn1_2_clicked()
  775. {
  776. liste_mesures3=liste_mesures1;
  777. }
  778.  
  779. void MainWindowImpl::on_Btn2_2_clicked()
  780. {
  781. int n;
  782. for (n=0; n<512; n++) { tracer_1point(n, &liste_mesures3, pen_memo2);}
  783. }
  784.  
  785. void MainWindowImpl::on_Btn1_3_clicked()
  786. {
  787. liste_mesures4=liste_mesures1;
  788. }
  789.  
  790. void MainWindowImpl::on_Btn2_3_clicked()
  791. {
  792. int n;
  793. for (n=0; n<512; n++) { tracer_1point(n, &liste_mesures4,pen_memo3);}
  794. }
  795.  
  796. void MainWindowImpl::on_Btn1_4_clicked()
  797. {
  798. liste_mesures5=liste_mesures1;
  799. }
  800.  
  801. void MainWindowImpl::on_Btn2_4_clicked()
  802. {
  803. int n;
  804. for (n=0; n<512; n++) { tracer_1point(n, &liste_mesures5,pen_memo4);}
  805. }
  806.  
  807.  
  808. void MainWindowImpl::on_comboBox1_currentIndexChanged(int index)
  809. {
  810. Led2->setEtat(0);
  811. }
  812.  
  813.  
  814. void MainWindowImpl::on_pushButton_clicked()
  815. {
  816. QMessageBox::information(this, "Aide", + "Essayez un autre port \n ou la commande (dans un terminal) \n 'sudo chown <your_user> /dev/ttyxxx' \n (change le proprietaire sous Linux)");
  817. }
  818.  
  819. void MainWindowImpl::on_Btn_save_img_clicked()
  820. {
  821. save_image_ecran();
  822. }
  823.  

11 Copies d'écran du logiciel pilote

L'appareil est entièrement pilotable depuis l'ordinateur, par le biais de la liaison RS232.

Le soft est développé en C++ Qt4 avec l'IDE Qt Creator. Jusqu'à présent je travaillais avec l'IDE Qdevelop, mais ce dernier ne tourne plus correctement (se ferme en fin compilation) sous Mint13 Mate (base Ubuntu 12.04). Donc vive Qt Creator! (Les fichiers en C++ Qt4 .cpp et les interfaces graphiques .ui restent 100% compatible avec cet IDE).

12 Test avec un quartz

Voici la réponse d'un quartz de 8,000 000 MHz.
On distingue nettement les deux fréquences de résonance série et parallèle, très proches. On reconnaît la réaction d'un circuit série pour une fréquence donnée et d'un circuit parallèle pour une fréquence légèrement supérieure. Quand on approche de la résonance série (Fs),l'impédance passe par un minimum, (pointe vers le bas sur l'image) puis remonte rapidement vers un maximum (pointe vers le haut) à la fréquence parallèle (Fp).

Cela fait partie des choses qui peuvent créer des surprises dans les oscillateurs à quartz mal conçus...

13 -

Il est tout à fait possible de zoomer, le pas de synthèse de l'AD9850 avec un quartz de 125MHz étant de 0,0291 Hz !!! (dans cette réalisation, le pas mini programmé par l'ATmega est de 1Hz). Nous remarquons que la fréquence 8,000MHz ne coïncide ni avec la résonance parallèle ni avec la résonance série. Mais ce test a été effectué sur "plaque d’essai" c'est à dire avec d'énormes capacités parasites en parallèle sur le quartz! et un rajout de capacité supplémentaire décale effectivement un peu plus la courbe. Donc il faudra voir le résultat obtenu avec un circuit imprimé bien propre.

L'interface Qt4 à déjà évolué depuis la veille... L'échelle de graduation du réticule est semi-automatique par pas 1,2,5,10...

14 -

Nouvelle programmation QT4: utilisation des classes (objets) QGraphicsScene et QGraphicsItemGroup permettant de dessiner sur des calques superposés (layers, couches) pour le réticule, les graduations et la trace et donc d'effectuer des changements d'échelle dynamiques sans devoir refaire une acquisition.

15 -

06 octobre 2012: Quatre mémoires de trace sont maintenant disponibles. Utilité: Pouvoir comparer les résultats lors de la mise au point d'un filtre par exemple. (Sur la copie d'écran ci-dessus je me suis contenté de modifier l'échelle (le pas de wobulation) entre chaque tracés).
Le rectangle vert en bas est un bouton_led, c'est à dire un bouton bistable (toggle) lumineux (lorsqu'il est enfoncé, gris sinon). J'ai créé cette classe Qt en dérivant un QPushbutton et en surchargeant la méthode paintEvent afin de dessiner un rectangle lumineux. Vous trouverez le source de cette classe dans les documents fournis.
Remarque: avec tout ça, on pourrait se créer un petit scope numérique?

16 Evolution

13 octobre 2012:
Le programme en Qt4 évolue. Des LED indiquent l'état de l'interface.
La fenêtre principale voit sa hauteur diminuer afin d'être entièrement affichée sur l'écran des Netbook (Aspire One 1024x600)

17 -

Ci-contre une image créée et enregistrée sur le disque dur par le programme lui-même (en cliquant sur le bouton "save" dans le cadre "Image") Il s'agit, vous l'aurez deviné, de l'analyse d'un petit transfo FI à 455kHz. La résonance n'est pas exactement calée sur 455KHz? En effet, et si l'on tourne la petite vis de réglage du transfo, on déplace la courbe...

18 Le board

27 octobre 2012:
Voici l'implantation des composants, effectuée avec le logiciel libre de saisie de schéma et de routage Kicad (build 2012-01-19), sous l'OS libre et gratuit Linux (Mint13 mate).

19 Documents techniques

Voici les autres fichiers nécessaires à cette réalisation: TUTOS :

Aperçu du contenu de l'archive

20

J'ai voulu savoir s'il était possible de compiler un programme écrit en C++ Qt4 sur et pour l'ultra micro-ordinateur Raspberry Pi. J'ai donc googlelisé ici et là, et j'ai trouvé ce tuto, décrivant l'installation de qtcreator sur le Raspberry, et je l'ai suivi. Et j'ai été étonné du résultat; qtcreator a été installé en quelques instants, et une petite configuration plus tard j'ai pu éditer et compiler le soft pour le wobulateur décrit plus haut, sans changer une virgule au code source. Et l'exécutable fonctionne parfaitement sur le Raspberry pi comme vous pouvez le voir sur les images ci-dessous:

21 -

Ci-contre une capture d'écran de qtcreator en cours d'exécution sur le Raspberry pi. La compilation du programme du wobulateur s'est bien déroulée, bien que nettement plus lentement (environ 2mn) que sur un PC AMD64 à 4GHz.

L'exécutable obtenu fonctionne parfaitement sur le Raspberry.

Pour obtenir les copies d'écran, j'utilise la commande "scrot -d 5" après avoir installé le paquet: ( sudo apt-get install scrot )

22 -

23 -

Liens...



35717