Machine CNC de phototraçage de typons par diode laser 405nm

Je vous décris ici ma réalisation (en cours) d'une petite CNC (Machine à commande numérique) de flashage par laser de typons pour la fabrication de circuits imprimés.


Attention : Ce projet peut comporter un danger. Selon la puissance et la longueur d’onde d’émission du laser, celui-ci peut représenter un réel danger pour la vue et provoquer des brûlures irréparables de la rétine. Le faisceau laser est dangereux dans l'axe, mais également en réflexion (sur une surface métallique par exemple). La rétine est irrémédiablement endommagée BIEN AVANT que le réflexe pupillaire n'ai pu intervenir. Les lunettes tintées "de protection" n'offrent qu'une protection (limitée) contre une diffusion du faisceau (si puissance faible, qq mW), mais PAS contre une réflexion. Les plus dangereux sont les lasers infra-rouge ou ultra-violet dont le rayon est invisible. Ce projet n’est donc présenté qu’à titre d’information. Voilà, il vous reste la possibilité de remplacer le laser par un stylo feutre "spécial circuit imprimé" ou une bonne LED + fibre optique (une "plume" optique).

Voici quelques photos de la machine proprement dite :
Cliquez sur une image ci-dessous pour visualiser l'album. Certaines sont commentées
Sommaire :
1 - Les grandes lignes du projet :
2 - Dessin du circuit imprimé : Le logiciel kicad
3 - Exportation du tracé au format gerber depuis Kicad
4 - Le fichier gerber obtenu :
5 - Les drivers pour moteur pas à pas : EasyDriver et carte Arduino Mega2560
6 - Le film photosensible à coller sur le cuivre :
7 - Ecriture du programme
8 - Principe du calcul des déplacement
9 - suite du calcul
10 - Le schéma :
11 - Le firmware pour carte Mega2560
12 - Le programme de pilotage en C++ & Qt5 pour le PC :
13 - Le code source du soft en Qt5 :
14 - Documents :
15 - Passage à un Arduino Mega2560
16 - Premier tracé d'un circuit
17 - Les choses se précisent
18 - L'ATmega est content !
19 - Optimisation du tracé
20 - Optimisation AUTOMATIQUE
21 - Remplissage des pastilles
22 - Le laser a remplacé le stylo !
23 - Le même tracé laser vu à la loupe :
24 - Le laser bleu-violet est en place
25 - Premier essai de flashage du film en place sur le cuivre
26 - Vue de plus près...
27 - Détails :
28 - Essai d'élargissement des pistes par défocalisation du laser :
29 - Remplacement de la diode laser par un modèle de 50mW :
30 - Nous sommes sur la bonne voie
31 - La buse
32 - Le dernier problème... ?
33 - On touche au but...
34 - Flashage du film
35 - Réglage de la focalisation
36 - Parfait ?
37 - Procédons méthodiquement
38 - La méthode était la bonne !
39 - Le circuit imprimé gravé :
40 - Les principaux composants CMS sont soudés :
41 - Pilotage de la luminosité de la diode laser
42 - Image miroir & trous débouchés
43 - Calcul du raccourcissement des pistes
44 - Résultat gravé :
45 - La platine driver terminée, opérationnelle
46 - Pilotage de la hauteur du laser
47 - ...et largeurs des pistes obtenues
48 - Pilotage de la luminosité du laser en fonction des largeurs de pistes
49 - Réalisation de la carte mère
50 - Test de la carte mère
51 - Flashage d'un circuit mixte (classique + cms) + plans de masse
52 - Flashage virtuel sur SDcard
53 - Version du soft en QT5
54 - Réduction de la taille de la fenêtre du programme
55 - Nouveau format des fichiers Gerber
56 - La version du nouveau Kicad
57 - Nouvelle version disponible

1 Les grandes lignes du projet :

10 octobre 2014:

La table sur laquelle sera fixé le circuit se déplacera en x, et le laser la surplombant en y. Les déplacements seront réalisés par des moteurs pas-à-pas piloté par un arduino (et firmware open source) + interface moteurs.

Un logiciel perso (Open source) en C++ Qt4 interprétera le contenu d'un fichier vectoriel "gerber" obtenu directement avec le logiciel libre Open source Kicad de conception de circuits imprimés.

Une protection (écran) sérieuse contre les réflexions du laser (bleu-violet, dans le domaine visible donc) sera prévue dès le départ.

Le typon sera en fait directement un "negative dry film photoresist for PCB" laminé à chaud sur le cuivre de la plaque du circuit imprimé.

Le révélateur n'est autre que du carbonate de calcium (Na2CO3 = cristaux de soude = Lessive St Marc) vendus parfois à prix d'or pour cette utilisation. Attention : ne pas confondre avec la soude caustique (NaOH) dangereuse.

Cette machine offrant une précision de 1/100mm pour les déplacements du laser devrait permettre de pister directement des boîtiers SMD TQFP 48 au pas de 0.5mm et toute la cms 0805 voire 0603 tout en buvant le café.


2 Dessin du circuit imprimé : Le logiciel kicad

Je l'utilise sous Linux Mint17. Voici enfin une liste d'astuces que j'ai rédigée au fur et à mesure de mon utilisation de ce logiciel, depuis plusieurs années :

3 Exportation du tracé au format gerber depuis Kicad

