SDcard data Recorder - ATmega32 + LCD 5110

Ce montage permet d'enregistrer en temps réel, c'est à dire rapidement, une grande quantité de données logiques. Je l'ai conçu afin de surveiller précisément le fonctionnement de ma photo-flasheuse laser décrite sur ce site ici.. Une fois connecté sur les signaux de commandes des moteurs pas-à-pas, il enregistre tout ce qui se passe pendant une heure, dans une carte mémoire SDcard. Je fais ensuite relire le contenu de cette carte par un programme en Qt4 qui trace le parcours de la plume à l'écran. Et comme l'enregistrement peut se faire sans alimenter les moteurs, le tout constitue une flasheuse virtuelle dont ont peut observer le tracé dans ses moindres détails. Tout cela était devenu indispensable pour maîtriser le tracé des cercles constituant les pastilles rondes.

1 Vue d'ensemble du proto :

Cet appareil sera réalisé en composants cms, le circuit imprimé sera flashé... par la machine en question.

J'ai soudé l'ATmega32 cms sur une petite carte d'adaptation, pour ce proto. Non, ce n'est pas un Arduino.

Une astuce en passant : Si comme moi vous constatez que les ports PC2...PC5 ne fonctionnent pas, voyez si par hasard le fuse JTAG enable n'est pas programmé. En effet les fonctions JTAG de l'ATmega32 invalident ces quatre bits du port C. Comme je venais de souder le composant cms à la loupe, j'ai tout d'abord pensé à un court-circuit... et bien non !

2 Un exemple de tracé virtuel obtenu :

Ce tracé d'une carte mesurant à peine 30x20mm représente pas moins de quatre millions de pas moteurs. Autant dire qu'il n'était pas possible de les mémoriser dans la RAM de l'ATmega32.

La couleur évolutive du trait (des couleurs chaudes vers les couleurs froides) permet de bien suivre le tracé dans l'ordre.

3 Zoom 1

On peut voir ici un défaut de tracé sur une des pastilles rectangulaires, défaut qui n'est pas imputable à un quelconque dysfonctionnement ou jeu mécanique puisque en l’occurrence aucune mécanique n'intervient dans le tracé. Il s'agit donc d'un problème purement logiciel de la flasheuse, vraisemblablement un "télescopage" d'interruptions.

Jusqu'à présent un tel problème fugace me laissait perplexe... Maintenant, grâce à cet enregistreur, je vais pouvoir faire la chasse au glitch !

4 Zoom 2

5 Zoom 3

10 juillet 2015:
On remarquera au passage que les cercles sont maintenant tracés bien rond...

6 Tracé avec plume large

Outre le petit côté psychédélique, cela met en évidence les fameux "glitches" (pb résolu depuis) ainsi que l'espacement trop grand des cercles formant les pastilles. Quant aux traits qui se croisent, il s'agit simplement de déplacements de la plume laser éteint.

7 Correction de l'espacement des cercles

Un jeu d'enfant, sans être obligé de flasher un film pour voir le résultat...

Le quadrillage est ici de 1mm. Il s'agit donc d'une pastille de 3,5mm de diamètre avec un trou de 0,5mm pour guider le foret de perçage de 0,8mm.

La couleur évolutive du trait nous permet de voir que le tracé des cercles s'effectue dans le sens anti-horaire (j'aurais pu dire "le sens trigonométrique" pour faire bien...) avec saut (quantique ?) du diamètre à chaque tour (pour alpha = 2k*pi ...) voir l'implantation dans le firmware de la machine laser.

8 Le schéma

L'ATmega32 représenté sur le schéma est en boîtier smd TQFP44 au pas de 0.8mm.

J'ai relié tous les ports (même ceux inutilisés donc) à des connecteurs, rendant ainsi cette carte universelle, prête à servir à tout autre réalisation comprenant un ATmega32, un afficheur LCD et une SDcard... Ce ne sont pas les idées qui manquent. hop ! Ajoutez moi encore une vie supplémentaire pour avoir le temps de réaliser plein de trucs avec ça !

Les deux diodes 1N4148 en série permettent d'alimenter le LCD (3V3) depuis le +5V. Méthode que je... enfin disons : qui marche ! Idem les résistances de 10k.

