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
Table des matières :
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++ & Qt4 pour le PC :
13 - Le code source du soft en Qt4 :
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

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

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++ & Qt4 pour le PC :

13 Le code source du soft en Qt4 :

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.  
  26. // #include <QMouseEvent>
  27.  
  28.  
  29. QString version = "19.2";
  30.  
  31.  
  32. QColor couleur_ecran = QColor::fromRgb(0x002020); // 0x001A00
  33.  
  34. QColor couleur_piste = QColor::fromRgb(0x999999); // non tracé
  35. QColor couleur_pisteT = QColor::fromRgb(0x1E90FF); // en cours de tracé
  36. QColor couleur_pisteC = QColor::fromRgb(0xFFFF00); // tracée
  37.  
  38. QColor couleur_pastille = QColor::fromRgb(0x999999);// // non tracée
  39. QColor couleur_pastilleT = QColor::fromRgb(0x1E90FF); // en cours de tracé
  40. QColor couleur_pastilleC = QColor::fromRgb(0xFFFF00); // tracée
  41.  
  42. QColor couleur_fait = QColor::fromRgb(255, 0, 0, 255);
  43. QColor couleur_envoye = QColor::fromRgb(0, 100, 0, 255);
  44. QColor couleur_pointage = QColor::fromRgb(0x7F7F7F);
  45. QColor couleur_milieu = QColor::fromRgb(255, 0, 0, 255);
  46. QColor couleur_DT = QColor::fromRgb(0, 255, 0, 255);
  47. QColor couleur_trou = QColor::fromRgb(0, 0, 0, 255);
  48. QColor couleur_texte = QColor::fromRgb(255, 255, 255, 255);
  49. QColor couleur_erreur = QColor::fromRgb(255, 255, 100, 255);
  50.  
  51. QBrush brush_pastille;
  52. QBrush brush_milieu;
  53. QBrush brush_DT;
  54. QBrush brush_trou;
  55.  
  56. QPen pen_piste(couleur_piste, 2, Qt::SolidLine);
  57. QPen pen_pastille(couleur_pastille, 1, Qt::SolidLine);
  58. QPen pen_trou(couleur_trou, 1, Qt::SolidLine);
  59. QPen pen_pointage(couleur_pointage, 1, Qt::SolidLine);
  60. QPen pen_milieu(couleur_milieu, 1, Qt::SolidLine);
  61. QPen pen_DT(couleur_DT, 1, Qt::SolidLine);
  62.  
  63. int x_scene, y_scene;
  64.  
  65. QStringList liste_gerber1;
  66. QList <Aperture> liste_apertures; //<aperture> désigne une structure déclarée dans le mainwindow.h
  67. QList <Element> liste_elements; // tous types confondus, segments et pastilles
  68. QList <Element> liste_elements_tries;
  69. QList <Element> liste_pastilles; // en vue de les "creuser" logiciellement (ici) en raccourcissant les segments qui y aboutissent
  70. QList <Element> liste_segments;
  71. QList <Element> liste_trous;
  72.  
  73. //QString dir_gerber;
  74. QString fileName_en_cours;
  75.  
  76. quint8 num_ap_use; // numéro de l'aperture en service lors de la phase de traçage
  77. QString s_aperture_use; // même chose en texte (D10, D11... etc)
  78.  
  79. quint16 num_ligne_en_cours1;
  80. quint16 num_ligne_en_cours3;
  81. quint16 num_ligne3;
  82. quint16 nb_lignes_envoyees =0;
  83. quint16 i_dess_tri;
  84. quint16 nb_erreurs =0;
  85. quint16 nb_pistes_nulles; // décrivant des déplacements ou des tracés de pste de longueur = 0
  86. quint16 nb_corrections =0;
  87.  
  88. quint16 num_element_proche_en_cours =0;
  89.  
  90. float zoom;
  91. float ech;
  92.  
  93.  
  94. qint16 Xcent, Ycent; // en 1/100 mm
  95. qint16 memo_X, memo_Y;
  96. QPointF pos_actuelle;
  97. 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)
  98. qint16 Y_max =0; // coordonné Y max la plus grande parmis toutes celles de tous les éléments du circuit
  99.  
  100.  
  101. quint8 connect_ok =0;
  102. quint8 recu_PRET =0;
  103. quint8 flashage_en_cours = 0;
  104. quint8 simulation = 0;
  105. quint8 laser_on = 0; // allumage du laser en continu. POURS TESTS SEULEMENT !
  106. quint8 laser_bloque = 0; // empêche l'allumage du laser si =1 ; pour tests déplacements laser éteint
  107. quint8 trace_aspect_reel;
  108. quint8 findecourse = 0;
  109.  
  110. quint8 miroir=0;
  111. quint8 pastilles_creuses=0;
  112.  
  113. char buffer[100];
  114. QString msg ="";
  115.  
  116.  
  117. qreal sqr(qreal r1)
  118. {
  119. return r1 * r1;
  120. }
  121.  
  122.  
  123. QString valeur2txt4(int v1)
  124. {
  125. QString s;
  126. s.setNum(v1); // conversion num -> txt
  127. s = "0000"+s;
  128. s = s.right(4);
  129. return s;
  130. }
  131.  
  132.  
  133. QString valeur2txt2(int v1)
  134. {
  135. QString s;
  136. s.setNum(v1); // conversion num -> txt
  137. s = "00"+s;
  138. s = s.right(2);
  139. return s;
  140. }
  141.  
  142.  
  143.  
  144. MainWindow::MainWindow(QWidget *parent) :
  145. QMainWindow(parent)
  146. {
  147.  
  148. setupUi(this);
  149. setWindowTitle("Gerber Photoplotter v:" + version + " - Silicium628");
  150. window()->setGeometry(0,0,1279,1023);
  151.  
  152. scene1 = new SceneCliquable(this);
  153.  
  154. InitTable3();
  155. InitTable4();
  156. InitTable5();
  157.  
  158. brush_pastille.setColor(couleur_pastille);
  159. brush_pastille.setStyle(Qt::SolidPattern);
  160.  
  161. brush_milieu.setColor(couleur_milieu);
  162. brush_milieu.setStyle(Qt::SolidPattern);
  163.  
  164. brush_DT.setColor(couleur_DT);
  165. brush_DT.setStyle(Qt::SolidPattern);
  166.  
  167. brush_trou.setColor(couleur_trou);
  168. brush_trou.setStyle(Qt::SolidPattern);
  169.  
  170. pen_piste.setCapStyle(Qt::RoundCap); // extrémités des pistes arrondies - voir QPen dans la doc.
  171.  
  172. // QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
  173.  
  174.  
  175. effacer_ecran(); // et crée les groupes héritiers de la scene ainsi que la connexion entre les clics et le slot ici concerné
  176.  
  177. BoutonLed1 = new QBoutonLed(frame_10);
  178. BoutonLed1->setGeometry(QRect(4, 4, 40, 40));
  179. BoutonLed1->setCouleur(QColor (0,255,0));
  180. connect(BoutonLed1, SIGNAL(toggled(bool)), this, SLOT(on_BoutonLed1_toggled(bool)));
  181.  
  182. BoutonLed2 = new QBoutonLed(frame_12);
  183. BoutonLed2->setGeometry(QRect(4, 4, 40, 40));
  184. BoutonLed2->setCouleur(QColor::fromRgb(0x1E90FF));
  185. connect(BoutonLed2, SIGNAL(toggled(bool)), this, SLOT(on_BoutonLed2_toggled(bool)));
  186.  
  187. trace_aspect_reel=1;
  188.  
  189. BoutonLed3 = new QBoutonLed(frame_3); // allumage laser
  190. BoutonLed3->setGeometry(QRect(4, 4, 40, 40));
  191. BoutonLed3->setCouleur(QColor::fromRgb(0x1E90FF));
  192. connect(BoutonLed3, SIGNAL(toggled(bool)), this, SLOT(on_BoutonLed3_toggled(bool)));
  193.  
  194. BoutonLed4 = new QBoutonLed(frame_17); // blocage laser
  195. BoutonLed4->setGeometry(QRect(4, 4, 40, 40));
  196. BoutonLed4->setCouleur(QColor::fromRgb(0xFF0000));
  197. connect(BoutonLed4, SIGNAL(toggled(bool)), this, SLOT(on_BoutonLed4_toggled(bool)));
  198.  
  199.  
  200. Led0 = new QLed(frame1); // Connexion USB ; verte = ok ; rouge = impossible
  201. Led0->setForme(1);
  202. Led0->setGeometry(QRect(4, 4, 40, 40));
  203. Led0->setCouleur(QColor (0,255,0));
  204. Led0->setEtat(0);
  205.  
  206. //=======================================================================================
  207. // jaune = Connexion USB établie, attente message bienvenue ; verte message "ATMEGA" reçu
  208. Led1 = new QLed(frame_4);
  209. Led1->setForme(1);
  210. Led1->setGeometry(QRect(4, 4, 40, 40));
  211. Led1->setCouleur(QColor (255,255,0));
  212. Led1->setEtat(0);
  213.  
  214. //=======================================================================================
  215. // envoi commande par USB ; jaune = message envoyé - verte = reçu réponse ("PRET")
  216. Led2 = new QLed(frame_6);
  217. Led2->setForme(1);
  218. Led2->setGeometry(QRect(4, 4, 40, 40));
  219. Led2->setCouleur(QColor (255,255,0));
  220. Led2->setEtat(0);
  221.  
  222. //=======================================================================================
  223. // VERTE ; allumée (verte) = flashage en cours
  224. Led3 = new QLed(frame_7);
  225. Led3->setForme(1);
  226. Led3->setGeometry(QRect(4, 4, 40, 40));
  227. Led3->setCouleur(QColor (0,255,0)); // VERTE
  228. Led3->setEtat(0);
  229.  
  230. //=======================================================================================
  231. // ROUGE, ronde ; allumée = FIN DE COURSE ACTIF !
  232. Led4 = new QLed(frame_11);
  233. Led4->setForme(2); // ronde
  234. Led4->setGeometry(QRect(4, 4, 40, 40));
  235. Led4->setCouleur(QColor (255,0,0)); // ROUGE
  236. Led4->setEtat(0);
  237.  
  238. //=======================================================================================
  239. /**
  240.   Led5 = new QLed(frame_18);
  241.   Led5->setForme(1);
  242.   Led5->setGeometry(QRect(2, 2, 20, 20));
  243.   Led5->setCouleur(QColor (255,255,0));
  244.   Led5->setEtat(0);
  245. **/
  246. //=======================================================================================
  247. Timer1 = new QTimer(this);
  248. connect(Timer1, SIGNAL(timeout()), this, SLOT(Timer1_clic()));
  249.  
  250. Timer2 = new QTimer(this);
  251. connect(Timer2, SIGNAL(timeout()), this, SLOT(Timer2_clic()));
  252.  
  253.  
  254. memo_X =0;
  255. memo_Y =0;
  256. num_ligne_en_cours1 = 0;
  257.  
  258. spinBox_zoom2->setValue(5);
  259.  
  260.  
  261. // QString filename = "./Fichiers_gerber/attenuateur-F_Cu.gtl";
  262. // QString filename = "./Fichiers_gerber/largeur_pistes-B_Cu.gbl";
  263. fileName_en_cours = "./Fichiers_gerber/largeur_pistes-F_Cu.gtl";
  264.  
  265. lecture_fichier_init();
  266.  
  267.  
  268. lineEdit_6->setText(fileName_en_cours);
  269.  
  270. int result = lire_fichier_gerber(fileName_en_cours);
  271.  
  272. if (result == 0)
  273. {
  274. analyse_fichier_gerber();
  275. //tout_decliquer();
  276. //dessiner_liste_elements_tries();
  277. classer_auto();
  278. trace_liste_apertures();
  279. on_BoutonLed2_toggled(true);
  280. BoutonLed2->setChecked(true);
  281. }
  282.  
  283. BoutonLed4->setChecked(1);
  284. }
  285.  
  286.  
  287.  
  288. MainWindow::~MainWindow()
  289. {
  290.  
  291. }
  292.  
  293.  
  294.  
  295.  
  296. void MainWindow::InitTable3()
  297. {
  298. QStringList liste_entetes;
  299. tableWidget_3->clear();
  300.  
  301. liste_entetes <<"CMD"<<"longueur";
  302.  
  303. tableWidget_3->setHorizontalHeaderLabels (liste_entetes);
  304. tableWidget_3->setColumnCount(2);
  305. tableWidget_3->setRowCount(1);
  306.  
  307. }
  308.  
  309.  
  310. void MainWindow::InitTable4()
  311. {
  312. QStringList liste_entetes;
  313. liste_entetes <<"CMD";
  314.  
  315. tableWidget_4->clear();
  316. tableWidget_4->setColumnCount(2);
  317. tableWidget_4->setRowCount(0);
  318. tableWidget_4->setHorizontalHeaderLabels (liste_entetes);
  319.  
  320. }
  321.  
  322.  
  323. void MainWindow::InitTable5()
  324. {
  325. QStringList liste_entetes;
  326.  
  327. tableWidget_5->clear();
  328. tableWidget_5->setColumnCount(2);
  329. tableWidget_5->setRowCount(0);
  330. tableWidget_5->setHorizontalHeaderLabels (liste_entetes);
  331.  
  332. }
  333.  
  334.  
  335.  
  336. void MainWindow::effacer_ecran()
  337. {
  338. scene1->clear();
  339. scene1 = new SceneCliquable(this);
  340. connect(scene1, SIGNAL(envoiPosition(int, int) ), this, SLOT (onReceptPos(int, int) ));
  341.  
  342. scene1->setBackgroundBrush(couleur_ecran);
  343. scene1->setSceneRect(-800,-1200,2000,2000); // plus grand que la partie affichée
  344. graphicsView1->setScene(scene1);
  345. graphicsView1->setGeometry(5,10,896,611); // POSITION et dimensions de l'écran
  346. graphicsView1->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
  347.  
  348. groupe_trace = new QGraphicsItemGroup();
  349. scene1->addItem(groupe_trace);
  350.  
  351. groupe_segments_faits = new QGraphicsItemGroup();
  352. scene1->addItem(groupe_segments_faits);
  353.  
  354. groupe_pastilles_faites = new QGraphicsItemGroup();
  355. scene1->addItem(groupe_pastilles_faites);
  356.  
  357. groupe_pointage = new QGraphicsItemGroup();
  358. scene1->addItem(groupe_pointage);
  359.  
  360. groupe_trous = new QGraphicsItemGroup();
  361. scene1->addItem(groupe_trous);
  362.  
  363. }
  364.  
  365.  
  366.  
  367. void MainWindow::effacer_pointage()
  368. {
  369. foreach( QGraphicsItem *item, groupe_pointage->childItems() )
  370. {
  371. delete item;
  372. }
  373. }
  374.  
  375.  
  376. void MainWindow::lecture_fichier_init()
  377. {
  378. QString line, tx1, tx2;
  379. int p2;
  380. tx1 = "";
  381.  
  382. QFile file1(QDir::homePath() + "/.cnc_gerber/cnc_gerber.ini");
  383.  
  384. if (file1.open(QIODevice::ReadOnly | QIODevice::Text))
  385. {
  386. QTextStream in(&file1);
  387. in.reset();
  388. while ( !in.atEnd() )
  389. {
  390. line = in.readLine();
  391. if (line.left(1) !="#")
  392. {
  393. if (line.indexOf("fileName_en_cours") != -1)
  394. {
  395. if ( (p2 = line.indexOf('=')) != -1)
  396. {
  397. line.remove(0,p2+1);
  398. fileName_en_cours = line;
  399. }
  400. }
  401. }
  402. }
  403. file1.close();
  404. } else { fileName_en_cours = ""; tx1 = "fichier .ini non trouve, ce n'est pas grave !"; }
  405.  
  406. if (tx1 !="")
  407. {
  408. QMessageBox msgBox;
  409. msgBox.setText(tx1);
  410. msgBox.exec();
  411. }
  412. // statusBar1->showMessage(tx1 + " "+ tx2);
  413. }
  414.  
  415.  
  416.  
  417. void MainWindow::enregistrer_fichier_init()
  418. {
  419. QDir dossierIni(QDir::homePath() + "/.cnc_gerber/");
  420. if (!dossierIni.exists())
  421.  
  422. {
  423. QMessageBox msgBox;
  424. msgBox.setText("Le repertoire : ~/" + dossierIni.dirName() + " n'existe pas, je vais le creer, et y placer le fichier de configuration 'cnc_gerber.ini' ");
  425. msgBox.exec();
  426. QDir dossierPerso(QDir::homePath());
  427. dossierPerso.mkdir(".cnc_gerber");
  428. }
  429.  
  430. QFile file1(QDir::homePath() + "/.cnc_gerber/cnc_gerber.ini");
  431. if (file1.open(QIODevice::WriteOnly | QIODevice::Text))
  432. {
  433. QTextStream out(&file1);
  434. QString V;
  435.  
  436. out << "# les lignes commeneant par # sont ignorees";
  437. out << '\n';
  438. out << "# Ce fichier est genere automatiquement par le programme";
  439. out << '\n';
  440. out << "#";
  441. out << '\n';
  442.  
  443. out << "fileName_en_cours=" << fileName_en_cours;
  444. out << '\n';
  445. }
  446. file1.close();
  447. }
  448.  
  449.  
  450.  
  451. int MainWindow::lire_fichier_gerber(QString nom_fichier1)
  452. {
  453. QFile file1(nom_fichier1);
  454.  
  455. if( !file1.exists() )
  456. {
  457. qDebug() << "Le fichier n'existe pas.";
  458. return 1;
  459. }
  460.  
  461. QString s1;
  462.  
  463. liste_gerber1.clear();
  464. listWidget_1->clear();
  465.  
  466. if (!file1.open(QIODevice::ReadOnly | QIODevice::Text)) return 1;
  467. QTextStream in1(&file1);
  468.  
  469. int num_ligne = 0;
  470. while ( !in1.atEnd() )
  471. {
  472. num_ligne++;
  473. s1.setNum(num_ligne);// conversion num -> txt
  474. QString line_i = in1.readLine();
  475. listWidget_1->addItem(s1+": "+line_i);
  476. liste_gerber1 << line_i;
  477. }
  478. file1.close();
  479.  
  480. checkBox_creuser->setChecked(false);
  481. checkBox_miroir->setChecked(false);
  482.  
  483.  
  484.  
  485. return 0;
  486.  
  487. }
  488.  
  489.  
  490.  
  491. void MainWindow::pointer(int i)
  492. { // à l'écran
  493.  
  494. QPoint M;
  495. Element element_i;
  496. element_i = liste_elements_tries[i];
  497. M = element_i.M;
  498.  
  499. effacer_pointage();
  500.  
  501. M.setY(-M.y());// symétrie verticale
  502. M /= ech;
  503.  
  504.  
  505. // trace ligne verticale
  506. QGraphicsLineItem *segment_pointage1 = new QGraphicsLineItem(groupe_pointage);
  507. segment_pointage1->setPen(pen_pointage);
  508. 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)
  509.  
  510. // trace ligne horizontale
  511. QGraphicsLineItem *segment_pointage2 = new QGraphicsLineItem(groupe_pointage);
  512. segment_pointage2->setPen(pen_pointage);
  513. segment_pointage2->setLine(-1000, M.y()+1, 1000, M.y()+1);
  514. }
  515.  
  516.  
  517.  
  518. void MainWindow::tracer_mire_origine()
  519. { // à l'écran
  520. QPoint P;
  521.  
  522. P.setX(0);
  523. P.setY(0);
  524. P /= ech;
  525.  
  526.  
  527. QGraphicsLineItem *segment_pointage1 = new QGraphicsLineItem(groupe_pointage);
  528. segment_pointage1->setPen(pen_pointage);
  529. segment_pointage1->setLine(P.x()-10, P.x()-10, P.x()+10, P.x()+10);
  530.  
  531. QGraphicsLineItem *segment_pointage2 = new QGraphicsLineItem(groupe_pointage);
  532. segment_pointage2->setPen(pen_pointage);
  533. segment_pointage2->setLine(P.x()-10, P.x()+10, P.x()+10, P.x()-10);
  534. tracer_texte(-20, -20, "(x=0; y=0)");
  535.  
  536. }
  537.  
  538.  
  539.  
  540. void MainWindow::tracer_segment(QPoint A, QPoint B, qreal largeur, QColor couleur_i, QGraphicsItemGroup *groupe_i )
  541. { // à l'écran
  542.  
  543. QPoint M; // milieu
  544. QPoint DT; // deux tiers
  545.  
  546. QLine SEG;
  547.  
  548. A.setY(-A.y());// symétrie verticale
  549. B.setY(-B.y());
  550.  
  551. A /= ech;
  552. B /= ech;
  553.  
  554. SEG.setPoints(A,B);
  555.  
  556. M.setX((A.x()+B.x())/2);
  557. M.setY((A.y()+B.y())/2);
  558.  
  559. DT.setX( A.x()/3 + 2*B.x()/3 );
  560. DT.setY( A.y()/3 + 2*B.y()/3 );
  561.  
  562. largeur /= ech;
  563. if (largeur <1) {largeur = 1;}
  564.  
  565. QGraphicsLineItem *segment_trace = new QGraphicsLineItem(groupe_i);
  566. pen_piste.setWidth(largeur);
  567. pen_piste.setColor(couleur_i);
  568. segment_trace->setPen(pen_piste);
  569. segment_trace->setLine (SEG );
  570.  
  571.  
  572. QGraphicsEllipseItem *ellipse1 = new QGraphicsEllipseItem (groupe_i); // repere de selection au centre du segment
  573. pen_piste.setWidth(1);
  574. ellipse1->setPen(pen_milieu);
  575. ellipse1->setBrush(brush_milieu);
  576. ellipse1->setRect(M.x()-1, M.y()-1, 2, 2);
  577.  
  578. QGraphicsEllipseItem *ellipse2 = new QGraphicsEllipseItem (groupe_i); // repere au 2/3 du segment
  579. pen_piste.setWidth(1);
  580. ellipse2->setPen(pen_DT);
  581. ellipse2->setBrush(brush_DT);
  582. ellipse2->setRect(DT.x()-1, DT.y()-1, 2, 2);
  583. }
  584.  
  585.  
  586.  
  587. void MainWindow::tracer_pastille_rectangulaire(QPoint A, QPoint B, QColor couleur_i, QGraphicsItemGroup *groupe_i)
  588. { // à l'écran
  589. // A est le coin en bas à gauche, B est le coin en haut à droite
  590.  
  591. qreal w, h;
  592. qreal n;
  593. qreal x1, y1, x2, y2;
  594. qreal largeur_trait, d;
  595.  
  596. A.setY(-A.y());// symétrie verticale
  597. B.setY(-B.y());
  598.  
  599. A /= ech;
  600. B /= ech;
  601.  
  602. w = (A-B).x(); // largeur pastille
  603. h = (A-B).y(); // hauteur
  604.  
  605.  
  606. if(trace_aspect_reel == 0)
  607. {
  608. QGraphicsRectItem *rectangle = new QGraphicsRectItem(groupe_i);
  609. brush_pastille.setColor(couleur_i);
  610. rectangle->setBrush(brush_pastille);
  611. rectangle->setRect(A.x()-w/2, A.y()-h/2, w, h);
  612. }
  613.  
  614. if(trace_aspect_reel == 1)
  615. {
  616. largeur_trait = 10; // à transmettre par paramètre...
  617. largeur_trait /= ech;
  618.  
  619.  
  620. pen_piste.setWidth(largeur_trait);
  621. pen_piste.setColor(couleur_i);
  622.  
  623. n=0.5;
  624. int stop =0;
  625. while ((n<6) && (stop == 0))
  626. {
  627. d= 2 * n * largeur_trait;
  628. if (d > (-w/2) || d > (h/2)) {stop =1;}
  629.  
  630. //---------------------------------------------------------------------------------
  631. x1=A.x()-w/2 - d;
  632. y1=A.y()-h/2 + d;
  633. x2=A.x()+w/2 + d;
  634. y2=A.y()-h/2 + d;
  635.  
  636. QGraphicsLineItem *segment_trace1 = new QGraphicsLineItem(groupe_i);
  637. segment_trace1->setPen(pen_piste);
  638. segment_trace1->setLine (x1,y1,x2,y2);
  639. //---------------------------------------------------------------------------------
  640. x1=A.x()+w/2 + d;
  641. y1=A.y()-h/2 + d;
  642. x2=A.x()+w/2 + d;
  643. y2=A.y()+h/2 - d;
  644.  
  645. QGraphicsLineItem *segment_trace2 = new QGraphicsLineItem(groupe_i);
  646. segment_trace2->setPen(pen_piste);
  647. segment_trace2->setLine (x1,y1,x2,y2);
  648. //---------------------------------------------------------------------------------
  649.  
  650. x1=A.x()+w/2 + d;
  651. y1=A.y()+h/2 - d;
  652. x2=A.x()-w/2 - d;
  653. y2=A.y()+h/2 - d;
  654.  
  655. QGraphicsLineItem *segment_trace3 = new QGraphicsLineItem(groupe_i);
  656. segment_trace3->setPen(pen_piste);
  657. segment_trace3->setLine (x1,y1,x2,y2);
  658. //---------------------------------------------------------------------------------
  659.  
  660. x1=A.x()-w/2 - d;
  661. y1=A.y()+h/2 - d;
  662. x2=A.x()-w/2 - d;
  663. y2=A.y()-h/2 + d;
  664.  
  665. QGraphicsLineItem *segment_trace4 = new QGraphicsLineItem(groupe_i);
  666. segment_trace4->setPen(pen_piste);
  667. segment_trace4->setLine (x1,y1,x2,y2);
  668.  
  669. n++;
  670. }
  671. }
  672.  
  673. }
  674.  
  675.  
  676. void MainWindow::tracer_trou(QPoint A, quint16 diametre)
  677. { // à l'écran
  678. qreal x, y;
  679.  
  680. A.setY(-A.y());// symétrie verticale
  681. A /= ech;
  682.  
  683. x=A.x();
  684. y=A.y();
  685.  
  686. diametre /= ech;
  687.  
  688. QGraphicsEllipseItem *ellipse1 = new QGraphicsEllipseItem (groupe_trous); // PASTILLE
  689. ellipse1->setBrush(brush_trou);
  690.  
  691. ellipse1->setRect(x-diametre/2, y-diametre/2, diametre, diametre);
  692. }
  693.  
  694.  
  695.  
  696.  
  697. void MainWindow::tracer_pastille_ronde(QPoint A, QPoint B, QColor couleur_i, QGraphicsItemGroup *groupe_i)
  698. { // à l'écran
  699.  
  700. qreal w, h;
  701. qreal n;
  702. qreal x, y;
  703. qreal largeur_trait, d, rt;
  704.  
  705. rt = 80 / ech; // rayon du trou
  706.  
  707. A.setY(-A.y());// symétrie verticale
  708. B.setY(-B.y());// symétrie verticale
  709.  
  710. A /= ech;
  711. B /= ech;
  712.  
  713. w = A.x()-B.x();
  714. h = A.y()-B.y();
  715.  
  716. if (h<(100/ech)) {rt=0;} // ne pas percer les très petites pastilles qui sont des raccordements de pistes
  717.  
  718. brush_pastille.setColor(couleur_i);
  719.  
  720.  
  721. if(trace_aspect_reel == 0)
  722. {
  723. // trace un disque plein
  724. QGraphicsEllipseItem *ellipse1 = new QGraphicsEllipseItem (groupe_i); // PASTILLE
  725. ellipse1->setBrush(brush_pastille);
  726.  
  727. x=A.x()-w/2;
  728. y=A.y()-h/2;
  729.  
  730. ellipse1->setRect(x, y, w, h);
  731. }
  732.  
  733.  
  734. if(trace_aspect_reel == 1)
  735. {
  736. // trace des cercles concentriques avec l'épaisseur de la plume
  737. largeur_trait = 10; // à transmettre par paramètre...
  738. largeur_trait /= ech;
  739.  
  740. pen_piste.setWidth(largeur_trait);
  741. pen_piste.setColor(couleur_i);
  742.  
  743. n=0.5;
  744. int stop =0;
  745. while ((n<12) && (stop == 0))
  746. {
  747. d= 2.5 * n * largeur_trait;
  748.  
  749. // if (d > (-w/2) || d > (h/2)) {stop =1;}
  750. if ((h-d) < rt ) {stop =1;}
  751. //---------------------------------------------------------------------------------
  752.  
  753. QGraphicsEllipseItem *ellipse1 = new QGraphicsEllipseItem ();
  754. ellipse1->setPen(pen_piste);
  755.  
  756. x=A.x()-w/2;
  757. y=A.y()-h/2;
  758.  
  759. ellipse1->setRect(x-d/2, y+d/2, w+d, h-d);
  760. groupe_i->addToGroup(ellipse1);
  761.  
  762. //---------------------------------------------------------------------------------
  763.  
  764. n++;
  765. }
  766. }
  767. }
  768.  
  769.  
  770. void MainWindow::tracer_texte(int x, int y, QString texte_i)
  771. { // à l'écran
  772. y=-y;
  773.  
  774. x /= ech;
  775. y /= ech;
  776.  
  777. QGraphicsTextItem *gtexte = new QGraphicsTextItem(groupe_trace);
  778. gtexte->setDefaultTextColor(couleur_texte);
  779. gtexte->setPos(x,y);
  780. gtexte->setPlainText(texte_i);
  781. }
  782.  
  783.  
  784.  
  785.  
  786.  
  787.  
  788.  
  789. int MainWindow::dessine_1ligne_liste_triee(int i) // dessine à l'écran 1 ligne de la "liste_elements_tries"
  790. {
  791. QColor couleur1;
  792. char sorte_i;
  793. char forme_i;
  794. int clique_i;
  795. QPoint A, B, M, WH; // M : milieu de l'élément
  796. quint8 num_ap, w;
  797. Element element_i;
  798. Aperture aperture_i;
  799.  
  800. // liste_elements_i = *lst_elm;
  801. int i_max = liste_elements_tries.count();
  802. if (i>i_max) {return 1;}
  803. element_i = liste_elements_tries[i];
  804. sorte_i = element_i.sorte;
  805. clique_i = element_i.clique;
  806.  
  807. A = element_i.A;
  808. B = element_i.B;
  809. M = element_i.M;
  810.  
  811. num_ap = element_i.num_aperture-10;
  812.  
  813. int n_max = liste_apertures.length();
  814. if (num_ap >= n_max) {return 1;}
  815.  
  816. aperture_i = liste_apertures[num_ap];
  817.  
  818. WH = aperture_i.WH;
  819. w = WH.x();
  820.  
  821. forme_i=aperture_i.forme;
  822.  
  823. if (sorte_i == 'S') // SEGMENT
  824. {
  825. if (clique_i == 0) { couleur1 = couleur_piste;}; // non tracée
  826. if (clique_i == 1) { couleur1 = couleur_pisteT;} // en cours de traçage
  827. if (clique_i > 1) { couleur1 = couleur_pisteC;} // tracée
  828. tracer_segment(A, B, w, couleur1 , groupe_segments_faits);
  829. }
  830.  
  831. if (sorte_i == 'P') // PASTILLE
  832. {
  833. if (clique_i == 0) { couleur1 = couleur_pastille;}
  834. if (clique_i == 1) { couleur1 = couleur_pastilleT;}
  835. if (clique_i > 1) { couleur1 = couleur_pastilleC;}
  836.  
  837. if (forme_i == 'R') { tracer_pastille_rectangulaire(A, A + WH, couleur1, groupe_pastilles_faites); }
  838. if (forme_i == 'C') { tracer_pastille_ronde(A, A + WH, couleur1, groupe_pastilles_faites); }
  839. }
  840.  
  841. // s1.setNum(i); // conversion num -> txt
  842. // tracer_texte(Xcent, Ycent, s1);
  843. return 0;
  844. }
  845.  
  846.  
  847.  
  848.  
  849. void MainWindow::trace_liste_apertures()
  850. { // à l'écran
  851. Aperture aperture_i;
  852. uint i, i_max;
  853. QPoint Ai, Bi;
  854. // int xi, yi, wi, hi;
  855. QString s1, nom_aperture;
  856. i_max = liste_apertures.length();
  857.  
  858. for(i=0; i<i_max; i++)
  859. {
  860. aperture_i = liste_apertures[i];
  861. Ai.setX(-1000);
  862. Ai.setY(300*i);
  863.  
  864. Bi = Ai+aperture_i.WH;
  865.  
  866. s1.setNum(i+10); // conversion num -> txt
  867. s1="00" + s1;
  868. s1 = s1.right(2);
  869. nom_aperture = "D" + s1;
  870. if (aperture_i.forme == 'R'){ tracer_pastille_rectangulaire(Ai, Bi, QColor::fromRgb(0xE3E300), groupe_trace);}
  871. if (aperture_i.forme == 'C'){ tracer_pastille_ronde(Ai, Bi, QColor::fromRgb(0xE3E300), groupe_trace);}
  872. tracer_texte(Ai.x()+200, Ai.y()+100, nom_aperture);
  873. }
  874. }
  875.  
  876.  
  877.  
  878. void MainWindow::dessiner_liste_elements_tries()
  879. {
  880. int i, i_max;
  881. i_max = liste_elements_tries.count();
  882.  
  883.  
  884. for (i=0; i<i_max; i++ )
  885. {
  886. dessine_1ligne_liste_triee(i);
  887. }
  888. tracer_mire_origine();
  889. }
  890.  
  891.  
  892.  
  893.  
  894. int MainWindow::detecte_element_pointe(QPoint P) // par un clic sur le pt milieu d'un élément scene1 (non déjà cliqué)
  895. {
  896. int i, i_max, num_origine, nouveau_num;
  897.  
  898. qreal distance;
  899. QPointF M;
  900. QString s1, s2;
  901. //int clique_i;
  902.  
  903. i_max = liste_elements.count();
  904.  
  905.  
  906. for (i=0; i<i_max; i++ )
  907. {
  908. //clique_i = liste_elements[i].clique;
  909.  
  910. M = liste_elements[i].M;
  911.  
  912.  
  913. distance = sqrt(sqr(M.x()-P.x()) + sqr(M.y()-P.y()));
  914.  
  915. if (distance < 50)
  916. {
  917. // qDebug() << "trouvé élément num (TW4): " << i;
  918. liste_elements[i].clique=1;
  919.  
  920. num_origine = liste_elements[i].num_origine;
  921. nouveau_num = liste_elements[i].nouveau_num;
  922.  
  923. s1.setNum(num_origine); // conversion num -> txt
  924. s2.setNum(nouveau_num); // conversion num -> txt
  925. lineEdit_5->setText(s1+" - "+s2);
  926.  
  927.  
  928. // affiche_liste_elements(); // dans le tableau
  929. // affiche_liste_elements_tries(); // dans le tableau
  930.  
  931. tableWidget_4->selectRow(num_origine);
  932. tableWidget_5->selectRow(nouveau_num-1);
  933.  
  934. pointer(nouveau_num-1);
  935. liste_elements_tries[nouveau_num-1].clique =2;
  936. dessine_1ligne_liste_triee(nouveau_num-1); // afin de positionner le pointage sur l'élément cliqué
  937. }
  938. }
  939. return 0;