Remarque : Il convient de placer "l'origine des coordonnées de perçage" (c'est dans le menu "Placer") dans le coin en bas à gauche du circuit afin que toutes les coordonnées des pistes et des pastilles soient positives.

4 Le fichier gerber obtenu :


Nous utiliserons directement le fichier gerber, optimisé pour le photo-traçage des circuits imprimés. Il décrit l'allumage, l'extinction, et le déplacement d'un spot lumineux de forme ronde ou rectangulaire le long de l'axe des pistes. Simple et efficace !

La piste, un moment envisagée, du passage par un fichier g-code (adapté aux machines CNC de détourage ) est abandonnée au profit de l'utilisation directe du fichier gerber. Le logiciel d'interprétation gerger ? je vais l'écrire moi-même.

Comme on peut le voir sur l'extrait ci-dessus, le fichier greber est un fichier texte, vectoriel, de description du tracé. La première partie du fichier définit des formes de bases (cercles de différents diamètres, carrés... nommées "apertures") puis vient la liste des opérations à effectuer qui sont essentiellement de trois sortes :
  • Déplacements de la plume sans rien tracer
  • Flashage des pastilles (par positionnement d'une forme de base à un endroit précis)
  • Tracés des pistes (par déplacement d'une forme de base, un cercle à priori...)
Dans la pratique c'est le rayon laser qui effectuera ces opérations sur la surface photosensible du film.


5 Les drivers pour moteur pas à pas : EasyDriver et carte Arduino Mega2560

Les drivers pour moteur pas à pas : (projet open source) J'ai choisi la carte Mega2560 pour son nombre important de Timers 16 bits indépendants et de ports d'E/S. J'ajouterai qu'il faut commander les entrées "enable" des cartes EasyDriver afin de couper le courant dans les moteurs lorsqu'ils sont à l'arrêt.

6 Le film photosensible à coller sur le cuivre :

Il s'agit de film NEGATIF : Le partie qui sera exposée aux UV (par le rayon laser qui trace directement les pistes) reste sur le cuivre, les surfaces non exposées seront dissoutes par le révélateur. En anglais vous devez trouver dans la description quelque chose comme ça :

"Negative acting (Means UV exposed area of film will stay on board) photoresist"

Je vous laisse deviner où l'on peut se le procurer.

Certains vendeurs de précisent pas si le film proposé est négatif ou positif, donc attention !

Le film en question est constitué de trois couches, seule celle prise en sandwich au centre est photosensible, les deux autres à l'extérieur sont des protections.

  • TRAVAILLER DANS LE NOIR ou en lumière rouge. Il faut tout d'abord retirer une des protections en la séparant des deux autres couches à l'aide de deux morceaux de ruban adhésif.
  • Ensuite placer la surface sensible qui n'est plus protégée sur le cuivre préalablement dégraissé , séché, (nickel !)
  • puis passer le tout dans une plastifieuse (éventuellement bricolée afin qu'elle chauffe suffisamment, 80 à 100 °C, je n'ai pas eu besoin de modifier la mienne, le circuit ressort à 84°C en trois passages).
  • Retirer la seconde couche de protection en tirant avec un bout de scotch dans un coin.
  • Ensuite insoler aux UV à travers un masque ou insoler directement au rayon laser 405nm (bleu-violet)
  • développer dans de la lessive St Marc (carbonate de calcium (Na2CO3 = "cristaux de soude" + eau tiède, 10g de cristaux dans un grand verre d'eau) - La partie non exposée du film va se barrer. Aider le cas échéant après une ou deux minutes avec une brosse à dents douce. Travailler en lumière rouge si possible.
  • rincer et sécher au sèche cheveux. Vérifier en pleine lumière et avec une bonne loupe (compte-fils par exemple).
Voici les documents qui vous expliquent tout ça mieux que moi (en anglais)

7 Ecriture du programme

6 novembre 2014:
Je suis en train d'écrire le programme en Qt4 (C++) qui analyse les fichiers gerber et qui envoie les commandes à la carte Arduino Mega2560.

Dans un premier temps je me suis intéressé à l'ordre dans lequel le tracé des pistes apparaît dans le fichier gerber. Voici ce que ça donne (en vidéo).

Il va falloir optimiser ça en ordonnant les lignes du fichier (1 ligne = 1 segment ou 1 pastille) afin de minimiser les déplacements inutiles de la plume. Au moins rassembler les différents segments d'un même "net", ce qui n'est pas le cas dans le fichier produit par Kicad.

Traceur Gerber from Silicium628 on Vimeo.



Voici ce qui fonctionne actuellement :
  • L'analyse des lignes gerber est ok, ça ne pose pas de problème particulier
  • Le programme en Qt4 dialogue correctement avec la carte Arduino en USB
  • La carte Arduino pilote correctement les drivers "EasyDriver"
Donc les choses avancent. Je vous tiens au courant.

8 Principe du calcul des déplacement

9 nov 2014

9 suite du calcul

Je travaille sur l'électronique (ajout de détecteurs de fin de course, sous forme d'optocoupleurs IR) ainsi que sur les softs (Qt4 pour le PC et C++ pour la carte Arduino). Je publierai tout ça très bientôt. Je viens de recevoir la diode laser, je vais donc pouvoir tester le flashage.

En attendant je vous donne une aperçu de mes cogitations: Il va falloir utiliser des timers 16bits afin d'obtenir une grande précision dans les déplacements. Problème : l'ATmega328 de la carte Arduino UNO ne possède qu'un seul timer 16 bits (le « timer1 »). Donc soit on utilise une carte plus performante (Mega2560 qui possède 4 timers 16 bits indépendants) soit on simule deux timers 16 bits logiciellement (par deux compteurs (de type uint16_t)incrémentés par l'interruption du timer.

10 Le schéma :

Comme vous pouvez le voir, je vais en fait utiliser une carte Mega2560. J'explique pourquoi plus bas, par ordre chronologique.

vendredi 14 novembre 2014:
La conception et la mise au point de la CNC me dévore (presque) tout mon temps libre, mais c'est passionnant !! Les choses avancent rapidement, tant côté mécanique qu'électronique et programmation (PC en Qt4 et Arduino en C++, oui plus+ plus+, il est en effet tout a fait possible de faire de la programmation orientée objet avec les Arduino, en fait avec le compilateur avr-gcc et donc de créer des objets ("class" en C++), ce que j'ai fait, et ça décuple la puissance de la partie logicielle ainsi que sa lisibilité).

Bien entendu je publierai tout ça en intégralité.

11 Le firmware pour carte Mega2560

  1. /***************************
  2. gerber.ccp
  3. pour carte Arduino Mega2560
  4. pilote deux (ou trois) moteurs pas-a-pas d'une photoflasheuse + diode laser
  5. liaison USB au PC
  6. par Silicium628
  7. logiciel Libre Open Source
  8. ****************************/
  9.  
  10.  
  11. /****************************
  12. PRINCIPE DE LA MESURE DE POSITION
  13.  
  14. les moteurs pas-à-pas utilisés pour les déplacement en X et Y sont de modèles 200 pas/tour (1.8 deg/ pas)
  15.  
  16. moteur1.steps_par_tour = 200;
  17. moteur1.tours_par_mm = 2; // pas-de-vis 0.5mm (pour un diamètre M3)
  18. moteur1.impuls_par_step = 2; // de par la configuration de la carte "EasyDriver"
  19.  
  20. calcul du nb d'impulsions par 1/100mm :
  21.  
  22. 2 imp/pas * 200 pas/T * 2 T/mm = 800 imp/mm
  23. 800 imp/mm = 8 impulsions /centième de mm
  24.  
  25. Ce nombre 8 est un chiffre rond, un entier, ce qui va nous simplifier la vie !
  26.  
  27. si nous utilisons des "long" (long int = 32 bits = 4 octets, soit -2 147 483 648 à 2 147 483 647) pour compter ces impulsions
  28. nous pourrons controler les déplacements sur une distance de +/-2 147 483 647 / 800 = 2684354 mm = 2684 m = +/-2.7 km... ça devrait le faire !!
  29. remarque1 : l'utilisation de uint16_t (16 bits) = +/-32768 ne permettrait des déplacements que sur 32768/800 = +/-40mm ce qui n'est pas suffisant, la machine
  30. est prévue pour graver des circuits de 100*100mm
  31. remarque2 : des uint24_t (3 octets soit +/- 20m) conviendraient, mais c'est typiquement le truc à ne pas faire pour des tas de raisons (alignement sur avr 8 bits)
  32. qui provoquent la chute prématurée des cheveux.
  33.  
  34. *****************************/
  35.  
  36. // #include "WProgram.h"
  37. // #include <avr/power.h>
  38.  
  39. #include <Arduino.h>
  40. #include <avr/delay.h>
  41. #include <HardwareSerial.h>
  42. #include "../lib/step_motor6.cpp" // lib perso
  43.  
  44. #define bit_LED_rouge 0b00100000; // sur PORTA
  45. #define bit_LED_verte 0b01000000; // sur PORTA
  46. #define bit_buzzer 0b10000000; // sur PORTA
  47.  
  48. //#define bit_LED_blanche1 0b01000000; // sur PORTC - sert de stroboscope moteur1 (pour contrôle visuel de la position angulaire du moteur)
  49. //#define bit_LED_blanche2 0b10000000; // sur PORTC - sert de stroboscope moteur2 (pour contrôle visuel de la position angulaire du moteur)
  50.  
  51. #define bit_LASER 0b01000000; // sur PORTL
  52. #define bit_pointage_LASER 0b10000000; // sur PORTL - allume la diode laser avec une très faible intensité pour faire un pointage visuel
  53.  
  54.  
  55. String version = "23.4";
  56.  
  57. String inputString = ""; // a string to hold incoming data
  58. String data_in ="";
  59. // String outputString = "";
  60. boolean stringComplete = false; // whether the string is complete
  61. char inChar;
  62.  
  63. /**
  64. struct aperture
  65. {
  66.   char forme; // = 'R' ou 'C'
  67.   uint16_t x;
  68.   uint16_t y; // peut eventuellement etre utilisé aussi dans le cas d'un cercle pour définir une éllipse...
  69. }
  70.   cette structure est utilisée par le soft en Qt4. Ici on va faire autrement, avec un tableau...
  71. **/
  72.  
  73.  
  74.  
  75. struct aperture
  76. {
  77. uint16_t x;
  78. uint16_t y;
  79. char type;
  80. };
  81.  
  82. aperture apertures[40];
  83.  
  84. //uint16_t apertures[40][3]; // apertures[i][0] -> x ; apertures[i][1] -> y; (x et y ne sont pas des positions mais des dimensions); apertures[i][2] -> 'R' ou 'C'
  85.  
  86. uint8_t num_aperture;
  87. long ax, ay; // pour les dimensions des apertures; ce type "long" est rendu nécessire pour l'emploi d'un passage de paramètre par adresse dans la fonction "decode_xy_A()"
  88. char type_ap;
  89. uint8_t digits_ap[2];
  90.  
  91. Step_Motor moteur1; // moteur X (déplacements de la table)
  92. Step_Motor moteur2; // moteur Y (déplacements sur la potence)
  93. //Step_Motor moteur3; // = Z (déplacements verticaux de la plume (stylo ou focalisation du laser)) -> remplacé par un servo
  94. uint8_t etat_laser; // eteint=0 ; allumé=1;
  95. uint8_t blocage_laser=1;
  96. uint8_t simulation;
  97. uint8_t n_octet;
  98. uint8_t etat_FC_x1, etat_FC_x2, etat_FC_y1, etat_FC_y2, etat_FC_z1;
  99. uint8_t fdc_envoi_fait =0;
  100. uint16_t cpt1=0;
  101.  
  102. uint16_t compteur1;
  103. uint16_t compteur2;
  104. uint16_t periode1;
  105. uint16_t periode2;
  106.  
  107. float fpx; // facteur periode x
  108. float fpy; // facteur periode y
  109.  
  110. uint16_t ocr3a_min; // pour moteur1
  111. uint16_t ocr3a_depart; // pour moteur1
  112. uint16_t ocr4a_min; // pour moteur2
  113. uint16_t ocr4a_depart; // pour moteur2
  114.  
  115. uint16_t base_ocr; // sera décrémenté par l'INT pour accélérer les 2 moteurs
  116. uint16_t base_ocr_depart; // determine la vitesse de rotation à la mise en rotation. La vitesse augmentera ensuite
  117. uint16_t base_ocr_min; // determine la vitesse max de rotation permise, voir les fonctions "ISR(TIMER3_COMPA_vect)" et "ISR(TIMER4_COMPA_vect)"
  118. uint16_t base_ocr_min2; // determine la vitesse max de rotation permise lors des "goto zero" et "goto safe pos"
  119.  
  120. uint8_t pas_mot1_demande; // drapeau permettant de déplacer le traitement de interruptions moteur dans vers la boucle principale, afin d'éviter les collisions d'interruption.
  121. uint8_t pas_mot2_demande;
  122.  
  123. uint8_t mm_par_s;
  124. uint8_t mm_par_s_max;
  125.  
  126. /*************************************************
  127. rappel1 :
  128. vis au pas de 0.5mm -> 2 tours /mm
  129. 2 imp/pas * 200 pas/tour * 2 tours/mm = 800 imp/mm
  130. 800 imp/mm = 8 impulsions /centième de mm
  131. **************************************************
  132. rappel2 :
  133. long = entier signé codé sur 32 bits = 4 octets
  134. **************************************************/
  135.  
  136. long x0, y0; // position actuelle en nb/impulsions moteur (plus exactement impulsions vers EasyDriver). (8 impulsions = 1/100 mm)
  137. //long x1, y1; // position à atteindre en nb/impulsions moteur (plus exactement impulsions vers EasyDriver). (8 impulsions = 1/100 mm)
  138. long d_impuls_x, d_impuls_y; // distances à parcourir en nb d'impulsions
  139.  
  140. /*
  141. Mémorisation numérique du tracé pour contôle sans tracé physique:
  142. la fréquence max envoyée aux cartes EasyDriver en vitesse rapide est = 2500Hz
  143. Si l'on veut mémoriser toutes les positions parcourues, chaque position étant définie par 2 x 4 octets = 8 octets,
  144. il faut 2500*8 = 20 000 octets/s (=160 kbits/s) (on voit déjà que la transmission à 115200 bauds ne suffit pas si l'on veut envoyer le résultat au PC en temps réel)
  145. si on veut mémoriser 1/2h de tracé, il faut 20 kB/s * 1800s = 36MB. -> la RAM du Mega2560 = 8kB soit 4500 fois moins !!! juste de quoi mémoriser 0,4s de tracé.
  146. utiliser un processeur ARM ? un Raspberry Pi2 quad core ?
  147. J'envisage une acquisition externe par un second ATmega connecté sur les sorties des signaux, qui mémoriserait dans une SDcard.
  148. */
  149.  
  150. long diff_x;
  151. long diff_y;
  152. int16_t erreur_X = 0; // en nb d'impulsions. Peut varier entre -200 et +200 (pour 0 à 360° d'angle moteur). Doit être = 0 si pas d'erreur.
  153. int16_t erreur_Y = 0; // en nb d'impulsions. Peut varier entre -200 et +200 (pour 0 à 360° d'angle moteur). Doit être = 0 si pas d'erreur.
  154.  
  155. uint8_t largeur_plume = 5;
  156.  
  157. uint8_t digits_X[4];
  158. uint8_t digits_Y[4];
  159. uint8_t n;
  160. uint16_t nb_operations = 0;
  161.  
  162. uint8_t goto_en_cours_xy = 0; // dans le plan horizontal
  163. uint8_t goto_en_cours_z = 0; // concerne la hauteur de la plume
  164.  
  165. uint8_t raz_demande = 0;
  166. uint8_t acceleration_permise = 0;
  167. char msg_ret_gto[3]; // permet à la fonction goto d'envoyer plusieurs types de message en retour vers le PC, suivant la nature de son appel
  168.  
  169.  
  170. int16_t table_sinus[1024];
  171.  
  172. int8_t data_valide = 0;
  173.  
  174.  
  175. void init_ports (void) // ports perso
  176. // 0 = entree, 1=sortie ; les 1 sur les pins en entrees activent les R de Pull Up (tirage à VCC)
  177. {
  178.  
  179.  
  180. PORTA = 0b00000000;
  181. DDRA = 0b11111111; // affichage LCD (ecran Nokia 5110) sur bits 0..4 + LEDS + Buzzer
  182.  
  183. PORTB = 0b00000000;
  184. DDRB = 0b11111111; // PB4 = OC2A = sortie PWM du Timer2 utilisé pour piloter la luminosité du laser
  185.  
  186. PORTC = 0b00000000;
  187. DDRC = 0b11111111; // PC0 et PC1 -> enables des moteurs ; PC6 et PC7 -> LEDS blanches stroboscopiques
  188.  
  189. PORTD = 0b00000000;
  190. DDRD = 0b11111100; // PD0 = entrée (=INT0) ; PD1 = entrée (=INT1)
  191.  
  192. PORTG = 0b00000000;
  193. DDRG = 0b11111111;
  194.  
  195. PORTK= 0b00000000; // ne PAS activer les R de Pull Up (tirage à VCC) sinon risque de ne pas detecter le rayon IR
  196. DDRK = 0b11100000; // entré des fin de course
  197.  
  198. PORTL= 0b00000000;
  199. DDRL = 0b11111111; // commandes des 2 moteurs (X, Y), de la diode laser et sortie PWM pour servomoteur(PL4)
  200. }
  201.  
  202.  
  203.  
  204. /**
  205. Info pour le Timer2 (8bits) : (Pb : on utilise ici les timers 3 et 4 (16 bits) )
  206. 20.4.2 (page:172 du datasheet ATmega2560)
  207.  
  208. Clear Timer on Compare Match (CTC) Mode
  209. In Clear Timer on Compare or CTC mode (WGM22:0 = 2), the OCR2A Register is used to manipulate the counter
  210. resolution. In CTC mode the counter is cleared to zero when the counter value (TCNT2) matches the OCR2A. The
  211. OCR2A defines the top value for the counter, hence also its resolution. This mode allows greater control of the
  212. compare match output frequency
  213. **/
  214.  
  215. /**
  216. Pour les timers 16 bits(3 et 4...) voir le tableau p:145
  217. Il faut mettre le bit WGMn2 à 1
  218. Pb: où est ce bit ???
  219. panique à bord...
  220. Voir le grand tableau "Register Summary" p:399
  221. ce qui permet de le localiser dans le registre TCCRnB - voir page 156
  222. **/
  223.  
  224.  
  225. /**
  226. void int_EXT_setup()
  227. {
  228. // voir 15. External Interrupts p:109
  229.  
  230.  
  231. // EICRA |= 0b00001010; // bits[1,0] = 10 -> int0 sur front descendant; bits[3,2] = 10 -> int1 sur front descendant; - p: 110
  232. // EICRA |= 0b00000101; // bits[1,0] = 10 -> int0 ; bits[3,2] = 10 -> int1 ; Any edge of INTn generates asynchronously an interrupt request - p: 110
  233. EICRA |= 0b00001111; //The rising edge of INTn generates asynchronously an interrupt request
  234. //je choisis "Any edge of INTn generates asynchronously an interrupt request" pour éviter de déclencher sur le mauvais sur rebonds.
  235. EIMSK |= 0b00000011; // bit[0]=1 -> INT0 enable ; bit[1]=1 -> INT1 enable - parag 15.2.3 EIMSK - p:111
  236. }
  237. **/
  238.  
  239.  
  240.  
  241. void timer2_setup()
  242. {
  243. // génère un signal PWM (luminosité du laser) - sortie sur PB4 (OC2A)
  244.  
  245. cli();
  246. //mode Fast PWM - voir p:184
  247. // WGM2 [2,1,0] = 011
  248. // WGM2[1,0] sont les bits [1,0] du registre TCCR2A - voir p:182
  249. /**
  250. Il faut mettre le bit WGM22 à 0
  251. Pb: où est ce bit ??? panique à bord...
  252. Voir le grand tableau "Register Summary" p:401
  253. ce qui permet de le localiser dans le registre TCCR2B et nous renvoie à la page 185
  254. WGM22 est donc le bit 3 du registre TCC2B
  255. * */
  256. // COM2A[1,0] = 1,0 - voir p:183 non inverting mode
  257. // COM2A[1,0] sont les bits [7,6] de TCCR2A - p:401 et p:182
  258. // sortie du signal sur pin OC2A ( pin PB4 = pin 23 du Mega2560)
  259. TCCR2A |= 0b10000011;
  260. TCCR2B = 0b11110010; // bit WGM22=1 et prescaler = 1/8 voir Table 20-9. (Clock Select Bit Description) p:185
  261.  
  262. OCR2A = 50;
  263. sei();
  264. }
  265.  
  266.  
  267.  
  268. void timer3_setup()
  269. // Pour générer les pas du moteur1 (axe X = déplacements de la table)
  270. {
  271. cli();
  272. TIMSK3 = 0b00000010; // Bit 1 – OCIE3A: Timer/Counter1, Output Compare A Match Interrupt Enable - p:161
  273.  
  274. TCCR3A = 0b00000000; // p:154
  275. TCCR3B = 0b00001010; // prescaler = 1/8 - CTC mode - p:156 et tableau p:157
  276.  
  277. OCR3A = ocr3a_depart; // compare match register - valeur la plus faible possible afin d'augmenter la fréquence primaire
  278. sei();
  279. }
  280.  
  281.  
  282. void timer4_setup()
  283. {
  284. // Pour générer les pas du moteur2 ((axe Y = déplacements ddu laser sur le portique)
  285.  
  286. cli();
  287. TIMSK4 = 0b00000010; // Bit 1 – OCIE4A: Timer/Counter3, Output Compare A Match Interrupt Enable
  288.  
  289. TCCR4A = 0b00000000; // p:154
  290. TCCR4B = 0b00001010; // prescaler = 1/8 - CTC mode - p:156 et tableau p:157
  291.  
  292. OCR4A = ocr4a_depart; // compare match register - valeur la plus faible possible afin d'augmenter la fréquence primaire
  293. sei();
  294. }
  295.  
  296.  
  297. void timer5_setup()
  298. {
  299. //pour piloter le servomoteur (type micro servo C141 Graupner) (hauteur du laser = réglage de la focalisation -> largeur du trait)
  300. // génère un signal PWM - sortie sur PL4 (OC5B)
  301. // mode15 = fast PWM - WGM[3,2,1,0] = 1111 - voir p:145
  302. // WGM[1,0] sont les bits [1,0] de TCCR5A - p:154
  303. // WGM[3,2] sont les bits [4,3] de TCCR5B - p:156
  304.  
  305. cli();
  306.  
  307. TIMSK5 = 0b00000000; // p:162
  308. // TCCR5A = (TCCR5A & 0xCC) | _BV(COM5B1) | _BV(WGM10); // fast PWM 8-bit, sortie sur pin OC5b
  309.  
  310. //mode7 = Fasr PWM, 10 bits -> WGM[2,1,0] = 111
  311. TCCR5A = 0b00100011;
  312. // bits[5,4] = COM5B[1,0] = compare output mode = 10 select-> canal B (sortie sur pin OC5b) - p:144, 146, 154, 155
  313. // bits[1,0] = WGM[1,0] = 11 -> Clear OCnA/OCnB/OCnC on compare match, set OCnA/OCnB/OCnC at BOTTOM (non-inverting mode) - p:154 et p:155 - et tableau des modes p:145
  314.  
  315. TCCR5B = 0b00001100; // prescaler = 1/256 - tableau p:157
  316. // bit[4,3] = WGM[3,2]
  317. // bit[2,1,0] = CS[2,1,3] (clock selection)
  318.  
  319. TCCR5C = 0;
  320.  
  321. OCR5B = 140; // largeur impulsion min=50 ; max=145 -> [0,8ms..2,4ms]
  322. sei();
  323. }
  324.  
  325.  
  326.  
  327. void beep()
  328. {
  329. uint8_t n;
  330. for (n=0; n<255; n++)
  331. {
  332. PORTA ^= bit_buzzer;
  333. _delay_us(300);
  334. }
  335. }
  336.  
  337.  
  338. void beep2()
  339. {
  340. uint8_t n;
  341. for (n=0; n<255; n++)
  342. {
  343. PORTA ^= bit_buzzer;
  344. _delay_us(200);
  345. }
  346. }
  347.  
  348.  
  349. void clic()
  350. {
  351. uint8_t n;
  352. for (n=0; n<10; n++)
  353. {
  354. PORTA ^= bit_buzzer;
  355. _delay_us(50);
  356. }
  357. }
  358.  
  359.  
  360. void n_beep(uint8_t n)
  361. {
  362. uint8_t i;
  363. for (i=0; i<n; i++)
  364. {
  365. beep2();
  366. _delay_ms(200);
  367. }
  368. }
  369.  
  370.  
  371.  
  372.  
  373. void laser_on()
  374. {
  375. if (blocage_laser == 0)
  376. {
  377. PORTL |= bit_LASER;
  378. etat_laser=1;
  379. }
  380. }
  381.  
  382.  
  383. void laser_off()
  384. {
  385. PORTL &= ~bit_LASER;
  386. etat_laser=0;
  387. }
  388.  
  389.  
  390. void laser_bloque()
  391. {
  392. laser_off();
  393. etat_laser=0;
  394. blocage_laser=1;
  395. }
  396.  
  397. void laser_debloque()
  398. {
  399. laser_off();
  400. etat_laser=0;
  401. blocage_laser=0;
  402. }
  403.  
  404.  
  405. void laser_set_lum(uint8_t lum)
  406. {
  407. OCR2A = lum;
  408. }
  409.  
  410.  
  411. void laser_set_Z(uint16_t z)
  412. {
  413. OCR5B = z;
  414. }
  415.  
  416.  
  417.  
  418. uint16_t calcul_rapide_periode_x(uint16_t base)
  419. {
  420. float px;
  421. px = 1000.0 * (float)base * fpx;
  422. if (px > 0xFFFF) {px = 0xFFFF;}
  423. return (int) px;
  424. }
  425.  
  426.  
  427. uint16_t calcul_rapide_periode_y(uint16_t base)
  428. {
  429. float py;
  430. py = 1000.0 * (float)base * fpy;
  431. if (py > 0xFFFF) {py = 0xFFFF;}
  432. return (int) py;
  433. }
  434.  
  435.  
  436. /**
  437. ISR(INT0_vect )
  438. {
  439. // interruption externe déclenchée par le disque à fente sur l'axe moteur X + opto-switch
  440. PORTA ^= bit_LED_verte;
  441. if(moteur1.get_sens() == 0) { erreur_X = 226 - moteur1.position_impuls % 400; } else { erreur_X = 189 - moteur1.position_impuls % 400; } // 18 = largeur fente...
  442. }
  443. **/
  444.  
  445.  
  446. /**
  447. ISR(INT1_vect )
  448. {
  449. // interruption externe déclenchée par le disque à fente sur l'axe moteur Y + opto-switch
  450. PORTA ^= bit_LED_verte;
  451. if(moteur2.get_sens() == 0) { erreur_Y = 211 - moteur2.position_impuls % 400; } else { erreur_Y = 198 - moteur2.position_impuls % 400; } // 18 = largeur fente...
  452. }
  453. **/
  454.  
  455.  
  456.  
  457.  
  458.  
  459. /**
  460. Le code exécuté par la RA1 du flag 'pas_mot1_demande' sera executé dans la boucle principale et/ou dans les boucles des goto_xy, mais pas dans l'interruption elle-même
  461. ceci afin d'éviter les problèmes de collision d'interruption
  462. Ces interruptions (qui codent des 'cli durant leur exécution' interdisant l'autre int) peuvent ainsi rendre la main le plus rapidement possible
  463. voir les fonctions 'pas_mot1' et 'pas_mot2'
  464. **/
  465.  
  466. ISR(TIMER3_COMPA_vect) // MOTEUR 1
  467. {
  468. if (moteur1.get_marche() == 1) { pas_mot1_demande +=1; }
  469. }
  470.  
  471.  
  472. ISR(TIMER4_COMPA_vect) // MOTEUR 2
  473. {
  474. if (moteur2.get_marche() == 1) { pas_mot2_demande +=1; }
  475. }
  476.  
  477.  
  478.  
  479. /*
  480. ISR(TIMER5_COMPA_vect)
  481. {
  482.  
  483.  
  484. }
  485. */
  486.  
  487.  
  488. int calcul_periode_deplacement(long x1_i, long y1_i, long x2_i, long y2_i)
  489. {
  490. float dx, dy, dx2, dy2, v0, px, py, dt; // vx, vy,
  491.  
  492. v0=1;
  493.  
  494. if (x2_i >= x1_i) { dx=x2_i-x1_i; moteur1.set_sens(0); } else { dx=x1_i-x2_i; moteur1.set_sens(1); }
  495. if (y2_i >= y1_i) { dy=y2_i-y1_i; moteur2.set_sens(0); } else { dy=y1_i-y2_i; moteur2.set_sens(1); }
  496.  
  497. if ( (x2_i == x1_i) && (y2_i == y1_i) ) // les points sont confondus, le ds est nul
  498. {
  499. OCR3A = ocr3a_depart;
  500. OCR4A = ocr4a_depart;
  501. return(1); // pour éviter les divisions par zéro plus bas
  502. }
  503.  
  504. dx2=dx*dx; // dx²
  505. dy2=dy*dy; // dy²
  506. dt = (sqrt(dx2+dy2))/v0;
  507.  
  508. //vx= dx/dt;
  509. //vy= dy/dt;
  510.  
  511. px = 600000.0 * ((float) mm_par_s_max / (float) moteur1.impuls_par_mm) * dt/dx; // 400000.0
  512. py = 600000.0 * ((float) mm_par_s_max / (float) moteur2.impuls_par_mm) * dt/dy; // 400000.0
  513.  
  514. if (px > 0xFFFF) {px = 0xFFFF;}
  515. if (py > 0xFFFF) {py = 0xFFFF;}
  516.  
  517. OCR3A = (int) px;
  518. OCR4A = (int) py;
  519.  
  520. return(0);
  521. }
  522.  
  523.  
  524.  
  525. void envoi_FDC()
  526. {
  527. Serial.println("FIN DE COURSE");
  528. }
  529.  
  530.  
  531.  
  532. int lecture_fins_de_course_xy() // xy, et pas z
  533. {
  534. // lecture de l'etat des capteurs (optiques) de fin de course et activations des blocages logiciels correspondants
  535. etat_FC_x1 = PINK & moteur1.bit_finCourse1; if (etat_FC_x1 != 0) { moteur1.set_stop_sens1(1); } else { moteur1.set_stop_sens1(0); }
  536. etat_FC_x2 = PINK & moteur1.bit_finCourse2; if (etat_FC_x2 != 0) { moteur1.set_stop_sens2(1); } else { moteur1.set_stop_sens2(0); }
  537.  
  538. etat_FC_y1 = PINK & moteur2.bit_finCourse1; if (etat_FC_y1 != 0) { moteur2.set_stop_sens1(1); } else { moteur2.set_stop_sens1(0); }
  539. etat_FC_y2 = PINK & moteur2.bit_finCourse2; if (etat_FC_y2 != 0) { moteur2.set_stop_sens2(1); } else { moteur2.set_stop_sens2(0); }
  540.  
  541. //etat_FC_z1 = PINK & moteur3.bit_finCourse1; if (etat_FC_z1 != 0) { moteur3.set_stop_sens2(1); } else { moteur3.set_stop_sens2(0); }
  542.  
  543. //if ((etat_FC_x1 != 0)||(etat_FC_x2 != 0)||(etat_FC_y1 != 0)||(etat_FC_y2 != 0)||(etat_FC_z1 != 0))
  544. if ((etat_FC_x1 != 0)||(etat_FC_x2 != 0)||(etat_FC_y1 != 0)||(etat_FC_y2 != 0))
  545. {
  546. if ( ((PORTA & 32) == 0) && (fdc_envoi_fait == 0) )
  547. {
  548. envoi_FDC();
  549. fdc_envoi_fait = 1;
  550. } // si la led était éteinte, on signale le changement d'état au soft Qt4 ;
  551. // REMARQUE on lit l'état d'un port conf en sortie, ce qui est tout à fait possible, on lit ce qu'on y a écrit précédemment...
  552.  
  553. PORTA |= bit_LED_rouge;
  554. return 1;
  555. }
  556. else
  557. {
  558. PORTA &= ~bit_LED_rouge;
  559. fdc_envoi_fait == 0;
  560. }
  561. return 0;
  562. }
  563.  
  564.  
  565.  
  566. void pas_mot1()
  567. {
  568. // la fréquence max du signal envoyé à la carte EasyDriver (lors d'un goto_origine par exemple) est de 2500Hz.
  569.  
  570. if (acceleration_permise ==1)
  571. {
  572. if (OCR3A > ocr3a_min)
  573. {
  574. compteur1++;
  575. compteur1=0;
  576. OCR3A -=1;
  577. }
  578. }
  579. cli();
  580. moteur1.step();
  581. sei();
  582.  
  583. //if ((goto_en_cours_xy == 1) && ( moteur1.get_compteur_impuls() >= d_impuls_x )) {moteur1.set_marche(0); } // ARRET
  584. //if (moteur1.position_impuls % 400 == 0 ) {PORTC |= bit_LED_blanche1; } else {PORTC &= ~bit_LED_blanche1; }
  585.  
  586. }
  587.  
  588.  
  589. void pas_mot2()
  590. {
  591. if (acceleration_permise ==1)
  592. {
  593. if (OCR4A > ocr4a_min)
  594. {
  595. compteur2++;
  596. compteur2=0;
  597. OCR4A -=1;
  598. }
  599. }
  600. cli();
  601. moteur2.step();
  602. sei();
  603.  
  604. //if ((goto_en_cours_xy == 1) && ( moteur2.get_compteur_impuls() >= d_impuls_y )) {moteur2.set_marche(0); }
  605. //if (moteur2.position_impuls % 400 == 0 ) {PORTC |= bit_LED_blanche2; } else {PORTC &= ~bit_LED_blanche2; }
  606. }
  607.  
  608.  
  609.  
  610. void envoi_PRET(long xi, long yi)
  611. {
  612. // ATTENTION : cette fonction envoi le signal qui déclenche en retour l'envoi de la commande suivante par le prog Qt4.
  613. // Donc ne pas l'appeler inconsidérement ! en particulier pas avant que l'éxécution de la commande en cours soit terminée.
  614. // en particulier pas dans une sous-fonction de la commande en cours (comme "laser_set_lum" par exemple)
  615.  
  616. unsigned char r ;
  617. // char msg_position[20]="0123456789012345678"; // 19 car max pour un string20
  618. char msg_position[20]="X+0000Y+0000 PRET "; // 19 car max pour un string20
  619. char signe_x, signe_y;
  620.  
  621. int i, fdc;
  622. long valeur;
  623.  
  624. fdc = lecture_fins_de_course_xy();
  625.  
  626. msg_position[0]='X';
  627. if (xi >=0) {valeur = xi/8; signe_x ='+';} else {valeur = -xi/8; signe_x ='-';}
  628. msg_position[1]=signe_x;
  629. for (i=0; i<4; i++)
  630. {
  631. r=48 + valeur % 10; // modulo (reste de la division)
  632. valeur /= 10; // quotient
  633. msg_position[5-i]=r;
  634. }
  635.  
  636. msg_position[7]='Y';
  637. if (yi >=0) {valeur = yi/8; signe_y ='+';} else {valeur = -yi/8; signe_y ='-';}
  638. msg_position[7]=signe_y;
  639. for (i=0; i<4; i++)
  640. {
  641. r=48 + valeur % 10; // modulo (reste de la division)
  642. valeur /= 10; // quotient
  643. msg_position[11-i]=r;
  644. }
  645.  
  646. if (fdc == 1)
  647. {
  648. msg_position[11]=' '; msg_position[13]='F'; msg_position[14]='I'; msg_position[15]='N'; msg_position[16]='C' ; msg_position[17]=' ';
  649. // c.à.d : envoi "X+0000Y+0000 FINC " (= fin de course)
  650. }
  651.  
  652. clic();
  653.  
  654. nb_operations ++;
  655. _delay_ms(100);
  656. Serial.println("-");
  657. _delay_ms(100);
  658. Serial.println(msg_position);
  659. }
  660.  
  661.  
  662. void envoi_diff_xy()
  663. {
  664. unsigned char r ;
  665. // char msg_erreur[20]="0123456789012345678"; // 19 car max pour un string20
  666. char msg_diff[20]="difX= 000 difY= 000"; // 19 car max pour un string20
  667. char signe_x, signe_y;
  668.  
  669. int i;
  670. uint16_t valeurX, valeurY;
  671.  
  672. valeurX = abs(diff_x);
  673. if (diff_x >=0) {valeurX = diff_x; signe_x ='+';} else {valeurX = -diff_x; signe_x ='-';}
  674. msg_diff[5]=signe_x;
  675. for (i=0; i<3; i++)
  676. {
  677. r=48 + valeurX % 10; // modulo (reste de la division)
  678. valeurX /= 10; // quotient
  679. msg_diff[8-i]=r;
  680. }
  681.  
  682. valeurY = abs(diff_y);
  683. if (diff_y >=0) {valeurY = diff_y; signe_y ='+';} else {valeurY = -diff_y; signe_y ='-';}
  684. msg_diff[15]=signe_y;
  685. for (i=0; i<3; i++)
  686. {
  687. r=48 + valeurY % 10; // modulo (reste de la division)
  688. valeurY /= 10; // quotient
  689. msg_diff[18-i]=r;
  690. }
  691.  
  692. _delay_ms(50);
  693. Serial.println(msg_diff);
  694.  
  695. }
  696.  
  697.  
  698.  
  699. void envoi_pos()
  700. {
  701. unsigned char r ;
  702. // char msg_erreur[20]="0123456789012345678"; // 19 car max pour un string20
  703. char msg_pos[20]="-------------------"; // 19 car max pour un string20
  704.  
  705. int i;
  706. long valeurX, valeurY;
  707.  
  708. valeurX = moteur1.position_impuls;
  709. for (i=0; i<10; i++)
  710. {
  711. r=48 + valeurX % 10; // modulo (reste de la division)
  712. valeurX /= 10; // quotient
  713. msg_pos[10-i]=r;
  714. }
  715. /*
  716. valeurY = moteur2.position_impuls;
  717. for (i=0; i<3; i++)
  718. {
  719. r=48 + valeurY % 10; // modulo (reste de la division)
  720. valeurY /= 10; // quotient
  721. msg_pos[18-i]=r;
  722. }
  723. */
  724. _delay_ms(50);
  725. Serial.println(msg_pos);
  726.  
  727. }
  728.  
  729.  
  730. void test_fin_goto(long xi, long yi)
  731. {
  732. //uint8_t n;
  733. // teste si la destination est atteinte, si oui stoppe le ou les moteurs.
  734. // la valeur du "msg_ret_gto" déterminera plus précisément le comportement de cette fonction
  735.  
  736. // if ((goto_en_cours_xy == 1) && ( moteur1.get_compteur_impuls() >= d_impuls_x )) {moteur1.set_marche(0); }
  737. // if ((goto_en_cours_xy == 1) && ( moteur2.get_compteur_impuls() >= d_impuls_y )) {moteur2.set_marche(0); }
  738.  
  739.  
  740. if (moteur1.get_sens() == 0) { if (moteur1.position_impuls >= xi) { moteur1.set_marche(0); } }
  741. else { if (moteur1.position_impuls <= xi) { moteur1.set_marche(0); } }
  742.  
  743. if (moteur2.get_sens() == 0) { if (moteur2.position_impuls >= yi) { moteur2.set_marche(0); } }
  744. else { if (moteur2.position_impuls <= yi) { moteur2.set_marche(0); } }
  745.  
  746.  
  747. if ((goto_en_cours_xy == 1) && (moteur1.get_marche() == 0) && (moteur2.get_marche() == 0) )
  748. { // FIN DU GOTO si LES DEUX coordonnées x ET y sont atteintes.
  749.  
  750. goto_en_cours_xy = 0;
  751. laser_off();
  752.  
  753.  
  754. //POUR TEST ***********************************************************
  755. // envoi_pos(); // test ok, les coordonnées sont bonne, pourtant il manque des impulsion lors de déplacements doubles (les 2 mot en marche) vers l'origine
  756. //**********************************************************************
  757.  
  758. x0 = moteur1.position_impuls;
  759. y0 = moteur2.position_impuls;
  760.  
  761. if (raz_demande)
  762. {
  763. moteur1.RAZ_pos();
  764. moteur2.RAZ_pos();
  765. x0=0; y0=0;
  766. raz_demande = 0;
  767. }
  768.  
  769. //----------------------------------------------------------------------
  770. if ( (msg_ret_gto[0]=='T') && (msg_ret_gto[1]=='S') && (msg_ret_gto[2]=='G') ) // fin du tracé de segment
  771. {
  772. laser_off();
  773.  
  774. _delay_ms(10); // frein (immobilisation le courant est maintenu sans impulsions)
  775. moteur1.disable(); // stop courant
  776. moteur2.disable();
  777.  
  778. envoi_PRET(x0, y0);
  779. }
  780. //----------------------------------------------------------------------
  781. if (msg_ret_gto[0]=='L') // fin du tracé demandé en local
  782. {
  783. _delay_ms(10); // frein (immobilisation, le courant est maintenu sans impulsions)
  784. moteur1.disable(); // stop courant
  785. moteur2.disable();
  786. }
  787. //----------------------------------------------------------------------
  788. if (msg_ret_gto[0]=='S') // fin du tracé de segment sans delai de freinage (pour tracer les nombreux segments des pastilles rondes)
  789. {
  790. ;
  791. }
  792.  
  793. //----------------------------------------------------------------------
  794. if ( (msg_ret_gto[0]=='-') && (msg_ret_gto[1]=='X') && (msg_ret_gto[2]=='Y') )
  795. {
  796. _delay_ms(5);
  797. envoi_PRET(x0, y0);
  798. } // fin du goto plume levée
  799.  
  800. //----------------------------------------------------------------------
  801. if ( (msg_ret_gto[0]=='Z') && (msg_ret_gto[1]=='0') && (msg_ret_gto[2]=='0') ) // fin du goto ZERO (origine XY)
  802. {
  803. laser_off();
  804. n_beep(3);
  805. moteur1.disable(); // stop courant
  806. moteur2.disable();
  807. envoi_PRET(x0, y0);
  808. }
  809. //----------------------------------------------------------------------
  810. if (msg_ret_gto[0]=='P') // fin du tracé d'une pastille
  811. {
  812. laser_off();
  813.  
  814. _delay_ms(20); // frein (immobilisation le courant est maintenu sans impulsions)
  815. moteur1.disable(); // stop courant
  816. moteur2.disable();
  817. envoi_PRET(x0, y0);
  818. }
  819. }
  820.  
  821. }
  822.  
  823.  
  824.  
  825.  
  826. void goto_xy(long xi, long yi)
  827. {
  828. if (simulation == 1)
  829. {
  830. _delay_ms(50);
  831. return;
  832. }
  833. acceleration_permise = 0;
  834. int r = calcul_periode_deplacement(x0, y0, xi, yi); // fixe également le sens de rotation
  835.  
  836. // remarque :labs est la version pour les entiers "long" de abs.
  837. // d_impuls_x = labs(xi-x0); // longueur du segment en x. Non signé. servira à détecter la fin du trajet. précis à 1 impulsion près, soit (1/100mm)/8
  838. // d_impuls_y = labs(yi-y0); // longueur du segment en y. Non signé. servira à détecter la fin du trajet.
  839.  
  840. // moteur1.RAZ_compteur_impuls(); // servira à mesurer la distance parcourue exactement à une impulsion près.
  841. // moteur2.RAZ_compteur_impuls();
  842.  
  843. moteur1.enable();
  844. moteur2.enable();
  845. moteur1.set_marche(1);
  846. moteur2.set_marche(1);
  847.  
  848. goto_en_cours_xy = 1;
  849.  
  850. while (goto_en_cours_xy == 1)
  851. {
  852. lecture_fins_de_course_xy();
  853. if (pas_mot1_demande > 0) {pas_mot1(); pas_mot1_demande--; _delay_us(1);} // rotation de 1 pas du moteur1 (x)
  854. if (pas_mot2_demande > 0) {pas_mot2(); pas_mot2_demande--; _delay_us(1);} // rotation de 1 pas du moteur2 (y)
  855. test_fin_goto(xi, yi); // le goto s'arretera lorsque position_impuls == (xi, yi)
  856. }
  857. }
  858.  
  859.  
  860.  
  861. void X_plus_mm(long valeur)
  862. {
  863. long n, n_max;
  864.  
  865. n_max = moteur1.impuls_par_mm * valeur;
  866.  
  867. moteur1.set_sens(0);
  868. moteur1.enable();
  869. for (n=0; n<n_max; n++)
  870. {
  871. lecture_fins_de_course_xy();
  872. moteur1.step();
  873. _delay_us(1200);
  874. }
  875. moteur1.disable();
  876.  
  877. x0 = moteur1.position_impuls;
  878. y0 = moteur2.position_impuls;
  879. envoi_PRET(x0, y0);
  880. }
  881.  
  882.  
  883. void X_moins_mm(long valeur)
  884. {
  885. long n, n_max;
  886.  
  887. n_max = moteur1.impuls_par_mm * valeur;
  888.  
  889. moteur1.set_sens(1);
  890. moteur1.enable();
  891. for (n=0; n<n_max; n++)
  892. {
  893. lecture_fins_de_course_xy();
  894. moteur1.step();
  895. _delay_us(1200);
  896. }
  897. moteur1.disable();
  898.  
  899. x0 = moteur1.position_impuls;
  900. y0 = moteur2.position_impuls;
  901. envoi_PRET(x0, y0);
  902. }
  903.  
  904.  
  905. void rotX_n_pas(long valeur)
  906. {
  907. long n, n_max;
  908.  
  909. n_max = valeur;
  910. moteur1.set_sens(1);
  911. moteur1.enable();
  912.  
  913. for (n=0; n<n_max; n++)
  914. {
  915. lecture_fins_de_course_xy();
  916. moteur1.step();
  917. _delay_us(1200);
  918. }
  919. moteur1.disable();
  920. }
  921.  
  922.  
  923. void rotY_n_pas(long valeur)
  924. {
  925. long n, n_max;
  926.  
  927. n_max = valeur;
  928.  
  929. moteur2.set_sens(0);
  930. moteur2.enable();
  931.  
  932. for (n=0; n<n_max; n++)
  933. {
  934. lecture_fins_de_course_xy();
  935. moteur2.step();
  936. _delay_us(1800);
  937. }
  938. moteur2.disable();
  939. }
  940.  
  941.  
  942. void Y_plus_mm(uint8_t valeur)
  943. {
  944. uint16_t n, n_max;
  945.  
  946. n_max = moteur2.impuls_par_mm * valeur;
  947.  
  948. moteur2.set_sens(0);
  949. moteur2.enable();
  950. for (n=0; n<n_max; n++)
  951. {
  952. lecture_fins_de_course_xy();
  953. moteur2.step();
  954. _delay_us(1200);
  955. }
  956. moteur2.disable();
  957.  
  958. x0 = moteur1.position_impuls;
  959. y0 = moteur2.position_impuls;
  960. _delay_ms(50);
  961. envoi_PRET(x0, y0);
  962.  
  963. }
  964.  
  965.  
  966. void Y_moins_mm(uint8_t valeur)
  967. {
  968. uint16_t n, n_max;
  969.  
  970. n_max = moteur2.impuls_par_mm * valeur;
  971.  
  972. moteur2.set_sens(1);
  973. moteur2.enable();
  974. for (n=0; n<n_max; n++)
  975. {
  976. lecture_fins_de_course_xy();
  977. moteur2.step();
  978. _delay_us(1200);
  979. }
  980. moteur2.disable();
  981.  
  982. x0 = moteur1.position_impuls;
  983. y0 = moteur2.position_impuls;
  984. _delay_ms(50);
  985. envoi_PRET(x0, y0);
  986.  
  987. }
  988.  
  989.  
  990.  
  991.  
  992.  
  993.  
  994. void envoi_nombre(uint16_t valeur) // valeur = 0..9999
  995. {
  996. // char msg_nbr[20]="0123456789012345678"; // 19 car max pour un string20
  997. char msg_nbr[20]=" "; // 19 car max pour un string20
  998.  
  999. uint8_t i, r;
  1000. for (i=0; i<4; i++)
  1001. {
  1002. r=48 + valeur % 10; // modulo (reste de la division)
  1003. valeur /= 10; // quotient
  1004. msg_nbr[5-i]=r;
  1005. }
  1006. _delay_ms(50);
  1007. Serial.println(msg_nbr);
  1008. }
  1009.  
  1010.  
  1011. /**
  1012. void envoi_texte(char *txt, int lg) // txt = 19 caract max
  1013. {
  1014. // char msg_nbr[20]="0123456789012345678"; // 19 car max pour un string20
  1015. char msg_txt[20]=" "; // 19 car max pour un string20
  1016.  
  1017. uint8_t i;
  1018. for (i=0; i<=lg; i++)
  1019. {
  1020. msg_txt[i]=txt[i];
  1021. }
  1022. Serial.println(msg_txt);
  1023. _delay_ms(300);
  1024. }
  1025. **/
  1026.  
  1027.  
  1028.  
  1029. void ajout_aperture(uint8_t num, char type)
  1030. {
  1031. if (num < 40)
  1032. {
  1033. apertures[num].x = ax;
  1034. apertures[num].y = ay;
  1035. apertures[num].type = type; // "C" ou "R" ou "O"
  1036. }
  1037. envoi_PRET(x0, y0);
  1038.  
  1039. }
  1040.  
  1041.  
  1042.  
  1043. void RAZ_POS()
  1044. {
  1045. moteur1.RAZ_pos();
  1046. moteur2.RAZ_pos();
  1047. x0=0;
  1048. y0=0;
  1049. }
  1050.  
  1051.  
  1052. void STOP()
  1053. {
  1054. laser_off();
  1055. moteur1.set_marche(0);
  1056. moteur2.set_marche(0);
  1057. moteur1.disable(); // stop courant
  1058. moteur2.disable();
  1059. //PORTA &= ~bit_LED_verte;
  1060. goto_en_cours_xy = 0; // le cas échéant...
  1061. envoi_PRET(x0, y0);
  1062. }
  1063.  
  1064.  
  1065.  
  1066. void rotation_2k_pi()
  1067. {
  1068. // fait tourner les axes pour les orienter toujours à 2k pi près (pour test visuel de dérives éventuelles de positions)
  1069. // rappel : 2pi radians = 1 tour = 400 pas.
  1070. long n0;
  1071. long n, n_max;
  1072.  
  1073. n0 = moteur1.position_impuls;
  1074. n_max = 400 - (n0 % 400); // modulo (reste de la division)
  1075.  
  1076. moteur1.set_sens(0);
  1077. moteur1.enable();
  1078. for (n=0; n<n_max; n++)
  1079. {
  1080. lecture_fins_de_course_xy();
  1081. moteur1.step();
  1082. _delay_us(2000);
  1083. }
  1084. moteur1.disable();
  1085.  
  1086. n0 = moteur2.position_impuls;
  1087. n_max = 400- (n0 % 400); // modulo (reste de la division)
  1088.  
  1089. moteur2.set_sens(0);
  1090. moteur2.enable();
  1091. for (n=0; n<n_max; n++)
  1092. {
  1093. lecture_fins_de_course_xy();
  1094. moteur2.step();
  1095. _delay_us(2000);
  1096. }
  1097. moteur2.disable();
  1098. beep();
  1099. x0 = moteur1.position_impuls;
  1100. y0 = moteur2.position_impuls;
  1101. _delay_ms(500); // temps d'observation visuelle.
  1102. }
  1103.  
  1104.  
  1105.  
  1106.  
  1107. //GHA0195YA0600
  1108. //SXA0572YA0381XB1143YB0381D27
  1109. void decode_xy_A(long *x, long *y) // retourne des valeurs numériques (en 1/100mm) de la partie numerique qui suit le "XA" et le "YA" dans le message recu
  1110. {
  1111. for (n=0; n<4; n++)
  1112. {
  1113. digits_X[n]=data_in[n+3]-48;
  1114. digits_Y[n]=data_in[n+9]-48;
  1115. }
  1116. *x =(long) 8 * ((long)digits_X[3]+(long)10*digits_X[2]+(long)100*digits_X[1]+(long)1000*digits_X[0]); // 8 -> parceque 8 imps /100eme de mm
  1117. *y =(long) 8 * ((long)digits_Y[3]+(long)10*digits_Y[2]+(long)100*digits_Y[1]+(long)1000*digits_Y[0]);
  1118. }
  1119.  
  1120.  
  1121.  
  1122. //SXA0572YA0381XB1143YB0381D27
  1123. void decode_xy_B(long *x, long *y) // retourne des valeurs numériques (en nb impulsions) de la partie numerique qui suit le "XB" et le "YB"
  1124. {
  1125. for (n=0; n<4; n++)
  1126. {
  1127. digits_X[n]=data_in[n+15]-48;
  1128. digits_Y[n]=data_in[n+21]-48;
  1129. }
  1130. *x = (long) 8 * (digits_X[3]+(long)10*digits_X[2]+(long)100*digits_X[1]+(long)1000*digits_X[0]);
  1131. *y = (long) 8 * (digits_Y[3]+(long)10*digits_Y[2]+(long)100*digits_Y[1]+(long)1000*digits_Y[0]);
  1132. }
  1133.  
  1134.  
  1135. //XN000000
  1136. //LA000000
  1137. void decode_6_digits(long *x) // retourne des valeurs numériques [0..1E6] de la partie numérique qui débute à la position 2
  1138. {
  1139. uint8_t digits[6];
  1140. for (n=0; n<6; n++)
  1141. {
  1142. digits[n]=data_in[n+2]-48;
  1143. }
  1144. *x = digits[5]+(long)10*digits[4]+(long)100*digits[3]+(long)1000*digits[2]+(long)10000*digits[1]+(long)100000*digits[0];
  1145. }
  1146.  
  1147.  
  1148.  
  1149. uint8_t decode_num_aperture()
  1150. {
  1151. /*
  1152.  * Les formats de reception et d'utilisation des apertures sont volontairement similaires :
  1153. MXA1234YA1234XB0000YB0000D12R
  1154. PXA2149YA0411XB0000YB0000D13
  1155. */
  1156. uint8_t num_ap;
  1157.  
  1158. for (n=0; n<2; n++)
  1159. {
  1160. digits_ap[n]= data_in[n+26]-48;
  1161. }
  1162. num_ap = digits_ap[1]+10*digits_ap[0];
  1163. return num_ap;
  1164. }
  1165.  
  1166.  
  1167.  
  1168.  
  1169.  
  1170. void tracer_segment() // trace le segment
  1171. {
  1172.  
  1173. uint16_t d; // largeur x en 1/100mm
  1174. float z1, lum1;
  1175. long xi, yi;
  1176.  
  1177. if (simulation == 1)
  1178. {
  1179. _delay_ms(50);
  1180. envoi_PRET(0,0);
  1181. return;
  1182. }
  1183.  
  1184. decode_xy_A(&xi, &yi); // coorgonnées du début du segment
  1185. d = (apertures[num_aperture].x) /8; // largeur x en 1/100mm le /8 because dimensions memorisées en nb de pas moteur. voir la fonction decode_xy_A() (1/100mm = 8 pas)
  1186.  
  1187. /**
  1188. z = [50..145] . attention z=50 -> presque au contact
  1189. LARGEUR DES PISTES :
  1190. z = 80 -> ultrafines (avec lum = 20) pour d=0
  1191. z = 145 -> 2mm (avec lum = 200) pour d >= 200 (2mm)
  1192. **/
  1193.  
  1194. z1 = 80.0 + (float)d * 65.0/200.0;
  1195. if (z1>145.0) {z1 = 145.0;}
  1196. laser_set_Z((uint16_t)z1);
  1197.  
  1198. /**
  1199. lum = [0..250]
  1200. voir le fichier "platine_laser.ods"
  1201. -pistes ultra-fines -> lum = 20 ou moins (5) dans le cas de pastilles cms proches (circuits intégrés smd)
  1202. -pistes largeur mx (2mm) -> lum = 250 (à voir)
  1203. **/
  1204.  
  1205. lum1 = 20.0 + (z1-80.0) * 3.53; // 3.53 = dy/dx = (250-20)/(145-80) = 230/65;
  1206. if (lum1 < 20.0){lum1 = 20.0;}
  1207. if (lum1 > 250.0) {lum1 = 250.0;}
  1208. laser_set_lum((uint16_t)lum1);
  1209.  
  1210. laser_off();
  1211.  
  1212. msg_ret_gto[0]='L'; // demandé en local
  1213. goto_xy(xi, yi); // GOTO au début du segment
  1214.  
  1215. while (goto_en_cours_xy == 1) { lecture_fins_de_course_xy(); test_fin_goto(xi, yi); } // attend la fin du premier déplacement plume levée vers l'origine du segment
  1216.  
  1217. decode_xy_B(&xi, &yi); // coordonnées de l'extrémité du segment
  1218.  
  1219. msg_ret_gto[0]='T'; msg_ret_gto[1]='S'; msg_ret_gto[2]='G';
  1220.  
  1221. laser_on();
  1222.  
  1223. goto_xy(xi, yi); while (goto_en_cours_xy == 1) { lecture_fins_de_course_xy(); test_fin_goto(xi, yi); } // trace le segment
  1224. laser_off();
  1225.  
  1226. }
  1227.  
  1228.  
  1229.  
  1230. void tracer_pastille_ronde(uint16_t rayon) // rayon en nb d'implulsions
  1231. {
  1232. // par segments de droite juxtaposés
  1233. long centre_x, centre_y;
  1234. long xi, yi;
  1235. float rayon_i; // rayon des cercles concentriques qui constitueront la pastille. rayon_i sera diminué à chaque tour.
  1236. float largeur_trait, rt;
  1237. float x, y;
  1238. uint16_t n, i;
  1239. int8_t stop;
  1240.  
  1241. _delay_ms(50);
  1242. Serial.println("ronde");
  1243.  
  1244. if (simulation == 1)
  1245. {
  1246. _delay_ms(50);
  1247. envoi_PRET(0,0);
  1248. return;
  1249. }
  1250.  
  1251. largeur_trait = 8 * 15.0; // "8*" because 8 pas/mm
  1252.  
  1253. rayon_i = rayon;
  1254. // rt = (8*25) + largeur_trait; // rayon du trou; "8*" because 8 pas/mm
  1255. // rt = 400; // trous trops grands
  1256. rt = 300; // rayon du TROU
  1257.  
  1258. if (rayon < 100 ) {rt=0;} // ne pas percer les très petites pastilles qui sont des raccordements de pistes
  1259.  
  1260. laser_set_Z(80);
  1261. laser_set_lum(10); // 40
  1262.  
  1263. centre_x = x0;
  1264. centre_y = y0;
  1265.  
  1266. n = 0;
  1267. stop = 0;
  1268. while ((n<12) && (stop == 0))
  1269. {
  1270. //d= n * largeur_trait;
  1271. //if (d > (ax/1.5) || d > (ay/1.5))
  1272. if ( (rt>0) && (rayon_i < rt)) { stop=1; }
  1273. else
  1274. {
  1275. for (i=0; i<=180; i++) // tracé d'un cercle_i constitué par 180 minuscules segments
  1276. {
  1277. x = rayon_i * cos(2.0 * M_PI * i / 180.0);
  1278. y = rayon_i * sin(2.0 * M_PI * i / 180.0);
  1279. xi = centre_x + (long) round(x);
  1280. yi = centre_y + (long) round(y);
  1281.  
  1282. msg_ret_gto[0]='S'; // demandé en local et sans frein
  1283.  
  1284. if (i>=1) {laser_on();} // le premier déplacement, lui, du centre vers la circonférence, n'a pas été tracé
  1285.  
  1286. goto_xy(xi, yi); // trace la pastille - ce goto est effectué par les INT des timers 3 et 4. Il est terminé par le "test_fin_goto"
  1287. while (goto_en_cours_xy == 1) { lecture_fins_de_course_xy(); test_fin_goto(xi, yi); }
  1288. }
  1289. rayon_i -= largeur_trait;
  1290. if (rayon_i < 0) {stop=1; }
  1291. }
  1292. n++;
  1293. }
  1294.  
  1295. xi = centre_x;
  1296. yi = centre_y;
  1297.  
  1298. //affleure_plume();
  1299. laser_off();
  1300. msg_ret_gto[0]='P';
  1301. goto_xy(xi, yi); // retour au centre, non tracé
  1302.  
  1303.  
  1304. }
  1305.  
  1306.  
  1307.  
  1308.  
  1309. void tracer_pastille_rectangulaire(uint16_t ax_i, uint16_t ay_i)
  1310. {
  1311. // ax_i et ay_i sont les dimensions de la pastille
  1312. long centre_x, centre_y;
  1313. long xi, yi;
  1314. float x, y;
  1315. float largeur_trait, d; // epaisseur du trait
  1316. int8_t n;
  1317.  
  1318. _delay_ms(50);
  1319. Serial.println("rectangulaire");
  1320.  
  1321. if (simulation == 1)
  1322. {
  1323. _delay_ms(50);
  1324. envoi_PRET(0,0);
  1325. return;
  1326. }
  1327.  
  1328. laser_set_Z(80);
  1329. laser_set_lum(5); // 40
  1330.  
  1331. largeur_trait = 8.0 * 7.5;
  1332.  
  1333. ax_i -= largeur_trait; // pour retrécir le rectangle extérieur de la largeur du trait
  1334. ay_i -= largeur_trait; // pour retrécir le rectangle extérieur
  1335.  
  1336. centre_x = x0;
  1337. centre_y = y0;
  1338.  
  1339. msg_ret_gto[0]='L'; // demandé en local
  1340.  
  1341. // ici on se trouve au centre de la pastille
  1342. // on va se rendre à un coin, plume levée, sans tracer.
  1343.  
  1344. xi = centre_x -ax_i;
  1345. yi = centre_y -ay_i;
  1346. goto_xy(xi, yi); while (goto_en_cours_xy == 1) { lecture_fins_de_course_xy(); test_fin_goto(xi, yi); }
  1347.  
  1348. // ici on se trouve au premier coin, on commence le tracé
  1349.  
  1350.  
  1351. n=0; // au départ n=0 ne retrécit pas la pastille de la largeur du trait, mais cela a été fait ci-dessus au préalable.
  1352. while (n<12)
  1353. {
  1354. d= n * largeur_trait;
  1355. if ((d < ax_i) && (d < ay_i))
  1356. {
  1357. xi = centre_x +ax_i -d;
  1358. yi = centre_y -ay_i +d;
  1359. laser_on();
  1360. goto_xy(xi, yi); while (goto_en_cours_xy == 1) { lecture_fins_de_course_xy(); test_fin_goto(xi, yi); }
  1361.  
  1362. xi = centre_x +ax_i -d;
  1363. yi = centre_y +ay_i -d;
  1364. laser_on();
  1365. goto_xy(xi, yi); while (goto_en_cours_xy == 1) { lecture_fins_de_course_xy(); test_fin_goto(xi, yi); }
  1366.  
  1367. xi = centre_x -ax_i +d;
  1368. yi = centre_y +ay_i -d;
  1369. laser_on();
  1370. goto_xy(xi, yi); while (goto_en_cours_xy == 1) { lecture_fins_de_course_xy(); test_fin_goto(xi, yi); }
  1371.  
  1372. xi = centre_x -ax_i +d;
  1373. yi = centre_y -ay_i +d;
  1374. laser_on();
  1375. goto_xy(xi, yi); while (goto_en_cours_xy == 1) { lecture_fins_de_course_xy(); test_fin_goto(xi, yi); }
  1376. }
  1377. n++;
  1378. }
  1379.  
  1380. //affleure_plume();
  1381. laser_off();
  1382.  
  1383. xi = centre_x;
  1384. yi = centre_y;
  1385. msg_ret_gto[0]='P';
  1386. goto_xy(xi, yi); while (goto_en_cours_xy == 1) { lecture_fins_de_course_xy(); test_fin_goto(xi, yi); } // retour au centre
  1387.  
  1388. }
  1389.  
  1390.  
  1391.  
  1392. void tracer_carre()
  1393. {
  1394. long centre_x, centre_y;
  1395. long xi, yi;
  1396. long x, y, c;
  1397.  
  1398.  
  1399. c = 8 * 200.0;
  1400.  
  1401. centre_x = x0;
  1402. centre_y = y0;
  1403.  
  1404. msg_ret_gto[0]='L'; // demandé en local
  1405.  
  1406. // ici on se trouve au centre du carré
  1407. // on va se rendre à un coin, plume levée, sans tracer.
  1408.  
  1409. xi = centre_x -c;
  1410. yi = centre_y -c;
  1411. goto_xy(xi, yi); while (goto_en_cours_xy == 1) { lecture_fins_de_course_xy(); test_fin_goto(xi, yi); }
  1412.  
  1413. // ici on se trouve au premier coin, on commence le tracé
  1414.  
  1415. xi = centre_x +c;
  1416. yi = centre_y -c;
  1417. laser_on();
  1418. goto_xy(xi, yi); while (goto_en_cours_xy == 1) { lecture_fins_de_course_xy(); test_fin_goto(xi, yi); }
  1419.  
  1420. xi = centre_x +c;
  1421. yi = centre_y +c;
  1422. laser_on();
  1423. goto_xy(xi, yi); while (goto_en_cours_xy == 1) { lecture_fins_de_course_xy(); test_fin_goto(xi, yi); }
  1424.  
  1425. xi = centre_x -c;
  1426. yi = centre_y +c;
  1427. laser_on();
  1428. goto_xy(xi, yi); while (goto_en_cours_xy == 1) { lecture_fins_de_course_xy(); test_fin_goto(xi, yi); }
  1429.  
  1430. xi = centre_x -c;
  1431. yi = centre_y -c;
  1432. laser_on();
  1433. goto_xy(xi, yi); while (goto_en_cours_xy == 1) { lecture_fins_de_course_xy(); test_fin_goto(xi, yi); }
  1434.  
  1435. //affleure_plume();
  1436. laser_off();
  1437.  
  1438. xi = centre_x;
  1439. yi = centre_y;
  1440. msg_ret_gto[0]='P';
  1441. goto_xy(xi, yi); // retour au centre
  1442.  
  1443. }
  1444.  
  1445.  
  1446.  
  1447. void recupere_data()
  1448. {
  1449. String cs_recu = "";
  1450. String cs_calc = "";
  1451. uint16_t CS_a, CS_b;
  1452.  
  1453.  
  1454. int p1 = inputString.indexOf("TRNS:"); // ce string "TRANSMIS:" a été rajouté pour fiabiliser le dialogue USB
  1455. int p2 = inputString.indexOf("-CS=["); // ce string "-CS="" précède le checksum ajouté à la fin de la ligne
  1456. int p3 = inputString.indexOf("]");
  1457.  
  1458. if ((p1>-1) && (p2>-1))
  1459. {
  1460.  
  1461. int lg= inputString.length();
  1462. data_in = inputString.substring(p1+5, p2);
  1463. cs_recu = inputString.substring(p2+5, p3);
  1464.  
  1465. _delay_ms(50);
  1466. Serial.print("CS extrait = " + cs_recu);
  1467.  
  1468. CS_a = cs_recu.toInt();
  1469.  
  1470.  
  1471. //calcul du checksum de la ligne telle qu'elle est reçue
  1472. uint8_t taille = data_in.length();
  1473. int8_t n, v;
  1474. uint16_t somme = 0;
  1475. for (n=0; n<taille; n++)
  1476. {
  1477. v=data_in[n];
  1478. somme += v;
  1479. }
  1480. CS_b = somme;
  1481. cs_calc = String(CS_b);
  1482.  
  1483. //_delay_ms(50);
  1484. Serial.print(" - CS recalcul = " + cs_calc);
  1485.  
  1486. if (CS_b == CS_a)
  1487. {
  1488. data_valide = 1;
  1489. Serial.println(" -> Ok");
  1490. }
  1491. else
  1492. {
  1493. data_valide = 0;
  1494. n_beep(5);
  1495. _delay_ms(50);
  1496. Serial.println(" erreur checksum !");
  1497. }
  1498.  
  1499. // uint16_t cs = qChecksum(qPrintable(data_in), data_in.length()); //checksum
  1500. inputString="";
  1501.  
  1502. }
  1503. }
  1504.  
  1505.  
  1506. void X_plus()
  1507. {
  1508. char inChar2;
  1509.  
  1510. acceleration_permise = 1;
  1511. OCR3A = ocr3a_depart;
  1512. OCR4A = ocr4a_depart;
  1513. moteur1.enable();
  1514. moteur1.set_sens(0);
  1515. moteur1.set_marche(1);
  1516.  
  1517. uint8_t sortir = 0;
  1518. lecture_fins_de_course_xy();
  1519. while ( ((etat_FC_x2 == 0) || (etat_FC_y2 == 0) ) && (sortir==0) )
  1520. {
  1521. lecture_fins_de_course_xy();
  1522.  
  1523. while( Serial.available() && ( stringComplete == 0) )
  1524. {
  1525. inChar2 = (char)Serial.read(); inputString += inChar2;
  1526. if (inChar2 == '\n') { stringComplete = 1; }
  1527. }
  1528. if (stringComplete == 1)
  1529. {
  1530. recupere_data();
  1531. if (data_in[0] == 'A')
  1532. {
  1533. Serial.println("STOP2");
  1534. sortir =1;
  1535. }
  1536. inputString = "";
  1537. stringComplete = 0;
  1538. }
  1539.  
  1540. if (pas_mot1_demande > 0) {pas_mot1(); pas_mot1_demande--; }
  1541. if (pas_mot2_demande > 0) {pas_mot2(); pas_mot2_demande--; }
  1542. }
  1543. moteur1.disable();
  1544. envoi_PRET(x0, y0);
  1545.  
  1546. }
  1547.  
  1548.  
  1549. void X_moins()
  1550. {
  1551. char inChar2;
  1552.  
  1553. OCR3A = ocr3a_depart;
  1554. OCR4A = ocr4a_depart;
  1555. acceleration_permise = 1;
  1556. moteur1.set_sens(1);
  1557. moteur1.enable();
  1558. moteur1.set_marche(1);
  1559.  
  1560. uint8_t sortir = 0;
  1561. lecture_fins_de_course_xy();
  1562. while ( ((etat_FC_x2 == 0) || (etat_FC_y2 == 0) ) && (sortir==0) )
  1563. {
  1564. lecture_fins_de_course_xy();
  1565.  
  1566. while( Serial.available() && ( stringComplete == 0) )
  1567. {
  1568. inChar2 = (char)Serial.read();
  1569. inputString += inChar2;
  1570. if (inChar2 == '\n') { stringComplete = 1; }
  1571. }
  1572. if (stringComplete == 1)
  1573. {
  1574. recupere_data();
  1575. if (data_in[0] == 'A')
  1576. {
  1577. Serial.println("STOP2");
  1578. sortir =1;
  1579. }
  1580. inputString = "";
  1581. stringComplete = 0;
  1582. }
  1583.  
  1584. if (pas_mot1_demande > 0) {pas_mot1(); pas_mot1_demande--; }
  1585. if (pas_mot2_demande > 0) {pas_mot2(); pas_mot2_demande--; }
  1586. }
  1587. moteur1.set_marche(0);
  1588. moteur1.disable();
  1589. envoi_PRET(x0, y0);
  1590. }
  1591.  
  1592.  
  1593. void Y_plus()
  1594. {
  1595. char inChar2;
  1596.  
  1597. OCR3A = ocr3a_depart;
  1598. OCR4A = ocr4a_depart;
  1599. acceleration_permise = 1;
  1600. moteur2.set_sens(0);
  1601. periode2=36;
  1602. moteur2.enable();
  1603. moteur2.set_marche(1);
  1604.  
  1605. uint8_t sortir = 0;
  1606. lecture_fins_de_course_xy();
  1607. while ( ((etat_FC_x2 == 0) || (etat_FC_y2 == 0) ) && (sortir==0) )
  1608. {
  1609. lecture_fins_de_course_xy();
  1610.  
  1611. while( Serial.available() && ( stringComplete == 0) )
  1612. {
  1613. inChar2 = (char)Serial.read();
  1614. inputString += inChar2;
  1615. if (inChar2 == '\n') { stringComplete = 1; }
  1616. }
  1617. if (stringComplete == 1)
  1618. {
  1619. recupere_data();
  1620. if (data_in[0] == 'A')
  1621. {
  1622. Serial.println("STOP2");
  1623. sortir =1;
  1624. }
  1625. inputString = "";
  1626. stringComplete = 0;
  1627. }
  1628.  
  1629. if (pas_mot1_demande > 0) {pas_mot1(); pas_mot1_demande--; }
  1630. if (pas_mot2_demande > 0) {pas_mot2(); pas_mot2_demande--; }
  1631. }
  1632. moteur2.set_marche(0);
  1633. moteur2.disable();
  1634. envoi_PRET(x0, y0);
  1635. }
  1636.  
  1637.  
  1638. int Y_moins()
  1639. {
  1640. char inChar2;
  1641.  
  1642. OCR3A = ocr3a_depart;
  1643. OCR4A = ocr4a_depart;
  1644. acceleration_permise = 1;
  1645. moteur2.set_sens(1);
  1646. periode2=36;
  1647. moteur2.enable();
  1648. moteur2.set_marche(1);
  1649.  
  1650. uint8_t sortir = 0;
  1651. lecture_fins_de_course_xy();
  1652. while ( ((etat_FC_x2 == 0) || (etat_FC_y2 == 0) ) && (sortir==0) )
  1653. {
  1654. lecture_fins_de_course_xy();
  1655.  
  1656. while( Serial.available() && ( stringComplete == 0) )
  1657. {
  1658. inChar2 = (char)Serial.read();
  1659. inputString += inChar2;
  1660. if (inChar2 == '\n') { stringComplete = 1; }
  1661. }
  1662. if (stringComplete == 1)
  1663. {
  1664. recupere_data();
  1665. if (data_in[0] == 'A')
  1666. {
  1667. Serial.println("STOP2"); sortir =1;
  1668. }
  1669. inputString = "";
  1670. stringComplete = 0;
  1671. }
  1672.  
  1673. if (pas_mot1_demande > 0) {pas_mot1(); pas_mot1_demande--; }
  1674. if (pas_mot2_demande > 0) {pas_mot2(); pas_mot2_demande--; }
  1675. }
  1676. moteur1.set_marche(0);
  1677. moteur2.disable();
  1678. envoi_PRET(x0, y0);
  1679. }
  1680.  
  1681.  
  1682.  
  1683.  
  1684.  
  1685. int goto_Safe_POS()
  1686. {
  1687. char inChar2;
  1688. String inputString2 = "";
  1689. uint8_t stringComplete2 = 0;
  1690.  
  1691. // déplacement en vitesse rapide jusqu'aux arrêts fin de course, puis positionnement précis
  1692. laser_off();
  1693. laser_set_Z(140); // position haute
  1694.  
  1695. acceleration_permise = 1;
  1696.  
  1697. OCR3A = ocr3a_depart;
  1698. OCR4A = ocr4a_depart;
  1699.  
  1700. moteur1.enable();
  1701. moteur1.set_sens(0);
  1702. moteur1.set_marche(1);
  1703.  
  1704. moteur2.enable();
  1705. moteur2.set_sens(0);
  1706. moteur2.set_marche(1);
  1707.  
  1708. uint8_t sortir = 0;
  1709. lecture_fins_de_course_xy();
  1710. while ( ((etat_FC_x2 == 0) || (etat_FC_y2 == 0) ) && (sortir==0) )
  1711. {
  1712. lecture_fins_de_course_xy();
  1713. stringComplete2 = 0;
  1714. while( Serial.available() && ( stringComplete2 == 0) )
  1715. {
  1716. inChar2 = (char)Serial.read();
  1717. inputString2 += inChar2;
  1718. if (inChar2 == '\n')
  1719. {
  1720. stringComplete2 = 1;
  1721. }
  1722. }
  1723. if (stringComplete2 == 1)
  1724. {
  1725. int p = inputString2.indexOf("TRNS:");
  1726. if (p>-1)
  1727. {
  1728. data_valide = 1;
  1729. int lg= inputString.length();
  1730. data_in = inputString2.substring(p+5, lg-1);
  1731. inputString2="";
  1732. }
  1733. if (data_in[0] == 'A')
  1734. {
  1735. Serial.println("STOP2");
  1736. sortir =1;
  1737. }
  1738. inputString2 = "";
  1739. stringComplete2 = 0;
  1740. }
  1741.  
  1742. if (pas_mot1_demande > 0) {pas_mot1(); pas_mot1_demande--; }
  1743. if (pas_mot2_demande > 0) {pas_mot2(); pas_mot2_demande--; }
  1744. }
  1745.  
  1746. moteur1.disable();
  1747. moteur2.disable();
  1748. envoi_PRET(x0, y0);
  1749. return(0);
  1750. }
  1751.  
  1752.  
  1753.  
  1754. int goto_ZERO(long xi, long yi) // goto vers zéro mécanique (définit par les fin de course) + valeur de la marge envoyée par le PC
  1755. {
  1756. char inChar2;
  1757. String inputString2 = "";
  1758. uint8_t stringComplete2 = 0;
  1759.  
  1760. //----------------------------------------------------------------------
  1761. // déplacement en vitesse rapide jusqu'aux arrêts fin de course, puis positionnement précis
  1762. laser_off();
  1763. laser_set_Z(140); // position haute
  1764.  
  1765. acceleration_permise = 1;
  1766.  
  1767. OCR3A = ocr3a_depart;
  1768. OCR4A = ocr4a_depart;
  1769.  
  1770. moteur1.enable();
  1771. moteur1.set_sens(1);
  1772. moteur1.set_marche(1);
  1773.  
  1774. moteur2.enable();
  1775. moteur2.set_sens(1);
  1776. moteur2.set_marche(1);
  1777.  
  1778. uint8_t sortir3 = 0;
  1779. lecture_fins_de_course_xy();
  1780. stringComplete2 = 0;
  1781. while ( ((etat_FC_x1 == 0) || (etat_FC_y1 == 0) ) && (sortir3==0) )
  1782. {
  1783. lecture_fins_de_course_xy();
  1784. stringComplete2 = 0;
  1785. while( Serial.available() && ( stringComplete2 == 0) )
  1786. {
  1787. inChar2 = (char)Serial.read();
  1788. inputString2 += inChar2;
  1789. if (inChar2 == '\n')
  1790. {
  1791. stringComplete2 = 1;
  1792. }
  1793. }
  1794. if (stringComplete2 == 1)
  1795. {
  1796. int p = inputString2.indexOf("TRNS:");
  1797. if (p>-1)
  1798. {
  1799. data_valide = 1;
  1800. int lg= inputString.length();
  1801. data_in = inputString2.substring(p+5, lg-1);
  1802. inputString2="";
  1803. }
  1804. if (data_in[0] == 'A')
  1805. {
  1806. Serial.println("STOP3");
  1807. sortir3 =1;
  1808. }
  1809. inputString2 = "";
  1810. stringComplete2 = 0;
  1811. }
  1812.  
  1813. if (pas_mot1_demande > 0) {pas_mot1(); pas_mot1_demande--; }
  1814. if (pas_mot2_demande > 0) {pas_mot2(); pas_mot2_demande--; }
  1815. }
  1816.  
  1817. if (sortir3 == 1)
  1818. {
  1819. moteur1.disable();
  1820. moteur2.disable();
  1821. envoi_PRET(x0, y0);
  1822. return 1;
  1823. }
  1824. _delay_ms(50);
  1825. Serial.println("goto pt origine dessin");
  1826.  
  1827.  
  1828. RAZ_POS(); // ICI : REFERENCE ABSOLUE DES POSITIONS PROVISOIRE (pour pouvoir rejoindre le pt de départ du tracé (valeur de marge envoyée par la commande))
  1829.  
  1830. // goto vers valeur envoyée par la commande
  1831. acceleration_permise = 0;
  1832. base_ocr = 500;
  1833. calcul_periode_deplacement(0, 0, 100, 100);
  1834.  
  1835. msg_ret_gto[0]='Z'; msg_ret_gto[1]='0'; msg_ret_gto[2]='0';
  1836. goto_xy(xi, yi);
  1837.  
  1838. while (goto_en_cours_xy == 1)
  1839. {
  1840. lecture_fins_de_course_xy();
  1841. test_fin_goto(xi, yi);
  1842. if (pas_mot1_demande > 0) {pas_mot1(); pas_mot1_demande--; }
  1843. if (pas_mot2_demande > 0) {pas_mot2(); pas_mot2_demande--; }
  1844. }
  1845. _delay_ms(50);
  1846.  
  1847. moteur1.disable();
  1848. moteur2.disable();
  1849.  
  1850. _delay_ms(50);
  1851. Serial.println("RAZ coordonnees XY");
  1852.  
  1853. RAZ_POS(); // ICI : REFERENCE ABSOLUE DES POSITIONS DEFINITIVE
  1854.  
  1855. envoi_PRET(x0, y0);
  1856. return(0);
  1857.  
  1858. }
  1859.  
  1860.  
  1861.  
  1862. void test()
  1863. {
  1864. //tracer_pastille_ronde(8*75);
  1865. /* goto_xy(800* 10, 800* 10);
  1866. goto_xy(800* 3, 800* 5);
  1867. goto_xy(800* 7, 800* 2);
  1868. goto_xy(800* 12, 800* 4);
  1869. goto_xy(800* 20, 800* 20);
  1870. goto_xy(800* 5, 800* 8);
  1871. goto_xy(800* 1, 800* 6);
  1872. goto_xy(800* 4, 800* 17);
  1873. goto_xy(800* 2, 800* 7);
  1874. goto_xy(800* 6, 800* 14);
  1875.  
  1876. */
  1877.  
  1878. /*
  1879. goto_xy(800*1, 1*800);
  1880. goto_xy(800*1, 5*800);
  1881. goto_xy(800*8, 5*800);
  1882. goto_xy(800*8, 2*800);
  1883. goto_xy(800*10, 2*800);
  1884. goto_xy(800*10, 3*800);
  1885. goto_xy(800*7, 3*800);
  1886. goto_xy(800*7, 4*800);
  1887. goto_xy(800*3, 4*800);
  1888. goto_xy(800*3, 1*800);
  1889. goto_xy(800*5, 1*800);
  1890. goto_xy(800*1, 6*800);
  1891. goto_xy(800*5, 6*800);
  1892. goto_xy(800*5, 4*800);
  1893. goto_xy(800*5, 3*800);
  1894.  
  1895. tracer_pastille_ronde(400);
  1896. */
  1897.  
  1898.  
  1899.  
  1900.  
  1901. /*
  1902.  uint8_t n;
  1903. goto_xy(800*1, 1*800);
  1904. for (n=1; n<=10; n++)
  1905. {
  1906. goto_xy(800*1, 5*800);
  1907. goto_xy(800*6, 5*800);
  1908. goto_xy(800*6, 1*800);
  1909. goto_xy(800*1, 1*800);
  1910. }
  1911. */
  1912.  
  1913. Serial.println("Essai de transmission d'un message long");
  1914.  
  1915. }
  1916.  
  1917.  
  1918. void envoi_liste_apertures()
  1919. // vers le PC, pour test
  1920. // rappel : apertures[i][0] -> x ; apertures[i][1] -> y; (x et y ne sont pas des positions mais des dimensions); apertures[i][2] -> 'R' ou 'C'
  1921. {
  1922.  
  1923. uint16_t x, y, type;
  1924. uint8_t i, r, valeur;
  1925. // char msg_position[20]="0123456789012345678"; // 19 car max pour un string20
  1926. char msg_aperture[20]="APERTURE 00 = X "; // 19 car max pour un string20
  1927.  
  1928. Serial.println("Liste APERTURES:");
  1929. for (n=0; n<40; n++)
  1930. {
  1931. x = apertures[n].x;
  1932. y = apertures[n].y;
  1933. type = apertures[n].type; // "C" ou "R" ou "O" (en principe!))
  1934.  
  1935. valeur = n;
  1936. for (i=0; i<2; i++)
  1937. {
  1938. r=48 + valeur % 10; // modulo (reste de la division)
  1939. valeur /= 10; // quotient
  1940. msg_aperture[10-i]=r;
  1941. }
  1942. msg_aperture[14]=type;
  1943.  
  1944. _delay_ms(50);
  1945. Serial.println(msg_aperture);
  1946. }
  1947.  
  1948. }
  1949.  
  1950.  
  1951.  
  1952. void setup()
  1953. {
  1954. // CLKPR = 0b10000000;
  1955. // CLKPR = 0b00000000;
  1956.  
  1957. init_ports();
  1958.  
  1959. laser_off();
  1960.  
  1961. simulation=0;
  1962.  
  1963. // int_EXT_setup(); // desable (inutile depuis que les moteurs ne "glissent" plus )
  1964.  
  1965. timer2_setup();
  1966. timer3_setup();
  1967. timer4_setup();
  1968. timer5_setup();
  1969.  
  1970. // init_table_sinus(); <- ***************************** A VOIR !!! *************************
  1971.  
  1972. // lcd_init(); // voir l'attribution des bits dans le ficher LCD_5110-b28b.h - Note: j'ai supprimé l'affichage LCD.
  1973.  
  1974.  
  1975.  
  1976. // ==== Le port est défini dans le step_motor4.h ========
  1977. moteur1.bit_ENABLE = 0b00000001; // X
  1978. moteur2.bit_ENABLE = 0b00000010; // Y
  1979.  
  1980. // ==== Le port est défini dans le step_motor4.h ========
  1981. moteur1.bit_finCourse1 = 0b00000001; // X
  1982. moteur1.bit_finCourse2 = 0b00000010; // X
  1983.  
  1984. moteur2.bit_finCourse1 = 0b00000100; // Y
  1985. moteur2.bit_finCourse2 = 0b00001000; // Y
  1986.  
  1987. // ==== Le port est défini dans le step_motor4.h ========
  1988. moteur1.bit_DIR = 0b00000001; // X
  1989. moteur1.bit_STEP = 0b00000010; // X
  1990.  
  1991. moteur2.bit_DIR = 0b00000100; // Y
  1992. moteur2.bit_STEP = 0b00001000; // Y
  1993.  
  1994. // =====================================================
  1995.  
  1996. mm_par_s_max = 2; // par défaut
  1997. mm_par_s = mm_par_s_max;
  1998.  
  1999. moteur1.steps_par_tour = 200;
  2000. moteur1.tours_par_mm = 2; // pas-de-vis 0.5mm (pour un diamètre M3)
  2001. moteur1.impuls_par_step = 2; // de par la configuration de la carte "EasyDriver"
  2002.  
  2003. moteur2.steps_par_tour = 200;
  2004. moteur2.tours_par_mm = 2;
  2005. moteur2.impuls_par_step = 2;
  2006.  
  2007. ocr3a_min = 800; // pour moteur1 800
  2008. ocr3a_depart = 2000; // pour moteur1 1300
  2009.  
  2010. ocr4a_min = 800; // pour moteur2 800
  2011. ocr4a_depart = 2000; // pour moteur2 2000
  2012.  
  2013. base_ocr_depart = 3000; // 3000 determine la vitesse de rotation (basse) appliquée lors de la mise en rotation
  2014. base_ocr_min = 500; // determine la vitesse max de rotation permise, voir les fonctions "ISR(TIMER3_COMPA_vect)" et "ISR(TIMER4_COMPA_vect)"
  2015. base_ocr_min2 = 300; // determine la vitesse max de rotation permise lors des "goto zero" et "goto safe pos"
  2016.  
  2017.  
  2018. moteur1.init(); // calcule les autres paramètres...
  2019. moteur2.init(); // calcule les autres paramètres...
  2020.  
  2021. n_octet=0;
  2022.  
  2023. Serial.begin(38400); // 4800, 9600, 14400, 19200, 28800, 38400, 57600, 115200
  2024.  
  2025. uint16_t x2, y2, p1, p2;
  2026.  
  2027. x0=0;
  2028. y0=0;
  2029. x2=100;
  2030. y2=100;
  2031. calcul_periode_deplacement(x0, y0, x2, y2);
  2032.  
  2033. moteur1.RAZ_pos();
  2034. moteur2.RAZ_pos();
  2035.  
  2036. for (int i=0; i<40; i++) { apertures[i].type = 'x'; }
  2037. //apertures[23][2] = 'R'; // pour test
  2038.  
  2039. sei();
  2040.  
  2041. laser_off();
  2042. laser_set_lum(40);
  2043. laser_set_Z(145);
  2044.  
  2045. _delay_ms(50);
  2046. Serial.println("hello");
  2047. _delay_ms(50);
  2048. Serial.println("CNC version " + version);
  2049. _delay_ms(50);
  2050. Serial.println("Bonjour");
  2051. _delay_ms(50);
  2052. Serial.println("Mega2560 INIT");
  2053. _delay_ms(50);
  2054. beep();
  2055. _delay_ms(50);
  2056. beep();
  2057. Serial.println("USB ok");
  2058. envoi_PRET(x0, y0);
  2059. }
  2060.  
  2061.  
  2062.  
  2063.  
  2064. void loop() // BOUCLE PRINCIPALE
  2065. {
  2066. // ne pas inclure de tempo dans cette boucle principale sous peine de perdre la précision des positions de fin de goto, et donc la précision de position de la plume !
  2067.  
  2068. long xil, yil;
  2069. lecture_fins_de_course_xy();
  2070. // test_fin_goto(); // stope le cas échéant le goto en cours
  2071.  
  2072. cpt1++;
  2073.  
  2074. if (cpt1 >= 1000)
  2075. {
  2076. PORTA ^= bit_LED_verte;
  2077. cpt1=0;
  2078. }
  2079.  
  2080.  
  2081. if (goto_en_cours_xy == 0)
  2082. {
  2083. uint8_t n;
  2084.  
  2085. stringComplete = false;
  2086. while(stringComplete == false)
  2087. {
  2088. if (Serial.available())
  2089. {
  2090. inChar = (char)Serial.read();
  2091. inputString += inChar;
  2092. if (inChar == '\n') {stringComplete = true; }
  2093. }
  2094. }
  2095. data_valide = 0;
  2096. if (stringComplete = true)
  2097. {
  2098. _delay_ms(50);
  2099. Serial.println("recu: "+inputString);
  2100. _delay_ms(1);
  2101. recupere_data();
  2102. }
  2103.  
  2104. if (data_valide == 1) // lorsque les checksums sont égaux...
  2105. {
  2106. // decodage du message reçu par USB
  2107. //--------------------------------------------------------------------------------------------------------------------------------------------------------
  2108. if (data_in[0] == 'A') // STOP
  2109. {
  2110. _delay_ms(50);
  2111. Serial.println("STOP");
  2112. STOP();
  2113. data_in="";
  2114. stringComplete = false;
  2115. }
  2116. //--------------------------------------------------------------------------------------------------------------------------------------------------------
  2117. else if (data_in[0] == 'T') // TESTS
  2118. {
  2119. if (data_in[1] == 'E')
  2120. {
  2121. test();
  2122. data_in="";
  2123. stringComplete = false;
  2124. }
  2125. if (data_in[1] == 'A') // envoyer liste liste apertures
  2126. {
  2127. envoi_liste_apertures();
  2128. data_in="";
  2129. stringComplete = false;
  2130. }
  2131. if (data_in[1] == 'I') // mode simulation
  2132. {
  2133. simulation=1;
  2134. _delay_ms(50);
  2135. Serial.println("MODE SIMULATION");
  2136. data_in="";
  2137. stringComplete = false;
  2138. }
  2139. if (data_in[1] == 'N') // stop du mode simulation
  2140. {
  2141. _delay_ms(50);
  2142. Serial.println("FIN SIMULATION");
  2143. simulation=0;
  2144. data_in="";
  2145. stringComplete = false;
  2146. }
  2147.  
  2148. }
  2149. //--------------------------------------------------------------------------------------------------------------------------------------------------------
  2150. else if (data_in[0] == 'Z') // LASER ON/OFF
  2151. {
  2152. if (data_in[1] == '1') // allume LASER
  2153. {
  2154. laser_on();
  2155. data_in="";
  2156. stringComplete = false;
  2157. }
  2158.  
  2159. if (data_in[1] == '0') // éteint LASER
  2160. {
  2161. laser_off();
  2162. data_in="";
  2163. stringComplete = false;
  2164. }
  2165.  
  2166. if (data_in[1] == 'B') // BLOCAGE LASER
  2167. {
  2168. laser_bloque();
  2169. _delay_ms(50);
  2170. Serial.println("Blocage laser");
  2171. data_in="";
  2172. stringComplete = false;
  2173. }
  2174.  
  2175. if (data_in[1] == 'D') // DEBLOCAGE LASER
  2176. {
  2177. laser_debloque();
  2178. data_in="";
  2179. stringComplete = false;
  2180. }
  2181. }
  2182.  
  2183.  
  2184. else if (data_in[0] == 'L')
  2185. {
  2186. if (data_in[1] == 'U') // intensité luminosité laser
  2187. {
  2188. long LU;
  2189. decode_6_digits(&LU);
  2190. laser_set_lum((uint8_t)LU);
  2191. data_in="";
  2192. stringComplete = false;
  2193. }
  2194. if (data_in[1] == 'Z') // Hauteur du laser
  2195. {
  2196. long LZ;
  2197. decode_6_digits(&LZ);
  2198. laser_set_Z((uint16_t)LZ); // remarque: le timer5 est configuré en mode "fast PWM 10 bits"
  2199. data_in="";
  2200. stringComplete = false;
  2201. }
  2202.  
  2203. }
  2204. //--------------------------------------------------------------------------------------------------------------------------------------------------------
  2205. else if (data_in[0] == 'X') // déplacements en X
  2206. {
  2207. _delay_ms(50);
  2208. Serial.println("DEPLACEMENT MANUEL");
  2209. moteur1.set_marche(0);
  2210. if (data_in[1] == 'P') // X Plus
  2211. {
  2212. _delay_ms(50);
  2213. Serial.println("X+");
  2214. data_in="";
  2215. stringComplete = false;
  2216. X_plus();
  2217. }
  2218. if (data_in[1] == 'M') // X Moins
  2219. {
  2220. _delay_ms(50);
  2221. Serial.println("X-");
  2222. data_in="";
  2223. stringComplete = false;
  2224. X_moins();
  2225. }
  2226. if (data_in[1] == 'N') // rotation de N pas du moteur X (pour tester des moteurs)
  2227. {
  2228. _delay_ms(50);
  2229. Serial.println("XN");
  2230. long P;
  2231. decode_6_digits(&P);
  2232. rotX_n_pas(P);
  2233. data_in="";
  2234. stringComplete = false;
  2235. }
  2236. if (data_in[1] == '1') {Serial.println("X+1"); X_plus_mm(1); data_in="";}
  2237. if (data_in[1] == '2') {Serial.println("X+10"); X_plus_mm(10); data_in=""; }
  2238. if (data_in[1] == '3') {Serial.println("X-1"); X_moins_mm(1); data_in="";}
  2239. if (data_in[1] == '4') {Serial.println("X-10"); X_moins_mm(10); data_in="";}
  2240.  
  2241. }
  2242.  
  2243. //--------------------------------------------------------------------------------------------------------------------------------------------------------
  2244. else if (data_in[0] == 'Y') // déplacement en Y
  2245. {
  2246. _delay_ms(50);
  2247. Serial.println("DEPLACEMENT MANUEL");
  2248. moteur2.set_marche(0);
  2249. if (data_in[1] == 'P') // Y Plus
  2250. {
  2251. _delay_ms(50);
  2252. Serial.println("Y+");
  2253. data_in="";
  2254. stringComplete = false;
  2255. Y_plus();
  2256. }
  2257. if (data_in[1] == 'M') // Y Moins
  2258. {
  2259. _delay_ms(50);
  2260. Serial.println("Y-");
  2261. data_in="";
  2262. stringComplete = false;
  2263. Y_moins();
  2264. }
  2265. if (data_in[1] == 'N') // rotation de N pas du moteur Y (pour tester des moteurs)
  2266. {
  2267. _delay_ms(50);
  2268. Serial.println("YN");
  2269. long P;
  2270. decode_6_digits(&P);
  2271. rotY_n_pas(P);
  2272. data_in="";
  2273. stringComplete = false;
  2274. }
  2275. if (data_in[1] == '1') {Serial.println("Y+1"); Y_plus_mm(1); data_in="";}
  2276. if (data_in[1] == '2') {Serial.println("Y+10"); Y_plus_mm(10); data_in=""; }
  2277. if (data_in[1] == '3') {Serial.println("Y-1"); Y_moins_mm(1); data_in="";}
  2278. if (data_in[1] == '4') {Serial.println("Y-10"); Y_moins_mm(10); data_in="";}
  2279. }
  2280.  
  2281. //--------------------------------------------------------------------------------------------------------------------------------------------------------
  2282. else if (data_in[0] == 'P') // TRACER PASTILLE à partir d'une ligne de commande reçue
  2283. {
  2284. _delay_ms(50);
  2285. Serial.println("TRACE pastille");
  2286. if (data_in[1] == 'X') // en x,y
  2287. {
  2288. laser_off();
  2289.  
  2290. type_ap = apertures[num_aperture].type; // -> 'R'(rectangulaire) ou 'C'(circulaire) ou 'O'(ovale)
  2291. _delay_ms(50);
  2292. Serial.println(" de type ");
  2293. Serial.println(type_ap);
  2294.  
  2295. decode_xy_A(&xil, &yil);
  2296. num_aperture = decode_num_aperture();
  2297.  
  2298. ax = (apertures[num_aperture].x /2); // dimension x (rayon = diam/2) - épaisseur de la trace
  2299. if (ax > largeur_plume) {ax -= largeur_plume;} else {ax = 0;}
  2300.  
  2301. ay = (apertures[num_aperture].y /2);
  2302. if (ay > largeur_plume) {ay -= largeur_plume;} else {ay = 0;}
  2303.  
  2304. msg_ret_gto[0]='R'; msg_ret_gto[1]=' '; msg_ret_gto[2]=' ';
  2305.  
  2306. goto_xy(xil, yil); // goto vers l'emplacement de la pastille à tracer
  2307. while (goto_en_cours_xy == 1) { lecture_fins_de_course_xy(); test_fin_goto(xil, yil); }
  2308.  
  2309. if ((type_ap == 'R') || (type_ap == 'O')) { tracer_pastille_rectangulaire(ax, ay); } // les pastilles ovales seront tracées rectangulaires...
  2310. // todo : écrire une fonction qui trace une pastille ovale
  2311. else if (type_ap == 'C') { tracer_pastille_ronde(ax);}
  2312. else
  2313. {
  2314. _delay_ms(50);
  2315. Serial.println(type_ap + " inconnue");
  2316. }
  2317.  
  2318. data_in="";
  2319. stringComplete = false;
  2320. }
  2321.  
  2322.  
  2323. if (data_in[1] == '1') // PASTILLE rectangulaire là où on se trouve
  2324. {
  2325. _delay_ms(50);
  2326. Serial.println("PST ici");
  2327. tracer_pastille_rectangulaire(8*200, 8*200); // 8 (impls/100eme de mm) x 200 (100eme de mm); -> 1600 impulsions
  2328. stringComplete = false;
  2329. data_in="";
  2330. }
  2331.  
  2332. if (data_in[1] == 'R') // PASTILLE ronde là où on se trouve
  2333. {
  2334. Serial.println("PST ici");
  2335. decode_xy_A(&ax, &ay);
  2336. tracer_pastille_ronde(8 * ax/2); // le diametre est transmis par le PC, on va tracer le rayon.
  2337. data_in="";
  2338. stringComplete = false;
  2339. }
  2340. }
  2341.  
  2342. //--------------------------------------------------------------------------------------------------------------------------------------------------------
  2343. else if (data_in[0] == 'R')
  2344. {
  2345. if (data_in[1] == 'P')
  2346. {
  2347. _delay_ms(50);
  2348. Serial.println("RAZ POS");
  2349. RAZ_POS();
  2350. data_in="";
  2351. stringComplete = false;
  2352. }
  2353. if (data_in[1] == 'K')
  2354. {
  2355. _delay_ms(50);
  2356. Serial.println("Rot 2kPi");
  2357. rotation_2k_pi();
  2358. data_in="";
  2359. stringComplete = false;
  2360. }
  2361. }
  2362.  
  2363. //--------------------------------------------------------------------------------------------------------------------------------------------------------
  2364. else if (data_in[0] == 'S') // TRACER SEGMENT
  2365. {
  2366. _delay_ms(50);
  2367. Serial.println("TRACE segment");
  2368. if (data_in[1] == 'X')
  2369. {
  2370. num_aperture = decode_num_aperture();
  2371. tracer_segment();
  2372. data_in="";
  2373. stringComplete = false;
  2374. }
  2375. if (data_in[1] == '1') // segment là où on se trouve
  2376. {
  2377. Serial.println("SEG ici");
  2378. xil=x0 + (8 * 1000); // 10.00 mm
  2379. yil=y0;
  2380. msg_ret_gto[0]='S'; msg_ret_gto[1]='1';
  2381. laser_on();
  2382. goto_xy(xil, yil); // trace le segment
  2383. data_in="";
  2384. stringComplete = false;
  2385. }
  2386. }
  2387. //--------------------------------------------------------------------------------------------------------------------------------------------------------
  2388. else if (data_in[0] == 'C')
  2389. {
  2390. if (data_in[1] == '1')
  2391. {
  2392. _delay_ms(50);
  2393. Serial.println("carre ici");
  2394. tracer_carre();
  2395. data_in="";
  2396. stringComplete = false;
  2397. }
  2398. }
  2399. //--------------------------------------------------------------------------------------------------------------------------------------------------------
  2400. else if (data_in[0] == 'H') // TRACER mire pour la mise au point
  2401. {
  2402. Serial.println("TRACE MIRE");
  2403. if (data_in[1] == '1')
  2404. {
  2405. for (n=0; n<3; n++)
  2406. {
  2407. Y_plus_mm(5);
  2408.  
  2409. moteur1.RAZ_pos();
  2410. moteur2.RAZ_pos();
  2411. x0=0;
  2412. y0=0;
  2413.  
  2414. xil=8*(x0+1000); // 10.00 mm
  2415. yil=8*(y0);
  2416. laser_on();
  2417. goto_xy(xil, yil); while (goto_en_cours_xy == 1) { lecture_fins_de_course_xy(); test_fin_goto(xil, yil); }// trace un segment
  2418.  
  2419. tracer_pastille_rectangulaire(8*200, 8*200);
  2420.  
  2421. xil=8*(x0+1000); // 10.00 mm
  2422. yil=8*y0;
  2423. laser_on();
  2424. goto_xy(xil, yil); while (goto_en_cours_xy == 1) { lecture_fins_de_course_xy(); test_fin_goto(xil, yil); }// trace un segment
  2425.  
  2426. tracer_pastille_ronde(8*200);
  2427.  
  2428. xil=8*(x0+1000); // 10.00 mm
  2429. yil=8*y0;
  2430. laser_on();
  2431. goto_xy(xil, yil); while (goto_en_cours_xy == 1) { lecture_fins_de_course_xy(); test_fin_goto(xil, yil); }// trace un segment
  2432.  
  2433. tracer_pastille_ronde(8*200);
  2434.  
  2435. xil=8*(x0-3000); // 30.00 mm
  2436. yil=8*y0;
  2437. laser_off();
  2438. goto_xy(xil, yil); while (goto_en_cours_xy == 1) { lecture_fins_de_course_xy(); test_fin_goto(xil, yil); }// trace un segment
  2439.  
  2440. }
  2441. data_in="";
  2442. stringComplete = false;
  2443. }
  2444.  
  2445. }
  2446.  
  2447. //--------------------------------------------------------------------------------------------------------------------------------------------------------
  2448. else if (data_in[0] == 'G') // GOTO ( DEPLACEMENT laser éteint / plume levée)
  2449. {
  2450. laser_off();
  2451. if (data_in[1] == 'X')
  2452. {
  2453. _delay_ms(50);
  2454. Serial.println("GOTO");
  2455. decode_xy_A(&xil, &yil);
  2456. msg_ret_gto[0]='-'; msg_ret_gto[1]='X'; msg_ret_gto[2]='Y';
  2457. OCR3A = 1000; // pour moteur1
  2458. OCR4A = 1500; // pour moteur2
  2459. goto_xy(xil, yil);
  2460. data_in="";
  2461. stringComplete = false;
  2462.  
  2463. }
  2464. if (data_in[1] == 'S')
  2465. {
  2466. _delay_ms(50);
  2467. Serial.println("goto SAFE POS");
  2468. goto_Safe_POS();
  2469. data_in="";
  2470. stringComplete = false;
  2471. }
  2472. if (data_in[1] == 'H')
  2473. {
  2474. _delay_ms(50);
  2475. Serial.println("GOTO ZERO XYZ");
  2476. decode_xy_A(&xil, &yil);
  2477. goto_ZERO(xil, yil);
  2478. data_in="";
  2479. stringComplete = false;
  2480. }
  2481. }
  2482.  
  2483. // -------------------- AJOUT APERTURES à la liste -----------------------------------------------------------------------------------------------
  2484.  
  2485. else if (data_in[0] == 'M')
  2486. {
  2487. laser_off();
  2488. //PORTA &= ~bit_LED_verte;
  2489. beep2();
  2490. _delay_ms(50);
  2491. Serial.println("ajout APERTURE ");
  2492. _delay_ms(50);
  2493. if (data_in[1] == 'X')
  2494. {
  2495. decode_xy_A(&ax, &ay); // attention : ici il ne s'agit pas de la position de l'objet mais de ses dimensions (qui sont codées de la même façon au même endroit dans la ligne)
  2496. type_ap = data_in[28]; // "C" ou "R" ou "O"
  2497. if (type_ap == 'C') {n_beep(1);}
  2498. else if (type_ap == 'R') {n_beep(1);}
  2499. else if (type_ap == 'O') {n_beep(1);}
  2500. else {n_beep(5);}
  2501. num_aperture = decode_num_aperture();
  2502. ajout_aperture(num_aperture, type_ap);
  2503. data_in="";
  2504. stringComplete = false;
  2505. }
  2506. data_in="";
  2507. }
  2508. else
  2509. {
  2510. _delay_ms(50);
  2511. Serial.println("non interpretable ");
  2512. }
  2513.  
  2514.  
  2515. }
  2516. // ---------------------------------------------------------------------------------------------------------------------------------------------------
  2517.  
  2518. //x0 = moteur1.position_impuls;
  2519. //y0 = moteur2.position_impuls;
  2520. }
  2521. else
  2522. { // ICI goto en cours -> procédures au ralenti pour minimiser les ressources nécessaires afin de laisser le max pour la détection de position de fin de goto
  2523.  
  2524.  
  2525. }
  2526. }
  2527.  
  2528.  
  2529.  

La class step_motor




C'est un objet perso (Silicium628, code libre et open source).
CODE SOURCE en C++ pour l'Arduino
  1. /*
  2.   step_motor6.cpp
  3.   Cree par Silicium628, 09 nov 2014
  4.   version 3.1
  5.   code free Open source
  6. */
  7.  
  8. #include "Arduino.h"
  9. #include "step_motor6.h"
  10.  
  11.  
  12. #define _nop_() do { __asm__ volatile ("mov r0, r0"); } while (0)
  13.  
  14. Step_Motor::Step_Motor()
  15. {
  16.  
  17. }
  18.  
  19. void Step_Motor::init()
  20. {
  21. marche=0;
  22. sens=0;
  23. disable();
  24. steps_par_mm = steps_par_tour * tours_par_mm;
  25. impuls_par_mm = steps_par_mm * impuls_par_step;
  26. }
  27.  
  28.  
  29. void Step_Motor::set_marche(uint8_t valeur)
  30. {
  31. marche = valeur;
  32. }
  33.  
  34.  
  35. uint8_t Step_Motor::get_marche()
  36. {
  37. return marche;
  38. }
  39.  
  40.  
  41. void Step_Motor::enable()
  42. {
  43. PORT_moteur_enable &= ~bit_ENABLE;
  44. }
  45.  
  46.  
  47. void Step_Motor::disable()
  48. {
  49. PORT_moteur_enable |= bit_ENABLE;
  50. }
  51.  
  52.  
  53. void Step_Motor::RAZ_pos()
  54. {
  55. position_impuls=0;
  56. compteur_impuls=0;
  57. }
  58.  
  59.  
  60. void Step_Motor::RAZ_compteur_impuls()
  61. {
  62. compteur_impuls =0;
  63. }
  64.  
  65.  
  66. long Step_Motor::get_compteur_impuls()
  67. {
  68. return compteur_impuls;
  69. }
  70.  
  71.  
  72. void Step_Motor::step()
  73. {
  74. if ( ((sens == 1) && (stop_sens1 == 0) ) || ((sens == 0) && (stop_sens2 == 0) ) )
  75. {
  76. PORT_step |= bit_STEP; // RA1
  77. if (sens == 0) { position_impuls += 1; } else { position_impuls -= 1; }
  78. compteur_impuls++;
  79. PORT_led_blanche ^= bit_LED_blanche; // cette ligne sert surtout de tempo pour la largeur d'impulsion
  80. PORT_step &= ~bit_STEP; // RAZ
  81. }
  82. }
  83.  
  84.  
  85. void Step_Motor::set_sens(uint8_t valeur)
  86. {
  87. sens = valeur;
  88. if(sens == 0) {PORT_dir |= bit_DIR; } else {PORT_dir &= ~bit_DIR;}
  89. }
  90.  
  91.  
  92. void Step_Motor::set_stop_sens1(uint8_t valeur)
  93. {
  94. stop_sens1 = valeur;
  95. }
  96.  
  97.  
  98. void Step_Motor::set_stop_sens2(uint8_t valeur)
  99. {
  100. stop_sens2 = valeur;
  101. }
  102.  
  103.  
  104. uint8_t Step_Motor::get_sens()
  105. {
  106. return sens;
  107. }
  108.  



CODE SOURCE en C++ pour l'Arduino
  1. /*
  2.   step_motor6.h
  3.   Cree par Silicium628, 9 nov 2014
  4.   code free Open source
  5. */
  6.  
  7. #ifndef STEP_MOTOR6_H
  8. #define STEP_MOTOR6_H
  9.  
  10.  
  11. // ces ports doivent correspondre à la configuration matérielle. On peut les changer ici.
  12. #define PORT_moteur_enable PORTC
  13. #define PORT_fin_de _course PORTK
  14. #define PORT_step PORTL
  15. #define PORT_dir PORTL
  16.  
  17. #define PORT_led_blanche PORTC
  18. #define bit_LED_blanche 0b01000000;
  19.  
  20. class Step_Motor
  21. {
  22. private :
  23.  
  24. uint8_t sens;
  25. uint8_t marche; // peut prendre les valeurs 0 et 1 mais aussi d'autres valeurs >1 correspondants à des modes de fonctionnement divers.
  26. long compteur_impuls; //32 bits (4 bytes) ; compte les impulsions recues. est incrémenté automatiquement. ne fait qu'augmenter qq soit le sens de rotation.
  27.  
  28.  
  29. public:
  30. Step_Motor();
  31.  
  32. uint8_t bit_ENABLE;
  33. uint8_t bit_finCourse1;
  34. uint8_t bit_finCourse2;
  35. uint8_t bit_STEP;
  36. uint8_t bit_DIR;
  37.  
  38. uint16_t steps_par_tour;
  39. uint8_t tours_par_mm;
  40. uint8_t impuls_par_step;
  41. uint16_t steps_par_mm;
  42. uint16_t impuls_par_mm;
  43.  
  44. uint8_t stop_sens1; // (fin de course)
  45. uint8_t stop_sens2; // (fin de course)
  46.  
  47. long position_impuls; //32 bits (4 bytes) ; nombre d'impulsions; automatiquement incrémenté ou décrémenté par la rotation
  48.  
  49. void init();
  50. void enable();
  51. void disable();
  52. void RAZ_pos();
  53. int32_t get_compteur_impuls();
  54. void RAZ_compteur_impuls();
  55. void step();
  56. void set_sens(uint8_t valeur); // valeur = 0 ou 1.
  57. void set_marche(uint8_t valeur); // valeur = 0 ou 1. (1 -> mise en marche si l'état du stop_sens le permet. 0 -> arret du moteur)
  58. uint8_t get_marche();
  59. uint8_t get_sens();
  60. void set_stop_sens1(uint8_t valeur); // valeur = 0 ou 1. (1->signifie arret du moteur, 0 -> remise en marche possible)
  61. void set_stop_sens2(uint8_t valeur);
  62. };
  63.  
  64.  
  65. #endif
  66.  

12 Le programme de pilotage en C++ & Qt5 pour le PC :

13 Le code source du soft en Qt5 :

CODE SOURCE en Qt : le fichier mainwindow.cpp
  1. /*
  2. Gerber
  3. Programme écrit par Silicium628
  4. ce logiciel est libre et open source
  5. */
  6.  
  7. /**
  8. Si le programme ne se lance pas (plante au démarrage), effacer le fichier ~home/.cnc_gerber/cnc_gerber.ini
  9. qui a sans doute été corrompu par un autre programme.
  10. */
  11.  
  12.  
  13. #include "mainwindow.h"
  14. #include "math.h"
  15. #include <QFile>
  16. #include <QFileDialog>
  17. #include <QTextStream>
  18. #include <QDebug>
  19. #include "boutonled.cpp"
  20. #include "qled.cpp"
  21. #include <QMessageBox>
  22. #include <QTextCodec>
  23. #include <QTimer>
  24. #include <stdlib.h> /* atoi */
  25. #include <QScrollBar>
  26. #include <QProcess>
  27.  
  28.  
  29. // #include <QMouseEvent>
  30.  
  31.  
  32. QString version = "23.5";
  33.  
  34.  
  35. QColor couleur_ecran = QColor::fromRgb(0x002020); // 0x001A00
  36.  
  37. QColor couleur_piste1 = QColor::fromRgb(0x147E00); // non flashée1 0xF57900
  38. QColor couleur_piste2 = QColor::fromRgb(0x999999); // non flashée2
  39.  
  40. QColor couleur_pisteT = QColor::fromRgb(0x1E90FF); // en cours de tracé
  41. QColor couleur_pisteC = QColor::fromRgb(0x179CFF); // flashée
  42.  
  43. QColor couleur_pastille1 = QColor::fromRgb(0xFCE94F);// non flashée
  44. QColor couleur_pastilleT = QColor::fromRgb(0x1E90FF); // en cours de tracé
  45. QColor couleur_pastilleC = QColor::fromRgb(0x179CFF); // flashée
  46.  
  47. QColor couleur_fait = QColor::fromRgb(255, 0, 0, 255);
  48. QColor couleur_envoye = QColor::fromRgb(0, 100, 0, 255);
  49. QColor couleur_pointage = QColor::fromRgb(0x1B85FF);
  50. QColor couleur_milieu = QColor::fromRgb(255, 0, 0, 255);
  51. QColor couleur_DT = QColor::fromRgb(0, 255, 0, 255);
  52. QColor couleur_trou = QColor::fromRgb(0, 0, 0, 255);
  53. QColor couleur_texte = QColor::fromRgb(255, 255, 255, 255);
  54. QColor couleur_erreur = QColor::fromRgb(255, 255, 100, 255);
  55.  
  56. QBrush brush_pastille;
  57. QBrush brush_milieu;
  58. QBrush brush_DT;
  59. QBrush brush_trou;
  60.  
  61. QPen pen_piste(couleur_piste1, 2, Qt::SolidLine);
  62. QPen pen_pastille(couleur_pastille1, 1, Qt::SolidLine);
  63. QPen pen_trou(couleur_trou, 1, Qt::SolidLine);
  64. QPen pen_pointage(couleur_pointage, 1, Qt::SolidLine);
  65. QPen pen_milieu(couleur_milieu, 1, Qt::SolidLine);
  66. QPen pen_DT(couleur_DT, 1, Qt::SolidLine);
  67.  
  68. int x_scene, y_scene;
  69. int memo_x, memo_y;
  70.  
  71. QStringList liste_gerber1;
  72. QList <Aperture> liste_apertures; //<aperture> désigne une structure déclarée dans le mainwindow.h
  73. QList <Element> liste_elements; // tous types confondus, segments et pastilles
  74. QList <Element> liste_elements_tries;
  75. QList <Element> liste_pastilles; // en vue de les "creuser" logiciellement (ici) en raccourcissant les segments qui y aboutissent
  76. QList <Element> liste_segments;
  77. QList <Element> liste_trous;
  78.  
  79. //QString dir_gerber;
  80. QString fileName_en_cours;
  81. QStringList liste_log;
  82.  
  83. QString s_type;
  84. QString s_format;
  85. quint8 num_ap_use; // numéro de l'aperture en service lors de la phase de traçage
  86. QString s_aperture_use; // même chose en texte (D10, D11... etc)
  87.  
  88. quint16 num_ligne_en_cours1;
  89. quint16 num_ligne_en_cours3;
  90. quint16 num_ligne3;
  91. quint16 nb_lignes_envoyees =0;
  92. quint16 i_dess_tri;
  93. quint16 nb_erreurs =0;
  94. quint16 nb_pistes_nulles; // décrivant des déplacements ou des tracés de pste de longueur = 0
  95. quint16 nb_corrections =0;
  96.  
  97. quint16 num_element_proche_en_cours =0;
  98.  
  99. float zoom;
  100. float ech;
  101.  
  102.  
  103. qint16 Xcent, Ycent; // en 1/100 mm
  104. qint16 memo_X, memo_Y;
  105. QPointF pos_actuelle;
  106. qint16 X_max =0; // coordonné X max la plus grande parmis toutes celles de tous les éléments du circuit (servira à faire un miroir_x)
  107. qint16 Y_max =0; // coordonné Y max la plus grande parmis toutes celles de tous les éléments du circuit
  108.  
  109.  
  110. quint8 connect_ok =0;
  111. quint8 recu_PRET =0;
  112. quint8 flashage_en_cours = 0;
  113. quint8 simulation = 0;
  114. quint8 laser_on = 0; // allumage du laser en continu. POURS TESTS SEULEMENT !
  115. quint8 laser_bloque = 0; // empêche l'allumage du laser si =1 ; pour tests déplacements laser éteint
  116. quint8 trace_aspect_reel;
  117. quint8 findecourse = 0;
  118.  
  119. quint8 miroir=0;
  120. quint8 pastilles_creuses=0;
  121.  
  122. char buffer[100];
  123. QString msg ="";
  124. int pourcent = 0;
  125. QList <int> annonces_faites;
  126.  
  127. QString port_usb_name;
  128.  
  129. qreal sqr(qreal r1)
  130. {
  131. return r1 * r1;
  132. }
  133.  
  134.  
  135. QString valeur2txt4(int v1)
  136. {
  137. QString s;
  138. s.setNum(v1); // conversion num -> txt
  139. s = "0000"+s;
  140. s = s.right(4);
  141. return s;
  142. }
  143.  
  144.  
  145. QString valeur2txt2(int v1)
  146. {
  147. QString s;
  148. s.setNum(v1); // conversion num -> txt
  149. s = "00"+s;
  150. s = s.right(2);
  151. return s;
  152. }
  153.  
  154.  
  155.  
  156. MainWindow::MainWindow(QWidget *parent) :
  157. QMainWindow(parent)
  158. {
  159. setupUi(this);
  160. setWindowTitle("Gerber Photoplotter v:" + version + " - Silicium628");
  161.  
  162. // window()->setGeometry(0,0,1279,889);
  163. window()->setGeometry(0,0,1900,900); // -> affchage étendu
  164. textEdit_1->setGeometry(1285,5,595,855);
  165. //Btn_close_Tx1->setGeometry(1247,715,20,20);
  166. Btn_close_Tx1->setGeometry(1855,10,20,20);
  167. Btn_save_log->setGeometry(1780,10,70,20);
  168.  
  169. scene1 = new SceneCliquable(this);
  170.  
  171. InitTable3();
  172. InitTable4();
  173. InitTable5();
  174.  
  175. brush_pastille.setColor(couleur_pastille1);
  176. brush_pastille.setStyle(Qt::SolidPattern);
  177.  
  178. brush_milieu.setColor(couleur_milieu);
  179. brush_milieu.setStyle(Qt::SolidPattern);
  180.  
  181. brush_DT.setColor(couleur_DT);
  182. brush_DT.setStyle(Qt::SolidPattern);
  183.  
  184. brush_trou.setColor(couleur_trou);
  185. brush_trou.setStyle(Qt::SolidPattern);
  186.  
  187. pen_piste.setCapStyle(Qt::RoundCap); // extrémités des pistes arrondies - voir QPen dans la doc.
  188.  
  189. // QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
  190.  
  191.  
  192. effacer_ecran(); // et crée les groupes héritiers de la scene ainsi que la connexion entre les clics et le slot ici concerné
  193.  
  194. BoutonLed1 = new QBoutonLed(frame_10);
  195. BoutonLed1->setGeometry(QRect(4, 4, 40, 40));
  196. BoutonLed1->setCouleur(QColor (255,255,0));
  197. connect(BoutonLed1, SIGNAL(toggled(bool)), this, SLOT(on_BoutonLed1_toggled(bool)));
  198.  
  199. BoutonLed2 = new QBoutonLed(frame_12); // affichage REEL
  200. BoutonLed2->setGeometry(QRect(4, 4, 40, 40));
  201. BoutonLed2->setCouleur(QColor::fromRgb(0xFFFF00));
  202. connect(BoutonLed2, SIGNAL(toggled(bool)), this, SLOT(on_BoutonLed2_toggled(bool)));
  203.  
  204. trace_aspect_reel=1;
  205.  
  206. BoutonLed3 = new QBoutonLed(frame_3); // allumage laser
  207. BoutonLed3->setGeometry(QRect(4, 4, 40, 40));
  208. BoutonLed3->setCouleur(QColor::fromRgb(0x1E90FF));
  209. connect(BoutonLed3, SIGNAL(toggled(bool)), this, SLOT(on_BoutonLed3_toggled(bool)));
  210.  
  211. BoutonLed4 = new QBoutonLed(frame_17); // blocage laser
  212. BoutonLed4->setGeometry(QRect(4, 4, 40, 40));
  213. BoutonLed4->setCouleur(QColor::fromRgb(0xFF0000));
  214. connect(BoutonLed4, SIGNAL(toggled(bool)), this, SLOT(on_BoutonLed4_toggled(bool)));
  215.  
  216.  
  217. Led0 = new QLed(frame1); // Connexion USB ; verte = ok ; rouge = impossible
  218. Led0->setForme(1);
  219. Led0->setGeometry(QRect(4, 4, 40, 40));
  220. Led0->setCouleur(QColor (0,255,0));
  221. Led0->setEtat(0);
  222.  
  223. //=======================================================================================
  224. // jaune = Connexion USB établie, attente message bienvenue ; verte message "ATMEGA" reçu
  225. Led1 = new QLed(frame_4);
  226. Led1->setForme(1);
  227. Led1->setGeometry(QRect(4, 4, 40, 40));
  228. Led1->setCouleur(QColor (255,255,0));
  229. Led1->setEtat(0);
  230.  
  231. //=======================================================================================
  232. // envoi commande par USB ; jaune = message envoyé - verte = reçu réponse ("PRET")
  233. Led2 = new QLed(frame_6);
  234. Led2->setForme(1);
  235. Led2->setGeometry(QRect(4, 4, 40, 40));
  236. Led2->setCouleur(QColor (255,255,0));
  237. Led2->setEtat(0);
  238.  
  239. //=======================================================================================
  240. // VERTE ; allumée (verte) = flashage en cours
  241. Led3 = new QLed(frame_7);
  242. Led3->setForme(1);
  243. Led3->setGeometry(QRect(4, 4, 40, 40));
  244. Led3->setCouleur(QColor (0,255,0)); // VERTE
  245. Led3->setEtat(0);
  246.  
  247. //=======================================================================================
  248. // ROUGE, ronde ; allumée = FIN DE COURSE ACTIF !
  249. Led4 = new QLed(frame_11);
  250. Led4->setForme(2); // ronde
  251. Led4->setGeometry(QRect(4, 4, 40, 40));
  252. Led4->setCouleur(QColor (255,0,0)); // ROUGE
  253. Led4->setEtat(0);
  254.  
  255. //=======================================================================================
  256. /**
  257.   Led5 = new QLed(frame_18);
  258.   Led5->setForme(1);
  259.   Led5->setGeometry(QRect(2, 2, 20, 20));
  260.   Led5->setCouleur(QColor (255,255,0));
  261.   Led5->setEtat(0);
  262. **/
  263. //=======================================================================================
  264. Timer1 = new QTimer(this);
  265. connect(Timer1, SIGNAL(timeout()), this, SLOT(Timer1_clic()));
  266.  
  267. Timer2 = new QTimer(this);
  268. connect(Timer2, SIGNAL(timeout()), this, SLOT(Timer2_clic()));
  269.  
  270. Timer3 = new QTimer(this);
  271. connect(Timer3, SIGNAL(timeout()), this, SLOT(Timer3_clic()));
  272.  
  273. memo_X =0;
  274. memo_Y =0;
  275. num_ligne_en_cours1 = 0;
  276.  
  277. spinBox_zoom2->setValue(5);
  278.  
  279.  
  280. // QString filename = "./Fichiers_gerber/attenuateur-F_Cu.gtl";
  281. // QString filename = "./Fichiers_gerber/largeur_pistes-B_Cu.gbl";
  282. fileName_en_cours = "./Fichiers_gerber/largeur_pistes-F_Cu.gtl";
  283.  
  284. lecture_fichier_init();
  285.  
  286.  
  287. lineEdit_6->setText(fileName_en_cours);
  288.  
  289. int result = lire_fichier_gerber(fileName_en_cours);
  290.  
  291. if (result == 0)
  292. {
  293. analyse_fichier_gerber();
  294. //tout_decliquer();
  295. //dessiner_liste_elements_tries();
  296. classer_auto();
  297. trace_liste_apertures();
  298. on_BoutonLed2_toggled(true);
  299. BoutonLed2->setChecked(true);
  300. }
  301.  
  302. BoutonLed4->setChecked(1);
  303.  
  304. // pardéfaut :
  305. // port_usb_name="ttyUSB0";
  306. efface_buffer();
  307.  
  308. port_usb_name="ttyACM0";
  309. actionConnexion->setText("Connexion_ttyACM0");
  310.  
  311. }
  312.  
  313.  
  314.  
  315. MainWindow::~MainWindow()
  316. {
  317.  
  318. }
  319.  
  320.  
  321.  
  322.  
  323. void MainWindow::InitTable3()
  324. {
  325. QStringList liste_entetes;
  326. tableWidget_3->clear();
  327.  
  328. liste_entetes <<"CMD"<<"longueur";
  329.  
  330. tableWidget_3->setHorizontalHeaderLabels (liste_entetes);
  331. tableWidget_3->setColumnCount(2);
  332. tableWidget_3->setRowCount(1);
  333.  
  334. }
  335.  
  336.  
  337. void MainWindow::InitTable4()
  338. {
  339. QStringList liste_entetes;
  340. liste_entetes <<"CMD";
  341.  
  342. tableWidget_4->clear();
  343. tableWidget_4->setColumnCount(2);
  344. tableWidget_4->setRowCount(0);
  345. tableWidget_4->setHorizontalHeaderLabels (liste_entetes);
  346.  
  347. }
  348.  
  349.  
  350. void MainWindow::InitTable5()
  351. {
  352. QStringList liste_entetes;
  353.  
  354. tableWidget_5->clear();
  355. tableWidget_5->setColumnCount(2);
  356. tableWidget_5->setRowCount(0);
  357. tableWidget_5->setHorizontalHeaderLabels (liste_entetes);
  358.  
  359. }
  360.  
  361.  
  362.  
  363. void MainWindow::effacer_ecran()
  364. {
  365. scene1->clear();
  366. scene1 = new SceneCliquable(this);
  367. connect(scene1, SIGNAL(envoiPosition(int, int) ), this, SLOT (onReceptPos(int, int) ));
  368. connect(scene1, SIGNAL(envoiMove(int, int) ), this, SLOT (onReceptMove(int, int) ));
  369.  
  370. scene1->setBackgroundBrush(couleur_ecran);
  371. scene1->setSceneRect(-800,-1200,2000,2000); // plus grand que la partie affichée
  372. graphicsView1->setScene(scene1);
  373. graphicsView1->setGeometry(5,10,896,510); // POSITION et dimensions de l'écran
  374. graphicsView1->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
  375.  
  376. groupe_trace = new QGraphicsItemGroup();
  377. scene1->addItem(groupe_trace);
  378.  
  379. groupe_segments_faits = new QGraphicsItemGroup();
  380. scene1->addItem(groupe_segments_faits);
  381.  
  382. groupe_pastilles_faites = new QGraphicsItemGroup();
  383. scene1->addItem(groupe_pastilles_faites);
  384.  
  385. groupe_pointage = new QGraphicsItemGroup();
  386. scene1->addItem(groupe_pointage);
  387.  
  388. groupe_trous = new QGraphicsItemGroup();
  389. scene1->addItem(groupe_trous);
  390.  
  391. }
  392.  
  393.  
  394.  
  395. void MainWindow::effacer_pointage()
  396. {
  397. foreach( QGraphicsItem *item, groupe_pointage->childItems() )
  398. {
  399. delete item;
  400. }
  401. }
  402.  
  403.  
  404. void MainWindow::lecture_fichier_init()
  405. {
  406. QString line, tx1, tx2;
  407. int p2;
  408. tx1 = "";
  409.  
  410. QFile file1(QDir::homePath() + "/.cnc_gerber/cnc_gerber.ini");
  411.  
  412. if (file1.open(QIODevice::ReadOnly | QIODevice::Text))
  413. {
  414. QTextStream in(&file1);
  415. in.reset();
  416. while ( !in.atEnd() )
  417. {
  418. line = in.readLine();
  419. if (line.left(1) !="#")
  420. {
  421. if (line.indexOf("fileName_en_cours") != -1)
  422. {
  423. if ( (p2 = line.indexOf('=')) != -1)
  424. {
  425. line.remove(0,p2+1);
  426. fileName_en_cours = line;
  427. }
  428. }
  429. }
  430. }
  431. file1.close();
  432. } else { fileName_en_cours = ""; tx1 = "fichier .ini non trouve, ce n'est pas grave !"; }
  433.  
  434. if (tx1 !="")
  435. {
  436. QMessageBox msgBox;
  437. msgBox.setText(tx1);
  438. msgBox.exec();
  439. }
  440. // statusBar1->showMessage(tx1 + " "+ tx2);
  441. }
  442.  
  443.  
  444.  
  445. void MainWindow::enregistrer_fichier_init()
  446. {
  447. QDir dossierIni(QDir::homePath() + "/.cnc_gerber/");
  448. if (!dossierIni.exists())
  449.  
  450. {
  451. QMessageBox msgBox;
  452. msgBox.setText("Le repertoire : ~/" + dossierIni.dirName() + " n'existe pas, je vais le creer, et y placer le fichier de configuration 'cnc_gerber.ini' ");
  453. msgBox.exec();
  454. QDir dossierPerso(QDir::homePath());
  455. dossierPerso.mkdir(".cnc_gerber");
  456. }
  457.  
  458. QFile file1(QDir::homePath() + "/.cnc_gerber/cnc_gerber.ini");
  459. if (file1.open(QIODevice::WriteOnly | QIODevice::Text))
  460. {
  461. QTextStream out(&file1);
  462. QString V;
  463.  
  464. out << "# les lignes commençant par # sont ignorees";
  465. out << '\n';
  466. out << "# Ce fichier est genere automatiquement par le programme";
  467. out << '\n';
  468. out << "#";
  469. out << '\n';
  470.  
  471. out << "fileName_en_cours=" << fileName_en_cours;
  472. out << '\n';
  473. }
  474. file1.close();
  475. }
  476.  
  477.  
  478.  
  479. int MainWindow::lire_fichier_gerber(QString nom_fichier1)
  480. {
  481. QFile file1(nom_fichier1);
  482.  
  483. if( !file1.exists() )
  484. {
  485. qDebug() << "Le fichier n'existe pas.";
  486. return 1;
  487. }
  488.  
  489. QString s1;
  490.  
  491. liste_gerber1.clear();
  492. listWidget_1->clear();
  493.  
  494. if (!file1.open(QIODevice::ReadOnly | QIODevice::Text)) return 1;
  495. QTextStream in1(&file1);
  496.  
  497. int num_ligne = 0;
  498. while ( !in1.atEnd() )
  499. {
  500. num_ligne++;
  501. s1.setNum(num_ligne);// conversion num -> txt
  502. QString line_i = in1.readLine();
  503. listWidget_1->addItem(s1+": "+line_i);
  504. liste_gerber1 << line_i;
  505. }
  506. file1.close();
  507.  
  508. checkBox_creuser->setChecked(false);
  509. checkBox_miroir->setChecked(false);
  510.  
  511.  
  512.  
  513. return 0;
  514.  
  515. }
  516.  
  517.  
  518.  
  519. void MainWindow::pointer(int i)
  520. { // à l'écran
  521.  
  522. QPoint M;
  523. Element element_i;
  524. element_i = liste_elements_tries[i];
  525. M = element_i.M;
  526.  
  527. effacer_pointage();
  528.  
  529. M.setY(-M.y());// symétrie verticale
  530. M /= ech;
  531.  
  532.  
  533. // trace ligne verticale
  534. QGraphicsLineItem *segment_pointage1 = new QGraphicsLineItem(groupe_pointage);
  535. segment_pointage1->setPen(pen_pointage);
  536. segment_pointage1->setLine(M.x()+1, -1000, M.x()+1, 1000); // le '+1' évite de masquer la couleur de l'élément pointé si très fin (=1 pixel)
  537.  
  538. // trace ligne horizontale
  539. QGraphicsLineItem *segment_pointage2 = new QGraphicsLineItem(groupe_pointage);
  540. segment_pointage2->setPen(pen_pointage);
  541. segment_pointage2->setLine(-1000, M.y()+1, 1000, M.y()+1);
  542. }
  543.  
  544.  
  545.  
  546. void MainWindow::tracer_mire_origine()
  547. { // à l'écran
  548. QPoint P;
  549.  
  550. P.setX(0);
  551. P.setY(0);
  552. P /= ech;
  553.  
  554.  
  555. QGraphicsLineItem *segment_pointage1 = new QGraphicsLineItem(groupe_pointage);
  556. segment_pointage1->setPen(pen_pointage);
  557. segment_pointage1->setLine(P.x()-10, P.x()-10, P.x()+10, P.x()+10);
  558.  
  559. QGraphicsLineItem *segment_pointage2 = new QGraphicsLineItem(groupe_pointage);
  560. segment_pointage2->setPen(pen_pointage);
  561. segment_pointage2->setLine(P.x()-10, P.x()+10, P.x()+10, P.x()-10);
  562. tracer_texte(-20, -20, " (x=0; y=0)");
  563. QString S1, S2;
  564.  
  565. S1 = "Unités du fichier Gerber = ";
  566. S2="";
  567. if (s_type == "IN"){S2 = "pouces"; }
  568. if (s_type == "MM"){S2 = "mm"; }
  569. S1 += S2;
  570. tracer_texte(-20, -300, S1);
  571.  
  572. S1 = "Format des nombres décimaux = " + s_format + " soit : ";
  573. S2=s_format[0];
  574. S2 += " digits unités +";
  575. S2 += s_format[1];
  576. S2 += " décimales" ;
  577. S1 += S2;
  578. tracer_texte(-20, -600, S1);
  579.  
  580. }
  581.  
  582.  
  583. void MainWindow::tracer_segment(QPoint A, QPoint B, qreal largeur, QColor couleur_i, QGraphicsItemGroup *groupe_i )
  584. { // à l'écran
  585.  
  586. QPoint M; // milieu
  587. QPoint DT; // deux tiers
  588.  
  589. QLine SEG;
  590.  
  591. A.setY(-A.y());// symétrie verticale
  592. B.setY(-B.y());
  593.  
  594. A /= ech;
  595. B /= ech;
  596.  
  597. SEG.setPoints(A,B);
  598.  
  599. M.setX((A.x()+B.x())/2);
  600. M.setY((A.y()+B.y())/2);
  601.  
  602. DT.setX( A.x()/3 + 2*B.x()/3 );
  603. DT.setY( A.y()/3 + 2*B.y()/3 );
  604.  
  605. largeur /= ech;
  606. if (largeur <1) {largeur = 1;}
  607.  
  608. QGraphicsLineItem *segment_trace = new QGraphicsLineItem(groupe_i);
  609. pen_piste.setWidth(largeur);
  610. pen_piste.setColor(couleur_i);
  611. segment_trace->setPen(pen_piste);
  612. segment_trace->setLine (SEG );
  613.  
  614.  
  615. QGraphicsEllipseItem *ellipse1 = new QGraphicsEllipseItem (groupe_i); // repere de selection au centre du segment
  616. pen_piste.setWidth(1);
  617. ellipse1->setPen(pen_milieu);
  618. ellipse1->setBrush(brush_milieu);
  619. ellipse1->setRect(M.x()-1, M.y()-1, 2, 2);
  620.  
  621. QGraphicsEllipseItem *ellipse2 = new QGraphicsEllipseItem (groupe_i); // repere au 2/3 du segment
  622. pen_piste.setWidth(1);
  623. ellipse2->setPen(pen_DT);
  624. ellipse2->setBrush(brush_DT);
  625. ellipse2->setRect(DT.x()-1, DT.y()-1, 2, 2);
  626. }
  627.  
  628.  
  629.  
  630. void MainWindow::tracer_pastille_rectangulaire(QPoint A, QPoint B, QColor couleur_i, QGraphicsItemGroup *groupe_i)
  631. { // à l'écran
  632. // A est le coin en bas à gauche, B est le coin en haut à droite
  633.  
  634. qreal w, h;
  635. qreal n;
  636. qreal x1, y1, x2, y2;
  637. qreal largeur_trait, d;
  638.  
  639. A.setY(-A.y());// symétrie verticale
  640. B.setY(-B.y());
  641.  
  642. A /= ech;
  643. B /= ech;
  644.  
  645. w = (A-B).x(); // largeur pastille
  646. h = (A-B).y(); // hauteur
  647.  
  648.  
  649. if(trace_aspect_reel == 0)
  650. {
  651. QGraphicsRectItem *rectangle = new QGraphicsRectItem(groupe_i);
  652. brush_pastille.setColor(couleur_i);
  653. rectangle->setBrush(brush_pastille);
  654. rectangle->setRect(A.x()-w/2, A.y()-h/2, w, h);
  655. }
  656.  
  657. if(trace_aspect_reel == 1)
  658. {
  659. largeur_trait = 10; // à transmettre par paramètre...
  660. largeur_trait /= ech;
  661.  
  662.  
  663. pen_piste.setWidth(largeur_trait);
  664. pen_piste.setColor(couleur_i);
  665.  
  666. n=0.5;
  667. int stop =0;
  668. while ((n<6) && (stop == 0))
  669. {
  670. d= 2 * n * largeur_trait;
  671. if (d > (-w/2) || d > (h/2)) {stop =1;}
  672.  
  673. //---------------------------------------------------------------------------------
  674. x1=A.x()-w/2 - d;
  675. y1=A.y()-h/2 + d;
  676. x2=A.x()+w/2 + d;
  677. y2=A.y()-h/2 + d;
  678.  
  679. QGraphicsLineItem *segment_trace1 = new QGraphicsLineItem(groupe_i);
  680. segment_trace1->setPen(pen_piste);
  681. segment_trace1->setLine (x1,y1,x2,y2);
  682. //---------------------------------------------------------------------------------
  683. x1=A.x()+w/2 + d;
  684. y1=A.y()-h/2 + d;
  685. x2=A.x()+w/2 + d;
  686. y2=A.y()+h/2 - d;
  687.  
  688. QGraphicsLineItem *segment_trace2 = new QGraphicsLineItem(groupe_i);
  689. segment_trace2->setPen(pen_piste);
  690. segment_trace2->setLine (x1,y1,x2,y2);
  691. //---------------------------------------------------------------------------------
  692.  
  693. x1=A.x()+w/2 + d;
  694. y1=A.y()+h/2 - d;
  695. x2=A.x()-w/2 - d;
  696. y2=A.y()+h/2 - d;
  697.  
  698. QGraphicsLineItem *segment_trace3 = new QGraphicsLineItem(groupe_i);
  699. segment_trace3->setPen(pen_piste);
  700. segment_trace3->setLine (x1,y1,x2,y2);
  701. //---------------------------------------------------------------------------------
  702.  
  703. x1=A.x()-w/2 - d;
  704. y1=A.y()+h/2 - d;
  705. x2=A.x()-w/2 - d;
  706. y2=A.y()-h/2 + d;
  707.  
  708. QGraphicsLineItem *segment_trace4 = new QGraphicsLineItem(groupe_i);
  709. segment_trace4->setPen(pen_piste);
  710. segment_trace4->setLine (x1,y1,x2,y2);
  711.  
  712. n++;
  713. }
  714. }
  715.  
  716. }
  717.  
  718.  
  719. void MainWindow::tracer_trou(QPoint A, quint16 diametre)
  720. { // à l'écran
  721. qreal x, y;
  722.  
  723. A.setY(-A.y());// symétrie verticale
  724. A /= ech;
  725.  
  726. x=A.x();
  727. y=A.y();
  728.  
  729. diametre /= ech;
  730.  
  731. QGraphicsEllipseItem *ellipse1 = new QGraphicsEllipseItem (groupe_trous); // PASTILLE
  732. ellipse1->setBrush(brush_trou);
  733.  
  734. ellipse1->setRect(x-diametre/2, y-diametre/2, diametre, diametre);
  735. }
  736.  
  737.  
  738.  
  739.  
  740. void MainWindow::tracer_pastille_ronde(QPoint A, QPoint B, QColor couleur_i, QGraphicsItemGroup *groupe_i)
  741. { // à l'écran
  742.  
  743. qreal w, h;
  744. qreal n;
  745. qreal x, y;
  746. qreal largeur_trait, d, rt;
  747.  
  748. rt = 80 / ech; // rayon du trou
  749.  
  750. A.setY(-A.y());// symétrie verticale
  751. B.setY(-B.y());// symétrie verticale
  752.  
  753. A /= ech;
  754. B /= ech;
  755.  
  756. w = A.x()-B.x();
  757. h = A.y()-B.y();
  758.  
  759. if (h<(100/ech)) {rt=0;} // ne pas percer les très petites pastilles qui sont des raccordements de pistes
  760.  
  761. brush_pastille.setColor(couleur_i);
  762.  
  763.  
  764. if(trace_aspect_reel == 0)
  765. {
  766. // trace un disque plein
  767. QGraphicsEllipseItem *ellipse1 = new QGraphicsEllipseItem (groupe_i); // PASTILLE
  768. ellipse1->setBrush(brush_pastille);
  769.  
  770. x=A.x()-w/2;
  771. y=A.y()-h/2;
  772.  
  773. ellipse1->setRect(x, y, w, h);
  774. }
  775.  
  776.  
  777. if(trace_aspect_reel == 1)
  778. {
  779. // trace des cercles concentriques avec l'épaisseur de la plume
  780. largeur_trait = 10; // à transmettre par paramètre...
  781. largeur_trait /= ech;
  782.  
  783. pen_piste.setWidth(largeur_trait);
  784. pen_piste.setColor(couleur_i);
  785.  
  786. n=0.5;
  787. int stop =0;
  788. while ((n<12) && (stop == 0))
  789. {
  790. d= 2.5 * n * largeur_trait;
  791.  
  792. // if (d > (-w/2) || d > (h/2)) {stop =1;}
  793. if ((h-d) < rt ) {stop =1;}
  794. //---------------------------------------------------------------------------------
  795.  
  796. QGraphicsEllipseItem *ellipse1 = new QGraphicsEllipseItem ();
  797. ellipse1->setPen(pen_piste);
  798.  
  799. x=A.x()-w/2;
  800. y=A.y()-h/2;
  801.  
  802. ellipse1->setRect(x-d/2, y+d/2, w+d, h-d);
  803. groupe_i->addToGroup(ellipse1);
  804.  
  805. //---------------------------------------------------------------------------------
  806.  
  807. n++;
  808. }
  809. }
  810. }
  811.  
  812.  
  813. void MainWindow::tracer_texte(int x, int y, QString texte_i)
  814. { // à l'écran
  815. y=-y;
  816.  
  817. x /= ech;
  818. y /= ech;
  819.  
  820. QGraphicsTextItem *gtexte = new QGraphicsTextItem(groupe_trace);
  821. gtexte->setDefaultTextColor(couleur_texte);
  822. gtexte->setPos(x,y);
  823. gtexte->setPlainText(texte_i);
  824. }
  825.  
  826.  
  827.  
  828.  
  829.  
  830.  
  831.  
  832. int MainWindow::dessine_1ligne_liste_triee(int i) // dessine à l'écran 1 ligne de la "liste_elements_tries"
  833. {
  834. QColor couleur1;
  835. char sorte_i;
  836. char forme_i;
  837. int clique_i;
  838. QPoint A, B, M, WH; // M : milieu de l'élément
  839. quint8 num_ap, w;
  840. Element element_i;
  841. Aperture aperture_i;
  842.  
  843. // liste_elements_i = *lst_elm;
  844. int i_max = liste_elements_tries.count();
  845. if (i>i_max) {return 1;}
  846. element_i = liste_elements_tries[i];
  847. sorte_i = element_i.sorte;
  848. clique_i = element_i.clique;
  849.  
  850. A = element_i.A;
  851. B = element_i.B;
  852. M = element_i.M;
  853.  
  854. num_ap = element_i.num_aperture-10;
  855.  
  856. int n_max = liste_apertures.length();
  857. if (num_ap >= n_max) {return 1;}
  858.  
  859. aperture_i = liste_apertures[num_ap];
  860.  
  861. WH = aperture_i.WH;
  862. w = WH.x();
  863.  
  864. forme_i=aperture_i.forme;
  865.  
  866. if (sorte_i == 'S') // SEGMENT
  867. {
  868. if (clique_i == 0) { couleur1 = couleur_piste1;}; // non tracée
  869. if (clique_i == 1) { couleur1 = couleur_pisteT;} // en cours de traçage
  870. if (clique_i > 1) { couleur1 = couleur_pisteC;} // tracée
  871. tracer_segment(A, B, w, couleur1 , groupe_segments_faits);
  872. }
  873.  
  874. if (sorte_i == 'P') // PASTILLE
  875. {
  876. if (clique_i == 0) { couleur1 = couleur_pastille1;}
  877. if (clique_i == 1) { couleur1 = couleur_pastilleT;}
  878. if (clique_i > 1) { couleur1 = couleur_pastilleC;}
  879.  
  880. if (forme_i == 'R') { tracer_pastille_rectangulaire(A, A + WH, couleur1, groupe_pastilles_faites); }
  881. if (forme_i == 'C') { tracer_pastille_ronde(A, A + WH, couleur1, groupe_pastilles_faites); }
  882. }
  883.  
  884. // s1.setNum(i); // conversion num -> txt
  885. // tracer_texte(Xcent, Ycent, s1);
  886. return 0;
  887. }
  888.  
  889.  
  890.  
  891.  
  892. void MainWindow::trace_liste_apertures()
  893. { // à l'écran
  894. Aperture aperture_i;
  895. uint i, i_max, i_max2;
  896. QPoint Ai, Bi;
  897. // int xi, yi, wi, hi;
  898. QString s1, nom_aperture;
  899. i_max = liste_apertures.length();
  900. i_max2 = i_max/2;
  901. int n=0;
  902. for(i=0; i<i_max; i++)
  903. {
  904. aperture_i = liste_apertures[i];
  905.  
  906. if (i<=i_max2)
  907. {
  908. Ai.setX(-2500);
  909. Ai.setY(500*n);
  910. }
  911. else
  912. {
  913. Ai.setX(-1000);
  914. Ai.setY(500*n);
  915. }
  916.  
  917. Bi = Ai+aperture_i.WH;
  918.  
  919. s1.setNum(i+10); // conversion num -> txt
  920. s1="00" + s1;
  921. s1 = s1.right(2);
  922. nom_aperture = "D" + s1;
  923. if (aperture_i.forme == 'R'){ tracer_pastille_rectangulaire(Ai, Bi, QColor::fromRgb(0xE3E300), groupe_trace);}
  924. if (aperture_i.forme == 'C'){ tracer_pastille_ronde(Ai, Bi, QColor::fromRgb(0xE3E300), groupe_trace);}
  925. tracer_texte(Ai.x()+200, Ai.y()+100, nom_aperture);
  926. n++;
  927. if (n>i_max2) {n=0;}
  928. }
  929.  
  930. }
  931.  
  932.  
  933.  
  934. void MainWindow::dessiner_liste_elements_tries()
  935. {
  936. int i, i_max;
  937. i_max = liste_elements_tries.count();
  938.  
  939.  
  940. for (i=0; i<i_max; i++ )
  941. {
  942. dessine_1ligne_liste_triee(i);
  943. }
  944. tracer_mire_origine();
  945. }
  946.  
  947.  
  948.  
  949.  
  950. int MainWindow::detecte_element_pointe(QPoint P) // par un clic sur le pt milieu d'un élément scene1 (non déjà cliqué)
  951. {
  952. int i, i_max, num_origine, nouveau_num;
  953.  
  954. qreal distance;
  955. QPointF M;
  956. QString s1, s2;
  957. //int clique_i;
  958.  
  959. i_max = liste_elements.count();
  960.  
  961.  
  962. for (i=0; i<i_max; i++ )
  963. {
  964. //clique_i = liste_elements[i].clique;
  965.  
  966. M = liste_elements[i].M;
  967.  
  968.  
  969. distance = sqrt(sqr(M.x()-P.x()) + sqr(M.y()-P.y()));
  970.  
  971. if (distance < 50)
  972. {
  973. // qDebug() << "trouvé élément num (TW4): " << i;
  974. liste_elements[i].clique=1;
  975.  
  976. num_origine = liste_elements[i].num_origine;
  977. nouveau_num = liste_elements[i].nouveau_num;
  978.  
  979. s1.setNum(num_origine); // conversion num -> txt
  980. s2.setNum(nouveau_num); // conversion num -> txt
  981. lineEdit_5->setText(s1+" - "+s2);
  982.  
  983.  
  984. // affiche_liste_elements(); // dans le tableau
  985. // affiche_liste_elements_tries(); // dans le tableau
  986.  
  987. tableWidget_4->selectRow(num_origine);
  988. tableWidget_5->selectRow(nouveau_num-1);
  989.  
  990. pointer(nouveau_num-1);
  991. liste_elements_tries[nouveau_num-1].clique =2;
  992. dessine_1ligne_liste_triee(nouveau_num-1); // afin de positionner le pointage sur l'élément cliqué
  993. }
  994. }
  995. return 0;
  996. }
  997.  
  998.  
  999.  
  1000.  
  1001.  
  1002. void MainWindow::inverser_element(Element *EL)
  1003. {
  1004. QPoint PP;
  1005. QString s_xa, s_xb, s_ya, s_yb, s_ap, s1;
  1006.  
  1007. PP = EL->A;
  1008. EL->A = EL->B;
  1009. EL->B = PP;
  1010.  
  1011. s_xa.setNum(EL->A.x()); // conversion num -> txt
  1012. s_xa = "0000"+s_xa;
  1013. s_xa = s_xa.right(4);
  1014.  
  1015. s_xa = valeur2txt4(EL->A.x());
  1016. s_ya = valeur2txt4(EL->A.y());
  1017. s_xb = valeur2txt4(EL->B.x());
  1018. s_yb = valeur2txt4(EL->B.y());
  1019. s_ap = "D" + valeur2txt2(EL->num_aperture);
  1020.  
  1021. s1 = "SXA" + s_xa + "YA" + s_ya + "XB" + s_xb + "YB" + s_yb + s_ap;
  1022. EL->texte_affichable = s1;
  1023.  
  1024. }
  1025.  
  1026.  
  1027.  
  1028. quint16 MainWindow::detecte_element_proche()
  1029. // source: liste_elements
  1030. // destination: liste_elements_tries
  1031. {
  1032. quint16 i, i_max;
  1033. Element EL_ok;
  1034.  
  1035. qreal distance1, distance2;
  1036. quint16 num_element_proche, nouveau_num;
  1037. QPointF ptIa, ptIb;
  1038. QPointF D1, D2;
  1039. qreal ds_min;
  1040.  
  1041.  
  1042. int clique_i;
  1043. int a_permuter;
  1044.  
  1045. // pos_actuelle = liste_elements[num_element].B; // point d'une des extremités du segment, celle où on se trouve
  1046.  
  1047. i_max = liste_elements.count();
  1048.  
  1049. ds_min = 1e6;
  1050. num_element_proche = 0; // pour retourner 0 si aucune occurence trouvée
  1051.  
  1052. a_permuter=0;
  1053.  
  1054. for (i=1; i<i_max; i++ ) // attention : 1 et pas 0 ! les numeros d'ordres commencent à 1. (zéro = pas d'élémént trouvé)
  1055. {
  1056. clique_i = liste_elements[i].clique;
  1057. if (clique_i == 0) // on ignore les éléments déjà cliqués
  1058. {
  1059. ptIa = liste_elements[i].A;
  1060. ptIb = liste_elements[i].B;
  1061.  
  1062. D1=pos_actuelle-ptIa;
  1063. distance1 = sqrt(sqr(D1.x())+sqr(D1.y()));
  1064. D2=pos_actuelle-ptIb;
  1065. distance2 = sqrt(sqr(D2.x())+sqr(D2.y()));
  1066.  
  1067. if ((distance1 < ds_min) )
  1068. {// cas(2) le point B(x2,y2) du segment où on se trouve est proche du point A(x1, y1) du segment[i]
  1069. ds_min = distance1;
  1070. num_element_proche = i;
  1071. EL_ok = liste_elements[num_element_proche];
  1072. a_permuter=0;
  1073. }
  1074.  
  1075. if ((distance2 < ds_min) )
  1076. {// cas(4) le point B(x2,y2) du segment où on se trouve est proche du point B(x2, y2) du segment[i]
  1077. ds_min = distance2;
  1078. num_element_proche = i;
  1079. EL_ok = liste_elements[num_element_proche];
  1080. a_permuter=1;
  1081. }
  1082. // sachant que les segments seront toujours tracés dans l'ordre A->B, pour les cas (3) et (4) on permute les points A et B du segment[i]
  1083. // et vu que l'on se trouve toujours sur le point B à la fin d'un tracé, les cas (1) et (3) ne se présentent pas.
  1084. }
  1085. }
  1086. //qDebug() << num_element_proche +1; // +1 pour correspondre aux num du tableau
  1087. //qDebug() << "permuter = " << a_permuter;
  1088.  
  1089. if (a_permuter == 1)
  1090. {
  1091. //qDebug() << "A="<< EL_ok.A << " B=" << EL_ok.B;
  1092. //qDebug() << EL_ok.texte_affichable;
  1093.  
  1094. inverser_element(&EL_ok);
  1095.  
  1096. //qDebug() << "j'ai permuté";
  1097. //qDebug() << "A=" << EL_ok.A << " B=" << EL_ok.B;
  1098. //qDebug() << EL_ok.texte_affichable;
  1099. }
  1100.  
  1101. liste_elements[num_element_proche].clique=1;
  1102. EL_ok.clique=1;
  1103. if(num_element_proche !=0)
  1104. {
  1105. liste_elements_tries << EL_ok;
  1106. nouveau_num = liste_elements_tries.count();
  1107. liste_elements[num_element_proche].nouveau_num = nouveau_num;
  1108. }
  1109. pos_actuelle = EL_ok.B;
  1110.  
  1111. /*
  1112.   affiche_liste_elements(); // dans le tableau
  1113.   affiche_liste_elements_tries(); // dans le tableau
  1114.  
  1115.   dessiner_liste_elements(); // sur la scene graphique
  1116. */
  1117. // if(num_element_proche !=0) { dessine_1ligne_liste(num_element_proche); } // afin de positionner le pointage sur l'élément cliqué
  1118.  
  1119. return num_element_proche; // retournera 0 si aucune occurence trouvée
  1120.  
  1121. }
  1122.  
  1123.  
  1124.  
  1125.  
  1126.  
  1127. /**
  1128. Rappel : différentes lignes de la liste de sortie :
  1129.  
  1130. +AP:D11R=X0200Y0045
  1131. USE:D12
  1132. PX1550Y0851
  1133. GX2985Y1080
  1134. TX3429Y1334
  1135.  
  1136. **/
  1137.  
  1138.  
  1139. float convert_unite(float f_i)
  1140. {
  1141. //entree en inch ( = pouce; 1 pouce = 25.4mm) ou en mm suivant le type de fichier gerber
  1142. //resultat en mm
  1143. float f2;
  1144. f2 =0;
  1145.  
  1146. if (s_type == "IN") {f2 = f_i * 25.4; }
  1147. if (s_type == "MM") {f2 = f_i;}
  1148.  
  1149. return f2;
  1150. }
  1151.  
  1152.  
  1153. int MainWindow::analyse_1ligne_fichier() // du fichier gerber d'origine
  1154. {
  1155. /*
  1156.   D02 moves the current point to the coordinate pair. Nothing is created. This used to be
  1157.   called a lights-off move.
  1158.  
  1159.   D01 creates a straight or circular line segment by interpolating from the current point to the
  1160.   coordinate pair. This used to be called a lights-on move. When region mode is off these
  1161.   segments are converted to draw or arc objects by stroking them with the current aperture,
  1162.   see 2.4. When region mode is on these segments form a contour defining a region, see 4.5.
  1163.  
  1164.   D03 creates a flash object by replicating the current aperture at the coordinate pair.
  1165.  */
  1166. QString ligne_i, s_X, s_Y, s_D, s_Xcent, s_Ycent, s_val;
  1167. QString s1, s2, s3, s_num3, s_aperture, s_ds, s_nb_err;
  1168.  
  1169. int p1, p2, p3;
  1170.  
  1171. Aperture aperture_i;
  1172.  
  1173. int n_max, a, b, c, d, dx, dy, ds;
  1174. int imax;
  1175. float float_val, float_cent_val;
  1176. int row;
  1177.  
  1178. n_max= listWidget_1->count();
  1179. ligne_i =" ";
  1180.  
  1181. if(num_ligne_en_cours1 < n_max-1)
  1182. {
  1183. listWidget_1->setCurrentRow(num_ligne_en_cours1);
  1184. ligne_i = liste_gerber1[num_ligne_en_cours1];
  1185.  
  1186. // lecture des paramètres
  1187.  
  1188. if ( ligne_i.left(6)==("%FSLAX")) // ce paramètre va définir le format des nombres
  1189. {
  1190. s_format = ligne_i.mid(6,2); // s_format = "34" ou "45" ou "46"... (par exemple)
  1191. qDebug() << "s_format = " << s_format;
  1192. }
  1193.  
  1194. if ( ligne_i.left(3)==("%MO")) // ce paramètre va définir le type d'unité utilisé, MM->mm ou IN->pouces
  1195. {
  1196. s_type = ligne_i.mid(3,2); // s_type = "IN" ou "MM"
  1197. qDebug() << "s_type = " << s_type;
  1198. }
  1199.  
  1200.  
  1201. // ================= première partie : constitution de la LISTE DES APERTURES ===========================
  1202.  
  1203. //Les dimensions des apertures dans le fichier gerber d'origine sont de la forme :
  1204. //0.0787
  1205. //c'est une fraction de pouce qu'il faut multiplier par 25.4 pour obtenir des mm.
  1206. //puis x par 100 et prendre la partie entière pour obtenir la valeur en centièmes de mm que nous utiliserons exclusivement
  1207.  
  1208.  
  1209. //-----------------------------------------------------------------------------------------------
  1210. if ( ligne_i.left(4)==("%ADD")) //ajout...
  1211. {
  1212.  
  1213. s_aperture = ligne_i.mid(4,3); // ex: 10C ou 16R ou 21O (21 + lettre O)
  1214. qDebug() << "s_aperture = " << s_aperture;
  1215.  
  1216. if ((s_aperture.right(1) == "C") || (s_aperture.right(1) == "P") ) // cercle ou polygone
  1217.  
  1218. {
  1219. aperture_i.forme = 'C';
  1220. s_val = s_aperture.mid(1,2);
  1221.  
  1222. p1 = ligne_i.indexOf(",");
  1223. p2 = ligne_i.indexOf("*");
  1224. s_val = ligne_i.mid(p1+1,p2-p1-1);
  1225. float_val = s_val.toFloat(); // conversion txt -> num
  1226. float_cent_val =0;
  1227. float_cent_val = 100 * convert_unite(float_val); // calcul val en 1/100 mm
  1228. s_Xcent = valeur2txt4(round(float_cent_val));
  1229.  
  1230. s_Ycent = s_Xcent; //ceci conduit à tracer des cercles et non des ovales (je ne dis pas ellipse puisqu'un cercle EST une ellipse)
  1231.  
  1232. s2 = "MX"+s_Xcent+"Y"+s_Ycent+s_aperture;
  1233.  
  1234. row = tableWidget_3->rowCount();
  1235. tableWidget_3->insertRow(row);
  1236. tableWidget_3->setItem(row, 0,new QTableWidgetItem (s2));
  1237. tableWidget_3->setItem(row, 1,new QTableWidgetItem ("-"));
  1238. tableWidget_3->resizeColumnsToContents();
  1239.  
  1240. aperture_i.WH.setX(round(float_cent_val));
  1241. aperture_i.WH.setY(round(float_cent_val));
  1242.  
  1243.  
  1244. liste_apertures << aperture_i;
  1245. }
  1246.  
  1247. if ((s_aperture.right(1) == "R") || (s_aperture.right(1) == "O") ) // rectangle ou oblond (ovale)
  1248. {
  1249. aperture_i.forme = 'R';
  1250. s_val = s_aperture.mid(1,2);
  1251.  
  1252. p1 = ligne_i.indexOf(","); // pour découpage
  1253. p2 = ligne_i.indexOf("X"); // pour découpage
  1254. p3 = ligne_i.indexOf("*"); // pour découpage
  1255. s_val = ligne_i.mid(p1+1,p2-p1-1); // découpage
  1256. float_val = s_val.toFloat(); // conversion txt -> num
  1257. float_cent_val = 100 * convert_unite(float_val); // calcul val en 1/100 mm
  1258. aperture_i.WH.setX(round(float_cent_val)); // en 1/100 mm et en nombre entier positif
  1259. s_Xcent.setNum(round(float_cent_val)); // conversion num -> txt
  1260. s_Xcent = "0000"+s_Xcent; // ajout des zéros non significatifs
  1261. s_Xcent = s_Xcent.right(4); // forme fixe avec 4 chiffres, dont zéros non sinificatifs devant le cas échéant -> 1234 ou 0056
  1262.  
  1263. s_val = ligne_i.mid(p2+1,p3-p2-1);
  1264. float_val = s_val.toFloat(); // conversion txt -> num
  1265. float_cent_val = 100 * convert_unite(float_val); // calcul val en 1/100 mm
  1266. aperture_i.WH.setY(round(float_cent_val));
  1267. s_Ycent.setNum(round(float_cent_val)); // conversion num -> txt
  1268. s_Ycent = "0000"+s_Ycent;
  1269. s_Ycent = s_Ycent.right(4);
  1270.  
  1271. s2 = "MX"+s_Xcent+"Y"+s_Ycent+s_aperture; // Le type "R" ou "O" est respecté ici, transmis tel quel
  1272. // note : le type "O" sera remplacé par une pastille rectangulaire "R" par le firmware de la carte Mega2560
  1273.  
  1274. row = tableWidget_3->rowCount();
  1275. tableWidget_3->insertRow(row);
  1276. tableWidget_3->setItem(row, 0,new QTableWidgetItem (s2));
  1277. tableWidget_3->setItem(row, 1,new QTableWidgetItem ("-"));
  1278. tableWidget_3->resizeColumnsToContents();
  1279.  
  1280. liste_apertures << aperture_i;
  1281.  
  1282. // Note : je pars du principe que les numéros d'aperture commencent à 10 (D10), il n'y a pas de D09 ou D8 etc...
  1283. // et se suivent dans l'ordre de la numérotation des entiers naturels lors de leur apparition dans le fichier gerber.
  1284. // ce qui permet d'appeller la bonne aperture simplement par sa place dans la Qlist des rectangles (ou dans celle des cercles)
  1285. // que nous constituons ici.
  1286.  
  1287. }
  1288. }
  1289. //-----------------------------------------------------------------------------------------------
  1290.  
  1291. //======== seconde partie : tracé immédiat à l'écran et constitution de la liste des actions différées (pour la machine)=======================
  1292.  
  1293. if ( ligne_i.left(3)==("G54")) // sélection aperture à utiliser pour la version Gerber Fmt 3.4
  1294. {
  1295. s_aperture = ligne_i.mid(3,3);
  1296. num_ap_use=(s_aperture.right(2)).toInt(); // conversion txt -> num ; ATTENTION : num_ap_use est une variable GLOBALE
  1297. s_aperture_use = s_aperture; // s_aperture_use est une var globale qui mémo pour util. lors des passages suivant dans cette fonction
  1298. }
  1299.  
  1300. if ( ligne_i.left(1)==("D")) // sélection aperture à utiliser pour la version Gerber Fmt 4.5
  1301. {
  1302. s_aperture = ligne_i.mid(0,3);
  1303. num_ap_use=(s_aperture.right(2)).toInt(); // conversion txt -> num ; ATTENTION : num_ap_use est une variable GLOBALE
  1304. s_aperture_use = s_aperture; // s_aperture_use est une var globale qui mémo pour util. lors des passages suivant dans cette fonction
  1305. }
  1306.  
  1307.  
  1308. if (ligne_i[0]==('X')) // coordonnées pour effectuer une action (déplacement à vide ou traçage segment, flash piste)
  1309. {
  1310.  
  1311. //IMPORTANT : Dans kicad, placer le "origin point of drill and place" sur le coin en bas à gauche du circuit avant de créer le fichier gerber.
  1312. //Les coordonnées des points dans le fichier gerber d'origine sont de la forme :
  1313.  
  1314. /* --------- pour la version %MOIN*% Gerber Fmt 3.4, Leading zero omitted
  1315. en pouces
  1316. 3+4= 7 chiffres
  1317. soit :
  1318. 3 digits in the integer part
  1319. 4 digits in the fractionnal part
  1320.  
  1321. à 14500 il faut donc rajouter 2 chiffres (zéros) au début pour obtenir les 7 chiffres :
  1322. 0014500
  1323. il reste à placer la virgule au bon endroit afin d'obtenir les 3 digits de la partie entière suivie des 4 digits de la partie décimale
  1324. 0014500 représente donc 001.4500 pouce
  1325. il suffit de de prendre le nombre de départ (14500) et de le diviser par 100 pour obtenir la valeur en centièmes de pouces.
  1326.  
  1327.  
  1328. ------------ pour la version %MOMM*% Gerber Fmt 4.5
  1329. en mm
  1330. 4+5 = 9 chiffres
  1331. soit :
  1332. 4 digits in the integer part
  1333. 5 digits in the fractionnal part
  1334.  
  1335. à 4836714 il faut donc rajouter 2 chiffres (zéros) au début pour obtenir les 9 chiffres :
  1336. 004836714
  1337. il reste à placer la virgule au bon endroit afin d'obtenir les 4 digits de la partie entière suivie des 5 digits de la partie décimale
  1338. 4836714 représente donc 0048.36714 mm
  1339. ce qui montre que 4836714 est la valeur en centmillièmes de mm
  1340. il suffit donc en fait de prendre le nombre de départ (4836714) et de le diviser par 100 000 por obtenir la valeur en mm
  1341. et comme ce qui nous intéresse c'est la valeur en centièmes de mm,
  1342. il suffit de de prendre le nombre de départ (4836714) et de le diviser par 1000 pour obtenir la valeur en centièmes de mm.
  1343.  
  1344. */
  1345. memo_X = Xcent;
  1346. memo_Y = Ycent;
  1347.  
  1348. a = ligne_i.indexOf('X'); // pour découpage
  1349. b = ligne_i.indexOf('Y'); // pour découpage
  1350. c = ligne_i.indexOf('D'); // pour découpage
  1351. d = ligne_i.indexOf('*'); // pour découpage
  1352. s_X = ligne_i.mid(a+1,b-a-1); // decoupage1
  1353. s_Y = ligne_i.mid(b+1,c-b-1); // decoupage2
  1354. s_D = ligne_i.mid(c,d-c); // decoupage3
  1355.  
  1356. int FU=0;
  1357. FU = s_format.right(1).toInt();
  1358. float F2 = pow(10 , FU-2) ; // ici F2 = une puissance de 10
  1359.  
  1360. float_val = s_X.toFloat(); // conversion txt -> num
  1361. float_cent_val = convert_unite(float_val) / F2; // calcul val en 1/100 mmmm
  1362. Xcent = round(float_cent_val);
  1363.  
  1364. float_val = s_Y.toFloat(); // conversion txt -> num
  1365. float_cent_val = convert_unite(float_val) / F2; // calcul val en 1/100 mmmm
  1366. Ycent = round(float_cent_val);
  1367.  
  1368. imax = liste_apertures.length();
  1369. int i;
  1370. i= num_ap_use -10;
  1371. if (i<0) {i =0;}
  1372. if (i >= imax){i=imax-1;}
  1373. aperture_i = liste_apertures[i];
  1374.  
  1375. //---------------------------- SEGMENT de piste ----------------------------------
  1376. if (s_D == "D01")
  1377. {
  1378. // tracer_element(memo_X ,memo_Y, Xcent, Ycent, aperture_i.x, groupe_trace);
  1379. dx = abs(Xcent-memo_X);
  1380. dy = abs(Ycent-memo_Y);
  1381. ds = sqrt(dx*dx + dy*dy);
  1382. s_ds.setNum(ds); // conversion num -> txt
  1383. }
  1384.  
  1385. //--------------------------------- DEPLACEMENT ----------------------------------------
  1386. if (s_D == "D02")
  1387. {
  1388. dx = abs(Xcent-memo_X);
  1389. dy = abs(Ycent-memo_Y);
  1390. ds = sqrt(dx*dx + dy*dy);
  1391. s_ds.setNum(ds); // conversion num -> txt
  1392. }
  1393.  
  1394. //------------------ PASTILLE ("Flash")- -------------------------------------------------
  1395. if (s_D == "D03")
  1396. {
  1397.  
  1398. }
  1399.  
  1400.  
  1401. s1 ="-";
  1402. if (s_D == "D01") { s1="T";} // TRACE SEGMENT
  1403. if (s_D == "D02") { s1="G";} // DEPLACEMENT ("GOTO")
  1404. if (s_D == "D03") { s1="P";} // FLASH PASTILLE
  1405.  
  1406. s_Xcent.setNum(Xcent);// conversion num -> txt
  1407. s_Xcent = "0000"+s_Xcent;
  1408. s_Xcent = s_Xcent.right(4);
  1409.  
  1410. s_Ycent.setNum(Ycent);
  1411. s_Ycent = "0000"+s_Ycent;
  1412. s_Ycent = s_Ycent.right(4);
  1413.  
  1414. // contrairement au fichier gerber d'origine, les apertures utilisées seront ici précisées pour CHAQUE ligne.
  1415. // ceci dans le but de pouvoir ultérieurement déplacer plus facilement les lignes dans le fichier afin d'optimiser le tracé
  1416. // (moins de déplacements inutiles de la plume)
  1417.  
  1418. s3 = s1+"X"+s_Xcent+"Y"+s_Ycent + s_aperture_use;
  1419.  
  1420. row = tableWidget_3->rowCount();
  1421. tableWidget_3->insertRow(row);
  1422. tableWidget_3->setItem(row, 0,new QTableWidgetItem (s3));
  1423. tableWidget_3->setItem(row, 1,new QTableWidgetItem (s_ds));
  1424. tableWidget_3->resizeColumnsToContents();
  1425. }
  1426. }
  1427.  
  1428. // scene->addItem(groupe_trace);
  1429. return(0);
  1430. }
  1431.  
  1432.  
  1433.  
  1434. void MainWindow::affiche_liste_elements() // écrit dans le tableau TW4
  1435. {
  1436. QString ligne_i, s1;
  1437. quint16 nouveau_num_i;
  1438. int i, i_max, row;
  1439. int clique;
  1440. i_max = liste_elements.count();
  1441. tableWidget_4->clear();
  1442. tableWidget_4->setRowCount(0);
  1443. for (i=0; i<i_max; i++)
  1444. {
  1445. clique = liste_elements[i].clique;
  1446. nouveau_num_i = liste_elements[i].nouveau_num;
  1447. s1.setNum(nouveau_num_i);// conversion num -> txt
  1448. ligne_i = liste_elements[i].texte_affichable;
  1449. row = tableWidget_4->rowCount();
  1450. tableWidget_4->insertRow(row);
  1451. tableWidget_4->setItem(row, 0,new QTableWidgetItem (ligne_i));
  1452. if (clique == 1) {tableWidget_4->item(row,0)->setBackground(QBrush(QColor::fromRgb(0xFFFFB3)));}
  1453.  
  1454. tableWidget_4->setItem(row, 1,new QTableWidgetItem (s1));
  1455. tableWidget_4->item(row,1)->setBackground(QBrush(QColor::fromRgb(205, 255, 217)));
  1456. }
  1457. tableWidget_4->setColumnWidth(0,282);
  1458. tableWidget_4->setColumnWidth(1,36);
  1459. // tableWidget_4->resizeColumnsToContents();
  1460. }
  1461.  
  1462.  
  1463.  
  1464. void MainWindow::affiche_liste_elements_tries() // écrit dans le tableau TW5
  1465. {
  1466. QString ligne_i, s1;
  1467. int i, i_max, row;
  1468. quint16 num_origine_i;
  1469. i_max = liste_elements_tries.count();
  1470. s1.setNum(i_max);// conversion num -> txt
  1471. lineEdit_7->setText(s1);
  1472.  
  1473. tableWidget_5->clear();
  1474. tableWidget_5->setRowCount(0);
  1475. for (i=0; i<i_max; i++)
  1476. {
  1477.  
  1478. ligne_i = liste_elements_tries[i].texte_affichable;
  1479. num_origine_i = liste_elements_tries[i].num_origine;
  1480. s1.setNum(num_origine_i);// conversion num -> txt
  1481. row = tableWidget_5->rowCount();
  1482. tableWidget_5->insertRow(row);
  1483. tableWidget_5->setItem(row, 0,new QTableWidgetItem (ligne_i));
  1484.  
  1485. tableWidget_5->setItem(row, 1,new QTableWidgetItem (s1));
  1486. tableWidget_5->item(row,1)->setBackground(QBrush(QColor::fromRgb(0xADD8E6)));
  1487. }
  1488. tableWidget_5->setColumnWidth(0,282);
  1489. tableWidget_5->setColumnWidth(1,36);
  1490. // tableWidget_5->resizeColumnsToContents();
  1491. }
  1492.  
  1493.  
  1494.  
  1495. void MainWindow::creation_liste_elements()
  1496. {
  1497. // source : tableWidget_3 ; destination : tableWidget_4
  1498.  
  1499. QString ligne_A, ligne_B, ligne_out;
  1500. qint16 xa, ya, xb, yb, n_ap, x_plaque, y_plaque, m;
  1501. QString s1, s2, s3, s_xa, s_ya, s_xb, s_yb, s_ap;
  1502. Element element_i;
  1503. quint16 i;
  1504. quint16 num_ordre;
  1505. Aperture aperture_i;
  1506.  
  1507. X_max =0;
  1508. Y_max =0;
  1509.  
  1510. int i_max;
  1511. num_ordre = 1;
  1512. tableWidget_4->setRowCount(1);
  1513. InitTable4();
  1514. InitTable5();
  1515.  
  1516. liste_elements.clear();
  1517. liste_elements_tries.clear();
  1518.  
  1519. element_i.A.setX(0); element_i.A.setY(0);
  1520. element_i.B.setX(0); element_i.B.setY(0);
  1521. element_i.clique=0;
  1522. element_i.couleur=couleur_piste1;
  1523. element_i.M.setX(0); element_i.M.setY(0);
  1524. element_i.nouveau_num=0;
  1525. element_i.num=0;
  1526. element_i.num_aperture=0;
  1527. element_i.num_origine=0;
  1528. element_i.sorte='S';
  1529. element_i.texte_affichable="SXA0000YA0000XB0000YB0000D00";
  1530.  
  1531. liste_elements << element_i; // ajoute un element nul à l'adresse 0;
  1532.  
  1533. i_max = tableWidget_3->rowCount();
  1534. for (i = 0; i< i_max; i++)
  1535. {
  1536. ligne_A = tableWidget_3->item(i,0)->text();
  1537. if ((i+1)<i_max) {ligne_B = tableWidget_3->item(i+1,0)->text();}
  1538.  
  1539. //------------------------- AJOUT APERTURE ----------------------------
  1540.  
  1541. if ((ligne_A.left(2) == "MX")) //
  1542. {
  1543. element_i.num_origine = num_ordre;
  1544. element_i.sorte='M';
  1545. num_ordre++;
  1546.  
  1547. s_xa = ligne_A.mid(2,4);
  1548. xa = s_xa .toInt(); // conversion txt -> num
  1549.  
  1550. s_ya = ligne_A.mid(7,4);
  1551. ya = s_ya .toInt();
  1552.  
  1553. s_ap = ligne_A.mid(11,4);
  1554. qDebug() << "s_ap" << s_ap;
  1555.  
  1556. // n_ap = (s_ap.right(2)).toInt();
  1557.  
  1558. ligne_out = "MXA" + s_xa + "YA" + s_ya + "XB0000YB0000" + "D" + s_ap; // AJOUT APERTURE
  1559. element_i.texte_affichable = ligne_out;
  1560. liste_elements << element_i;
  1561. //liste_elements_tries << element_i;
  1562. //qDebug() << "AP= " << s_ap;
  1563. //qDebug() << "ligne_out = " << ligne_out;
  1564. }
  1565.  
  1566.  
  1567. //------------------------- AJOUT SEGMENT ----------------------------
  1568.  
  1569. if (((ligne_A.left(2) == "GX") && (ligne_B.left(2) == "TX"))|| ((ligne_A.left(2) == "TX") && (ligne_B.left(2) == "TX")))
  1570. // remarque : il peut y avoir deux lignes "TX" qui se suivent, c'est le cas pour les textes...
  1571. // mais dans tous les autres cas, il y a une alternance "GX".."TX" pour les pistes
  1572. // en conséquence si on omet la deuxième partie de la condition ci-dessus, les textes (par ex: "EL" ou "CU" seront mal retranscrits)
  1573. {
  1574. element_i.num_origine = num_ordre;
  1575.  
  1576. num_ordre++;
  1577.  
  1578. s_xa = ligne_A.mid(2,4);
  1579. xa = s_xa .toInt(); // conversion txt -> num
  1580. if (X_max < xa) {X_max = xa;}
  1581.  
  1582. s_ya = ligne_A.mid(7,4);
  1583. ya = s_ya .toInt();
  1584. if (Y_max < ya) {Y_max = ya;}
  1585.  
  1586. s_xb = ligne_B.mid(2,4);
  1587. xb = s_xb .toInt();
  1588.  
  1589. s_yb = ligne_B.mid(7,4);
  1590. yb = s_yb .toInt();
  1591.  
  1592. s_ap = ligne_B.mid(11,3);
  1593. n_ap = (s_ap.right(2)).toInt();
  1594.  
  1595. ligne_out = "SXA" + s_xa + "YA" + s_ya + "XB" + s_xb + "YB" + s_yb + s_ap; // SEGMENT ; 2 points (extrémités), A et B
  1596. element_i.texte_affichable = ligne_out;
  1597.  
  1598. element_i.A.setX(xa); element_i.A.setY(ya);
  1599. element_i.B.setX(xb); element_i.B.setY(yb);
  1600. element_i.M.setX((xa+xb)/2); element_i.M.setY((ya+yb)/2); // point milieu
  1601.  
  1602. element_i.num = i;
  1603. element_i.num_aperture = n_ap;
  1604. element_i.sorte='S'; // =segment
  1605. element_i.couleur = couleur_piste1;
  1606. element_i.clique = 0;
  1607.  
  1608. liste_elements << element_i;
  1609. }
  1610. //------------------------- AJOUT PASTILLE ----------------------------
  1611.  
  1612. if ((ligne_A.left(2) == "PX"))
  1613. {
  1614. element_i.num_origine = num_ordre;
  1615.  
  1616. num_ordre++;
  1617.  
  1618. s_xa = ligne_A.mid(2,4);
  1619. xa = s_xa .toInt(); // conversion txt -> num
  1620. if (X_max < xa) {X_max = xa;}
  1621.  
  1622. s_ya = ligne_A.mid(7,4);
  1623. ya = s_ya .toInt();
  1624. if (Y_max < ya) {Y_max = ya;}
  1625.  
  1626. s_ap = ligne_A.mid(11,3);
  1627. n_ap = (s_ap.right(2)).toInt();
  1628.  
  1629. ligne_out = "PXA" + s_xa + "YA" + s_ya + "xx0000xx0000" + s_ap; //dans le cas d'une pastille, seule la position du point A est définie
  1630. element_i.texte_affichable = ligne_out;
  1631.  
  1632. element_i.A.setX(xa); element_i.A.setY(ya);
  1633. element_i.B.setX(xa); element_i.B.setY(ya);
  1634. element_i.M.setX(xa); element_i.M.setY(ya);
  1635.  
  1636. element_i.num = i;
  1637. element_i.num_aperture = n_ap;
  1638. element_i.sorte='P'; // =pastille
  1639. element_i.couleur = couleur_pastille1;
  1640. element_i.clique = 0;
  1641.  
  1642. liste_elements << element_i;
  1643. }
  1644. }
  1645.  
  1646. creation_liste_segments();
  1647. creation_liste_trous();
  1648.  
  1649. s1.setNum(X_max); //conversion num->txt;
  1650. lineEdit_taille_X->setText(s1.left(s1.length()-2)+','+s1.right(2)+"mm");
  1651.  
  1652. s1.setNum(Y_max); //conversion num->txt;
  1653. lineEdit_taille_Y->setText(s1.left(s1.length()-2)+','+s1.right(2)+"mm");
  1654.  
  1655.  
  1656. x_plaque = X_max; //+1000;
  1657. m = x_plaque - (x_plaque % 100) + 1000;
  1658. s1.setNum(m); //conversion num->txt;
  1659. lineEdit_taille_X_2->setText(s1.left(s1.length()-2)+','+s1.right(2)+"mm");
  1660.  
  1661.  
  1662. y_plaque = Y_max; //+1000;
  1663. m = y_plaque - (y_plaque % 100) + 1000;
  1664. s1.setNum(m); //conversion num->txt;
  1665. lineEdit_taille_Y_2->setText(s1.left(s1.length()-2)+','+s1.right(2)+"mm");
  1666.  
  1667. spinBox_zoom2->setValue(14000 / sqrt(X_max * Y_max));
  1668.  
  1669. // qDebug() << "liste_elements[0].num_aperture=" << liste_elements[0].num_aperture;
  1670. // qDebug() << "liste_elements[0].texte_affichable=" << liste_elements[0].texte_affichable;
  1671. }
  1672.  
  1673.  
  1674. void MainWindow::creation_liste_segments()
  1675. {
  1676. quint16 i, i_max;
  1677. Element EL_i;
  1678.  
  1679. i_max = liste_elements.count();
  1680.  
  1681. for (i=1; i<i_max; i++ ) // attention : 1 et pas 0 ! les numeros d'ordres commencent à 1. (zéro = pas d'élémént trouvé)
  1682. {
  1683. EL_i = liste_elements[i];
  1684. if((EL_i.sorte == 'S' ) )
  1685. {
  1686. liste_segments << EL_i;
  1687. }
  1688. }
  1689. }
  1690.  
  1691.  
  1692.  
  1693. void MainWindow::creation_liste_trous()
  1694. {
  1695. quint16 n_max;
  1696. Element element_i;
  1697. quint16 i;
  1698. quint8 num_ap;
  1699. Aperture aperture_i;
  1700. quint16 diametre;
  1701.  
  1702. QPoint A;
  1703.  
  1704. n_max = liste_elements.count();
  1705. for (i=0; i<n_max; i++)
  1706. {
  1707. element_i = liste_elements[i];
  1708.  
  1709. if (element_i.sorte == 'P' ) // pastille (peut être circulaire ou rectangulaire)
  1710. {
  1711. num_ap = element_i.num_aperture-10;
  1712.  
  1713. int n_max = liste_apertures.length();
  1714. if (num_ap < n_max)
  1715. {
  1716. aperture_i = liste_apertures[num_ap];
  1717. if (aperture_i.forme == 'C') // pastille circulaire uniquement
  1718. {
  1719. liste_trous << element_i;
  1720. // qDebug() << "i= " << i << "forme=" << "num_ap = " << num_ap+10;
  1721. A = element_i.A;
  1722. diametre = aperture_i.WH.rx();
  1723. diametre /=4;
  1724. tracer_trou(A, diametre);
  1725. }
  1726. }
  1727. }
  1728. }
  1729. }
  1730.  
  1731.  
  1732.  
  1733. void MainWindow::inverser_miroir_elements()
  1734. {
  1735. // source et destination : liste_elements
  1736. //inverse tous les éléments suivant l'axe des X. Xmax doit être connu préalablement à l'apper de cette fonction
  1737. quint16 n_max;
  1738. Element element_i;
  1739. quint16 i;
  1740. QString sorte_i, s_xa, s_ya, s_xb, s_yb, s_ap, ligne_out;
  1741.  
  1742. int xai, xbi;
  1743.  
  1744. n_max = liste_elements.count();
  1745. for (i=0; i<n_max; i++)
  1746. {
  1747. element_i = liste_elements[i];
  1748. sorte_i = element_i.sorte;
  1749. if( (element_i.sorte == 'S' ) || (element_i.sorte == 'P' ) ) // sauf apertures !
  1750. {
  1751. xai = X_max - element_i.A.x();
  1752. element_i.A.setX(xai);
  1753.  
  1754. xbi = X_max - element_i.B.x();
  1755. element_i.B.setX(xbi);
  1756.  
  1757. element_i.M.setX((xai+xbi)/2); // point milieu
  1758.  
  1759. s_xa = valeur2txt4(element_i.A.x());
  1760. s_ya = valeur2txt4(element_i.A.y());
  1761. s_xb = valeur2txt4(element_i.B.x());
  1762. s_yb = valeur2txt4(element_i.B.y());
  1763. s_ap = "D" + valeur2txt2(element_i.num_aperture);
  1764.  
  1765. ligne_out = sorte_i + "XA" + s_xa + "YA" + s_ya + "XB" + s_xb + "YB" + s_yb + s_ap;
  1766. element_i.texte_affichable = ligne_out;
  1767.  
  1768. liste_elements[i]= element_i;
  1769. }
  1770. }
  1771.  
  1772. }
  1773.  
  1774.  
  1775.  
  1776.  
  1777. void MainWindow::efface_buffer()
  1778. {
  1779. int i;
  1780. for(i=0; i<100; i++)
  1781. buffer[i] =' ';
  1782. //portUSB->flush();
  1783. }
  1784.  
  1785.  
  1786. void MainWindow::onReadyRead()
  1787. {
  1788. int nb_Bytes;
  1789. QString s1, msg1;
  1790.  
  1791. // nb_Bytes = portUSB->bytesAvailable();
  1792.  
  1793. //if (nb_Bytes > 30) {nb_Bytes = 30;}
  1794. nb_Bytes = portUSB->bytesAvailable();
  1795. if (nb_Bytes >99) {nb_Bytes = 99;}
  1796. portUSB->read(buffer, nb_Bytes);
  1797. buffer[nb_Bytes] = '\0'; // permet de définir la fin du string au bon endroit.
  1798. msg += buffer;
  1799.  
  1800. if (msg.contains('\n'))
  1801. {
  1802. msg.remove('\n');
  1803. msg.remove('\r');
  1804.  
  1805. msg1 ="in = " + msg;
  1806.  
  1807.  
  1808. if (msg1.contains("hello"))
  1809. {
  1810. int p1 = msg1.indexOf("hello");
  1811. msg1 = msg1.right(p1);
  1812.  
  1813. // textEdit_1->setText("-");
  1814. Led0->setCouleur(QColor (0,255,0));
  1815. Led1->setCouleur(QColor (0,255,0));
  1816. Led1->setEtat(1);
  1817.  
  1818. effacer_ecran();
  1819. tout_decliquer();
  1820. dessiner_liste_elements_tries();
  1821. trace_liste_apertures();
  1822. }
  1823.  
  1824. publier (msg1);
  1825.  
  1826. if (msg1.contains("Mega"))
  1827. {
  1828. publier("-----------------------------------");
  1829. }
  1830.  
  1831. // textEdit_1->setText(textEdit_1->toPlainText()+'\n'+ "recu : " + msg);
  1832.  
  1833. findecourse =0;
  1834. Led4->setEtat(0);// éteint la LED fin de course
  1835. Timer3->stop();
  1836.  
  1837. if((msg[0]=='X') && (msg[6]=='Y') )
  1838. {
  1839. lineEdit_X->setText( msg.mid(1,3) +"." + msg.mid(4,2) + " mm" );
  1840. lineEdit_Y->setText( msg.mid(7,3) +"." + msg.mid(10,2) + " mm" );
  1841. }
  1842.  
  1843.  
  1844. if (msg.contains("PRET"))
  1845. {
  1846. recu_PRET=1;
  1847. //efface_buffer();
  1848. //msg.clear();
  1849. Led2->setCouleur(QColor (0,255,0));
  1850. Led2->setEtat(1);;
  1851. }
  1852.  
  1853.  
  1854. if (msg.contains("FINC"))
  1855. {
  1856. efface_buffer();
  1857. findecourse =1;
  1858. Led4->setEtat(1);
  1859. Timer3->start(300);
  1860. msg1 = '\n';
  1861. msg1 += "-----------------------------------------------";
  1862. msg1 += '\n';
  1863. msg1 += "Machine en position FIN DE COURSE -> arrêt forcé ";
  1864. msg1 += '\n';
  1865. msg1 += "Seule commande valide = GTO origine";
  1866. publier (msg1);
  1867. }
  1868.  
  1869. if ((flashage_en_cours == 1) && (recu_PRET == 1))
  1870. {
  1871. flash_ligne_suivante();
  1872. }
  1873. msg="";
  1874. }
  1875. }
  1876.  
  1877.  
  1878.  
  1879.  
  1880. qint8 MainWindow::Connexion(qint8 cnx)
  1881. {
  1882. msg.clear();
  1883. if (cnx == 1)
  1884. {
  1885. // portUSB = new QextSerialPort("/dev/ttyUSB0", QextSerialPort::EventDriven); // pour Arduino Mega2560 vrai
  1886. // portUSB = new QextSerialPort("/dev/ttyACM0", QextSerialPort::EventDriven); // pour Clone SainSmart Mega2560 vrai
  1887.  
  1888. portUSB = new QextSerialPort(port_usb_name, QextSerialPort::EventDriven);
  1889.  
  1890. portUSB->setBaudRate(BAUD38400); // 4800, 9600, 14400, 19200, 28800, 38400, 57600, 115200
  1891. // check the baud rate on the Arduino since it has to be the same
  1892. // as set with Serial.begin(...)
  1893. portUSB->setFlowControl(FLOW_OFF);
  1894. portUSB->setParity(PAR_NONE);
  1895. portUSB->setDataBits(DATA_8);
  1896. portUSB->setStopBits(STOP_1);
  1897.  
  1898. if(portUSB->open(QIODevice::ReadWrite) == true)
  1899. {
  1900. connect(portUSB, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
  1901. connect(portUSB, SIGNAL(dsrChanged()), this, SLOT(onDsrChanged()));
  1902. connect_ok = 1;
  1903. msg="";
  1904. efface_buffer();
  1905. portUSB->flush();
  1906. qDebug() << "listening for data on port " << portUSB->portName();
  1907.  
  1908. lineEdit_2->setText("attente");
  1909. Led1->setCouleur(QColor (255,255,0));
  1910. Led1->setEtat(1);
  1911. Led0->setCouleur(QColor (255,255,0));
  1912. }
  1913. else
  1914. {
  1915. qDebug() << "device failed to open:" << portUSB->errorString();
  1916. return (1);
  1917. Led1->setEtat(0);
  1918. Led2->setEtat(0);
  1919. recu_PRET=0;
  1920. }
  1921. }
  1922. else
  1923. {
  1924. portUSB->close();
  1925. }
  1926.  
  1927. return(0);
  1928. }
  1929.  
  1930.  
  1931. void MainWindow::publier(QString msg_i)
  1932. {
  1933. QString tx2;
  1934. tx2 = '\n';
  1935. tx2 += msg_i;
  1936. textEdit_1->setText(textEdit_1->toPlainText()+tx2);
  1937.  
  1938. QScrollBar *SB1 = textEdit_1->verticalScrollBar();
  1939. SB1->setValue(SB1->maximum());
  1940. liste_log << msg_i;
  1941.  
  1942.  
  1943. }
  1944.  
  1945.  
  1946. int MainWindow::Envoi_une_ligne()
  1947. {
  1948. QString ligne_i, s1, s2;
  1949. quint16 num_ligne, n_max, pc;
  1950.  
  1951. ligne_i ="-";
  1952. n_max = tableWidget_5->rowCount();
  1953. if (n_max == 0) {return(1);}
  1954.  
  1955. num_ligne = tableWidget_5->currentRow();
  1956. if (tableWidget_5->currentRow() == -1 ) {return(2);} // évite de planter si aucun élément sélectionné lors de l'appel de cette fonction
  1957.  
  1958. ligne_i = tableWidget_5->item(num_ligne,0)->text();
  1959. lineEdit_1->setText(ligne_i);
  1960. s1.setNum(num_ligne+1); //conversion num->txt; +1 parce que les tableaux sont num depuis 0, alors que les lignes affichés le sont depuis 1
  1961.  
  1962. QString msg1;
  1963. msg1 = "=========================================================";
  1964. msg1 += '\n';
  1965. msg1 += "traitement ligne numéro [ " + s1 + " ]";
  1966.  
  1967. publier(msg1);
  1968.  
  1969. // textEdit_1->setText(textEdit_1->toPlainText()+'\n'+msg);
  1970. // QScrollBar *SB1 = textEdit_1->verticalScrollBar();
  1971. // SB1->setValue(SB1->maximum());
  1972.  
  1973. nb_lignes_envoyees++;
  1974.  
  1975. pc = 100 * num_ligne / n_max;
  1976. progressBar_1->setValue(pc);
  1977.  
  1978. if (pc >= 99) {annonce_vocale(100);}
  1979. else if (pc >= 90) {annonce_vocale(90);}
  1980. else if (pc >= 80) {annonce_vocale(80);}
  1981. else if (pc >= 70) {annonce_vocale(70);}
  1982. else if (pc >= 60) {annonce_vocale(60);}
  1983. else if (pc >= 50) {annonce_vocale(50);}
  1984. else if (pc >= 40) {annonce_vocale(40);}
  1985. else if (pc >= 30) {annonce_vocale(30);}
  1986. else if (pc >= 20) {annonce_vocale(20);}
  1987. else if (pc >= 10) {annonce_vocale(10);}
  1988.  
  1989. s2.setNum(nb_lignes_envoyees);
  1990. lineEdit_4->setText(s2);
  1991.  
  1992. //------------------------------------------------------------
  1993. if ( connect_ok == 1 ) // mode NORMAL
  1994. {
  1995. if( recu_PRET == 1 )
  1996. {
  1997. if (num_ligne>1)
  1998. {
  1999. liste_elements_tries[num_ligne-1].clique++; // afin de changer sa couleur
  2000. dessine_1ligne_liste_triee(num_ligne-1);
  2001. }
  2002. pointer(num_ligne); // trace le réticule
  2003. liste_elements_tries[num_ligne].clique++;
  2004. dessine_1ligne_liste_triee(num_ligne); // afin de changer sa couleur
  2005.  
  2006. envoi_vers_Atmega(ligne_i); // ***************** envoi 1 ligne ver ATMEGA **********************
  2007.  
  2008. Led1->setCouleur(QColor (255,255,0));
  2009. Led1->setEtat(1);
  2010. Led2->setCouleur(QColor (255,255,0));
  2011. Led2->setEtat(1);
  2012.  
  2013.  
  2014. // liste_elements_tries[num_ligne].clique = 2;
  2015. // dessine_1ligne_liste_triee(num_ligne); // à l'écran
  2016. }
  2017. else
  2018. {
  2019. QMessageBox msgBox; msgBox.setText("Occupé"); msgBox.exec();
  2020. lineEdit_2->setText("Occupé");
  2021. Led1->setEtat(0);
  2022. }
  2023. }
  2024. else
  2025. {
  2026. QMessageBox msgBox; msgBox.setText("No connect2"); msgBox.exec();
  2027. lineEdit_2->setText("no connect2");
  2028. Led1->setEtat(0);
  2029. }
  2030.  
  2031. //num_ligne++;
  2032. //if(num_ligne < n_max) { tableWidget_3->setCurrentCell(num_ligne , 1); }
  2033. spinBox_depart->setValue(num_ligne+1); //+1 parce que les tableaux sont num depuis 0, alors que les lignes affichés sont depuis 1
  2034. return(0);
  2035. }
  2036.  
  2037.  
  2038.  
  2039.  
  2040. int MainWindow::on_Btn_envoi_clicked()
  2041. {
  2042. int n_max = tableWidget_5->rowCount();
  2043. int num_ligne = tableWidget_5->currentRow();
  2044. QString ligne_i = tableWidget_5->item(num_ligne,0)->text();
  2045.  
  2046. if(n_max == 0)
  2047. {
  2048. QString sx = "La liste des éléments classés est vide.\n";
  2049. sx +="Vous devez classer les éléments au préalable :\n\n";
  2050. sx +="-soit en cliquant sur les points rouges,\n";
  2051. sx +="-soit automatiquement.\n";
  2052. QMessageBox msgBox; msgBox.setText(sx); msgBox.exec();
  2053. }
  2054. if (findecourse == 1)
  2055. {
  2056. QString sx = "ATTENTION : en position Fin de course !";
  2057. QMessageBox msgBox; msgBox.setText(sx); msgBox.exec();
  2058. }
  2059. else
  2060. {
  2061. QString sx = "Attention : pour un tracé correct\n";
  2062. sx +="les apertures (lignes en haut de la liste)\n";
  2063. sx +="doivent avoir été transmises au préalable\n";
  2064. sx +="à l'Arduino.\n";
  2065.  
  2066. QMessageBox::StandardButton mb1;
  2067. mb1 = QMessageBox::question(this, "ATTENTION :", sx, QMessageBox::Ok|QMessageBox::Abort);
  2068. if (mb1 == QMessageBox::Abort) { return 1; }
  2069.  
  2070.  
  2071. envoi_vers_Atmega(ligne_i); // ***************** envoi 1 ligne ver ATMEGA **********************
  2072. num_ligne++;
  2073. if(num_ligne < n_max) { tableWidget_5->setCurrentCell(num_ligne , 1); }
  2074. }
  2075. return 0;
  2076. }
  2077.  
  2078.  
  2079. /*
  2080. void MainWindow::on_Btn_TRACER_AUTO_clicked()
  2081. {
  2082.   quint16 num_ligne;
  2083.   QString s1;
  2084.  
  2085.   num_ligne = listWidget_1->currentRow();
  2086.   s1.setNum(num_ligne);// conversion num -> txt
  2087.  
  2088.   listWidget_4->addItem(s1);
  2089.  
  2090.   listWidget_4->setCurrentRow(listWidget_4->count());
  2091.  
  2092.   num_ligne_en_cours1 = num_ligne;
  2093.   Timer1->start(1);
  2094. }
  2095. */
  2096.  
  2097.  
  2098. void MainWindow::record_fichier_log(QString dossier1, QString fichier_out)
  2099. {
  2100. QString ligne_i;
  2101. if (!liste_log.isEmpty())
  2102. {
  2103. QDir dossier_out(QDir::homePath() + "/"+ dossier1);
  2104.  
  2105. int nb_lignes;
  2106. QFile file1(dossier1 + "/" + fichier_out);
  2107.  
  2108. if (file1.open(QIODevice::WriteOnly | QIODevice::Text))
  2109. {
  2110. QTextStream file_out(&file1);
  2111. nb_lignes = liste_log.count();
  2112. for (int i=0; i<nb_lignes; i++)
  2113. {
  2114. file_out << liste_log[i] << char(10);
  2115. }
  2116. }
  2117. file1.close();
  2118. }
  2119. }
  2120.  
  2121.  
  2122. void MainWindow::Timer1_clic() // analyse fichier gerber
  2123. {
  2124. num_ligne_en_cours1 ++;
  2125. analyse_1ligne_fichier();
  2126. }
  2127.  
  2128.  
  2129. void MainWindow::Timer2_clic() // dessine au ralenti les éléments triés
  2130. {
  2131. QString s1;
  2132. int i_max = liste_elements.count();
  2133.  
  2134. if (i_dess_tri < i_max-1 )
  2135. {
  2136. s1.setNum(i_dess_tri);// conversion num -> txt
  2137. lineEdit_3->setText(s1);
  2138. liste_elements_tries[i_dess_tri].clique = 1;
  2139. dessine_1ligne_liste_triee(i_dess_tri);
  2140. tableWidget_5->selectRow(i_dess_tri-1);
  2141. i_dess_tri++;
  2142. }
  2143. else {Timer2->stop();}
  2144. }
  2145.  
  2146.  
  2147.  
  2148. void MainWindow::Timer3_clic()
  2149. {
  2150. qDebug() << "Timer3 clic ! ";
  2151. // flash_ligne_suivante();
  2152. Led4->setEtat(1^Led4->etat());
  2153. }
  2154.  
  2155.  
  2156.  
  2157. void MainWindow::flash_ligne_suivante()
  2158. {
  2159. int i, i_max;
  2160. i_max = tableWidget_5->rowCount();
  2161.  
  2162. if(i_max == 0)
  2163. {
  2164. QString sx = "La liste des éléments classés est vide.\n";
  2165. sx +="Vous devez classer les éléments au préalable :\n\n";
  2166. sx +="-soit en cliquant sur les points rouges,\n";
  2167. sx +="-soit automatiquement.\n";
  2168. QMessageBox msgBox; msgBox.setText(sx); msgBox.exec();
  2169. }
  2170.  
  2171. num_ligne_en_cours1 ++;
  2172.  
  2173. if(num_ligne_en_cours1 < i_max) { tableWidget_5->setCurrentCell(num_ligne_en_cours1 , 1); }
  2174. i = tableWidget_5->currentRow();
  2175.  
  2176. if ((num_ligne_en_cours1 < i_max) && (flashage_en_cours == 1) && ((recu_PRET == 1) ) )
  2177. {
  2178. tableWidget_5->setCurrentCell(i,1);
  2179. qDebug() << "flashage_en_cours, ligne " << num_ligne_en_cours1;
  2180. Envoi_une_ligne();
  2181. }
  2182.  
  2183. if (num_ligne_en_cours1 >= i_max)
  2184. {
  2185. on_Btn_stop_flash_clicked();
  2186. QMessageBox msgBox; msgBox.setText("Flashage terminé"); msgBox.exec();
  2187. }
  2188.  
  2189. }
  2190.  
  2191.  
  2192.  
  2193. QString MainWindow::mise_en_forme_ligne_pour_envoi(QString s_i)
  2194. {
  2195. QString ligne_out;
  2196. QString CS;
  2197.  
  2198. // calcul checksum de s1 (sans les parties rajoutées "--TRNS:" etc...) -------------
  2199. QByteArray caracts; // tableau de caractères
  2200. caracts = s_i.toLocal8Bit(); // convertit le string s1 en un tableau de caractères
  2201. uint8_t taille = caracts.size();
  2202. int8_t n, v;
  2203. uint16_t somme =0;
  2204. for (n=0; n<taille; n++)
  2205. {
  2206. v=caracts[n];
  2207. somme += v;
  2208. }
  2209. CS = QString::number(somme);
  2210.  
  2211. // ligne_out ="--TRNS:"+ s_i+"-CS=["+CS+']';
  2212. ligne_out ="--TRNS:"+ s_i+"-CS=["+CS+"]"+'\n';
  2213.  
  2214. return ligne_out;
  2215. }
  2216.  
  2217.  
  2218.  
  2219. void MainWindow::envoi_vers_Atmega(QString s1) // ok
  2220. {
  2221. QString ligne_i=mise_en_forme_ligne_pour_envoi(s1);
  2222.  
  2223. int Lg = ligne_i.length();
  2224. lineEdit_8->setText(ligne_i);
  2225.  
  2226. if (connect_ok==1)
  2227. {
  2228. Led2->setCouleur(QColor (255,255,0));
  2229. Led2->setEtat(1);
  2230. char* s_out = ligne_i.toLocal8Bit().data();
  2231.  
  2232. if(connect_ok==1)
  2233. {
  2234. portUSB->write(s_out, Lg);
  2235. label_8->setText("Message envoyé par USB :");
  2236. }
  2237. msg.clear();
  2238. efface_buffer();
  2239.  
  2240. QString msg1; // pour affichage dans le EditText_1
  2241. msg1 = '\n';
  2242. msg1 +="------------------------------------";
  2243. msg1 += '\n';
  2244. msg1 += "out-> ";
  2245. msg1 += ligne_i;
  2246. publier(msg1) ;
  2247.  
  2248. recu_PRET=0;
  2249. }
  2250. else { QMessageBox msgBox; msgBox.setText("USB non connecté, ligne non transmise"); msgBox.exec();}
  2251. }
  2252.  
  2253.  
  2254.  
  2255. void MainWindow::on_Btn_Xplus_clicked()
  2256. {
  2257. envoi_vers_Atmega("XP");
  2258. }
  2259.  
  2260.  
  2261. void MainWindow::on_Btn_Xmoins_clicked()
  2262. {
  2263. envoi_vers_Atmega("XM");
  2264. }
  2265.  
  2266.  
  2267. void MainWindow::on_Btn_Xplus1mm_clicked()
  2268. {
  2269. envoi_vers_Atmega("X1");
  2270. }
  2271.  
  2272.  
  2273. void MainWindow::on_Btn_Xplus10mm_clicked()
  2274. {
  2275. envoi_vers_Atmega("X2");
  2276. }
  2277.  
  2278.  
  2279. void MainWindow::on_Btn_Xmoins1mm_clicked()
  2280. {
  2281. envoi_vers_Atmega("X3");
  2282. }
  2283.  
  2284.  
  2285. void MainWindow::on_Btn_Xmoins10mm_clicked()
  2286. {
  2287. envoi_vers_Atmega("X4");
  2288. }
  2289.  
  2290.  
  2291. void MainWindow::on_Btn_stop_clicked()
  2292. {
  2293. envoi_vers_Atmega("A");
  2294. }
  2295.  
  2296.  
  2297.  
  2298.  
  2299.  
  2300. void MainWindow::on_Btn_Yplus_clicked()
  2301. {
  2302. envoi_vers_Atmega("YP");
  2303. }
  2304.  
  2305.  
  2306.  
  2307. void MainWindow::on_Btn_Ymoins_clicked()
  2308. {
  2309. envoi_vers_Atmega("YM");
  2310. }
  2311.  
  2312.  
  2313.  
  2314. void MainWindow::on_Btn_Yplus1mm_clicked()
  2315. {
  2316. envoi_vers_Atmega("Y1");
  2317. }
  2318.  
  2319.  
  2320. void MainWindow::on_Btn_Yplus10mm_clicked()
  2321. {
  2322. envoi_vers_Atmega("Y2");
  2323. }
  2324.  
  2325.  
  2326.  
  2327. void MainWindow::on_Btn_Ymoins1mm_clicked()
  2328. {
  2329. envoi_vers_Atmega("Y3");
  2330. }
  2331.  
  2332.  
  2333.  
  2334. void MainWindow::on_Btn_Ymoins10mm_clicked()
  2335. {
  2336. envoi_vers_Atmega("Y4");
  2337. }
  2338.  
  2339.  
  2340. /*
  2341. void MainWindow::on_Btn_Zmoins1mm_clicked()
  2342. {
  2343.   envoi_vers_Atmega("Z1");
  2344. }
  2345.  
  2346.  
  2347. void MainWindow::on_Btn_Zplus1mm_clicked()
  2348. {
  2349.   envoi_vers_Atmega("Z2");
  2350. }
  2351.  
  2352.  
  2353. void MainWindow::on_Btn_Zmoins5mm_clicked()
  2354. {
  2355.   envoi_vers_Atmega("Z3");
  2356. }
  2357.  
  2358.  
  2359. void MainWindow::on_Btn_Zplus5mm_clicked()
  2360. {
  2361.   envoi_vers_Atmega("Z4");
  2362. }
  2363. */
  2364.  
  2365. void MainWindow::on_Btn_RAZ_pos_clicked()
  2366. {
  2367. envoi_vers_Atmega("RP");
  2368. }
  2369.  
  2370.  
  2371.  
  2372. void MainWindow::on_Btn_gotoHome_clicked()
  2373. {
  2374. QString s_X, s_Y, ligne_i;
  2375. qint8 lg;
  2376. on_Btn_stop_flash_clicked(); //pour mettre fin au flashage encours éventuel
  2377.  
  2378. // listWidget_2->clear();
  2379.  
  2380. s_X = doubleSpinBox_X_origine->text();
  2381. lg = s_X.length(); if (lg<5) {s_X = "0"+s_X;}
  2382. s_X.remove(",");
  2383.  
  2384.  
  2385. s_Y = doubleSpinBox_Y_origine->text();
  2386. lg = s_Y.length(); if (lg<5) {s_Y = "0"+s_Y;}
  2387. s_Y.remove(",");
  2388. ligne_i = "GHA" + s_X + "YA" + s_Y;
  2389.  
  2390. envoi_vers_Atmega(ligne_i);
  2391.  
  2392. }
  2393.  
  2394.  
  2395.  
  2396. void MainWindow::on_Btn_gotoSafePos_clicked()
  2397. {
  2398. on_Btn_stop_flash_clicked(); //pour mettre fin au flashage encours éventuel
  2399. envoi_vers_Atmega("GS");
  2400. }
  2401.  
  2402.  
  2403.  
  2404. void MainWindow::on_Btn_GOTO_clicked()
  2405. {
  2406. QString s_X, s_Y, ligne_i;
  2407. qint8 lg;
  2408.  
  2409. Led2->setCouleur(QColor (255,255,0));
  2410. Led2->setEtat(1);
  2411. recu_PRET=0;
  2412.  
  2413. s_X = doubleSpinBox_X->text();
  2414. lg = s_X.length(); if (lg<5) {s_X = "0"+s_X;}
  2415. s_X.remove(",");
  2416.  
  2417.  
  2418. s_Y = doubleSpinBox_Y->text();
  2419. lg = s_Y.length(); if (lg<5) {s_Y = "0"+s_Y;}
  2420. s_Y.remove(",");
  2421. ligne_i = "GXA" + s_X + "YA" + s_Y;
  2422.  
  2423. envoi_vers_Atmega(ligne_i);
  2424. }
  2425.  
  2426.  
  2427.  
  2428. /*
  2429. void MainWindow::on_Btn_trace1ligne_clicked()
  2430. {
  2431.   qint16 num_ligne;
  2432.   num_ligne = listWidget_1->currentRow();
  2433.  
  2434.   num_ligne_en_cours1 = num_ligne;
  2435.   analyse_1ligne_fichier();
  2436.   num_ligne_en_cours1 ++;
  2437.   listWidget_1->setCurrentRow(num_ligne_en_cours1);
  2438.  
  2439. }
  2440. */
  2441.  
  2442.  
  2443. void MainWindow::on_Btn_R_clicked()
  2444. {
  2445. doubleSpinBox_X->setValue(0);
  2446. doubleSpinBox_Y->setValue(0);
  2447. }
  2448.  
  2449.  
  2450. /*
  2451. void MainWindow::on_Btn_ralentir_clicked()
  2452. {
  2453.   Timer1->stop();
  2454.   Timer1->start(500);
  2455. }
  2456. */
  2457.  
  2458.  
  2459. int MainWindow::on_actionOuvrir_triggered()
  2460. {
  2461. effacer_ecran();
  2462. int fich_charge_ok;
  2463. fileName_en_cours = lineEdit_6->text();
  2464. fileName_en_cours = QFileDialog::getOpenFileName(this, tr("Open File"), fileName_en_cours, tr("Files (*.gtl *gbl *.pho *.GBR *.gbr)"));
  2465. lineEdit_6->setText(fileName_en_cours);
  2466. liste_gerber1.clear();
  2467. listWidget_1->clear();
  2468.  
  2469. liste_apertures.clear();
  2470.  
  2471. fich_charge_ok = lire_fichier_gerber(fileName_en_cours); // <<------------
  2472. if (fich_charge_ok != 0) {return 1;}
  2473.  
  2474. enregistrer_fichier_init();
  2475.  
  2476. // effacer_ecran();
  2477. num_ligne_en_cours1 = 0;
  2478. pastilles_creuses=0;
  2479. analyse_fichier_gerber();
  2480. classer_auto();
  2481. trace_liste_apertures();
  2482. nb_corrections=0;
  2483.  
  2484.  
  2485. return 0;
  2486. }
  2487.  
  2488.  
  2489. int MainWindow::recharger_fichier()
  2490. {
  2491.  
  2492. int fich_charge_ok;
  2493. lineEdit_6->setText(fileName_en_cours);
  2494. liste_gerber1.clear();
  2495. listWidget_1->clear();
  2496.  
  2497. liste_apertures.clear();
  2498.  
  2499. fich_charge_ok = lire_fichier_gerber(fileName_en_cours); // <<------------
  2500. if (fich_charge_ok != 0) {return 1;}
  2501.  
  2502. effacer_ecran();
  2503. num_ligne_en_cours1 = 0;
  2504. pastilles_creuses=0;
  2505. analyse_fichier_gerber();
  2506. classer_auto();
  2507. trace_liste_apertures();
  2508. nb_corrections=0;
  2509. return 0;
  2510. }
  2511.  
  2512.  
  2513.  
  2514. void MainWindow::on_actionRecharger_triggered()
  2515. {
  2516.  
  2517. recharger_fichier();
  2518. checkBox_creuser->setEnabled(true);
  2519. }
  2520.  
  2521.  
  2522. void MainWindow::on_actionConnexion_triggered(bool checked)
  2523. {
  2524. msg="";
  2525. efface_buffer();
  2526. //listWidget_4->clear();
  2527.  
  2528. qint8 result;
  2529. if (checked)
  2530. {
  2531. result = Connexion(1);
  2532.  
  2533. if (result == 0)
  2534. {
  2535. Led0->setCouleur(QColor (0,255,0)); // vert
  2536. Led0->setEtat(1);
  2537.  
  2538. Led1->setCouleur(QColor (255,255,0)); // jaune
  2539. Led1->setEtat(1);
  2540.  
  2541. }
  2542. else
  2543. {
  2544. Led0->setCouleur(QColor (255,0,0)); // rouge
  2545. Led0->setEtat(1);
  2546. textEdit_1->setText("pas de connexion USB");
  2547. }
  2548. }
  2549. else
  2550. {
  2551. //on_Btn_stop_flash_clicked(); // le cas échéant...
  2552. Connexion(0);
  2553. Led0->setEtat(0);
  2554. Led1->setEtat(0);
  2555. Led2->setEtat(0);
  2556. Led3->setEtat(0);
  2557. // textEdit_1->clear();
  2558. // textEdit_1->setText("pas de connexion USB");
  2559.  
  2560. }
  2561. }
  2562.  
  2563.  
  2564. /*
  2565. void MainWindow::on_Btn_go_debut_fichier_clicked()
  2566. {
  2567.   Timer1->stop();
  2568.   InitTable3();
  2569.   InitTable4();
  2570.   InitTable5();
  2571.   tableWidget_3->setRowCount(0);
  2572.   liste_apertures.clear();
  2573.   listWidget_1->setCurrentRow(0);
  2574. }
  2575.  
  2576. */
  2577.  
  2578. void MainWindow::analyse_fichier_gerber()
  2579. {
  2580. int n, n_max;
  2581. n_max = liste_gerber1.length();
  2582. InitTable3();
  2583. InitTable4();
  2584. InitTable5();
  2585. tableWidget_3->setRowCount(0);
  2586. tableWidget_4->setRowCount(0);
  2587.  
  2588. liste_apertures.clear();
  2589. liste_elements.clear();
  2590.  
  2591. num_ligne_en_cours1=0;
  2592. num_ligne_en_cours3=0;
  2593. num_ligne3=0;
  2594.  
  2595. for (n=0; n<n_max-1; n++)
  2596. {
  2597. num_ligne_en_cours1 = n;
  2598. analyse_1ligne_fichier();
  2599. }
  2600. creation_liste_elements();
  2601. }
  2602.  
  2603.  
  2604.  
  2605. void MainWindow::on_Btn_effacer_clicked()
  2606. {
  2607. effacer_ecran();
  2608. }
  2609.  
  2610.  
  2611.  
  2612. void MainWindow::on_spinBox_zoom2_valueChanged(double arg1)
  2613. {
  2614. // effacer_ecran();
  2615.  
  2616. zoom = arg1;
  2617. ech = 40.0 / zoom;
  2618. effacer_ecran();
  2619. dessiner_liste_elements_tries();
  2620. trace_liste_apertures();
  2621. }
  2622.  
  2623.  
  2624.  
  2625. void MainWindow::on_Btn_trace_apertures_clicked()
  2626. {
  2627. trace_liste_apertures();
  2628. }
  2629.  
  2630.  
  2631.  
  2632. void MainWindow::on_Btn_redessiner_clicked()
  2633. {
  2634. on_Btn_classe_clicked(); // remplit la liste des éléments triés
  2635. tout_decliquer();
  2636. dessiner_liste_elements_tries();
  2637. }
  2638.  
  2639.  
  2640.  
  2641. int MainWindow::on_Btn_tout_flasher_clicked()
  2642. {
  2643. if ((fileName_en_cours.contains("B_Cu")) && (miroir == 0))
  2644. {
  2645. QMessageBox::StandardButton mb1;
  2646. mb1 = QMessageBox::question(this, "ATTENTION :", "Le fichier chargé est une face bottom, \n pensez à faire une inversion-miroir !",
  2647. QMessageBox::Ok|QMessageBox::Abort);
  2648. if (mb1 == QMessageBox::Abort) { return 1; }
  2649. }
  2650.  
  2651. if (laser_bloque) { QMessageBox msgBox; msgBox.setText("ATTENTION : blocage laser en cours"); msgBox.exec();}
  2652. nb_lignes_envoyees = 0;
  2653. nb_corrections=0;
  2654. if (findecourse == 1)
  2655. {
  2656. QString sx = "ATTENTION : en position Fin de course !";
  2657. QMessageBox msgBox; msgBox.setText(sx); msgBox.exec();
  2658. return 1;
  2659. }
  2660.  
  2661. if (connect_ok==1)
  2662. {
  2663. int n1= spinBox_depart->value();
  2664. if (n1>0)
  2665. {
  2666. QMessageBox::StandardButton mb1;
  2667. mb1 = QMessageBox::question(this, "Attention :", "Début > 0", QMessageBox::Ok|QMessageBox::Abort);
  2668. if (mb1 == QMessageBox::Abort) { return 1; }
  2669. }
  2670.  
  2671. if ( !checkBox_creuser->isChecked())
  2672. {
  2673. QMessageBox::StandardButton mb1;
  2674. mb1 = QMessageBox::question(this, "Attention :", "\"Creuser pastilles\" non coché...", QMessageBox::Ok|QMessageBox::Abort);
  2675. if (mb1 == QMessageBox::Abort) { return 1; }
  2676. }
  2677.  
  2678.  
  2679.  
  2680. tout_decliquer();
  2681. num_ligne_en_cours1=n1-1;// -1 parce que la fonction flash_ligne_suivante() va l'incrémenter
  2682. tableWidget_5->setCurrentCell(n1, 1);
  2683. flashage_en_cours = 1;
  2684. recu_PRET = 1; // pour ne pas coincer dès la première ligne
  2685. flash_ligne_suivante(); // en fait la première...
  2686. Led3->setEtat(1);
  2687. return 0;
  2688. }
  2689. else { QMessageBox msgBox; msgBox.setText("No connect3"); msgBox.exec();}
  2690. return 0;
  2691. }
  2692.  
  2693.  
  2694.  
  2695. void MainWindow::on_Btn_stop_flash_clicked()
  2696. {
  2697.  
  2698. envoi_vers_Atmega("TN");
  2699. flashage_en_cours=0;
  2700. BoutonLed1->setChecked(0);
  2701. Led3->setEtat(0);
  2702. // Connexion(0);
  2703. // Connexion(1);
  2704. /*
  2705.  
  2706.  // efface_buffer();
  2707.  // msg.clear();
  2708.   num_ligne_en_cours1 = 1000;
  2709.  
  2710. */
  2711. }
  2712.  
  2713.  
  2714.  
  2715.  
  2716. void MainWindow::on_Btn_pastille_clicked()
  2717. {
  2718. if (findecourse == 1)
  2719. {
  2720. QString sx = "ATTENTION : en position Fin de course !";
  2721. QMessageBox msgBox; msgBox.setText(sx); msgBox.exec();
  2722. }
  2723. else { envoi_vers_Atmega("P1");} // Pastille rectangulaire ici !
  2724. }
  2725.  
  2726.  
  2727. void MainWindow::on_Btn_pastille_2_clicked()
  2728. {
  2729. QString s_D, ligne_i;
  2730. if (findecourse == 1)
  2731. {
  2732. QString sx = "ATTENTION : en position Fin de course !";
  2733. QMessageBox msgBox; msgBox.setText(sx); msgBox.exec();
  2734. }
  2735.  
  2736. else
  2737. {
  2738. s_D = spinBox_diam_pastille->text();
  2739. s_D = "000000"+s_D;
  2740. s_D = s_D.right(6);
  2741. ligne_i = "PR" + s_D; // Pastille ronde ici !
  2742. envoi_vers_Atmega(ligne_i);
  2743. }
  2744. }
  2745.  
  2746.  
  2747.  
  2748. void MainWindow::on_Btn_piste_X_clicked()
  2749. {
  2750. if (findecourse == 1)
  2751. {
  2752. QString sx = "ATTENTION : en position Fin de course !";
  2753. QMessageBox msgBox; msgBox.setText(sx); msgBox.exec();
  2754. }
  2755. else { envoi_vers_Atmega("S1");} // piste ici !
  2756. }
  2757.  
  2758.  
  2759.  
  2760. void MainWindow::on_Btn_PETIT_CARRE_clicked()
  2761. {
  2762. if (findecourse == 1)
  2763. {
  2764. QString sx = "ATTENTION : en position Fin de course !";
  2765. QMessageBox msgBox; msgBox.setText(sx); msgBox.exec();
  2766. }
  2767. else { envoi_vers_Atmega("C1");} // petit carré ici !
  2768. }
  2769.  
  2770.  
  2771. void MainWindow::on_Btn_MIRE_clicked()
  2772. {
  2773. if (findecourse == 1)
  2774. {
  2775. QString sx = "ATTENTION : en position Fin de course !";
  2776. QMessageBox msgBox; msgBox.setText(sx); msgBox.exec();
  2777. }
  2778. else { envoi_vers_Atmega("H1");} // piste ici !
  2779. }
  2780.  
  2781.  
  2782. void MainWindow::on_Btn_rot_n_pas_X_clicked()
  2783. {
  2784. if (findecourse == 1)
  2785. {
  2786. QString sx = "ATTENTION : en position Fin de course !";
  2787. QMessageBox msgBox; msgBox.setText(sx); msgBox.exec();
  2788. }
  2789. else
  2790. {
  2791. QString s_X, ligne_i;
  2792.  
  2793. Led2->setCouleur(QColor (255,255,0));
  2794. Led2->setEtat(1);
  2795. recu_PRET=0;
  2796.  
  2797. s_X = spinBox_n_pas_X->text();
  2798. s_X = "000000"+s_X;
  2799. s_X = s_X.right(6);
  2800. ligne_i = "XN" + s_X;
  2801. envoi_vers_Atmega(ligne_i);
  2802. }
  2803. }
  2804.  
  2805.  
  2806. void MainWindow::on_Btn_rot_n_pas_Y_clicked()
  2807. {
  2808. if (findecourse == 1)
  2809. {
  2810. QString sx = "ATTENTION : en position Fin de course !";
  2811. QMessageBox msgBox; msgBox.setText(sx); msgBox.exec();
  2812. }
  2813. else
  2814. {
  2815. QString s_Y, ligne_i;
  2816.  
  2817. Led2->setCouleur(QColor (255,255,0));
  2818. Led2->setEtat(1);
  2819. recu_PRET=0;
  2820.  
  2821. s_Y = spinBox_n_pas_Y->text();
  2822. s_Y = "000000"+s_Y;
  2823. s_Y = s_Y.right(6);
  2824. ligne_i = "YN" + s_Y;
  2825. envoi_vers_Atmega(ligne_i);
  2826. }
  2827. }
  2828.  
  2829.  
  2830. /*
  2831.  
  2832. void MainWindow::on_Btn_lever_plume_clicked()
  2833. {
  2834.   envoi_vers_Atmega("ZA");
  2835. }
  2836.  
  2837.  
  2838. void MainWindow::on_Btn_affleure_plume_clicked()
  2839. {
  2840.   envoi_vers_Atmega("ZB");
  2841. }
  2842.  
  2843.  
  2844. void MainWindow::on_Btn_touche_plume_clicked()
  2845. {
  2846.   envoi_vers_Atmega("ZC");
  2847. }
  2848.  
  2849. */
  2850.  
  2851.  
  2852. void MainWindow::on_BoutonLed1_toggled(bool etat) // bouton LED simulation
  2853. {
  2854. if (connect_ok==1)
  2855. {
  2856. if (etat == 1)
  2857. {
  2858. if (laser_bloque != 1)
  2859. {
  2860. QMessageBox::StandardButton mb1;
  2861. mb1 = QMessageBox::question(this, "Attention :", "Laser non bloqué", QMessageBox::Ok|QMessageBox::Abort);
  2862. if (mb1 == QMessageBox::Abort) { return; }
  2863. }
  2864. envoi_vers_Atmega("TI");
  2865. }
  2866. else {envoi_vers_Atmega("TN");}
  2867. }
  2868. else
  2869. {
  2870. BoutonLed1->setChecked(0);
  2871. QMessageBox msgBox; msgBox.setText("USB non connecté, ligne non transmise"); msgBox.exec();
  2872. }
  2873. }
  2874.  
  2875.  
  2876.  
  2877. void MainWindow::on_BoutonLed2_toggled(bool etat)
  2878. {
  2879. on_Btn_classe_clicked();
  2880. trace_aspect_reel = etat;
  2881. effacer_ecran();
  2882. dessiner_liste_elements_tries();
  2883. trace_liste_apertures();
  2884. }
  2885.  
  2886.  
  2887. void MainWindow::on_BoutonLed3_toggled(bool etat)
  2888. {
  2889. if ((etat == 1) && (BoutonLed4->isChecked()))
  2890. {
  2891.  
  2892. BoutonLed3->setChecked(0);
  2893. QMessageBox msgBox; msgBox.setText("blocage laser en cours"); msgBox.exec();
  2894. }
  2895. else
  2896. {
  2897. laser_on = etat;
  2898. if (laser_on == 1) { envoi_vers_Atmega("Z1"); } else {envoi_vers_Atmega("Z0"); }
  2899. }
  2900.  
  2901. }
  2902.  
  2903.  
  2904. void MainWindow::on_BoutonLed4_toggled(bool etat)
  2905. {
  2906. laser_bloque = etat;
  2907.  
  2908. if (laser_bloque == 1)
  2909. {
  2910. BoutonLed3->setChecked(0);
  2911. if (connect_ok) { envoi_vers_Atmega("ZB"); } // bloque le laser
  2912. }
  2913. else {envoi_vers_Atmega("ZD"); } // débloque le laser
  2914. }
  2915.  
  2916.  
  2917.  
  2918.  
  2919.  
  2920. int MainWindow::on_tableWidget_4_cellClicked(int row, int column)
  2921. {
  2922. int num_origine, num_element_trie;
  2923. QString s1,s2;
  2924.  
  2925. column = column; // pour eviter message de compilation...
  2926. spinBox_depart->setValue(row);
  2927.  
  2928. if(row == 0) {return 1;}
  2929.  
  2930. num_origine = liste_elements[row].num_origine;
  2931. num_element_trie = liste_elements[row].nouveau_num;
  2932.  
  2933. s1.setNum(num_origine); // conversion num -> txt
  2934. s2.setNum(num_element_trie); // conversion num -> txt
  2935. lineEdit_5->setText(s1+" - "+s2);
  2936.  
  2937.  
  2938. liste_elements_tries[num_element_trie-1].clique =1;
  2939. dessine_1ligne_liste_triee(num_element_trie-1);
  2940.  
  2941. return 0;
  2942. }
  2943.  
  2944.  
  2945.  
  2946. void MainWindow::on_tableWidget_5_cellClicked(int row, int column)
  2947. {
  2948. QString ligne_i;
  2949. int num_origine, num_element_trie;
  2950. QString s1,s2;
  2951.  
  2952. ligne_i = tableWidget_5->item(row,0)->text();
  2953. lineEdit_1->setText(ligne_i);
  2954.  
  2955. column = column; // pour eviter message de compilation...
  2956. spinBox_depart->setValue(row);
  2957.  
  2958. num_origine = liste_elements_tries[row].num_origine;
  2959. num_element_trie = row;
  2960.  
  2961. s1.setNum(num_origine); // conversion num -> txt
  2962. s2.setNum(num_element_trie+1); // conversion num -> txt
  2963. lineEdit_5->setText(s1+" - "+s2);
  2964.  
  2965. //liste_elements[num_origine].clique =1;
  2966. liste_elements_tries[num_element_trie].clique =1;
  2967.  
  2968. dessine_1ligne_liste_triee(num_element_trie);
  2969. }
  2970.  
  2971.  
  2972.  
  2973. void MainWindow::onReceptPos(int px, int py) // emis depuis un clic sur scene1, voir ma class "scene_cliquable"
  2974. {
  2975. QString s1,s2;
  2976. QPoint P;
  2977. s1.setNum(px);// conversion num -> txt
  2978. s2.setNum(-py);
  2979.  
  2980. lineEdit_2->setText("x="+s1+" y="+s2);
  2981.  
  2982. P.setX(px * ech);
  2983. P.setY(-py*ech);
  2984. detecte_element_pointe(P);
  2985.  
  2986. memo_x = 0;
  2987. memo_y = 0;
  2988.  
  2989. }
  2990.  
  2991.  
  2992.  
  2993. /**
  2994.   REMARQUE :
  2995.  
  2996. QGraphicsView possède une propriété 'Dragmode' qui peut se règler à 'ScrollHandDrag' dans l'UI...
  2997. Mais ça donne un curseur 'Hand' tout le temps, ce qui ne m'arrange pas ici.
  2998.  
  2999. je cite :
  3000. "After setting "setDragMode(QGraphicsView::ScrollHandDrag)", the cursor is set permanently to a hand
  3001. while over the graphicsview. I thought that the correct mode was to turn the cursor to a hand only
  3002. when the left mouse button is pressed. I'd prefer the latter mode of operation, if possible.
  3003. Is there any way to correct this?"
  3004.  
  3005. Apparemment NON ! C'est la raison de la fonction suivante et du traitement
  3006. mousePressEvent et mouseWheelEvent dans la class 'scene_clicable'
  3007. ça fait pas mal de code en plus, mais je conserve le pointeur flèche pour cliquer sur les pistes
  3008. */
  3009.  
  3010. void MainWindow::onReceptMove(int dx, int dy) // emis depuis roulette souris sur scene1, voir ma class "scene_cliquable"
  3011. {
  3012. groupe_trace->moveBy(dx - memo_x, dy - memo_y);
  3013. groupe_trous->moveBy(dx - memo_x, dy - memo_y);
  3014. groupe_pointage->moveBy(dx - memo_x, dy - memo_y);
  3015. groupe_segments_faits->moveBy(dx - memo_x, dy - memo_y);
  3016. groupe_pastilles_faites->moveBy(dx - memo_x, dy - memo_y);
  3017.  
  3018. memo_x = dx;
  3019. memo_y = dy;
  3020. }
  3021.  
  3022.  
  3023.  
  3024. void MainWindow::classer_pastilles()
  3025. {
  3026. // source liste_elements ; destination : liste_elements_tries et liste_segments
  3027. // simple recopie des definitions des apertures et du traçage des pastilles dans l'ordre du fichier d'origine
  3028.  
  3029. quint16 i, i_max;
  3030. Element EL_i;
  3031.  
  3032. i_max = liste_elements.count();
  3033.  
  3034. for (i=1; i<i_max; i++ ) // attention : 1 et pas 0 ! les numeros d'ordres commencent à 1. (zéro = pas d'élémént trouvé)
  3035. {
  3036. EL_i = liste_elements[i];
  3037. if((EL_i.sorte == 'M' ) || (EL_i.sorte == 'P' ) )
  3038. {
  3039. liste_elements[i].clique=1;
  3040. EL_i.clique=1;
  3041. liste_elements_tries << EL_i;
  3042. if (EL_i.sorte == 'P' ) { liste_pastilles << EL_i; }
  3043. num_element_proche_en_cours++;
  3044. }
  3045. if((EL_i.sorte == 'S' ) )
  3046. {
  3047. liste_segments << EL_i;
  3048. }
  3049. }
  3050. }
  3051.  
  3052.  
  3053.  
  3054.  
  3055. void MainWindow::classer_le_reste()
  3056. {
  3057. // source : TW4 ; destination TW5
  3058.  
  3059. pos_actuelle.setX(0);
  3060. pos_actuelle.setY(0);
  3061. while (num_element_proche_en_cours != 0)
  3062. {
  3063. num_element_proche_en_cours = detecte_element_proche();
  3064. // qDebug() << "n = " << num_element_proche_en_cours;
  3065. }
  3066. dessine_1ligne_liste_triee(num_element_proche_en_cours); // afin de positionner le pointage sur l'élément cliqué
  3067.  
  3068. }
  3069.  
  3070.  
  3071. void MainWindow::raccourcir_segment(Element *segment_i, quint16 ls, char bout) // // dp est le diametre de la pastille, ls la largeur du segment
  3072. {
  3073. qreal x1, y1, x2, y2, x3, y3, dx, dy, rt,rc, a, b;
  3074.  
  3075. ls /=2;
  3076.  
  3077. //rt = dp/3.5; // rayon trou (proportionnel au diametre de la pastille)
  3078. rt = 15; // rayon trou (fixe)
  3079.  
  3080. rc = ls+rt; // recul (valeur à retrancher à la longueur du segment)
  3081.  
  3082. if (bout == 'A') { x1 = segment_i->A.x(); y1 = segment_i->A.y(); x2 = segment_i->B.x(); y2 = segment_i->B.y(); }
  3083. else { x1 = segment_i->B.x(); y1 = segment_i->B.y(); x2 = segment_i->A.x(); y2 = segment_i->A.y(); }
  3084.  
  3085. if (x2 == x1) {x2 = x1 + 1;} // ce qui évite les /0 en introduisanr une erreur négligeable
  3086.  
  3087. dx= x2-x1;
  3088. dy= y2-y1;
  3089.  
  3090. a=dy/dx;
  3091. b=y1-a*x1;
  3092.  
  3093. if(x2>x1) {x3=x1+sqrt((rc*rc)/((a*a)+1));} else {x3=x1-sqrt((rc*rc)/((a*a)+1));}
  3094. y3=a*x3+b;
  3095.  
  3096. if (bout == 'A') { segment_i->A.setX(x3); segment_i->A.setY(y3); }
  3097. if (bout == 'B') { segment_i->B.setX(x3); segment_i->B.setY(y3); }
  3098. }
  3099.  
  3100.  
  3101.  
  3102. void MainWindow::creuser_pastilles()
  3103. {
  3104. quint16 s, t, s_max, t_max, num_ap_p, num_ap_s, diametre_p, diametre_s;
  3105. Element element_i, trou_i;
  3106. Aperture aperture_p, aperture_s;
  3107. QPointF D1;
  3108. qreal distance, ds_min;
  3109.  
  3110. // Btn_creuser_pastilles->setEnabled(false);
  3111. ds_min = 49;
  3112. t_max = liste_trous.count();
  3113. s_max = liste_elements.count();
  3114. // n_max = liste_apertures.length();
  3115.  
  3116. for (t=0; t<t_max; t++ )
  3117. {
  3118. trou_i = liste_trous[t];
  3119. num_ap_p = trou_i.num_aperture-10;
  3120. aperture_p = liste_apertures[num_ap_p]; //aperture_p -> celle de la pastille et de son trou
  3121. diametre_p = aperture_p.WH.rx(); // diametre_p est le diametre de la pastille
  3122.  
  3123. if (diametre_p > 50) // pour ne pas raccourcir les segments raccordés aux très petites pastiles (= liaisons entre segments)
  3124. {
  3125. for (s=1; s<s_max; s++ )
  3126. {
  3127. element_i = liste_elements[s];
  3128. if((element_i.sorte == 'S' ) ) // si c'est un segment
  3129. {
  3130. D1=element_i.A-trou_i.A; // element_i est le segment; extrémité (A); trou_i est la pastille entourant le trou
  3131. distance = sqrt(sqr(D1.x())+sqr(D1.y()));
  3132. if ((distance < ds_min) )
  3133. {
  3134. num_ap_s = element_i.num_aperture-10;
  3135. aperture_s = liste_apertures[num_ap_s];
  3136. diametre_s = aperture_s.WH.rx(); // diametre_s est le diametre (=largeur) du segment
  3137. raccourcir_segment(&element_i, diametre_s, 'A');
  3138. liste_elements[s]=element_i;
  3139. }
  3140.  
  3141. D1=element_i.B-trou_i.A; // autre extrémité (B) du segment
  3142. distance = sqrt(sqr(D1.x())+sqr(D1.y()));
  3143. if ((distance < ds_min) )
  3144. {
  3145. num_ap_s = element_i.num_aperture-10;
  3146. aperture_s = liste_apertures[num_ap_s];
  3147. diametre_s = aperture_s.WH.rx(); // diametre_s est le diametre (=largeur) du segment
  3148. raccourcir_segment(&element_i, diametre_s, 'B');
  3149. liste_elements[s]=element_i;
  3150. }
  3151. }
  3152. }
  3153. }
  3154. }
  3155. pastilles_creuses = 1;
  3156. //Led5->setEtat(1);
  3157. }
  3158.  
  3159.  
  3160.  
  3161. void MainWindow::classer_auto()
  3162. {
  3163. liste_elements_tries.clear();
  3164. tout_decliquer();
  3165. num_element_proche_en_cours = 1;
  3166.  
  3167. // classer_pastilles(); // je ne le fais plus
  3168.  
  3169. classer_le_reste();
  3170.  
  3171. affiche_liste_elements(); // dans le tableau
  3172. affiche_liste_elements_tries(); // dans le tableau
  3173.  
  3174. effacer_ecran();
  3175. tout_decliquer();
  3176. dessiner_liste_elements_tries(); // sur la scene graphique
  3177. }
  3178.  
  3179.  
  3180.  
  3181. void MainWindow::on_Btn_classe_clicked()
  3182. {
  3183. classer_auto();
  3184. trace_liste_apertures();
  3185. }
  3186.  
  3187.  
  3188. void MainWindow::on_Btn_aff5_ralenti_clicked()
  3189. {
  3190. on_Btn_classe_clicked();
  3191. i_dess_tri=0;
  3192. effacer_ecran();
  3193. tout_decliquer();
  3194. dessiner_liste_elements_tries();
  3195. Timer2->start(100);
  3196. }
  3197.  
  3198.  
  3199. void MainWindow::tout_decliquer()
  3200. {
  3201. quint16 i, i_max;
  3202.  
  3203. i_max = liste_elements.count();
  3204. for (i=0; i<i_max; i++ ) { liste_elements[i].clique = 0; }
  3205.  
  3206. i_max = liste_elements_tries.count();
  3207. for (i=0; i<i_max; i++ ) { liste_elements_tries[i].clique = 0; }
  3208.  
  3209. affiche_liste_elements(); // dans le tableau
  3210. affiche_liste_elements_tries(); // dans le tableau
  3211. dessiner_liste_elements_tries(); // sur la scene graphique
  3212.  
  3213. }
  3214.  
  3215.  
  3216. void MainWindow::on_Btn_declique_tout_clicked()
  3217. {
  3218. tout_decliquer();
  3219. }
  3220.  
  3221.  
  3222. void MainWindow::on_actionAide_triggered()
  3223. {
  3224. QString sx = "Les pastilles ainsi que les points rouges sur les segments sont cliquables.\n";
  3225. sx +="Les listes sont cliquables ce qui permet de localiser l'élément sur la platine.\n";
  3226. sx +="C'est la liste classée (verte) qui sera flashée.\n";
  3227. sx +="Le tracage des segments se fera dans le sens points rouges vers points verts.\n\n";
  3228. sx +="Le bouton \"creuser pastilles\" raccourcit les pistes (en RAM) mais ne touche pas au fichier gerber.\n";
  3229. sx +="Il n'est pas possible d'annuler cette action, sauf en rechargeant le fichier.\n";
  3230. sx +="Il faut creuser les pastilles AVANT de faire (éventuellement) 'miroir x'.\n\n";
  3231. sx +="Le choix du port USB doit correspondre 'au device' affiché dans le dossier /dev (sous Linux).\n";
  3232. sx +="Ce 'device' apparait (en temps réel lors du branchement) dans /dev sous forme d'un fichier.\n\n";
  3233. sx +="Le mode simulation envoie les commandes à la machine, mais celle-ci restera moteurs à l'arrêt.\n";
  3234. sx +="Le mode simulation : cliquez sur 'simulation' puis sur 'tout flasher'\n\n";
  3235. sx +="Le zoom du graphique peut se faire avec la molette de la souris dans la case 'zoom' sous l'image.\n";
  3236. sx +="On peut déplacer le graphique à la souris (clic + drag).\n\n";
  3237. QMessageBox msgBox; msgBox.setText(sx); msgBox.exec();
  3238. }
  3239.  
  3240.  
  3241.  
  3242. void MainWindow::on_Btn_stop_ralenti_clicked()
  3243. {
  3244. Timer2->stop();
  3245. }
  3246.  
  3247.  
  3248. void MainWindow::annonce_vocale(int n)
  3249. {
  3250. // ces annonces ont pour but de travailler écran éteint pour éviter la lumière parasite dans la pièce
  3251. if (! annonces_faites.contains(n)) // s'agissant de l'annonce d'un pourcentage, il ne faut pas répèter 2x la même
  3252. {
  3253. annonces_faites << n;
  3254. QString s;
  3255. s.setNum(n); // conversion num -> txt
  3256. QString program = "/usr/bin/mplayer"; // sous Linux !
  3257. QStringList arguments;
  3258. arguments << "../audio/" + s + ".mp3" << "../audio/pourcent.mp3";
  3259.  
  3260. QProcess *myProcess = new QProcess();
  3261. myProcess->start(program, arguments);
  3262. }
  3263. }
  3264.  
  3265.  
  3266. void MainWindow::on_Btn_TEST_clicked()
  3267. {
  3268. /*
  3269.   QString s1;
  3270.   s1="Il était une fois une marchande de foie qui vendait du foie dans la ville de Foix";
  3271.   s1="ABCD";
  3272.   envoi_vers_Atmega(s1);
  3273. */
  3274. // envoi_vers_Atmega("TE");
  3275.  
  3276. // QScrollBar *SB1 = textEdit_1->verticalScrollBar();
  3277. // SB1->setValue(SB1->maximum());
  3278.  
  3279. // groupe_trace->moveBy(100,100);
  3280. // groupe_pastilles_faites->moveBy(100,100);
  3281. // groupe_segments_faits->moveBy(100,100);
  3282. // groupe_pointage->moveBy(100,100);
  3283. // groupe_trous->moveBy(100,100);
  3284.  
  3285. // pourcent +=10;
  3286. // annonce_vocale(pourcent);
  3287.  
  3288. checkBox_creuser->setEnabled(true);
  3289.  
  3290. }
  3291.  
  3292.  
  3293.  
  3294. void MainWindow::on_Btn_GOTO_10x10_clicked()
  3295. {
  3296. doubleSpinBox_X->setValue(10.0);
  3297. doubleSpinBox_Y->setValue(10.0);
  3298. on_Btn_GOTO_clicked();
  3299. }
  3300.  
  3301.  
  3302.  
  3303. void MainWindow::on_Btn_GOTO_20x20_clicked()
  3304. {
  3305. doubleSpinBox_X->setValue(20.0);
  3306. doubleSpinBox_Y->setValue(20.0);
  3307. on_Btn_GOTO_clicked();
  3308. }
  3309.  
  3310.  
  3311.  
  3312. void MainWindow::on_Btn_RAZ_flash_clicked()
  3313. {
  3314. tableWidget_5->selectRow(0);
  3315. spinBox_depart->setValue(0);
  3316. liste_elements_tries[0].clique =1;
  3317. dessine_1ligne_liste_triee(0);
  3318. }
  3319.  
  3320.  
  3321.  
  3322.  
  3323. /**
  3324. void MainWindow::on_Btn_creuser_pastilles_clicked()
  3325. {
  3326.   if(pastilles_creuses == 0)
  3327.   {
  3328.   creuser_pastilles();
  3329.   on_Btn_classe_clicked(); // remplit la liste des éléments triés
  3330.   effacer_ecran();
  3331.   dessiner_liste_elements_tries();
  3332.   }
  3333. }
  3334. **/
  3335.  
  3336.  
  3337. void MainWindow::on_Btn_lum_laser_clicked()
  3338. {
  3339. //envoi la consigne de luminosité au laser
  3340.  
  3341. QString s_L, ligne_i;
  3342.  
  3343. Led2->setCouleur(QColor (255,255,0));
  3344. Led2->setEtat(1);
  3345. recu_PRET=0;
  3346.  
  3347. s_L = spinBox_lum_laser->text();
  3348. s_L = "000000"+s_L;
  3349. s_L = s_L.right(6);
  3350. ligne_i = "LU" + s_L;
  3351. envoi_vers_Atmega(ligne_i);
  3352. }
  3353.  
  3354.  
  3355. void MainWindow::on_Btn_Z_laser_clicked()
  3356. {
  3357. //envoi la consigne de de hauteur (axe Z) au laser
  3358.  
  3359. QString s_Z, ligne_i;
  3360.  
  3361. Led2->setCouleur(QColor (255,255,0));
  3362. Led2->setEtat(1);
  3363. recu_PRET=0;
  3364.  
  3365. s_Z = spinBox_Z_laser->text();
  3366. s_Z = "000000"+s_Z;
  3367. s_Z = s_Z.right(6);
  3368. ligne_i = "LZ" + s_Z;
  3369. envoi_vers_Atmega(ligne_i);
  3370. }
  3371.  
  3372. void MainWindow::on_checkBox_miroir_toggled(bool checked)
  3373. {
  3374. checkBox_creuser->setDisabled(1);
  3375.  
  3376. miroir = checked;
  3377. inverser_miroir_elements();
  3378. classer_auto();
  3379. dessiner_liste_elements_tries();
  3380. trace_liste_apertures();
  3381. }
  3382.  
  3383. void MainWindow::on_Btn_correction_clicked()
  3384. {
  3385. // listWidget_4->addItem("envoi: CE");
  3386. envoi_vers_Atmega("CE");
  3387. }
  3388.  
  3389. void MainWindow::on_Btn_lire_ERR_clicked()
  3390. {
  3391.  
  3392. // listWidget_4->addItem("envoi: LE");
  3393. envoi_vers_Atmega("LE");
  3394. }
  3395.  
  3396.  
  3397. void MainWindow::on_Btn_gotoHome_2_clicked()
  3398. {
  3399. on_Btn_gotoHome_clicked();
  3400. }
  3401.  
  3402.  
  3403.  
  3404. void MainWindow::on_Btn_help_LW4_clicked()
  3405. {
  3406. QString sx = "Sur fond vert : les commandes envoyées par le PC vers l'ATmega\n";
  3407. sx +="Sur fond jaune : les réponses de l'ATmega\n";
  3408. QMessageBox msgBox; msgBox.setText(sx); msgBox.exec();
  3409. }
  3410.  
  3411.  
  3412.  
  3413. void MainWindow::on_checkBox_creuser_toggled(bool checked)
  3414. {
  3415. if(checked == 1)
  3416. {
  3417. creuser_pastilles();
  3418. on_Btn_classe_clicked(); // remplit la liste des éléments triés
  3419. effacer_ecran();
  3420. dessiner_liste_elements_tries();
  3421. }
  3422. else { recharger_fichier(); }
  3423. }
  3424.  
  3425.  
  3426. void MainWindow::on_actionttyUSB0_triggered()
  3427. {
  3428. port_usb_name="ttyUSB0";
  3429. actionConnexion->setText("Connexion_ttyUSB0");
  3430. }
  3431.  
  3432.  
  3433. void MainWindow::on_actionttyUSB1_triggered()
  3434. {
  3435. port_usb_name="ttyUSB1";
  3436. actionConnexion->setText("Connexion_ttyUSB1");
  3437. }
  3438.  
  3439. void MainWindow::on_actionttyACM0_triggered()
  3440. {
  3441. port_usb_name="ttyACM0";
  3442. actionConnexion->setText("Connexion_ttyACM0");
  3443. }
  3444.  
  3445. void MainWindow::on_actionttyACM1_triggered()
  3446. {
  3447. port_usb_name="ttyACM1";
  3448. actionConnexion->setText("Connexion_ttyACM1");
  3449. }
  3450.  
  3451. void MainWindow::on_actionttyACM2_triggered()
  3452. {
  3453. port_usb_name="ttyACM2";
  3454. actionConnexion->setText("Connexion_ttyACM2");
  3455. }
  3456.  
  3457. void MainWindow::on_actionttyACM3_triggered()
  3458. {
  3459. port_usb_name="ttyACM3";
  3460. actionConnexion->setText("Connexion_ttyACM3");
  3461. }
  3462.  
  3463. void MainWindow::on_actionttyACM4_triggered()
  3464. {
  3465. port_usb_name="ttyACM4";
  3466. actionConnexion->setText("Connexion_ttyACM4");
  3467. }
  3468.  
  3469.  
  3470.  
  3471. void MainWindow::on_Btn_LST_AP_clicked()
  3472. {
  3473. envoi_vers_Atmega("TA");
  3474. }
  3475.  
  3476.  
  3477.  
  3478. void MainWindow::on_actionAffichage_tendu_triggered(bool checked)
  3479. {
  3480. if (checked)
  3481. {
  3482. window()->setGeometry(0,0,1900,900);
  3483. textEdit_1->setGeometry(1285,5,595,855);
  3484. Btn_close_Tx1->setGeometry(1855,10,20,20);
  3485. Btn_save_log->setGeometry(1780,10,70,20);
  3486. }
  3487. else
  3488. {
  3489. window()->setGeometry(0,0,1279,889);
  3490. textEdit_1->setGeometry(910,710,361,151);
  3491. Btn_close_Tx1->setGeometry(1247,715,20,20);
  3492. Btn_save_log->setGeometry(1172,715,70,20);
  3493. }
  3494. }
  3495.  
  3496.  
  3497.  
  3498.  
  3499. void MainWindow::on_Btn_close_Tx1_clicked()
  3500. {
  3501. textEdit_1->clear();
  3502. }
  3503.  
  3504. void MainWindow::on_Btn_save_log_clicked()
  3505. {
  3506.  
  3507. int lg, p1, i, ok;
  3508. liste_log << "";
  3509. QString dossier1 = fileName_en_cours;
  3510. lg = fileName_en_cours.length();
  3511. i= lg;
  3512. ok=0;
  3513. while ((i>0) && (ok == 0) )
  3514. {
  3515. i--;
  3516. if (fileName_en_cours[i] == '/') { ok=1; }
  3517. }
  3518. dossier1 = fileName_en_cours.left(i);
  3519.  
  3520. // recherche du dernier '/'
  3521. qDebug() << "fileName_en_cours = " << fileName_en_cours;
  3522. qDebug() << "lg = " << lg;
  3523. qDebug() << "i = " << i;
  3524. qDebug() << "dossier1 = " << dossier1;
  3525. record_fichier_log(dossier1, "/log.txt");
  3526. QString sx = "Le fichier log ( " + dossier1 + "/log.txt" + " ) a été enregistré";
  3527. QMessageBox msgBox; msgBox.setText(sx); msgBox.exec();
  3528.  
  3529. }
  3530.  
  3531.  

Le fichier mainwindow.h


Le fichier mainwindow.h
  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H
  3.  
  4. #include <QMainWindow>
  5. #include <QGraphicsView>
  6. #include <QToolBar>
  7. #include "scene_cliquable.h"
  8. #include <QAction>
  9. #include <QGraphicsTextItem>
  10. #include <QGraphicsLineItem>
  11. #include <QGraphicsItemGroup>
  12. #include <QTimer>
  13.  
  14. #include "ui_mainwindow.h"
  15. #include "boutonled.h"
  16. #include "qled.h"
  17.  
  18. #include "qextserialport.h"
  19.  
  20.  
  21.  
  22. struct Aperture
  23. {
  24. char forme; // = 'R'(Rectangle) ou 'C'(Cercle) // attention : ne pas confondre avec R comme ronde...!
  25. QPoint WH;
  26. };
  27.  
  28.  
  29.  
  30. struct Element
  31. {
  32. quint16 num_origine; // numéro d'ordre dans la liste gerber d'origine
  33. quint16 nouveau_num; // numéro d'ordre dans la liste gerber triée
  34. QString texte_affichable; // reprend les données sous forme texte pour pouvoir les afficher dans les tables
  35. char sorte; // = 'M' (ajout aperture) ou 'S'(segment) ou ='P' (pastille)
  36. qint16 num;
  37. qint8 num_aperture;
  38. QPoint A;
  39. QPoint B;
  40. QPoint M; // milieu
  41.  
  42. QColor couleur;
  43. int clique; // cliqué. = 0 ou 1
  44. };
  45.  
  46. /*
  47. struct Distance
  48. {
  49.   quint16 num_origine;
  50.   qint16 ds; // distance à un élément donné
  51. };
  52. */
  53.  
  54.  
  55. //class QAction;
  56.  
  57.  
  58. class MainWindow : public QMainWindow, public Ui::MainWindow
  59. {
  60. Q_OBJECT
  61.  
  62. public:
  63. explicit MainWindow(QWidget *parent = 0);
  64. ~MainWindow();
  65.  
  66. SceneCliquable *scene1;
  67.  
  68. private slots:
  69.  
  70. // qreal square(qreal r1);
  71.  
  72. void InitTable3();
  73. void InitTable4();
  74. void InitTable5();
  75.  
  76. void lecture_fichier_init();
  77. void enregistrer_fichier_init();
  78.  
  79. void efface_buffer();
  80. void onReadyRead();
  81.  
  82. qint8 Connexion(qint8 cnx);
  83. void effacer_ecran();
  84. void effacer_pointage();
  85.  
  86.  
  87. void affiche_liste_elements();
  88. void affiche_liste_elements_tries();
  89.  
  90. int recharger_fichier();
  91. int lire_fichier_gerber(QString nom_fichier1);
  92. int analyse_1ligne_fichier();
  93. void analyse_fichier_gerber();
  94. void creation_liste_elements();
  95. void creation_liste_segments();
  96. void creation_liste_trous();
  97.  
  98. void raccourcir_segment(Element *segment_i, quint16 ls, char bout);
  99. void inverser_element(Element *EL);
  100.  
  101. // void compte_pistes_nulles();
  102. // void supprimer_pistes_nulles();
  103.  
  104. void Timer1_clic();
  105. void Timer2_clic();
  106. void Timer3_clic();
  107.  
  108. void flash_ligne_suivante();
  109.  
  110. int on_Btn_envoi_clicked();
  111.  
  112. void record_fichier_log(QString dossier1, QString fichier_out);
  113.  
  114. void on_Btn_RAZ_pos_clicked();
  115. void on_Btn_gotoHome_clicked();
  116. void on_Btn_gotoSafePos_clicked();
  117. void on_Btn_GOTO_clicked();
  118.  
  119. // void on_Btn_TRACER_AUTO_clicked();
  120. // void on_Btn_trace1ligne_clicked();
  121. int Envoi_une_ligne();
  122.  
  123. void pointer(int i);
  124. void tout_decliquer();
  125.  
  126. void tracer_mire_origine();
  127. void tracer_segment(QPoint A, QPoint B, qreal largeur, QColor couleur_i, QGraphicsItemGroup *groupe_i);
  128. void tracer_pastille_ronde(QPoint A, QPoint B, QColor couleur_i, QGraphicsItemGroup *groupe_i);
  129. void tracer_pastille_rectangulaire(QPoint A, QPoint B, QColor couleur_i, QGraphicsItemGroup *groupe_i);
  130. void tracer_trou(QPoint A, quint16 diametre);
  131.  
  132. void tracer_texte(int x, int y, QString texte_i);
  133. int dessine_1ligne_liste_triee(int i);
  134. void dessiner_liste_elements_tries();
  135. int detecte_element_pointe(QPoint P);
  136. quint16 detecte_element_proche();
  137.  
  138. void classer_pastilles();
  139. void classer_le_reste();
  140. void classer_auto();
  141. void creuser_pastilles();
  142.  
  143.  
  144. void inverser_miroir_elements();
  145.  
  146. void trace_liste_apertures();
  147. QString mise_en_forme_ligne_pour_envoi(QString s_i);
  148. void envoi_vers_Atmega(QString s1);
  149.  
  150. // void on_Btn_ralentir_clicked();
  151. void on_Btn_pastille_clicked();
  152. int on_actionOuvrir_triggered();
  153. void on_actionRecharger_triggered();
  154. void on_actionConnexion_triggered(bool checked);
  155.  
  156. void on_Btn_stop_clicked();
  157. // void on_Btn_stop3_clicked();
  158.  
  159. void on_Btn_R_clicked();
  160. void on_Btn_Xplus_clicked();
  161. void on_Btn_Xmoins_clicked();
  162. void on_Btn_Yplus_clicked();
  163. void on_Btn_Ymoins_clicked();
  164. void on_Btn_Xplus1mm_clicked();
  165. void on_Btn_Yplus1mm_clicked();
  166. void on_Btn_Xplus10mm_clicked();
  167. void on_Btn_Yplus10mm_clicked();
  168. void on_Btn_Xmoins10mm_clicked();
  169. void on_Btn_Xmoins1mm_clicked();
  170. void on_Btn_Ymoins1mm_clicked();
  171. void on_Btn_Ymoins10mm_clicked();
  172. // void on_Btn_Zmoins1mm_clicked();
  173. // void on_Btn_Zplus1mm_clicked();
  174. // void on_Btn_Zmoins5mm_clicked();
  175. // void on_Btn_Zplus5mm_clicked();
  176. // void on_Btn_go_debut_fichier_clicked();
  177. void on_Btn_effacer_clicked();
  178. // void on_BoutonLed1_toggled(bool etat);
  179.  
  180. void on_spinBox_zoom2_valueChanged(double arg1);
  181. void on_Btn_trace_apertures_clicked();
  182. void on_Btn_redessiner_clicked();
  183. int on_Btn_tout_flasher_clicked();
  184. void on_BoutonLed1_toggled(bool etat);
  185. void on_BoutonLed2_toggled(bool etat);
  186. void on_BoutonLed3_toggled(bool etat);
  187. void on_BoutonLed4_toggled(bool etat);
  188.  
  189. void on_Btn_stop_flash_clicked();
  190.  
  191. // void on_Btn_lever_plume_clicked();
  192. void on_Btn_pastille_2_clicked();
  193. // void on_Btn_affleure_plume_clicked();
  194. // void on_Btn_touche_plume_clicked();
  195.  
  196. // void on_tableWidget_4_cellClicked(int row, int column);
  197. void onReceptPos(int px, int py);
  198. void onReceptMove(int dx, int dy);
  199.  
  200. // void mousePressEvent(QMouseEvent *event);
  201. int on_tableWidget_4_cellClicked(int row, int column);
  202. void on_tableWidget_5_cellClicked(int row, int column);
  203. void on_Btn_classe_clicked();
  204. void on_Btn_aff5_ralenti_clicked();
  205. void on_Btn_declique_tout_clicked();
  206. void on_actionAide_triggered();
  207. void on_Btn_stop_ralenti_clicked();
  208. void on_Btn_piste_X_clicked();
  209. void on_Btn_MIRE_clicked();
  210. void on_Btn_TEST_clicked();
  211. void on_Btn_rot_n_pas_X_clicked();
  212. void on_Btn_rot_n_pas_Y_clicked();
  213. void on_Btn_PETIT_CARRE_clicked();
  214. void on_Btn_GOTO_20x20_clicked();
  215. void on_Btn_RAZ_flash_clicked();
  216. void publier(QString msg_i);
  217.  
  218. void annonce_vocale(int n_i);
  219. // void on_Btn_creuser_pastilles_clicked();
  220.  
  221. void on_Btn_lum_laser_clicked();
  222. void on_Btn_Z_laser_clicked();
  223. void on_Btn_correction_clicked();
  224. void on_Btn_lire_ERR_clicked();
  225. void on_Btn_GOTO_10x10_clicked();
  226. void on_Btn_gotoHome_2_clicked();
  227. void on_Btn_help_LW4_clicked();
  228. void on_checkBox_miroir_toggled(bool checked);
  229. void on_checkBox_creuser_toggled(bool checked);
  230. void on_actionttyUSB0_triggered();
  231. void on_actionttyUSB1_triggered();
  232. void on_actionttyACM0_triggered();
  233. void on_actionttyACM1_triggered();
  234. void on_Btn_LST_AP_clicked();
  235. void on_actionAffichage_tendu_triggered(bool checked);
  236. void on_actionttyACM2_triggered();
  237. void on_actionttyACM3_triggered();
  238. void on_actionttyACM4_triggered();
  239. void on_Btn_close_Tx1_clicked();
  240.  
  241. void on_Btn_save_log_clicked();
  242.  
  243. // void mouseMoveEvent(QMouseEvent *event);
  244.  
  245.  
  246. private:
  247.  
  248. void createActions();
  249. void createConnections();
  250. void createToolBar();
  251.  
  252. QAction* lineAction;
  253. QAction* selectAction;
  254. QActionGroup *actionGroup;
  255. QToolBar* drawingToolBar;
  256.  
  257.  
  258. QextSerialPort *portUSB;
  259.  
  260.  
  261. QGraphicsItemGroup *groupe_trace;
  262. QGraphicsItemGroup *groupe_pastilles_faites;
  263. QGraphicsItemGroup *groupe_segments_faits;
  264. QGraphicsItemGroup *groupe_pointage;
  265. QGraphicsItemGroup *groupe_trous;
  266.  
  267. //QGraphicsLineItem *ligne1;
  268. // QGraphicsEllipseItem *ellipse;
  269. //QGraphicsRectItem *rectangle;
  270. //QGraphicsLineItem *segment_trace;
  271. // QGraphicsLineItem *segment_pointage;
  272. //QGraphicsTextItem *gtexte;
  273.  
  274. //QGraphicsTextItem *texte_frq;
  275.  
  276. QLed *Led0;
  277. QLed *Led1;
  278. QLed *Led2;
  279. QLed *Led3;
  280. QLed *Led4;
  281. // QLed *Led5;
  282.  
  283. QBoutonLed *BoutonLed1;
  284. QBoutonLed *BoutonLed2;
  285. QBoutonLed *BoutonLed3;
  286. QBoutonLed *BoutonLed4;
  287.  
  288. QTimer *Timer1; // pour la lecture au ralenti du fichier gerber
  289. QTimer *Timer2; // pour le tracé au ralenti des éléments triés par ordre de proximités
  290. QTimer *Timer3; // pour faire clignoter des affichages (par exemple LED fon de course)
  291.  
  292. // QAction *centrerAct;
  293.  
  294.  
  295.  
  296. };
  297.  
  298.  
  299.  
  300.  
  301. #endif // MAINWINDOW_H
  302.  

14 Documents :

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

Ce programme est en cours de développement, la partie commande manuelle des moteurs X, Y et Z fonctionne ainsi que l'analyse (à l'écran) du fichier gerber. La liaison USB fonctionne. Les lignes gerber décodées peuvent être transmises à la carte Mega2560 par l'USB, mais les "apertures" (forme des pastilles) ne sont pas encore décodées par cette dernière afin d'ajuster la focalisation et la vitesse du laser.

Toutefois comme vous pouvez le voir, le programme en Qt interprète correctement lesdites apertures. Ce qui va permettre de réaliser des circuits en CMS comme sur l'exemple ci-dessus.

Concernant le perçage des trous : je prévois de flasher des pastilles avec réservation d'un trou au centre pour guider le foret afin de pouvoir effectuer manuellement les perçages (sans cela le centrage manuel est quasiment impossible). J'ai dores et déjà écrit une fonction qui déplace l'outil suivant un cercle, et ça fonctionne très bien, avec une jolie musique de la part des moteurs pas à pas en prime. Vous pouvez trouver cette fonction dans le firmware en C++ de l'ATmega, mais je vous la recopie ici :

void pastille()
{
int16_t centre_x, centre_y;
float x, y;
uint8_t n;
centre_x = x0;
centre_y = x0;
for (n=1; n<=12; n++)
{
x = 30.0 * cos(2.0 * M_PI * n / 12);
y = 30.0 * sin(2.0 * M_PI * n / 12);
x1 = centre_x + (int16_t) round(x);
y1 = centre_y + (int16_t) round(y);


x0 = centre_x;
y0 = centre_y;


lcd_goto_LC(0,12);
lcd_aff_nb (n, 2, 0);
affi_xy();
affi_x1y1();

goto_xy('T'); // trace la pastille

while (goto_en_cours == 1) { lecture_fins_de_course(); test_fin_goto(); }
}
}


Le principe consiste à tracer douze segments dont la disposition en cercle est calculée à la volée par la fonction paramétrique du cercle, à savoir :
x = cos(alpha)
y = sin(alpha)

L'angle 'alpha', fonction du temps (= omega * t) étant matérialisé ici par la succession des pas (n) de la boucle. Le temps en question est celui pris par le tracé du segment. Il est géré par interruption logicielle (Timer3 et Timer4 de l'ATmega2560 ).

15 Passage à un Arduino Mega2560

Mardi 18 nov 2014:

Il vous est sans doute déjà arrivé d'acheter des chaussures trop petites. Pendant un instant on se dit "elles me font un peu mal mais ça va passer, il faut "les casser". Sauf que si ça dure, c'est autre chose que ça casse très rapidement et la solution consiste à prendre une pointure au dessus ou un autre modèle.

C'est exactement ce qui m'est arrivé avec une carte Arduino UNO. Trop juste concernant le nombre de timers-compteurs 16bits libres (obligé d'en reconstituer logiciellement au prix d'une consommation de ressources), trop juste en nombre de ports d'E/S logiques (trois ports de commande et deux ports de détection fins de course par moteur avec trois moteurs...) + un petit affichage LCD pas obligatoire mais bien pratique lors de la phase de mise au point puis plus tard pour l'ergonomie du système (on peut faire afficher les retours sur le PC, mais si la liaison USB pose problème, on se retrouve "dans le noir").

D'où l'utilisation d'une carte Arduino Mega2560. (Le prix d'une 2560 est juste le double de celui d'une UNO, c'est à dire deux fois rien !)

Question : Pourquoi utiliser une carte Arduino et pas directement une puce ATmega128 par exemple ?

-Oui bonne question, d'autant plus que je n'utilise pas l'interface IDE de programmation de l'Arduino, et que j'écris mes propres bibliothèques... En fait j'apprécie le matériel Arduino, en particulier les points suivants :
  • La puce microcontrôleur ATmega en CMS est soudé sur une carte ce qui évite un gros travail de précision.
  • Les ports d'E/S sont reliés à des connecteurs bien pratiques
  • Un contrôleur USB est présent ainsi qu'une prise USB qui facilite la connexion au PC et qui de plus alimente la carte directement
  • Côté logiciel, il est très facile de programmer directement l'ATmega en C++



01 avr 2014:

Je commence à expérimenter avec une diode laser rouge de graveur DVD. Elle produit un rayon capable de brûler une surface noire ou très sombre ("Canson" bleu ou vert très foncé par exemple) ou matière plastique noire.
Problème, noir sur noir : le résultat n'est pas contrasté. Mais j'ai trouvé la solution : Il suffit de coller du sac poubelle très fin (le moins cher est super fin !) avec de la colle à bois, sur une feuille blanche (bristol).. Résultat : un trait blanc pur sur fond noir. Vitesse de tracé (de coupe) élevée.
Problème ça fume et ça sent très mauvais (sans aucun doute toxique, c'est du PVC ?).

Mais la version définitive avec un laser violet pour insoler un film, beaucoup moins puissant, (mais bien dangereux pour les yeux tout de même, quoique focalisé à 2cm et donc très divergent au-delà, tache de 10cm environ à 2m... oui mais l’œil refocalise sur la rétine la totalité de ce qu'il reçoit sur le cristallin, donc écran protecteur + lunettes OBLIGATOIRES ) ne provoquera pas de fumée.

Mardi 25 nov 2014: Je travaille sur les logiciels, en particulier sur le programme en Qt4. Les mises à jour (que je publie ici) sont pluri-journalières. Actuellement les "apertures" fournies en début du fichier gerber sont reconnues (sous forme d'une structure), mémorisées dans une QList <> (QList contenant des structures, c'est un truc que je trouve génial en C++ ! ) et utilisées lors de la phase de dessin (à l'écran).

1er dec 2014: J'ai déconnecté le petit écran LCD Nokia qui m'avait bien rendu service lors de la phase préliminaire de programmation. Mais cela provoquait des "glitch"sur les signaux de commandes des moteurs générés par les interruptions. Et j'ai donc supprimé les communications avec ledit LCD, et les signaux sont redevenus propres. (on entendait de petits "clocs", j'ai tout d'abord pensé à des décrochages mécaniques des moteurs, mais ça se voyait à l'oscillo sous forme de brusques inversions de phases... En fait il y avait des interruptions 16 bits qui passaient à la trappe).

J'ai en contrepartie utilisé un troisième timer 16 bits afin d'accélérer les moteurs qui ainsi ne démarrent plus directement à la vitesse maximale pour les déplacements à vide. (Pour le tracé, la vitesse doit être constante).

5 dec 2014: Hier j'ai passé pas mal de temps à dompter la liaison USB afin d'obtenir un dialogue fiable et rapide entre le PC et la carte Arduino. Vu que les deux logiciels travaillent en temps réel, l'un en affichant sur l'écran tout ce qui se passe, l'autre en pilotant avec précision la vitesse et les positions de la plume... la moindre approximation dans la programmation et c'est la cata. Mais maintenant les choses sont bien en place, tout réagit comme prévu.

L'ATmega mémorise maintenant la liste des apertures, il me reste à programmer leur utilisation à bon escient (seuls des cercles identiques sont tracés pour l'instant). Mais je vais quand même prendre le temps de boire un café avant !

6 dec 2014: Les apertures sont correctement reçues et mémorisées par l'ATmega, dans un tableau. Puis elles sont correctement retrouvées et utilisées lors de la phase de tracé réel par la machine.

Concernant les aperture rectangulaires : Le déplacement d'un stylo trace un trait. Il faut donc balayer la surface en fonction de l'épaisseur du trait. Dans le cas d'un faisceau laser, le spot est rond à priori mais on peut le rendre facilement divergent avec une lentille et obtenir un spot de taille voulue en fonction de la focalisation. Toutefois la distribution d’énergie est gaussienne, autant dire à bords flous! On peut aussi obtenir un faisceau carré à répartition d'énergie uniforme avec un peu d'optique et une astuce, mais figurez-vous que ça fait l'objet d'un brevet déposé par Toshiba ! Donc nous graverons des piste floues, c'est joli des pistes floues.

Actuellement il reste un problème de taille des pastilles (tracées trop grandes). Je vais corriger ça. Il reste également à piloter l'allumage et l'extinction du laser, et les déplacements en Z. Je vous tiens au courant. Et je réaliserai une petite vidéo de la machine en action...

16 Premier tracé d'un circuit

10 dec 2014:

La machine trace son premier circuit (ici avec un stylo sur du papier). On voit déjà des problèmes à corriger. Les traits en travers sont occasionnés par une hauteur de plume mal maîtrisée. Plus grave, les pistes sont décalées par rapport aux pastilles, et d'une valeur plus ou moins grande suivant les endroits. Il faudra aussi remplir les pastilles en réservant le trou au centre. J'ai donc encore un peu de travail à faire avant de déclarer le résultat satisfaisant. Ensuite on pourra s'occuper du laser. Il faudra maîtriser la vitesse de déplacement (= temps d'exposition) en fonction de la puissance du laser, ainsi que le diamètre du spot.

17 Les choses se précisent

12 dec 2014:

L'emprunte est celle d'un boîtier SMD TQFP32 pas 0.8mm (c'est un ATmega8 en cms). MAINTENANT je commence à "y croire" !

18 L'ATmega est content !

Ce qui montre que le tracé est effectué à la bonne échelle.

19 Optimisation du tracé

14 dec 2014:

Les segments à tracer se présentent en ordre dispersé dans le fichier gerber fourni par Kicad, ce qui occasionne de nombreux déplacements inutiles de la plume. J'ai donc décidé d'optimiser cela.
Il s'agit un peu de résoudre le problème du représentant de commerce, qui n'a pas de solution connue autre que celle consistant à essayer toutes les possibilités de trajets avant de pouvoir connaître la plus courte ! Toutefois l'humain est capable de trouver rapidement une solution approchée en voyant l'ensemble du tracé. Donc dans un premier temps je vais rendre cliquable le tracé des pistes à l'écran ce qui permettra, en les sélectionnant à tour de rôle, de les ranger dans une ordre plus judicieux.

Donc je vois d'ici une class segment comprenant les coordonnées des extrémités, celles du milieu (cliquable), et la possibilité d'une rotation de 180° afin de tracer dans le "bon" sens.

Aller, hop ! au boulot !

20 Optimisation AUTOMATIQUE

17 dec 2014:

Après avoir rendu la scène (Qt4) cliquable et de ce fait permis de sélectionner les objets afin de choisir manuellement l'ordre de traitement, j'ai fini par rendre l'opération automatique. La logique de la fonction consiste à traiter l'objet (segment de piste ou pastille) le plus proche de celui en cours. Et de proche en proche tous le circuit y passe. Ce n'est sans doute pas LE chemin le plus court, encore moins une géodésique de l'espace temps, mais c'est beaucoup mieux que le grand n'importe quoi du fichier original. Voici une vidéo (capture d'écran) de la procédure en cours (tracé au ralenti, l'optimisation que je viens de décrire est quasiment instantanée vu la puissance de nos ordinateurs).

Traceur Gerber3 from Silicium628 on Vimeo.


21 Remplissage des pastilles

3 janvier 2015:

Voici ce sur quoi je travaille en ce moment, après une pause en fin d'année : remplir les pastilles par déplacement de la plume compte tenu de l'épaisseur du trait (encre ou laser). Celle pointée par la flèche me semble pas mal. Tout ça est paramétrable facilement dans le firmware pour l'ATmega. En fait il faudra prévoir un trou au centre des pastilles rondes au contraire des pastilles rectangulaires qui sont les pads des composants CMS.

Je vais donc très prochainement monter le laser sur la machine et chausser mes lunettes anti-405nm. (en fait je ferai les premiers essai avec un laser rouge).

22 Le laser a remplacé le stylo !

14 janvier 2015:

Voici donc le premier tracé effectué avec une diode laser (rouge de graveur DVD). Le support est un papier canson bleu foncé, couleur qui absorbe bien le rayon,mais qui ne permet pas d'obtenir un bon contraste visuel. (noir sur fond bleu, bof...)

Voyons cela à la loupe :

23 Le même tracé laser vu à la loupe :

Nous pouvons constater quelques imprécisions de positionnement des pastilles, toutefois ne perdons pas de vue l'échelle du tracé, la distance entre deux pastilles de l’empreinte TQFP32 n'est que de 0,8mm, donc l'imprécision est de l'ordre de 0,1mm. Oui mais c'est dix fois trop puisqu'on vise le 1/100mm. Je remarque que ces défauts de positionnement n’apparaissent qu'entre des lots de pastilles tracés en des instants éloignés dans le temps. Donc il s'agit d'une dérive de position avec le temps. La cause est-elle logicielle (erreurs d'arrondis par exemple) ou bien est-elle mécanique (moteurs "ratant" des pas) ?

Il va donc falloir expérimenter pour le savoir.

Concernant le manque de contraste : ce n'est nullement un problème, puisqu'au final il s'agit de flasher (insoler) un film photosensible avec une diode laser bleue (405nm) de faible puissance. De même, le recouvrement des traces dans les pastilles sera obtenu par un spot réglé plus large.

24 Le laser bleu-violet est en place

17 janvier 2015:

J'ai remplacé la diode laser rouge 200mW de graveur DVD par une diode laser bleue 405nm beaucoup moins puissante (15mW) de lecteur Blu-ray. Le spot parait très large sur la photo, c'est dû à l'éblouissement de l'APN. En fait, focalisé à une vingtaine de mm, il est microscopique. (Mais bien trop faible pour enflammer quoi que ce soit).

J'ai ôté l'écran de protection le temps de faire la photo. Cet écran est nécessaire et obligatoire !

Comme je dispose des films photosensibles dont je vous ai déjà parlé, je vais maintenant expérimenter le flashage et le traitement chimique du film.

25 Premier essai de flashage du film en place sur le cuivre




19 janvier 2015:

Voici le premier résultat obtenu, après flashage et révélation dans un bain de cristaux de soude.

C'est plein d'imperfections, mais je vous avoue que je suis très content de ce premier résultat !

Je précise de suite que j'ai ajusté le focus pendant le tracé (d'où des déviations du faisceau), et que j'ai baladé à la main un papier percé d'un trou autour du faisceau pour supprimer les deux cercles lumineux parasites visibles sur la photo précédente. Et de temps en temps je masquais le rayon par maladresse. D'où des coupures... Toujours les mêmes erreurs de positionnement de pastilles, je ne m'en suis pas encore occupé.

Mais le contraste est nickel, la tenue du film sur le cuivre également.


26 Vue de plus près...

27 Détails :

Il va falloir attacher une attention particulière à la focalisation. L'exposition semble parfaite pour une focalisation fine, mais un peu juste dans le cas d'un spot plus large. En tout cas, adieu la pixellisation des imprimantes laser !! Encore quelques efforts et on obtiendra sans doute un résultat pro.

28 Essai d'élargissement des pistes par défocalisation du laser :

Lorsqu'on défocalise le laser (en l'éloignant de la platine) on s'aperçoit que le trait s'élargit, c'est normal, mais que l'exposition devient insuffisante ce qui conduit à la non-tenue des pistes lors de la phase de révélation dans la solution de carbonate de calcium. (elles partent en ballade et se redéposent n'importe où !)

J'ai donc décidé d'utiliser une diode laser plus puissante, d'une puissance de 50mW au lieu de 15mW. Voyons ce que ça donne :

29 Remplacement de la diode laser par un modèle de 50mW :


22 janvier 2015:
Cette fois la tenue des pistes est impeccables. L'adhésion au cuivre est à toute épreuve, elle résiste même à la caresse d'un pinceau lors du développement. L'épaisseur des pistes est parfaitement maîtrisable, il suffira de piloter la hauteur du laser (axe z), ce que je sais faire puisque c'était le cas pour la version avec stylo. Mais les grosses pastilles proches induisent une insolation parasite (effet de halo... comme dirait Nabila) qui se matérialise par des courts-circuits (qui ne partent pas, même en poussant la révélation). Donc il semblerait que j'ai eu la main un peu lourde avec ma diode laser 50mW (pour info elle est capable de faire fumer un morceau de chambre à air...)

Je vais donc diminuer la puissance du faisceau, simplement en diminuant le courant dans la diode (une résistance à changer sur le régulateur LM317, de la rigolade).

J'ai lu ici et là que le profil de puissance d'un faisceau laser en fonction de la distance au point d'impact est gaussien (courbe en cloche), et je m'attendais à des pistes aux contours flous, mais le contraste de cette résine photosensible donne un résultat très net, et c'est tant mieux.

Ah, il faudra aussi éteindre le laser au centre des pastilles pour éviter le trait traversant.

J'ai traduit un passage d'un document (dont je publie le lien au bas de cet article) qui vous explique plus précisément les difficultés que l'on rencontre en utilisant des diodes laser, je cite :
Le problème provient essentiellement des caractéristiques du composant laser. Cela ressemble un peu à une LED, ainsi vous verrez une tension d'environ 2,2V pour la plupart des courants raisonnables (exactement comme pour une LED, mais la tension est un peu plus élevée). Une commande par la tension est donc une idée extrêmement mauvaise. Une commande par le courant est un peu plus prévisible. Au dessous d'un certain courant - le courant de seuil - vous obtiendrez un dispositif se comportant comme une faible LED. Au dessus du courant de seuil, l'émission laser démarre correctement et la lumière émise augmente très rapidement proportionnellement au courant. Quelque chose comme ça :

(figure ci-dessus)

Le hic c'est que la différence entre le courant de seuil et le courant maximum est habituellement vraiment minime, pas plus que 10% ou 20% du courant de seuil. Le courant de seuil varie fortement d'un échantillon à l'autre (même pour un même numéro de type) et varie également avec la température. Résultat: Imposer une valeur fixe pour le courant est voué à l'échec. Pour certains lasers, et certains jours, il sera au dessous du seuil il n'y aura pas d'émission laser; d'autres fois, il sera au delà du courant maximum et votre précieux laser se transformera en une LED inutile." -fin de citation-



C'est pas simple tout ça, n'est-ce-pas? C'est ça qui est intéressant !

30 Nous sommes sur la bonne voie

23 janvier 2015:
Avec les mêmes réglages que précédemment (50mW) nous voyons qu'un flashage sur un circuit réel ne fait pratiquement pas apparaître de halo, pour la simple raison que des pastilles de grandes dimensions ne s'approchent pas à quelques centièmes de mm. Les pastilles sont toutefois toutes trop grandes vu la largeur du trait (j'ai oublié de la reprogrammer correctement). La piste fantôme horizontale en bas qui barre tout provient d'un essai préalable sur la même platine.

Je m'étais demandé pourquoi le fichier Gerber d'origine faisait tracer toutes les pastilles dans un premier temps puis ensuite toutes les pistes. Sans doute pour mieux gérer l'utilisation des apertures par la machine. Et je me dis que ça pourrait bien être la solution aux légères erreurs de position de ces pastilles. Je vais donc faire un essai après avoir modifié l'algorithme de tri des segments afin de tracer les pastilles en premier.
A suivre...

31 La buse

24 janvier 2015:
Pour éviter la dispersion de lumière parasite autour du spot, j'ai confectionné une buse avec du feuillard de cuivre plié et soudé, que j'ai fixé à l'extrémité du laser. Ce type de buse est couramment employé sur les machine de découpe au laser. Le résultat : les pastilles sont nettement isolées de leurs proches, en particulier celles du boîtier TQFP32 au pas de 0.8mm. Un soucis de moins.


32 Le dernier problème... ?

Côté logiciel, j'ai modifié l'algorithme de tri de façon à flasher toutes les pastilles dans un premier temps, puis ensuite les pistes. Résultat : chaque composant voit ses pads équidistants. Second soucis de moins.

Reste un énorme problème, toujours le même : certains groupes de pistes sont tracés décalés, c'est visible en particulier autour du TQFP32. Mais vu que cet énorme problème semble maintenant être le dernier de la liste, je sens qu'on va le résoudre une bonne fois pour toutes et dans pas tardant !

A suivre...

33 On touche au but...

34 Flashage du film

5 mars 2015:

Le flashage est maintenant précis mais... concernant la focalisation du laser, on peut mieux faire !! Résultat : pistes pas très nettes. De plus de la lumière diffuse provoque des bavures inter-pistes.

Donc ce n'est pas encore correct. J'y retourne immédiatement !

26 février 2015:

J'ai enfin trouvé le moyen de consacrer une journée à ce projet : et le résultat est au rendez-vous !
En effet, cette fois le traçage du circuit complet s'est effectué sans aucune erreur de positionnement.

Ci-dessus un essai avec diode rouge laser 400mW sur papier Canson bleu foncé.

Voici les modifications apportées :
  • Remplacement du moteur pas à pas de l'axe Y de 48 pas/tour par un modèle 200p/tour (comme celui pour l'axe X, ce qui permet, avec une tige filetée au pas de 0.5mm d'obtenir le 1/100 de mm exactement, ça tombe juste. -> 4 pas par /100eme de mm, c.a.d 8 impulsions vers le "easydriver")
  • Abaissement du rail des Y au ras de la table se déplaçant en X
  • Utilisation d'une entretoise filetée sur la tige filetée des Y à la place d'un simple écrou, modif que j'avais déjà faite pour l'axe X -> jeu non détectable.
  • Toutes les positions comptées par le firmware par des nombres entiers (en nb d'impulsions moteur) et non plus en floats, calculs simplifiés et rapides
Il me reste à publier tout ça, ici, avec photos (de la machine et du tracé), vidéos, photos d'un circuit imprimé fini gravé, composants soudés et fonctionnel.


35 Réglage de la focalisation

10 mars 2015:

J'ai effectué ces essais systématiques. ET CETTE FOIS... Nous n'avons jamais été aussi près de la réussite !

36 Parfait ?

11 mars 2015:

Ah cette fois c'est parfait, rien à dire, la précision est au rendez-vous, la finesse des pistes aussi, à tel point que j'ai pu en faire passer entre les pads des transistors et résistances au format 0805, ce qui a permis de supprimer des straps.

- heu m'sieur !
- oui, qu'est ce qu'il y a ?
- il y a des coupures monsieur, trois microcoupures !
- rogntudjû ! J'y retourne immédiatement !!

37 Procédons méthodiquement

14 mars 2015:

Les coupures vues ci-dessus sont provoquées par des (petites) erreurs de positionnement. Afin de débusquer la source du problème, j'ai ajouté une ligne dans la fonction d'interruption ISR(TIMER3_COMPA_vect) qui envoie les impulsions faisant tourner les moteurs. Cette ligne commande un bref allumage d'une LED blanche lorsque l'axe moteur est orienté dans une direction précise, toujours la même, appelons-la 0°, ce qui se produit tous les 400 impulsions. (200 pas par tour et 2 impuls / pas). J'ai également tracé un simple trait de repère sur l'axe moteur, et positionné la LED afin qu'elle éclaire cet axe, à la manière des lampes stroboscopiques utilisées jadis en automobile pour régler l'avance à l'allumage. (oui, bon maintenant il y a la "valise" avec le système ELM327 OBD2, je suis au courant).

Cette LED stroboscopique m'a permis effectivement de constater de petites erreurs de positionnement, de l'ordre d'une vingtaine de pas après une centaine d'objets (pistes, pastilles...) tracés, c'est à dire après environ un million de pas... Ce n'est pas grand chose me direz vous ? et bien si, vu que l'on tourne en rond sur une petite surface, et qu'on recoupe les premiers traits tracés, la précision doit rester absolue.

A quoi sont dûs ces écarts ? Vu que leur nombre à diminué suite à la diminution de la vitesse des moteurs, je penche fortement pour des décrochages lors de la mise en route des moteurs.

Je ne compte pas ralentir d'avantage la vitesse de tracé afin de ne pas pas trop augmenter le temps de flashage. Prévoir une accélération au démarrage ? mouais, mais c'est contraire au principe d'exposition à vitesse constante. (On pourrait moduler l'intensité du laser en fonction de la vitesse, mais ce n'est pas simple, il ne suffit pas de piloter le courant, les diodes laser ont un seuil au dessous duquel le faisceau n'est plus cohérent, plus "laser", donc focalisation HS, et très près au dessus un courant correspondant à une puissance lumineuse à ne pas dépasser sous peine de détruire les surfaces semi-réfléchissantes du cristal). Il faut donc passer par l'exploitation du signal fourni par la photodiode intégrée, dans une boucle de rétroaction. A voir...

Pour l'instant je vais placer un disque à fente + optocoupleur sur l'axe moteur et programmer une correction de position angulaire automatique, ça je sais faire.

Je vous tiens au courant...

13 juillet 2015:
NOTE : Ces erreurs de positionnement sont actuellement corrigées :
  • par un effet de frein moteur (en maintenant le courant dans les phases en dehors des déplacements)
  • par une réécriture du code de traçage des pastilles
  • par le traitement du code appelé par les interruptions en dehors des interruptions (dans une boucle, par positionnement d'un flag dans l'interruption) . Celà évite les surprises occasionnées par des collisions d'interruptions trop longues à traiter.
  • par un critère de fin de course des goto_xy basé sur la position absolue des moteurs et non plus relative.

38 La méthode était la bonne !

Il reste à déboucher les trous des pastilles rondes, celles qui sont reliées à des piste. Cela consiste à raccourcir lesdites pistes. C'est juste un peu de programmation pas bien méchante à faire, côté PC en Qt4.

Mais auparavant je vais graver ce circuit dans un bain de perchlorure de fer et souder les composants. Déjà on va voir si des pistes aussi fines (rappelez-vous, la pièce de 2€ du paragraphe 23, qui donne l'échelle) résistent à l'opération. Je suis confiant...

39 Le circuit imprimé gravé :

Pas si mal, non ?

40 Les principaux composants CMS sont soudés :

Pas de problème particulier, en particulier les dimensions sont bonnes. Le strap situé sous le TQFP32 est irréalisable (du moins en simple couche sans trous métallisés), mais c'est une erreur lors du dessin du circuit sous Kicad. Par chance pour moi, il relie deux GND qui sont déjà reliés à l'intérieur de la puce de l'ATmega8 (si, si, j'ai vérifié). Donc je me passerai de ce strap.

41 Pilotage de la luminosité de la diode laser

Sachant qu'il est hasardeux de fixer le courant alimentant la diode laser à une valeur figée, je compte utiliser l'information fournie par la photodiode incorporée dans son boîtier pour asservir ce courant, par une boucle de rétroaction. Toutefois il conviendra de garder la limitation haute (70mA dans le cas de la SLD3232 50mW) pour éviter les excursions de puissance possibles avec la seul asservissement par photodiode (si la diode laser est fortement suralimentée, elle va commencer à se détériorer, sa puissance lumineuse va diminuer et l'asservissement va AUGMENTER le courant pour compenser ce manque de luminosité, crash assuré!)

J'ai donc commencé par le commencement, à savoir : mesurer et tracer le courant dans la photodiode en fonction du courant principal dans la diode laser.

J'ai ensuite réalisé un circuit d'asservissement de la luminosité de la diode laser à une consigne générée par l'ATmega2560.
Ce circuit fait l'objet d' un article séparé pour ne pas trop surcharger celui-ci.

42 Image miroir & trous débouchés

29 mars 2015:
Je viens d'ajouter la possibilité de retourner le circuit (image miroir) dans le logiciel en Qt4. Cette fonction apparemment prévue dans Kicad reste curieusement "en grisé" (non opérationnelle) dans le module d'exportation au format Gerber (du moins dans ma version, sous Linux). Or ce retournement en miroir est nécessaire pour pouvoir graver des circuits imprimés traditionnels avec composants côté composants et pistes côté cuivre mais routées (ou dessinées) côté composants (par transparence) comme je le fais habituellement dans Kicad.

31 mars 2015:
Les pastilles rondes sont maintenant débouchées, par raccourcissement des extrémités des pistes (suppression des télomères ?), comme vous pouvez le voir sur les images ci-dessus (copie d'écran puis résultat flashé, reste à le graver). A la jonction de deux pistes je conseille de placer une micro-pastille, ce que j'ai fait sauf à un endroit (sur la gauche de l'image). Ce raccord en "T" est bien flashé, reste à savoir s'il ne se coupera pas lors de la gravure. Donc à éviter absolument. Mon prof d'électronique m'avait conseillé de terminer les pistes exclusivement sur des pastilles... Je confirme.

43 Calcul du raccourcissement des pistes


Je vous donne ci-dessus les calculs issus de ma cogitation, griffonnés sur un coin de table, qui m'ont permis d'écrire le code suivant :

(Voir le code complet au paragraphe 13 de cet article).

CODE SOURCE C
  1. void MainWindow::raccourcir_segment(Element *segment_i, quint16 r, char bout)
  2. {
  3. qreal x1, y1, x2, y2, x3, y3, dx, dy, a, b;
  4.  
  5. r *=8;
  6.  
  7. if (bout == 'A') { x1 = segment_i->A.x(); y1 = segment_i->A.y(); x2 = segment_i->B.x(); y2 = segment_i->B.y(); }
  8. else { x1 = segment_i->B.x(); y1 = segment_i->B.y(); x2 = segment_i->A.x(); y2 = segment_i->A.y(); }
  9.  
  10. if (x2 == x1) {x2 = x1 + 1;} // ce qui évite les /0 en introduisant une erreur négligeable
  11.  
  12. dx= x2-x1;
  13. dy= y2-y1;
  14.  
  15. a=dy/dx;
  16. b=y1-a*x1;
  17.  
  18. if(x2>x1) {x3=x1+sqrt(r/((a*a)+1));} else {x3=x1-sqrt(r/((a*a)+1));}
  19. y3=a*x3+b;
  20.  
  21. if (bout == 'A') { segment_i->A.setX(x3); segment_i->A.setY(y3); }
  22. if (bout == 'B') { segment_i->B.setX(x3); segment_i->B.setY(y3); }
  23. }
  24.  

44 Résultat gravé :

(après remplacement des pastilles rectangulaires par des pastilles rondes).
La résine photosensible qui recouvre les pistes, une fois polymérisée par la lumière du laser puis passée par le révélateur (cristaux de soude = lessive St Marc je le répète...) est EXTRÊMEMENT résistante ! Elle ne part même pas à l'alcool, ni à l'acétone !! Il faut utiliser de la lessive de soude chaude. Donc ATTENTION ! L'opération est dangereuse. La lessive de soude NaOH est un produit qui dissout la matière organique dont nous sommes constitués. Et quand ça fait mal, il est trop tard ! Vu ? Et pas que pour les yeux, les doigts aussi !

Image de droite, après décapage et perçage.

45 La platine driver terminée, opérationnelle

46 Pilotage de la hauteur du laser


16 avr 2015: J'ai ajouté un micro servo sur le support du laser qui permet d'ajuster automatiquement sa hauteur, c'est à dire la focalisation du faisceau, le tout piloté par le firmware en fonction de la largeur des pastilles. Les programmes qt4 et firmware (à partir de la version 13.1) prennent en charge ces nouvelles fonctions servo + luminosité. Je publierai également les résultats obtenus.


47 ...et largeurs des pistes obtenues

Test sur un ensemble varié de pistes et pastilles de différentes largeurs correspondant à un mélange de composants classiques et CMS. (Ne cherchez pas à savoir à quoi peut servir ce circuit, à part à griller l'ATmega, je ne vois pas moi non plus).
Sur le flashage obtenu nous voyons que les trous ne sont pas débouchés, c'est normal j'ai oublié de cliquer sur le bouton ! Les pastilles du TQFP32 au pas 0.8mm sont surexposées, je vais ajuster la formule qui règle la luminosité en fonction de la largeur de piste.

48 Pilotage de la luminosité du laser en fonction des largeurs de pistes

J'ai diminué la puissance du laser pour les pastilles uniquement. Nous voyons que les pastilles proches sont bien séparées. Toutefois certaines débordent toujours et à y regarder de plus près ce sont celles qui sont reliées à des pistes, principalement du côté d'où part la piste. Et en effet je n'ai pas réduit la puissance pour les pistes. Il est donc nécessaire de le faire.

Quant aux rayures sur le cuivre, elles proviennent du fait que je passe volontairement le cuivre au tampon vert (marque connue...) afin d'assurer une bonne adhérence à la couche photosensible lors du développement. Résultat : elle ne bronche pas au frottement d'une brosse à dents ! (réservée à cet usage, je tiens à le préciser !!)

Cette fois la luminosité est fonction de la largeur des pistes : les problèmes de bavures disparaissent. Nous pouvons donc réaliser des circuit mixtes, avec de la CMS et des composants classiques, ainsi que des pistes plus larges (jusqu'à 2mm) pouvant supporter des courant plus importants. (Remarque : Certaines largeurs ont été modifiées sous Kicad depuis l'essai précédent, pour éviter une brisure de diamètre dans un angle, ce qui n'est pas dans les règles de l'art).

Et les plans de masse ? J'y songe, surtout qu'avec le spot réglable à un diamètre de 2mm cela devient envisageable.

49 Réalisation de la carte mère

La carte mère de la machine est en cours de réalisation, carte qui se connectera directement sur l'extrémité de l'Arduino Mega2560 et qui supportera les deux petites cartes Easy-Driver ainsi que les leds et les quelques résistances qui alimentent les optocoupleurs de fin-de-course.

Et bien entendu cette carte a été flashée par la machine elle même, tout comme la carte de pilotage du laser.

Je publierai ici tous les fichiers Kicad.

50 Test de la carte mère

La carte mère est terminée. La voici enfichée sur l'Arduino Mega2560. Tout fonctionne comme prévu. Il reste à fixer les cartes au chassis métallique de la machine et à fabriquer une boîte opaque qui permettra de travailler en pleine lumière.

51 Flashage d'un circuit mixte (classique + cms) + plans de masse

09 juin 2015:
Actuellement avec la version 15.0 du logiciel il est tout à fait possible de réaliser un circuit comprenant des composants classiques et de la CMS ainsi que des plans de masse.

J'ai cherché la cause problèmes d'imprécision de positionnement. Logicielle ? Matérielle ? Un peu des deux en fait. Je m'explique. Lors de la fin du tracé de chaque segment (y compris les pastilles donc, qui sont constituées de 12 segments placés circulairement, on reparlera de ce point avec des "sin" et "cos", promis!) lors de la fin du tracé disais-je, le firmware arrêtait les impulsions de commande des moteurs (normal!) mais il coupait aussi instantanément le courant dans le moteur (par un "disable" des cartes Easydriver) afin de réduire la consommation et l'échauffement. Riche idée... qui revenait à placer les moteurs en roue libre !!! (on sent bien des "crans" en faisant tourner un moteur pas à pas à la main, mais rien de comparable au blocage ferme lorsqu'il est alimenté). D'où dépassement de quelques pas !

J'ai donc modifié le firmware de façon à alimenter les moteurs 10ms de plus après la fin de la rotation, et eurêka ! ça marche, plus aucune erreur !!

52 Flashage virtuel sur SDcard

J'ai conçu un enregistreur logique rapide sur SDcard afin de contrôler précisément les tracés de la plume sans être obligé de flasher (et donc gâcher) un film sur un board à chaque essai. Le résultat peut être affiché sur le moniteur du PC en très haute résolution, bien mieux que ce qu'on peut voir à la loupe sur un circuit. La description de cet enregistreur figure sur ce site, ici.

53 Version du soft en QT5

06 sept 2019 : Je publie une nouvelle version du soft en QT5 (au lieu de QT4)
Au passage j'ai rajouté la possibilité de choisir le numéro de device correspondant à la liaison USB utilisée, directement depuis le menu de l'application. Il n'est plus nécessaire de recompiler le programme pour modifier ce port. Ouf !

Le numéro en question s'affiche dans le dossier /dev (sous Linux), lors du branchement de la carte Arduino sur un port usb du PC.

54 Réduction de la taille de la fenêtre du programme

06 sept 2019 : Nouvelle version 20.1
J'ai réduit la hauteur de la fenêtre principale du programme en QT afin qu'elle puisse s'afficher en entier sur les PC portables ayant un écran 1440 x 900 px.

55 Nouveau format des fichiers Gerber

Ci-dessus, à gauche -> l'ancien, à droite -> le nouveau.

20 dec 2019 :
C'est une chose qui n'est pas de mon fait et dont je me serais bien passé : les fichiers de fabrication Gerber exportés par le nouveau Kicad sont dans un nouveau format, différent de celui décrit et exploité jusqu'à présent.
Il va falloir s'y adapter !

Déjà on remarque que les dimensions sont désormais en mm et non plus en inch. ça, j'approuve. Une conversion en moins pour notre programme. Pour le reste, je vais voir de plus près. Les nombres décimaux ne sont pas représentés avec un point ni avec un éventuel zéro au début, sauf semble-t-il pour les "apertures". Des exemples sont donnés dans le document ci-dessous, il va falloir soigner la mise en forme pour le décodage. Surprise : la commande "G54" (Select aperture) est dépréciée, elle n'est plus présente dans le fichier Gerber. Or notre programme utilisait son occurrence pour sélectionner l'aperture à utiliser pour tracer chaque piste. Et je trouve que ça rendait le fichier plus humainement compréhensible. C'est quand même le but d'un fichier texte, non ? Sinon autant générer un fichier binaire compilé. Et je vois qu'il y a en tout 16 commandes dépréciées, passées à la trappe. "They are superfluous and deprecated."
Bon il ne va donc pas être "superfluous" de revoir tout le programme de décodage. Hop, au boulot !

Voici le document pdf officiel qui donne les spécification du format Gerber :

56 La version du nouveau Kicad

Il s'agit de la version 5.99.0 qui est accéssible sous Linux Mint19 par l'ajout du PPa de js-reynaud suivant :
  • deb http://ppa.launchpad.net/js-reynaud/ppa-kicad/ubuntu bionic main
Je suis déjà en train de modifier le programme CNC. Ma nouvelle version sera compatible avec les deux formats, avec adaptation automatique.

57 Nouvelle version disponible

22 dec 2019 :
Je viens de mettre en ligne la nouvelle version compatible avec le nouveau format des fichiers Gerber (et qui reste compatible avec l'ancien).
Je vois que la dernière mouture de Kicad propose des apertures "trapézoïdales", que l'on retrouve d'ailleurs utilisées pour certains footprints DIL. Mon programme ne les prend pas en compte. je vais y remédier. Pour l'instant j'ai modifié les footprints DIL8 et DIL14 dans ma dernière réalisation (un alim 12V->90V pour alimenter une photodiode à avalanche (AD500-8) avec laquelle je vais vous proposer une nouvelle expérience de mesure de la vitesse de la lumière, bien plus précise que celles déjà présentes sur ce site). Je n'ai toutefois pas la prétention de rivaliser avec Alain Aspect ! Dans une autre vie peut-être ;)

58 -

Liens...

45181