J'ai remplacé le module lecteur SDcard par un module lecteur micro-SDcard qui offre en plus un convertisseur 5V<->3V3 permettant de relier les cartes alimentées exclusivement en 3V3 à l'ATmega alimenté en 5V (L'ATmega mais aussi ce qui s'y connecte : signaux d'entrée, programmateur ISP... )

9 Le module pour cartes micro-SDcard :

En plus du convertisseur de niveaux logiques (74LVC125A), ce module comprend un régulateur 3V3 pour alimenter la micro-SDcard. Tout cela réduit la connectique au strict minimum. Le 74LVC125A permet en outre d'effectuer des transferts rapides.

Où trouve-ton ce module ? Je vous laisse deviner. ça commence par "e" et ça finit par "y". Cherchez dans la direction où se lève le Soleil. (Moi je ne vends rien). Les plus enragés que moi le fabriqueront eux-même...

10 Le firmware pour l'ATmega32

CODE SOURCE en C++ pour ATmega32
  1. /***********************************************************************
  2. par Silicium628
  3.  
  4. ========================================================================
  5. ATmega32 + afficheur LCD 5110 Nokia
  6. Enregistreur d'octets rapide sur SDcard en mode RAW
  7.  
  8. ************************************************************************/
  9.  
  10. /**
  11. REMARQUE CONCERNANT L'UTILISATION DE CE PROGRAMME POUR LE CONTROLE DE LA MACHINE CNC :
  12.  
  13. Mémorisation numérique (dans une SDcard) du tracé pour contôle sans tracé physique puis tracé à l'écran par un soft en Qt4.
  14. La fréquence max envoyée aux cartes EasyDriver en vitesse rapide est = 2500Hz soit 5000 évènements/s (puisque 2 moteurs)
  15. Si l'on veut mémoriser toutes les impulsions moteur envoyées, chaque évènement étant défini par 2bits (moteur , sens ),
  16. chaque octet peut mémoriser 8bits/2bits = 4 évènements,
  17. 5000/4 = 1250 octets/s (= 10 kbits/s)
  18. si on veut mémoriser 1/2h de tracé, il faut 1250 octets/s * 1800s = 2.25MB. -> la RAM du Mega2560 = 8kB soit 280 fois moins !!
  19. utiliser un processeur ARM ? un Raspberry Pi2 quad core ?
  20. Plus simplement cette acquisition externe par un ATmega8 connecté sur les sorties des signaux, qui mémorise dans une SDcard.
  21. 1250 octets/s * 1800s / (512 octets/blocs) = un peu moins de 4400 blocs.
  22. (1250 oct/s) / (512 oct/blocs) = 2.44 blocs/s -> écriture de 1 bloc toutes les 0.41s
  23.  
  24. 4400 blocs x (512 octets/bloc) = 2252800 octets = 2200 kB = 2MB.
  25. vitesse d'enregistrement max : les 2252800 octets peuvent être écrits en 24s soit 184 blocs/s ou 94 kB/s = 750 kbits/s (750kHz) soit 75 fois plus rapide que la vitesse requise ici.
  26. En pratique, et sans utiliser le double buffering, ce programme peut enregistrer à 22 kB/s (22kHz). On enregistre dans le buffer que l'on est en train de graver sur la SDcard...
  27.  
  28. Le resultat enregistré sur la SDcard (non partitionnée !) est récupérable sous linux par la commande "dd" suivante:
  29. "sudo dd if=/dev/sdc of=nom_fichier bs=512 count=100" en adaptant bien sûr "sdc" à la valeur correspondant réellement à la SDcard dans le système.
  30. On obtient un fichier lisible sous linux avec jeex puisque écrit en binaire.
  31. Ce fichier est interprétable (et traçable) par le soft en Qt4 associé à ce programme.
  32. **/
  33.  
  34. /*
  35.  ================== PARAMETRES NUMERIQUE DE CE PROGRAMME ============================================================================================
  36.  
  37. (Les tempo sont mesurées à l'oscillo et/ou fréquencemètre) et non pas calculées exactement au simulateur
  38. OCR0 = 10; -> un octet est présenté toutes les 45us c.a.d avec une fréquence de 22kHz (pour test avec acquisition interne par Timer0))
  39. -Un enregistrement de 1 block (=512 octets) se fait en 8ms en moyenne avec quelques rares "coups" à 16ms (? deux blocs à la fois ????)
  40. -Le buffer de 512 octet se remplit en 512 x 45us = 23ms
  41. -Le temps de repos pendant lequel il n'y a pas d'écriture sur la carte = 23ms-8ms= 15ms; On voit très bien ce temps de 15ms à l'oscillo
  42. -Chaque octet est enregistré sur la carte en 8ms/512 = 15us soit moins que la période d'arrivée des octets qui est rappelons le de 45us soit 3x plus longue.
  43.  
  44. CE QUI SE PASSE:
  45. A l'instant où le buffer est plein, on dispose de 45us avant qu'un octet ne se présente à l'enregistrement en début du buffer.
  46. Lorsque cet octet incident arrive, on a déjà enregistré les trois premier octets du buffer sur la carte, lorsque le deuxième octet arrive on a enregistré 6 octets
  47. et ainsi de suite, de telle manière que l'adresse de stockage des octets entrants dans le buffer ne rattrape jamais celle des octets lus pour être expédié dans la SDcard.
  48. Ceci es important de façon à ce que les octets arrivant n'écrasent pas des octets pas encore enregistrés: Le serpent ne doit pas se mordre la queue !
  49. Il n'y a donc aucune perte de signal, tout ce qui arrive est enregistré sur la carte.
  50.  
  51. ======================================================================================================================================================
  52. //*/
  53.  
  54.  
  55. //#define F_CPU 16000000
  56.  
  57.  
  58. //#define ACQ_INTERNE
  59.  
  60. #include <avr/io.h>
  61. #include <stdint.h>
  62. #include <stdlib.h>
  63. #include "timeout.h" // comprend : #define F_CPU 16000000UL // 16 MHz
  64. #include <avr/interrupt.h>
  65. #include "LCD_5110-628b.cpp" // penser à paramétrer correctement les ports et bits popur le LCD dans le fichier "LCD_5110-628b.h"
  66. #include <math.h>
  67.  
  68. #include "SDcard_628_2.c"
  69.  
  70. const char * version = "v:3.1";
  71.  
  72.  
  73. #define PORT_led PORTD
  74. #define bit_LED_verte 0b00010000
  75. #define bit_LED_rouge 0b00100000
  76.  
  77.  
  78. uint16_t err;
  79. uint8_t octet_out;
  80. uint8_t n_evnt; // (=0..3) à mesure que l'octet_out se construit
  81. uint16_t adr_octet; // dans le block (=0..511)
  82. volatile uint32_t num_block;
  83. uint8_t compteur1;
  84. volatile uint16_t compteur2 = 0x0000;
  85. volatile uint8_t flag_ecrire_block = 0x00; // 'volatile' afin qu'elle soit prise en compte par l'interruption
  86. volatile uint8_t c;
  87. volatile char string1[16+1] = "00000_dataRecord";
  88. uint8_t flag_acquisition;
  89.  
  90. // static uint8_t buffer[BLOCK_SIZE]; // BLOCK_SIZE est défini dans "sdCard.h"
  91.  
  92.  
  93. void init_ports (void)
  94. // 0 = entree, 1=sortie ; les 1 sur les pins en entrees activent les R de Pull Up (tirage à VCC)
  95. {
  96. DDRB = 0b10111111; // PB6 en entrée (MISO)
  97. PORTB = 0b11101111; // PB4=0 (CS)
  98.  
  99. // le pin_CS est définit dans le fichier SD_routines628.h
  100.  
  101. DDRC = 0b11111111;
  102. PORTC = 0b00000000;
  103.  
  104. DDRD = 0b11110000; // PortD[0..3] = entrées (int0=STEP moteur1, int1=STEP moteur2 , DIR moteur1 et DIR moteur1)
  105. PORTD = 0b00000000;
  106. }
  107.  
  108.  
  109. void InitINTs (void)
  110. {
  111.  
  112. GICR |= 0b11000000; // gere les INTs - bit6 ->INT0 request - bit7 ->INT1 request; voir page 68 du pdf ATmega8A
  113. MCUCR |= 0b00001010; // Falling edge of INT0 (et INT1) generates an interrupt request. p:67 du pdf
  114.  
  115.  
  116. TCCR0 = 0;
  117. TCCR0 |= 0b00001000; // Timer0 en mode CTC (Clear Timer on Compare Match): WGM00=1 (bit6) et WGM01=0 (bit3) voir p:74 et 80
  118. TCCR0 |= 0b00000011; // Timer0 prescaller = f=clk/64. 0CS0[2,1,0] = 011 voir p:80 et p:82
  119. TIMSK = 0b00000010; // Timer/Counter0, Output Compare Match Interrupt Enable; Voir datasheet ATmega32 p:82
  120.  
  121. OCR0 = 10; // Timer0 (freq = 250/n environ) : n=249->1kHz; 126->2kHz; 50->5kHz; 30->8kHz; 20 -> 12kHz; 10-> 22kHz; 5->41kHz; 2->83kHz;
  122.  
  123. }
  124.  
  125.  
  126. void init_variables(void)
  127. {
  128. flag_ecrire_block=0;
  129. flag_acquisition=0;
  130. err=0;
  131. octet_out=0;
  132. n_evnt=0;
  133. adr_octet=0;
  134. num_block=1; // je reserve le bloc zero
  135. compteur1=0;
  136. compteur2=0;
  137. }
  138.  
  139.  
  140.  
  141. void num2string(uint16_t nb_i, volatile char str_i[17]) // remarque: ce tableau de caractères est implicitement passé par adresse, son contenu sera donc changé chez l'appelant
  142. {
  143. /** RAPPEL :
  144. Lors du passage d'un tableau en paramètre d'une fonction, la conversion implicite a lieu, les tableaux sont donc toujours passés par variable, jamais par valeur.
  145. Il est donc faux d'utiliser des pointeurs pour les passer en paramètre, car le paramètre aurait le type pointeur de tableau.
  146. On ne modifierait pas le tableau, mais bel et bien le pointeur du tableau. Le programme aurait donc de fortes chances de planter.
  147. **/
  148. //surcharge les 5 premiers caratères de la chaine avec les caratères représentant le nombre transmis
  149. uint8_t i, r;
  150. for (i=1; i<=5; i++)
  151. {
  152. r=48 + nb_i % 10; // % c'est le modulo (reste de la division)
  153. nb_i /= 10; // quotient
  154. str_i[5-i]=r; // surcharge le début de la ligne (les "0") avec le num
  155. }
  156. }
  157.  
  158.  
  159. void num2string_bin(uint16_t nb_i, volatile char str_i[17]) // remarque: ce tableau de caractères est implicitement passé par adresse, son contenu sera donc changé chez l'appelant
  160. {
  161. /** RAPPEL :
  162. Lors du passage d'un tableau en paramètre d'une fonction, la conversion implicite a lieu, les tableaux sont donc toujours passés par variable, jamais par valeur.
  163. Il est donc faux d'utiliser des pointeurs pour les passer en paramètre, car le paramètre aurait le type pointeur de tableau.
  164. On ne modifierait pas le tableau, mais bel et bien le pointeur du tableau. Le programme aurait donc de fortes chances de planter.
  165. **/
  166. //surcharge les 2 premiers caratères de la chaine avec le nombre trransmis
  167.  
  168. str_i[0] = nb_i >> 8;
  169. str_i[1] = nb_i & 0xFF;
  170. }
  171.  
  172.  
  173.  
  174. void bufferise_octet(uint8_t octet_i)
  175. {
  176. // buffer est définit dans "sdCard_628.h" c'est -> volatile uint8_t buffer[512];
  177.  
  178. buffer[adr_octet] = octet_i;
  179.  
  180. compteur1++; if (compteur1 > 50) {compteur1 =0; PORT_led ^= bit_LED_verte; }
  181. adr_octet++;
  182. if (adr_octet > 511)
  183. {
  184. adr_octet =0;
  185. flag_ecrire_block = 1; // l'écriture du block se fera dans la boucle principale, hors de l'INT donc.
  186. }
  187. }
  188.  
  189.  
  190.  
  191. void complete_octet(uint8_t bibit)
  192. {
  193. octet_out <<= 2; // décallage de deux bits à gauche, des zéros sont insérés à droite. Attention : si oubli du '=' pas de modif de la variable
  194. octet_out += bibit;
  195.  
  196. n_evnt++;
  197. if (n_evnt > 3)
  198. {
  199. n_evnt=0;
  200. bufferise_octet(octet_out);
  201. octet_out=0;
  202. }
  203. }
  204.  
  205.  
  206.  
  207. ISR (BADISR_vect)
  208. {
  209. // évite de planter si une int est enable et pas de procedure associée écrite (ce qui fait reseter l'ATmega)
  210. }
  211.  
  212.  
  213.  
  214. ISR (INT0_vect)
  215. {
  216. #ifndef ACQ_INTERNE
  217. //interruption sur front descendant sur l'entree Int0
  218. if (flag_acquisition==1) // bloque lors de la phase d'init
  219. {
  220. uint8_t etat;
  221. etat = PIND & 0b00000001;
  222. if (etat == 0) { complete_octet(0b01); } else { complete_octet(0b00); }
  223. }
  224. #endif
  225. }
  226.  
  227.  
  228. ISR (INT1_vect)
  229. {
  230. #ifndef ACQ_INTERNE
  231.  
  232. //interruption sur front descendant sur l'entree Int1
  233. if (flag_acquisition==1) // bloque lors de la phase d'init
  234. {
  235. uint8_t etat;
  236. etat = PIND & 0b00000010;
  237. if (etat == 0) { complete_octet(0b11); } else { complete_octet(0b10); }
  238. }
  239.  
  240. #endif
  241. }
  242.  
  243.  
  244.  
  245. ISR (TIMER0_COMP_vect) // acquisition "interne "pour tests
  246. {
  247. #ifdef ACQ_INTERNE
  248. if (flag_acquisition==1) // bloque lors de la phase d'init
  249. {
  250. PORT_led |= bit_LED_verte;
  251. //complete_octet(compteur2 & 0b00000011); // (compteur2 && 0b00000011) = [00..01..10..11..]
  252.  
  253. // char string1[16+1] = "00000_dataRecord";
  254.  
  255. //num2string(compteur2, string1);
  256. num2string_bin(compteur2, string1);
  257.  
  258. bufferise_octet(string1[c]);
  259. c++;
  260. if (c > 15)
  261. {
  262. c = 0;
  263. compteur2++;
  264. }
  265. PORT_led &= ~bit_LED_verte;
  266. }
  267. #endif
  268. }
  269.  
  270.  
  271.  
  272.  
  273. void raz_buffer()
  274. {
  275. uint16_t i;
  276. for (i=0; i<=512; i++)
  277. {
  278. buffer[i] = '-';
  279. }
  280. }
  281.  
  282.  
  283.  
  284.  
  285. void fill_buffer_Char(uint16_t num) // num est le numéro de block qui sera écrit en début de ligne
  286. {
  287. uint8_t c, r;
  288. uint16_t k;
  289.  
  290. //--------------------"1234567890123456"
  291. //char string1[16+1] = "00000solarImpuls";
  292. //char string1[16+1] = "00000silicium628";
  293. //char string1[16+1] = "00000tagadapouet";
  294. //char string1[16+1] = "00000tartalacrem";
  295. //char string1[16+1] = "00000hello World";
  296. //char string1[16+1] = "00000doremifasol";
  297. //char string1[16+1] = "00000_dataRecord";
  298. char string1[16+1] = "00000===========";
  299.  
  300.  
  301. num2string(num, string1);
  302.  
  303. c = 0;
  304. for (k=0; k<=512; k++)
  305. {
  306. buffer[k] = string1[c];
  307. c++;
  308. if (c > 15) { c = 0; }
  309. }
  310. }
  311.  
  312.  
  313.  
  314. void fill_buffer_Int()
  315. {
  316. uint16_t i;
  317. uint8_t c;
  318. c = 0;
  319. for (i=0; i<=512; i++)
  320. {
  321. buffer[i] = c;
  322. c++; // c = 1 octet = 0..255
  323. }
  324. }
  325.  
  326.  
  327.  
  328. int main (void)
  329. {
  330. uint16_t i;
  331. uint32_t adresse1;
  332. uint8_t ok=0;
  333.  
  334.  
  335. unsigned char option, error, data, FAT32_active;
  336.  
  337. init_variables();
  338. init_ports();
  339. InitINTs();
  340. lcd_init();
  341.  
  342. lcd_clear();
  343. _delay_ms(300);
  344.  
  345. lcd_puts("SDcrd Recorder");
  346. lcd_goto_LC(1, 0);
  347. lcd_puts(version);
  348. _delay_ms(1000);
  349. lcd_clear();
  350.  
  351. sei();
  352.  
  353. cardType = 0;
  354.  
  355. lcd_puts("init...");
  356. lcd_goto_LC(1,0);
  357.  
  358. SPI_SetSlow();
  359.  
  360. for (i=0; i<10; i++)
  361. {
  362. error = SD_init();
  363. if(!error) break;
  364. }
  365.  
  366. if(error)
  367. {
  368. if(error == 1) { lcd_puts("SD no detect"); }
  369. if(error == 2) { lcd_puts("SD Init fail"); }
  370. lcd_goto_LC(2, 0);
  371. lcd_puts("faire un reset");
  372. while(1) { ; }
  373. }
  374.  
  375. switch (cardType)
  376. {
  377. case 1:lcd_puts("Std Card 1.x");
  378. break;
  379. case 2:lcd_puts("SDHC Card ok");
  380. break;
  381. case 3:lcd_puts("Std Card 2.x");
  382. break;
  383. default:lcd_puts("SDcard ?");
  384. break;
  385. }
  386.  
  387. SPI_SetFast();
  388.  
  389. _delay_ms(1000);
  390. lcd_clear();
  391.  
  392. lcd_goto_LC(1, 0);
  393. lcd_puts("Effacement...");
  394.  
  395. uint16_t n;
  396.  
  397. fill_buffer_Int();
  398. SD_writeSingleBlock(0); // remplit le bloc zéro avec des entiers [0..511] On pourra y enregistrer tout autre chose, comme la longueur de l'enregistrement par exemple
  399.  
  400. for (n=1; n<2000; n++) //200 pour tests; ensuite 2000 ou plus... à voir
  401. {
  402. fill_buffer_Char(n);
  403. //PORT_led |= bit_LED_verte;
  404. SD_writeSingleBlock(n);
  405. //PORT_led &= ~bit_LED_verte;
  406. lcd_goto_LC(2,0);
  407. lcd_aff_nb (n, 4, 0);
  408. }
  409.  
  410.  
  411. lcd_goto_LC(2, 0);
  412. lcd_puts("Erase SDcrd ok");
  413. _delay_ms(1000);
  414. lcd_goto_LC(4, 1);
  415. lcd_puts("RECORDING...");
  416.  
  417. _delay_ms(100);
  418.  
  419. num_block=1;
  420. flag_acquisition=1;
  421.  
  422. while(1)
  423. {
  424. if (flag_ecrire_block == 1)
  425. {
  426. PORT_led |= bit_LED_rouge;
  427. //_delay_us(200);
  428.  
  429. //flag_acquisition=0;
  430. SD_writeSingleBlock(num_block); // écriture du buffer plein (qui n'est plus en cours) sur la SDcard.
  431. num_block++;
  432. //flag_acquisition=1;
  433.  
  434.  
  435. PORT_led &= ~bit_LED_rouge;
  436.  
  437. flag_ecrire_block = 0;
  438. }
  439. //_delay_ms(300);
  440. }
  441. }
  442.  
  443.  

11 Le programme graphique en Qt4

CODE SOURCE en Qt4 : le fichier mainwindow.cpp
  1. /*
  2. Enregistreur SDcard
  3. Programme écrit par Silicium628
  4. ce logiciel est libre et open source
  5. */
  6.  
  7. #include "mainwindow.h"
  8. #include "math.h"
  9. #include <QFile>
  10. #include <QFileDialog>
  11. #include <QTextStream>
  12. #include <QDebug>
  13. #include "boutonled.cpp"
  14. #include "qled.cpp"
  15. #include <QMessageBox>
  16. #include <QTextCodec>
  17. #include <QTimer>
  18.  
  19. #include <QMouseEvent>
  20.  
  21.  
  22. QString version = "3.3";
  23.  
  24. QColor blanc = QColor::fromRgb(0xFFFFFF);
  25. QColor noir = QColor::fromRgb(0x000000);
  26. QColor gris_fonce = QColor::fromRgb(0x4D4D4D);
  27. QColor gris_clair = QColor::fromRgb(0xBFBFBF);
  28. QColor rouge = QColor::fromRgb(0xFF0000);
  29. QColor vert = QColor::fromRgb(0x008000);
  30. QColor vert_clair = QColor::fromRgb(0x90EE90);
  31. QColor vert_fonce = QColor::fromRgb(0x003300);
  32. QColor vert_tres_fonce = QColor::fromRgb(0x001A00);
  33. QColor bleu = QColor::fromRgb(0x0000FF);
  34. QColor bleu_clair = QColor::fromRgb(0x1E90FF);
  35. QColor bleu_fonce = QColor::fromRgb(0x000066);
  36. QColor bleu_tres_fonce = QColor::fromRgb(0x00001A);
  37. QColor orange = QColor::fromRgb(0xFF9500);
  38. QColor jaune = QColor::fromRgb(0xFFFF00);
  39. QColor jaune_clair = QColor::fromRgb(0xFFFFBF);
  40. QColor jaune_fonce = QColor::fromRgb(0xB9B100);
  41. QColor cyan = QColor::fromRgb(0x00FFFF);
  42. QColor cyan_fonce = QColor::fromRgb(0x00CCCC);
  43. QColor violet = QColor::fromRgb(0x800080);
  44. QColor rose_pale = QColor::fromRgb(0xFFC0CB);
  45.  
  46. //----------------------------------------------
  47. QColor couleur_ecran = bleu_tres_fonce;
  48.  
  49. QColor couleur_piste = jaune;
  50. //QColor couleur_piste = gris_fonce;
  51.  
  52. QColor couleur_quadrillage1 = QColor::fromRgb(0x303030);
  53. QColor couleur_quadrillage2 = QColor::fromRgb(0x508888);
  54. QColor couleur_texte = QColor::fromRgb(0x888888);
  55.  
  56. QPen pen_trace(couleur_piste, 0, Qt::SolidLine);
  57. QPen pen_texte(couleur_texte, 1, Qt::SolidLine);
  58. QPen pen_quadrillage(couleur_quadrillage1,0, Qt::SolidLine);
  59. //--------------------;--------------------------
  60.  
  61.  
  62. int x_scene, y_scene;
  63.  
  64. QList <Element> liste_elements; // tous types confondus, segments et pastilles
  65. QString fileName_en_cours;
  66.  
  67.  
  68.  
  69. float zoom;
  70. quint8 ech; // echelle = 4, 8, 80 pour 1/4, 1/8, 1/80 ; 1/4 signifie 4 pas pour 1 pixel à lécran.
  71. quint8 systeme_mesure; // 1 (mm) ou 2 (mils US)
  72.  
  73. char datas_in[2000000];
  74. quint32 nb_octets=1000; // ajusté à la valeur exacte plus bas
  75. quint32 compteur_pas=0;
  76. qint32 pos_moteur1, pos_moteur2;
  77. QPointF pos_actuelle;
  78. qint32 X_max =0;
  79. qint32 Y_max =0;
  80.  
  81. qint16 offset_X =800;
  82. qint16 offset_Y =800;
  83.  
  84. int x_0, y_0;
  85. int x_clic_scene, y_clic_scene; // coordonnees du pixel clique
  86. int x_dyn, y_dyn, delta_x, delta_y;
  87. qint32 y_max = 10000;
  88.  
  89.  
  90.  
  91. QString msg ="";
  92.  
  93.  
  94. qreal sqr(qreal r1)
  95. {
  96. return r1 * r1;
  97. }
  98.  
  99.  
  100. QString valeur2txt2(int v1)
  101. {
  102. QString s;
  103. s.setNum(v1); // conversion num -> txt
  104. s = "00"+s;
  105. s = s.right(2);
  106. return s;
  107. }
  108.  
  109.  
  110. QString valeur2txt4(int v1)
  111. {
  112. QString s;
  113. s.setNum(v1); // conversion num -> txt
  114. s = "0000"+s;
  115. s = s.right(4);
  116. return s;
  117. }
  118.  
  119.  
  120.  
  121.  
  122. MainWindow::MainWindow(QWidget *parent) :
  123. QMainWindow(parent)
  124. {
  125.  
  126. setupUi(this);
  127. setWindowTitle("Traceur SDcard v:" + version + " - Silicium628");
  128. window()->setGeometry(0,0,1450,1023);
  129.  
  130. pixmap1 = new QPixmap(10300,10300 );
  131. scene1 = new SceneCliquable(this);
  132.  
  133.  
  134. QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
  135.  
  136. pen_trace.setCapStyle(Qt::RoundCap); // plume ronde
  137.  
  138. lecture_fichier_init();
  139.  
  140. lineEdit_1->setText(fileName_en_cours);
  141. lire_fichier(fileName_en_cours);
  142.  
  143. systeme_mesure = 1;
  144. zoom = 1;
  145. ech=8;
  146. effacer_ecran(); // et crée les groupes héritiers de la scene ainsi que la connexion entre les clics et le slot ici concerné
  147.  
  148.  
  149. }
  150.  
  151.  
  152.  
  153. MainWindow::~MainWindow()
  154. {
  155.  
  156. }
  157.  
  158.  
  159.  
  160. void MainWindow::effacer_ecran()
  161. {
  162. pixmap1->fill(vert_tres_fonce); // rappel : pixmap1 = 12010,12010
  163.  
  164. scene1->clear();
  165. scene1 = new SceneCliquable(this);
  166. connect(scene1, SIGNAL(envoiPosition_survol(int, int) ), this, SLOT (onReceptPos_survol(int, int) ));
  167. connect(scene1, SIGNAL(envoiPosition_clic(int, int) ), this, SLOT (onReceptPos_clic(int, int) ));
  168. connect(scene1, SIGNAL(envoiPosition_release(int, int) ), this, SLOT (onReceptPos_release(int, int) ));
  169.  
  170. scene1->setBackgroundBrush(couleur_ecran);
  171. scene1->setSceneRect(-100,-100,10500,10500); // plus grand que la partie affichée
  172. graphicsView1->setScene(scene1);
  173. graphicsView1->setGeometry(5,25,1266,890); // POSITION et dimensions de l'écran
  174. graphicsView1->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
  175.  
  176.  
  177. scene1->addPixmap(*pixmap1);
  178.  
  179. groupe_quadrillage = new QGraphicsItemGroup();
  180. scene1->addItem(groupe_quadrillage);
  181. tracer_quadrillage();
  182.  
  183. groupe_quadrillage->setZValue(1);
  184.  
  185. QPointF P(600,y_max);
  186. graphicsView1->centerOn(P);
  187.  
  188. }
  189.  
  190.  
  191.  
  192. void MainWindow::effacer_quadrillage()
  193. {
  194. foreach( QGraphicsItem *item, groupe_quadrillage->childItems() )
  195. {
  196. delete item;
  197. }
  198. }
  199.  
  200.  
  201.  
  202. /**
  203. void MainWindow::tracer_mire_origine()
  204. { // à l'écran
  205.   QPoint P;
  206.  
  207.   P.setX(0);
  208.   P.setY(0);
  209.   P /= ech;
  210.  
  211.  
  212.   QGraphicsLineItem *segment_pointage1 = new QGraphicsLineItem(groupe_pointage);
  213.   segment_pointage1->setPen(pen_pointage);
  214.   segment_pointage1->setLine(P.x()-10, P.x()-10, P.x()+10, P.x()+10);
  215.  
  216.   QGraphicsLineItem *segment_pointage2 = new QGraphicsLineItem(groupe_pointage);
  217.   segment_pointage2->setPen(pen_pointage);
  218.   segment_pointage2->setLine(P.x()-10, P.x()+10, P.x()+10, P.x()-10);
  219.   tracer_texte(-20, -20, "(x=0; y=0)");
  220.  
  221. }
  222. **/
  223.  
  224.  
  225. /**
  226. void MainWindow::tracer_texte(int x, int y, QString texte_i)
  227. { // à l'écran
  228.   y=-y;
  229.  
  230.   x /= ech;
  231.   y /= ech;
  232.  
  233.   QGraphicsTextItem *gtexte = new QGraphicsTextItem(groupe_trace);
  234.   gtexte->setDefaultTextColor(bleu);
  235.   gtexte->setPos(x,y);
  236.   gtexte->setPlainText(texte_i);
  237. }
  238. **/
  239.  
  240.  
  241. void MainWindow::tracer_quadrillage()
  242. {
  243. int n, n_max;
  244. int c;
  245. float x,dx, x_max;
  246. float y,dy;
  247. float facteur=1;
  248. float offset_x, offset_y;
  249. quint8 gradu_bold;
  250. quint16 unite;
  251. quint16 step_grad; // nb de lignes d'espacement pour les textes de graduation
  252. quint16 grad_max;
  253. QString sti;
  254. QPainter painter1(pixmap1); // rappel: pixmap1 = 12010,12010
  255. // painter1.setPen(pen_quadrillage);
  256.  
  257.  
  258. // valeurs par defaut :
  259. facteur = 8; // largeur des cases
  260. gradu_bold=10;
  261. unite = 10;
  262. n_max = 100;
  263. step_grad =10;
  264. y_max = 8000;
  265. grad_max=10;
  266.  
  267. if (systeme_mesure == 1) // mm
  268. {
  269. if (ech == 1)
  270. {
  271. facteur = 8; // largeur des cases
  272. gradu_bold=10;
  273. unite = 10;
  274. n_max = 100;
  275. step_grad =10;
  276. y_max = 8000;
  277. grad_max=10;
  278. }
  279.  
  280. if (ech == 4)
  281. {
  282. facteur = 2; // largeur des cases
  283. gradu_bold=10;
  284. unite = 10;
  285. n_max = 500;
  286. y_max = 10000;
  287. step_grad =10;
  288. grad_max=50;
  289. }
  290. if (ech == 8)
  291. {
  292. facteur = 10;
  293. gradu_bold=10;
  294. unite = 10;
  295. n_max =100;
  296. y_max = 10000;
  297. step_grad =1;
  298. grad_max=100;
  299. }
  300. if (ech == 80)
  301. {
  302. facteur = 10;
  303. gradu_bold=10;
  304. unite = 100;
  305. n_max =10;
  306. y_max = 1000;
  307. step_grad =1;
  308. grad_max=100;
  309. }
  310. }
  311.  
  312. if (systeme_mesure == 2) // 25mils
  313. {
  314. facteur = 6.35;
  315. gradu_bold=4;
  316. unite=250;
  317. n_max = 160; // 1000 /facteur;
  318. step_grad =1;
  319. y_max = 10160; // 3110;
  320. grad_max=4000;
  321.  
  322. }
  323.  
  324. if(systeme_mesure == 3) // grille en 0.8mm pour vérifier les smd en 0.8
  325. {
  326. facteur = 8;
  327. gradu_bold=10;
  328. unite = 0;
  329. n_max =100;
  330. step_grad =1;
  331. }
  332.  
  333.  
  334.  
  335. // n_max = 1000 /facteur;
  336.  
  337. dx=10*facteur; // largeur des cases
  338. x_max = dx * n_max;
  339.  
  340. dy=10*facteur; // hauteur des cases
  341. //y_max = dy * n_max;
  342.  
  343. offset_x = 100+spinBox_X->value();
  344. offset_y = 100+spinBox_Y->value();
  345.  
  346. ellipse_i = new QGraphicsEllipseItem(100-1,y_max+100-2,3,3);
  347. pen_quadrillage.setColor(rouge);
  348. ellipse_i->setPen(pen_quadrillage);
  349. groupe_quadrillage->addToGroup(ellipse_i);
  350. //painter1.setPen(pen_quadrillage);
  351. //painter1.drawEllipse(0,0,3,3);
  352.  
  353.  
  354. // lignes verticales
  355. for (n=0; n<=n_max; n++)
  356. {
  357. if ((n % gradu_bold) == 0) {pen_quadrillage.setColor(couleur_quadrillage2);} else {pen_quadrillage.setColor(couleur_quadrillage1);}
  358. x = dx * n;
  359. ligne_i = new QGraphicsLineItem(x+offset_x, offset_y, x+offset_x, y_max+offset_y);
  360. ligne_i->setPen(pen_quadrillage);
  361. groupe_quadrillage->addToGroup(ligne_i);
  362. //painter1.drawLine(x+offset, offset, x+offset, y_max+offset);
  363.  
  364. sti.setNum((unite * n)/10);
  365. if (((unite * n)/10) == 1 ) {sti += "mm";}
  366.  
  367. //painter1.setPen(pen_texte);
  368.  
  369. text_gradu = new QGraphicsTextItem(sti);
  370. text_gradu->setDefaultTextColor(couleur_texte);
  371. text_gradu->setPos(step_grad*x +offset_x+2, y_max+offset_y+1 );
  372. groupe_quadrillage->addToGroup(text_gradu);
  373.  
  374. if (ech == 1)
  375. {
  376. for (c=0; c<=n_max; c++)
  377. {
  378. y = dy * c;
  379. text_gradu = new QGraphicsTextItem(sti);
  380. text_gradu->setDefaultTextColor(couleur_texte);
  381. text_gradu->setPos(step_grad*x +offset_x+2, offset_y+1 + step_grad*y );
  382. groupe_quadrillage->addToGroup(text_gradu);
  383. //painter1.drawText(step_grad*x +offset+2, step_grad*y +offset+20, sti);
  384. }
  385. }
  386. }
  387.  
  388. // lignes horizontales
  389. for (n=0; n<=n_max; n++)
  390. {
  391. if (((n_max-n) % gradu_bold) == 0) {pen_quadrillage.setColor(couleur_quadrillage2);} else {pen_quadrillage.setColor(couleur_quadrillage1);}
  392. painter1.setPen(pen_quadrillage);
  393. y = dy * n;
  394. //painter1.drawLine(offset, y+offset, x_max+offset, y+offset);
  395. ligne_i = new QGraphicsLineItem(offset_x, y+offset_y, x_max+offset_x, y+offset_y);
  396. ligne_i->setPen(pen_quadrillage);
  397. groupe_quadrillage->addToGroup(ligne_i);
  398. sti.setNum(grad_max-((unite * n)/10));
  399.  
  400. //painter1.setPen(pen_texte);
  401. //painter1.drawText(offset+10, step_grad*y +offset+20, sti);
  402.  
  403. text_gradu = new QGraphicsTextItem(sti);
  404. text_gradu->setDefaultTextColor(couleur_texte);
  405. text_gradu->setPos(offset_x+10, step_grad*y +offset_y+1);
  406. groupe_quadrillage->addToGroup(text_gradu);
  407.  
  408.  
  409. }
  410. //scene1->addPixmap(*pixmap1);
  411. scene1->addItem(groupe_quadrillage);
  412. }
  413.  
  414.  
  415.  
  416. void MainWindow::tracer1pas()
  417. {
  418. quint32 x, y;
  419. float z1, z2, z3;
  420. quint8 R1,G1,B1, R2, G2, B2;
  421. QColor couleur_piste2;
  422.  
  423. QPainter painter1(pixmap1);
  424. painter1.setPen(pen_trace);
  425.  
  426. x = pos_moteur1;
  427. y = pos_moteur2;
  428.  
  429. x /= ech;
  430. y /= ech;
  431.  
  432. y=y_max-y;
  433.  
  434.  
  435.  
  436. if (checkBox_1->isChecked()) // couleur évolutive afin de repérer le parcours du tracé.
  437. {
  438. R1=130;
  439. G1=130;
  440. B1=130;
  441.  
  442. z3=124*cos((float)compteur_pas/10000.0);
  443. z2=124*cos((float)(compteur_pas/10000.0)+(M_PI/3.0));
  444. z1=124*cos((float)(compteur_pas/10000.0)+(2*M_PI/3.0));
  445.  
  446. R2= R1+z1;
  447. G2= G1+z2;
  448. B2= B1+z3;
  449. couleur_piste2 = QColor::fromRgb(R2,G2,B2);
  450.  
  451. pen_trace.setColor(couleur_piste2);
  452. }
  453. else { pen_trace.setColor(couleur_piste); }
  454.  
  455. // if (((x % 100)==0) || ((y % 100)==0)) {pen_trace.setColor(blanc);}
  456. if( ! checkBox_2->isChecked()){pen_trace.setWidth(15);} else {pen_trace.setWidth(1);}
  457.  
  458. painter1.drawPoint(x+100,y+100);
  459.  
  460. compteur_pas++;
  461. }
  462.  
  463.  
  464.  
  465.  
  466.  
  467. void MainWindow::tracer_circuit()
  468. {
  469. compteur_pas=0;
  470.  
  471. quint32 i, i_max, k;
  472. quint8 octet_i;
  473. quint8 evt[4]; //chaque octet du fichier code pour 4 evenements codés sur deux bits chacun
  474. quint8 evt_k;
  475. quint8 fin_trace;
  476. quint8 stop;
  477. QString s1;
  478.  
  479. pos_moteur1=0;
  480. pos_moteur2=0;
  481.  
  482. i_max = 800000; // nb_octets; // 4000000 et plus... A VOIR
  483. if (radioButton_6->isChecked()) {i_max=40000;} // tracer debut seulement (pour gagner du temps...)
  484.  
  485.  
  486. i=512; // pour commencer la lecture au block_n; le block0 est réservé pour une entete
  487. fin_trace=0;
  488. stop=0;
  489.  
  490.  
  491. while ((i<i_max) && (stop == 0) )
  492. {
  493. octet_i= datas_in[i];
  494. i++;
  495. // detection fin de trace
  496. if(octet_i == '=') {fin_trace++ ;} else {fin_trace=0;}
  497. if(fin_trace>10) {stop = 1;} // se produit si 10x '=' à la suite, se qui fera sortir de la boucle
  498.  
  499. evt[0] = octet_i & 0b00000011;
  500. evt[1] = (octet_i & 0b00001100) >>2;
  501. evt[2] = (octet_i & 0b00110000 )>>4;
  502. evt[3] = (octet_i & 0b11000000) >>6;
  503.  
  504. for (k=0; k<4; k++)
  505. {
  506. evt_k=evt[k];
  507. if (evt_k == 0b00) {pos_moteur1++; }
  508. if (evt_k == 0b01) {pos_moteur1--; }
  509. if (evt_k == 0b10) {pos_moteur2++; }
  510. if (evt_k == 0b11) {pos_moteur2--; }
  511.  
  512. //qDebug() << "M1=" << pos_moteur1 << " M2=" << pos_moteur2;
  513.  
  514. tracer1pas();
  515. }
  516. }
  517.  
  518. s1.setNum(i);
  519. lineEdit_3->setText(s1);
  520.  
  521.  
  522. scene1->addPixmap(*pixmap1);
  523. QPointF P(600,y_max);
  524. graphicsView1->centerOn(P);
  525.  
  526. }
  527.  
  528.  
  529.  
  530.  
  531. void MainWindow::lecture_fichier_init()
  532. {
  533. QString line, tx1, tx2;
  534. int p2;
  535. tx1 = "";
  536.  
  537. QFile file1(QDir::homePath() + "/.cnc_traces/traces.ini");
  538.  
  539. if (file1.open(QIODevice::ReadOnly | QIODevice::Text))
  540. {
  541. QTextStream in(&file1);
  542. in.reset();
  543. while ( !in.atEnd() )
  544. {
  545. line = in.readLine();
  546. if (line.left(1) !="#")
  547. {
  548. if (line.indexOf("fileName_en_cours") != -1)
  549. {
  550. if ( (p2 = line.indexOf('=')) != -1)
  551. {
  552. line.remove(0,p2+1);
  553. fileName_en_cours = line;
  554. }
  555. }
  556. }
  557. }
  558. file1.close();
  559. } else { fileName_en_cours = ""; tx1 = "fichier .ini non trouve, ce n'est pas grave !"; }
  560.  
  561. if (tx1 !="")
  562. {
  563. QMessageBox msgBox;
  564. msgBox.setText(tx1);
  565. msgBox.exec();
  566. }
  567. // statusBar1->showMessage(tx1 + " "+ tx2);
  568. }
  569.  
  570.  
  571.  
  572. void MainWindow::enregistrer_fichier_init()
  573. {
  574. QDir dossierIni(QDir::homePath() + "/.cnc_traces/");
  575. if (!dossierIni.exists())
  576.  
  577. {
  578. QMessageBox msgBox;
  579. msgBox.setText("Le repertoire : ~/" + dossierIni.dirName() + " n'existe pas, je vais le creer, et y placer le fichier de configuration 'cnc_gerber.ini' ");
  580. msgBox.exec();
  581. QDir dossierPerso(QDir::homePath());
  582. dossierPerso.mkdir(".cnc_traces");
  583. }
  584.  
  585. QFile file1(QDir::homePath() + "/.cnc_traces/traces.ini");
  586. if (file1.open(QIODevice::WriteOnly | QIODevice::Text))
  587. {
  588. QTextStream out(&file1);
  589. QString V;
  590.  
  591. out << "# les lignes commeneant par # sont ignorees";
  592. out << '\n';
  593. out << "# Ce fichier est genere automatiquement par le programme";
  594. out << '\n';
  595. out << "#";
  596. out << '\n';
  597.  
  598. out << "fileName_en_cours=" << fileName_en_cours;
  599. out << '\n';
  600. }
  601. file1.close();
  602. }
  603.  
  604.  
  605.  
  606. int MainWindow::lire_fichier(QString nom_fichier1)
  607. {
  608. QFile file1(nom_fichier1);
  609. QString s1;
  610.  
  611. if( !file1.exists() )
  612. {
  613. qDebug() << "Le fichier (" << nom_fichier1<< ") n'existe pas.";
  614. return 1;
  615. }
  616.  
  617. if (!file1.open(QIODevice::ReadOnly )) return 1;
  618. QDataStream in1(&file1);
  619.  
  620. nb_octets = file1.size();
  621. if (nb_octets> 2000000) {nb_octets = 2000000;}
  622.  
  623. s1.setNum(nb_octets);
  624. lineEdit_2->setText(s1);
  625.  
  626. in1.readRawData(datas_in, nb_octets);
  627.  
  628. file1.close();
  629. return 0;
  630. }
  631.  
  632.  
  633.  
  634.  
  635. int MainWindow::on_actionOuvrir_triggered() // menu "Fichier/ouvrir.."
  636. {
  637. int fich_charge_ok;
  638. // fileName_en_cours = lineEdit_6->text();
  639. fileName_en_cours = QFileDialog::getOpenFileName(this, tr("Open File"), fileName_en_cours, tr("Files (*.bin *.dat)"));
  640.  
  641. lineEdit_1->setText(fileName_en_cours);
  642.  
  643. fich_charge_ok = lire_fichier(fileName_en_cours); // <<------------
  644.  
  645. qDebug() << fich_charge_ok;
  646.  
  647. if (fich_charge_ok != 0) {return 1;}
  648.  
  649. enregistrer_fichier_init();
  650.  
  651. effacer_ecran();
  652. tracer_quadrillage();
  653. tracer_circuit();
  654.  
  655. return 0;
  656. }
  657.  
  658.  
  659.  
  660. int MainWindow::on_actionRecharger_triggered()
  661. {
  662. int fich_charge_ok;
  663. // lineEdit_6->setText(fileName_en_cours);
  664.  
  665. fich_charge_ok = lire_fichier(fileName_en_cours); // <<------------
  666. if (fich_charge_ok != 0) {return 1;}
  667.  
  668. effacer_ecran();
  669. tracer_quadrillage();
  670. tracer_circuit();
  671.  
  672. return 0;
  673. }
  674.  
  675.  
  676.  
  677. void MainWindow::onReceptPos_clic(int px, int py) // emis depuis un clic sur scene1, voir ma class "scene_cliquable"
  678. {
  679. QString s1,s2;
  680. QPoint P;
  681.  
  682. x_clic_scene = px; // ne change qu'ici lors du clic
  683. y_clic_scene = py; // ne change qu'ici lors du clic
  684.  
  685. s1.setNum(px);// conversion num -> txt
  686. s2.setNum(-py);
  687. // lineEdit_4->setText("x="+s1+" y="+s2);
  688.  
  689. P.setX(px);
  690. P.setY(py);
  691.  
  692. graphicsView1->centerOn(P);
  693. // P.setX(px * ech);
  694. // P.setY(-py*ech);
  695.  
  696. }
  697.  
  698.  
  699.  
  700. void MainWindow::onReceptPos_release(int px, int py)
  701. {
  702. px=px; // pour éviter les warnings à la compilation. c'est ce que j'ai trouvé de mieux...
  703. py=py;
  704.  
  705. }
  706.  
  707.  
  708.  
  709. void MainWindow::onReceptPos_survol(int px, int py) // emis lors d'un survol avec clic appuyé de scene1, voir ma class "scene_cliquable"
  710. {
  711. QString s1,s2;
  712. QPoint P;
  713.  
  714. x_dyn = px;
  715. y_dyn = py;
  716.  
  717. delta_x = x_clic_scene - x_dyn;
  718. delta_y = y_clic_scene - y_dyn;
  719.  
  720. s1.setNum(delta_x);// conversion num -> txt
  721. s2.setNum(delta_y);
  722. lineEdit_4->setText("dx="+s1+" dy="+s2);
  723.  
  724. P.setX(delta_x);
  725. P.setY(delta_y);
  726. }
  727.  
  728.  
  729.  
  730.  
  731.  
  732.  
  733.  
  734. void MainWindow::on_Btn_tracer_clicked()
  735. {
  736. tracer_quadrillage();
  737. tracer_circuit();
  738.  
  739. }
  740.  
  741.  
  742. void MainWindow::on_Btn_effacer_clicked()
  743. {
  744. effacer_ecran();
  745. }
  746.  
  747.  
  748. void MainWindow::on_Btn_redessiner_clicked()
  749. {
  750. effacer_ecran();
  751. tracer_quadrillage();
  752. tracer_circuit();
  753. }
  754.  
  755.  
  756. void MainWindow::on_spinBox_posX_valueChanged(int arg1)
  757. {
  758. arg1=arg1;
  759.  
  760. }
  761.  
  762. void MainWindow::on_Btn_test_clicked()
  763. {
  764. QPointF P;
  765. P.setX(delta_x);
  766. P.setY(delta_y);
  767.  
  768. // graphicsView1->translate(delta_x, delta_y);
  769.  
  770. graphicsView1->centerOn(P);
  771. }
  772.  
  773.  
  774.  
  775.  
  776.  
  777. void MainWindow::on_radioButton_1_clicked()
  778. {
  779. ech=4;
  780. effacer_ecran();
  781. }
  782.  
  783.  
  784. void MainWindow::on_radioButton_2_clicked()
  785. {
  786. ech=8;
  787. effacer_ecran();
  788. }
  789.  
  790.  
  791. void MainWindow::on_radioButton_3_toggled(bool checked)
  792. {
  793. checked=checked;
  794. if (radioButton_3->isChecked()) {systeme_mesure = 1;} // mm
  795. if (radioButton_4->isChecked()) {systeme_mesure = 2;} // mils
  796. if (radioButton_8->isChecked()) {systeme_mesure = 3;} // mils
  797. effacer_ecran();
  798. }
  799.  
  800.  
  801.  
  802. void MainWindow::on_radioButton_4_clicked()
  803. {
  804. radioButton_2->setChecked(true);
  805. on_radioButton_2_clicked();
  806. }
  807.  
  808.  
  809.  
  810. void MainWindow::on_radioButton_5_clicked()
  811. {
  812. ech=80;
  813. effacer_ecran();
  814. }
  815.  
  816. void MainWindow::on_radioButton_8_clicked()
  817. {
  818. systeme_mesure = 3;
  819. effacer_ecran();
  820. }
  821.  
  822. void MainWindow::on_radioButton_9_clicked()
  823. {
  824. ech=1;
  825. effacer_ecran();
  826. }
  827.  
  828.  
  829.  
  830. void MainWindow::on_Btn_eff_quadrillage_clicked()
  831. {
  832. effacer_quadrillage();
  833. }
  834.  
  835. void MainWindow::on_Btn_tracer_quadrillage_clicked()
  836. {
  837. tracer_quadrillage();
  838. }
  839.  
  840. void MainWindow::on_spinBox_X_valueChanged(int arg1)
  841. {
  842. arg1=arg1;
  843. effacer_quadrillage();
  844. tracer_quadrillage();
  845. }
  846.  
  847. void MainWindow::on_spinBox_Y_valueChanged(int arg1)
  848. {
  849. arg1=arg1;
  850. effacer_quadrillage();
  851. tracer_quadrillage();
  852. }
  853.  

12 Double buffering et Multitache

L'écriture des données dans la SDcard se fait en mode "RAW" (directement dans la mémoire sans partition ni fichier), par blocs de 512 octets. Un buffer de 512 octets est donc déclaré en RAM dans l'ATmega32. Les données y sont directement enregistrées et lorsque le buffer est plein, on l'enregistre dans la SDcard (en utilisant le port SPI de l'ATmega32). Le problème est que pendant cet enregistrement qui dure quelques ms, les données continuent à arriver et sont alors perdues. (Un faible pourcentage, moins de 1% ). Mais cela est inacceptable.

Je travaille donc actuellement sur la technique du "double-buffering" qui consiste à utiliser deux buffers, on en remplit un pendant qu'on enregistre l'autre. Et derrière ce mot "pendant" se cache une seconde technique, le "multitâche" qui est tout à fait implémentable sur un ATmega.

Afin de pouvoir loger deux buffers de 512 octets en RAM un ATmega32 qui est pourvu de 2kB de RAM est nécessaire, le 1kB d'un ATmega8 ne suffisant pas (il y a d'autre variables à loger en RAM en plus des buffers).


27 juillet 2015:

Il s'avère (suite à la mise en pratique d'une cogitation...) que le double buffering n'est pas indispensable dans le cas qui nous occupe.
Il est en effet possible d'enregistrer le signal incident dans le buffer pendant que l'on enregistre ce dernier dans la SDcard.
Ceci est rendu possible du fait que la cadence d'acquisition des octets est inférieure à celle d'écriture sur la carte.
Voici mon propre commentaire tel qu'il figure dans le firmware pour l'ATmega32 :

================== PARAMETRES NUMERIQUES DE CE PROGRAMME ========

(Les tempo sont mesurées à l'oscillo et/ou fréquencemètre) et non pas calculées exactement au simulateur OCR0 = 10; -> un octet est présenté toutes les 45us c.a.d avec une fréquence de 22kHz (pour test avec acquisition interne par Timer0))
-Un enregistrement de 1 block (=512 octets) se fait en 8ms en moyenne avec quelques rares "coups" à 16ms (? deux blocs à la fois ????)
-Le buffer de 512 octet se remplit en 512 x 45us = 23ms
-Le temps de repos pendant lequel il n'y a pas d'écriture sur la carte = 23ms-8ms= 15ms; On voit très bien ce temps de 15ms à l'oscillo
-Chaque octet est enregistré sur la carte en 8ms/512 = 15us soit moins que la période d'arrivée des octets qui est rappelons le de 45us soit 3x plus longue.

CE QUI SE PASSE:
A l'instant où le buffer est plein, on dispose de 45us avant qu'un octet ne se présente à l'enregistrement en début du buffer. Lorsque cet octet incident arrive, on a déjà enregistré les trois premier octets du buffer sur la carte, lorsque le deuxième octet arrive on a enregistré 6 octets et ainsi de suite, de telle manière que l'adresse de stockage des octets entrants dans le buffer ne rattrape jamais celle des octets lus pour être expédiés dans la SDcard.
Ceci est important de façon à ce que les octets arrivant n'écrasent pas des octets pas encore enregistrés: Le serpent ne doit pas se mordre la queue ! Il n'y a donc aucune perte de signal, tout ce qui arrive est enregistré sur la carte.

==================================================================

Les acquisitions se font par interruption, mais l'enregistrement sur la carte se fait dans la boucle du programme principal, appelé par un flag positionné par les interruptions dès que le buffer est plein. Ce qui donne la priorité aux acquisitions sans gêner les enregistrements qui se font tranquillement en tache de fond.

13 Le circuit imprimé :

28 juillet 2015: Le circuit imprimé est en cours de conception avec Kicad sous Linux Mint17

14 circuit gravé :

1er août 2015: J'ai flashé le circuit avec ma machine laser. Voici le résultat après gravure au perchlorure de fer. Certains trous ne sont pas débouchés, c'est un problème de révélation pas assez poussée.

15 Trous percés

16 Atmega soudé :

Chaque soudure se fait à la loupe, avec de la soudure hyper fine (0.5mm) et une panne spéciale CMS, ultra pointue comme une aiguille.
Reste à nettoyer le flux de soudure

17 Circuit terminé

10 août 2015:
Tout fonctionne comme prévu.
Pourquoi mélanger des textes en français et en anglais ?
Parce qu'en anglais les mots sont (en général) plus courts
ce qui est pratique pour l'affichage sur ces petits écrans.

18 Documents :

Ces archives qui comprennent la totalité des fichiers sources (des deux programmes, le firmware et le logiciel en Qt4, ainsi que quelques fichiers SDcard pour les essais) sont mis à jour plusieurs fois par jour au fur et à mesure de l'avancement du projet.

19 -

A voir également...

Liens externes :




Me contacter à propos de cet article :

Question mathématique :

Click to reload image
=
cliquez sur l'image pour un faire autre calcul.




Réponses à certaines de vos questions...
2027