diff --git a/matrice_3x3/README.md b/matrice_3x3/README.md new file mode 100644 index 0000000..2ae0e0f --- /dev/null +++ b/matrice_3x3/README.md @@ -0,0 +1,293 @@ +# Maquette 3 × 3 : matrice de LED RGB multiplexée + +Workshop « Vitrine pixels » : on fabrique un petit écran de **9 pixels** de +couleur, soudé sur une plaque à pastilles et piloté par un Arduino. +C'est la maquette d'apprentissage avant la grande matrice 7 × 39. + +![Implantation sur plaque à pastilles](schemas/implantation_plaque.svg) + +> **Le principe à retenir** : on n'allume jamais les 9 LED en même temps. +> On allume **une ligne à la fois**, très vite (environ 333 fois par seconde), +> et l'œil voit une image fixe. C'est le **multiplexage**. Tout est expliqué +> en détail dans les commentaires de [`matrice_3x3.ino`](matrice_3x3.ino). + +--- + +## Sommaire + +1. [Le matériel](#1-le-matériel) +2. [Reconnaître les composants](#2-reconnaître-les-composants) +3. [Le schéma électrique](#3-le-schéma-électrique) +4. [Le montage sur plaque à pastilles](#4-le-montage-sur-plaque-à-pastilles) +5. [Souder, étape par étape](#5-souder-étape-par-étape) +6. [Relier à l'Arduino](#6-relier-à-larduino) +7. [Téléverser le programme et tester](#7-téléverser-le-programme-et-tester) +8. [Ça ne marche pas ?](#8-ça-ne-marche-pas-) +9. [Le fichier Fritzing](#9-le-fichier-fritzing) + +--- + +## 1. Le matériel + +| Quoi | Combien | Remarque | +|---|---|---| +| Arduino Uno + câble USB | 1 | | +| LED RGB 5 mm **à cathode commune** | 9 | 4 pattes ; vérifier « common cathode » à l'achat | +| Transistor **BC337** (NPN) | 3 | ou 2N2222 / S8050 : **attention, pattes dans un autre ordre** (voir 2) | +| Résistance **220 Ω** | 3 | pour le rouge | +| Résistance **150 Ω** | 6 | pour le vert et le bleu | +| Résistance **1 kΩ** | 3 | pour les bases des transistors | +| Plaque à pastilles (pas 2,54 mm) | 1 | au moins **32 × 34 trous** | +| Fil nu étamé (ou chutes de pattes de résistances) | ~1 m | pour les liaisons côté soudures | +| Fil isolé fin (monobrin) | ~50 cm | pour les 3 lignes de cathodes | +| Fils souples avec embout mâle (« Dupont ») | 13 | vers l'Arduino : 9 rouges/verts/bleus, 3 jaunes, 1 noir | +| Fer à souder, étain, pince coupante, pince plate | | | + +--- + +## 2. Reconnaître les composants + +### La LED RGB à cathode commune + +Elle contient 3 petites LED (rouge, verte, bleue) qui partagent la même +patte « moins » : la **cathode commune**, qui est **la patte la plus longue**. + +``` + ___ + / \ Ordre des pattes le plus courant : + | | + |_____| R K G B + | | | | │ │ │ └ bleu + | | | | │ │ └──── vert + R K | | │ └──────── cathode (la plus longue) + G B └──────────── rouge (souvent côté du méplat) +``` + +> ⚠️ L'ordre peut changer selon le fabricant. **Testez une LED avant de +> souder** : cathode au GND de l'Arduino, et chaque autre patte, l'une après +> l'autre, au 5V **à travers une résistance de 220 Ω** (jamais sans résistance !). + +### Le transistor BC337 + +C'est un **interrupteur électronique** : quand un petit courant entre par la +**base (B)**, il laisse passer un gros courant entre le **collecteur (C)** et +l'**émetteur (E)**. Ici, chaque transistor allume ou éteint une ligne de LED. + +Face plate vers vous, pattes vers le bas, de gauche à droite : **C – B – E**. + +> ⚠️ Le **2N2222** et le **S8050** ont l'ordre inverse : **E – B – C**. +> Si vous les utilisez, tournez-les d'un demi-tour (face plate vers le haut +> de la plaque). + +### Les résistances + +Elles freinent le courant pour protéger les LED et l'Arduino. On lit leur +valeur avec les anneaux de couleur : + +| Valeur | Anneaux | Rôle | +|---|---|---| +| 220 Ω | rouge – rouge – marron | LED rouges | +| 150 Ω | marron – vert – marron | LED vertes et bleues | +| 1 kΩ | marron – noir – rouge | bases des transistors | + +Une résistance n'a pas de sens : on peut la souder dans les deux sens. + +--- + +## 3. Le schéma électrique + +![Schéma électrique](schemas/schema_electrique.svg) + +Comment le lire : + +- **À gauche, l'Arduino**. Ses broches **D2 à D10** envoient la couleur, + ses broches **D11 à D13** choisissent la ligne allumée. +- **Les 9 lignes verticales** : chaque broche D2 à D10 passe par une + résistance, puis rejoint **les anodes d'une même couleur dans une même + colonne** de la matrice. Par exemple, D2 alimente le rouge des 3 LED de la + colonne de gauche. +- **Les 3 lignes horizontales** relient **les cathodes d'une même ligne** + de LED, puis vont au **collecteur** d'un transistor. +- **À droite, les transistors** : leur base est commandée par D11, D12 ou D13 + (à travers 1 kΩ), et leur émetteur est relié au **GND** (la masse). + +Un pixel s'allume quand **sa ligne est active** (transistor passant) **et** +que **sa colonne envoie de la couleur**. C'est le croisement des deux qui +choisit le pixel. + +| Broche Arduino | Rôle | +|---|---| +| D2, D3, D4 | rouge, vert, bleu de la **colonne 0** (gauche) | +| D5, D6, D7 | rouge, vert, bleu de la **colonne 1** (milieu) | +| D8, D9, D10 | rouge, vert, bleu de la **colonne 2** (droite) | +| D11, D12, D13 | **lignes** 0 (haut), 1, 2 (bas), par les transistors T0, T1, T2 | +| GND | masse : émetteurs des 3 transistors | + +--- + +## 4. Le montage sur plaque à pastilles + +Le plan en haut de cette page montre la plaque **des deux côtés** : + +- **À gauche, côté composants** : ce qu'on voit quand la plaque est posée + devant soi, composants vers le haut. +- **À droite, côté soudures** : la plaque **retournée**. ⚠️ C'est une image + **en miroir** : ce qui était à gauche se retrouve à droite. Les numéros de + colonnes aident à s'y retrouver. + +Les trous sont repérés par **colonne** (de 1 à 32, de gauche à droite côté +composants) et **ligne** (de 1 à 34, de haut en bas). + +### L'idée du câblage + +- **Les colonnes de couleur** (anodes) sont des **fils nus verticaux**, côté + soudures. Comme les 3 LED d'une colonne sont alignées, leurs pattes R + tombent dans la même colonne de trous : un fil nu droit les relie toutes, + puis rejoint la résistance. +- **Les lignes de cathodes** croisent les colonnes : on ne peut pas les faire + en fil nu du même côté (ça ferait un court-circuit !). On les fait donc en + **fil isolé, côté composants**, qui passe **par-dessus**. +- Chaque fil isolé arrive dans **le trou juste à côté** de la patte à + relier ; on fait la liaison côté soudures avec une goutte d'étain ou un + bout de fil nu. + +### Où va chaque composant + +| Composant | Trous (colonne, ligne) | +|---|---| +| Résistances 220 Ω (rouge) | col. 3, 9, 15 : lignes 3 → 7 | +| Résistances 150 Ω (vert) | col. 5, 11, 17 : lignes 3 → 7 | +| Résistances 150 Ω (bleu) | col. 6, 12, 18 : lignes 8 → 12 | +| LED de la ligne 0 (haut) | ligne 19 ; colonnes 3-6, 9-12, 15-18 (pattes R K G B) | +| LED de la ligne 1 | ligne 25 ; mêmes colonnes | +| LED de la ligne 2 (bas) | ligne 31 ; mêmes colonnes | +| Transistors T0, T1, T2 | lignes 20, 26, 32 ; colonnes 22 (C), 23 (B), 24 (E) | +| Résistances 1 kΩ (bases) | lignes 21, 27, 33 ; colonnes 23 → 27 | + +> **Pourquoi des LED espacées de 6 trous (15 mm) ?** Pour laisser de la place +> aux fils et aux doigts. La matrice finale aura un pas de 10 mm. Pour tester +> la diffusion de la lumière à 10 mm, on peut refaire une version au pas de +> 4 trous (10,16 mm), mais le câblage est beaucoup plus serré. + +--- + +## 5. Souder, étape par étape + +**On soude des composants les plus bas aux plus hauts** : la plaque reste +stable quand on la retourne. + +1. **Les fils nus** (côté soudures) : les 9 colonnes de couleur, le bus de + masse (colonne 31) et les petits ponts. Suivez le plan de droite. +2. **Les 12 résistances**, couchées à plat contre la plaque (le long des + colonnes pour les 9 résistances de couleur, le long des lignes pour les 1 kΩ). + Vérifiez les couleurs : 220 Ω pour le rouge, 150 Ω pour le vert et le bleu, + 1 kΩ pour les bases. +3. **Les 3 transistors**, face plate vers le bas de la plaque. + Ne chauffez pas plus de 3 secondes par patte. +4. **Les 9 LED** : patte la plus longue (K) en 2e position. + **Testez chaque LED avant** (voir partie 2). +5. **Les 3 fils isolés** des cathodes (côté composants). +6. **Les 13 fils vers l'Arduino** (voir partie 6). + +Conseils de soudure : + +- Le fer chauffe **la pastille et la patte en même temps**, puis on apporte + l'étain. Une bonne soudure est brillante et en forme de petit volcan. +- Après chaque étape, **vérifiez qu'aucune goutte d'étain ne relie deux + pastilles voisines** par erreur, surtout entre les colonnes de couleur + voisines (G et B ne sont qu'à un trou d'écart !). +- Coupez les pattes qui dépassent **après** avoir soudé. Gardez les chutes : + elles font d'excellents fils nus. + +--- + +## 6. Relier à l'Arduino + +| Fil | Trou sur la plaque (col., ligne) | Broche Arduino | +|---|---|---| +| 🔴 rouge colonne 0 | 3, 2 | D2 | +| 🟢 vert colonne 0 | 5, 2 | D3 | +| 🔵 bleu colonne 0 | 6, 2 | D4 | +| 🔴 rouge colonne 1 | 9, 2 | D5 | +| 🟢 vert colonne 1 | 11, 2 | D6 | +| 🔵 bleu colonne 1 | 12, 2 | D7 | +| 🔴 rouge colonne 2 | 15, 2 | D8 | +| 🟢 vert colonne 2 | 17, 2 | D9 | +| 🔵 bleu colonne 2 | 18, 2 | D10 | +| 🟡 ligne 0 | 28, 21 | D11 | +| 🟡 ligne 1 | 29, 27 | D12 | +| 🟡 ligne 2 | 30, 33 | D13 | +| ⚫ masse | 31, 18 | GND (à côté de D13) | + +![Vue Fritzing de la plaque reliée à l'Arduino](schemas/plaque_fritzing.png) + +> Pas besoin d'alimentation externe : 9 LED à 15 mA, une seule ligne à la fois, +> c'est au maximum ~135 mA. Le port USB suffit. + +--- + +## 7. Téléverser le programme et tester + +1. Installez l'**IDE Arduino** et la bibliothèque **TimerOne** + (Outils → Gérer les bibliothèques… → « TimerOne » → Installer). +2. Ouvrez [`matrice_3x3.ino`](matrice_3x3.ino). +3. Outils → Type de carte → **Arduino Uno** ; Outils → Port → le bon port. +4. **Téléverser**. + +La première animation, `animTestPixels`, allume **chaque pixel l'un après +l'autre**, en rouge, puis en vert, puis en bleu, de gauche à droite et de haut +en bas. C'est le test de câblage : notez tout pixel qui ne s'allume pas, ou +pas au bon endroit, ou pas dans la bonne couleur. + +Ensuite, les animations s'enchaînent : aplats de couleur, chenille, damier, +balayages, scintillement. Pour créer les vôtres, lisez les parties 5 et 6 du +programme. + +--- + +## 8. Ça ne marche pas ? + +| Symptôme | Cause probable | Solution | +|---|---|---| +| **Rien ne s'allume** | Fil de masse (GND) absent | Vérifier le fil noir et le bus de masse (col. 31) | +| | Transistors à l'envers | Vérifier C-B-E (BC337) ou E-B-C (2N2222) | +| **Une ligne entière reste éteinte** | Transistor mal soudé ou à l'envers | Vérifier T0, T1 ou T2 et sa résistance de base | +| | Fil isolé de cathode mal soudé | Vérifier le pont entre la patte K et le fil | +| **Une couleur manque sur toute une colonne** | Résistance ou fil nu de colonne mal soudé | Suivre la colonne côté soudures | +| | Mauvais fil sur l'Arduino | Comparer avec le tableau de la partie 6 | +| **Un seul pixel ne marche pas** | LED mal soudée, ou grillée | Ressouder, ou tester la LED seule | +| **Couleurs mélangées** (rouge à la place du bleu…) | Ordre des pattes de la LED différent | Tester la LED (partie 2) ; corriger dans le code (`pinsCouleur`) | +| **Deux couleurs s'allument ensemble** | Pont d'étain entre deux colonnes voisines | Chauffer et retirer l'étain en trop (tresse à dessouder) | +| **Légers reflets sur des pixels éteints** (« fantômes ») | Normal si très faible | Le code éteint déjà les lignes avant de changer les couleurs | +| **L'image clignote** | `DUREE_LIGNE_US` trop grand | Revenir à 1000 dans le code | +| **La petite LED « L » de l'Arduino scintille** | Normal : elle est sur la broche 13 (ligne 2) | Rien à faire | + +--- + +## 9. Le fichier Fritzing + +[`matrice_3x3.fzz`](matrice_3x3.fzz) s'ouvre avec +[Fritzing](https://fritzing.org) (version 1.0 ou plus récente). Il contient : + +- la vue **Schéma** : le même circuit que la partie 3, avec les symboles + de Fritzing ([aperçu](schemas/schema_fritzing.png)) ; +- la vue **Platine d'essai** : la plaque à pastilles reliée à l'Arduino. + Les fils gris sont les liaisons en fil nu (côté soudures), les fils noirs + sur la plaque sont les fils isolés des cathodes. + +La vue **Circuit imprimé** n'est pas utilisée (pièces non placées). + +Les scripts du dossier [`outils/`](outils/) régénèrent le fichier Fritzing, +le plan d'implantation et le schéma électrique si on modifie le montage : + +```bash +python3 outils/gen_fzz.py matrice_3x3.fzz +``` + +```bash +python3 outils/render_plaque.py schemas/implantation_plaque.svg +``` + +```bash +python3 outils/schema_electrique.py schemas/schema_electrique.svg +``` diff --git a/matrice_3x3/matrice_3x3.fzz b/matrice_3x3/matrice_3x3.fzz new file mode 100644 index 0000000..b06c908 Binary files /dev/null and b/matrice_3x3/matrice_3x3.fzz differ diff --git a/matrice_3x3/outils/gen_fzz.py b/matrice_3x3/outils/gen_fzz.py new file mode 100644 index 0000000..ded5744 --- /dev/null +++ b/matrice_3x3/outils/gen_fzz.py @@ -0,0 +1,489 @@ +#!/usr/bin/env python3 +"""Génère matrice_3x3.fzz : maquette 3x3 sur plaque à pastilles (vue platine) ++ schéma électrique (vue schéma). Coordonnées en px Fritzing (90 dpi).""" +import math, sys, zipfile, os +from xml.sax.saxutils import escape + +PARTS = "/usr/share/fritzing/fritzing-parts/core/" +P = 9.0 # pas 0,1 pouce + +# ---------------------------------------------------------------- modèle +instances = [] # dict: id, module, path, title, props, views{view:{...}} +idx = [1000] +def nid(): + idx[0] += 1 + return idx[0] + +conns = {} # (inst_id, view, connector) -> list of (other_id, other_connector, other_layer) +def link(view, a, ca, la, b, cb, lb): + conns.setdefault((a, view, ca), []).append((b, cb, lb)) + conns.setdefault((b, view, cb), []).append((a, ca, la)) + +# ---------------------------------------------------------------- géométries des pièces +LED = dict(module="d4d5af9700923b8a114f57961f29a8a0ColorLEDModuleID", + path=PARTS + "led-rgb-4pin-cathode_v5.fzp", + legs={"connector0": (1.35, 40.6875), "connector1": (10.35, 40.6875), + "connector2": (19.35, 40.6875), "connector3": (28.35, 40.6875)}, + size=(0.33 * 90, 0.4521 * 90)) +NPN = dict(module="2244EBEC10e", path=PARTS + "transistor_signal_NPN_TO92_CBE.fzp", + # connector2 = C (gauche), connector1 = B, connector0 = E (droite) + legs={"connector2": (1.35, 30.101), "connector1": (10.326, 30.101), + "connector0": (19.309, 30.101)}, + size=(0.22954 * 90, 0.3344583 * 90)) +RES = dict(module="ResistorModuleID", path=PARTS + "resistor.fzp", + legs={"connector0": (2.619, 4.5405), "connector1": (36.0063, 4.5405)}, + size=(0.42917 * 90, 0.0971 * 90)) +ARDUINO = dict(module="arduino_Uno_Rev3(fix)", path=PARTS + "arduino_Uno_Rev3(fix).fzp", + size=(2.94961 * 90, 2.1 * 90)) +ARD_PIN = { # broche -> (connector id, x, y) dans le repère de la carte (non tournée) + "D2": ("connector63", 229.47, 9.0), "D3": ("connector64", 220.46, 9.0), + "D4": ("connector65", 211.46, 9.0), "D5": ("connector66", 202.46, 9.0), + "D6": ("connector67", 193.46, 9.0), "D7": ("connector68", 184.47, 9.0), + "D8": ("connector51", 170.06, 9.0), "D9": ("connector52", 161.06, 9.0), + "D10": ("connector53", 152.06, 9.0), "D11": ("connector54", 143.06, 9.0), + "D12": ("connector55", 134.06, 9.0), "D13": ("connector56", 125.06, 9.0), + "GND": ("connector57", 116.06, 9.0), +} + +def rot_matrix(deg, w, h): + """Rotation autour du centre (w/2, h/2), au format QTransform de Fritzing.""" + cx, cy = w / 2, h / 2 + a = math.radians(deg) + c, s = round(math.cos(a), 9), round(math.sin(a), 9) + m11, m12, m21, m22 = c, s, -s, c + m31 = cx - (m11 * cx + m21 * cy) + m32 = cy - (m12 * cx + m22 * cy) + return (m11, m12, m21, m22, m31, m32) + +def apply(m, x, y): + m11, m12, m21, m22, m31, m32 = m + return (m11 * x + m21 * y + m31, m12 * x + m22 * y + m32) + +def inv_apply(m, u, v): + m11, m12, m21, m22, m31, m32 = m + u, v = u - m31, v - m32 + det = m11 * m22 - m12 * m21 + return ((m22 * u - m21 * v) / det, (-m12 * u + m11 * v) / det) + +# ---------------------------------------------------------------- vue platine (plaque à pastilles) +COLS, ROWS = 32, 34 +BOARD = dict(id=nid(), module=f"{COLS}.{ROWS}PerfboardModuleID", + path=":/resources/parts/core/perfboard.fzp", title="Plaque à pastilles", + props={"size": f"{COLS}.{ROWS}"}, views={}) +BOARD["views"]["breadboardView"] = dict(layer="breadboardbreadboard", x=0, y=0, z=1.5) +instances.append(BOARD) + +def hole(c, r): + return (9 + P * c, 9 + P * r) + +def hole_id(c, r): + return f"connector{r * 1000 + c}" + +def add_part(spec, title, bb_pos, bb_rot=0, props=None, sch=None, legs_to=None): + """legs_to : {connector: (col, row)} trous de la plaque où arrivent les pattes.""" + inst = dict(id=nid(), module=spec["module"], path=spec["path"], title=title, + props=props or {}, views={}, legs={}) + w, h = spec["size"] + m = rot_matrix(bb_rot, w, h) if bb_rot else None + inst["views"]["breadboardView"] = dict(layer="breadboard", x=bb_pos[0], y=bb_pos[1], + z=2.5, m=m) + if legs_to: + for con, (c, r) in legs_to.items(): + sx, sy = spec["legs"][con] + hx, hy = hole(c, r) + u, v = hx - bb_pos[0], hy - bb_pos[1] + lx, ly = inv_apply(m, u, v) if m else (u, v) + inst["legs"][con] = ((sx, sy), (lx - sx, ly - sy)) + link("breadboardView", inst["id"], con, "breadboard", BOARD["id"], + hole_id(c, r), "breadboardbreadboard") + inst["sch"] = sch + inst["holes"] = legs_to or {} + instances.append(inst) + return inst + +def place_on_holes(spec, title, holes, rot=0, lift=1.0, props=None): + """Place la pièce pour que ses pattes soient au-dessus des trous, puis plie les + pattes vers les trous. lift = longueur de patte visible, en pas de 0,1 po.""" + cons = list(holes) + # centre des trous visés, centre des départs de pattes + hx = sum(hole(*holes[c])[0] for c in cons) / len(cons) + hy = sum(hole(*holes[c])[1] for c in cons) / len(cons) + sx = sum(spec["legs"][c][0] for c in cons) / len(cons) + sy = sum(spec["legs"][c][1] for c in cons) / len(cons) + w, h = spec["size"] + m = rot_matrix(rot, w, h) if rot else None + tx, ty = apply(m, sx, sy) if m else (sx, sy) + if rot == 0: + pos = (hx - tx, hy - ty - lift * P) + else: + pos = (hx - tx, hy - ty) + return add_part(spec, title, pos, rot, props, legs_to=holes) + +wires = [] +board_wires = [] # (trou a, trou b, couleur) pour le plan d'implantation +def bb_wire(a, b, color, title=None): + """Fil entre deux trous de la plaque (a, b = (col, row)).""" + (x1, y1), (x2, y2) = hole(*a), hole(*b) + board_wires.append((a, b, color)) + w = dict(id=nid(), title=title or f"Fil{len(wires) + 1}", view="breadboardView", + x=x1, y=y1, dx=x2 - x1, dy=y2 - y1, color=color, flags=64, + layer="breadboardWire", z=(1.8 if color == NU else 3.5)) + for k, (c, r) in enumerate((a, b)): + link("breadboardView", w["id"], f"connector{k}", "breadboardWire", + BOARD["id"], hole_id(c, r), "breadboardbreadboard") + wires.append(w) + return w + +ARD_POS = (40.0, -280.0) +ARD_M = rot_matrix(180, *ARDUINO["size"]) +ARD = dict(id=nid(), module=ARDUINO["module"], path=ARDUINO["path"], title="Arduino Uno", + props={}, views={}, legs={}) +ARD["views"]["breadboardView"] = dict(layer="breadboardbreadboard", x=ARD_POS[0], + y=ARD_POS[1], z=1.6, m=ARD_M) +instances.append(ARD) + +def ard_pin_xy(name): + _, px, py = ARD_PIN[name] + u, v = apply(ARD_M, px, py) + return (ARD_POS[0] + u, ARD_POS[1] + v) + +def ard_wire(pin, hole_cr, color): + x1, y1 = ard_pin_xy(pin) + x2, y2 = hole(*hole_cr) + w = dict(id=nid(), title=f"Fil {pin}", view="breadboardView", x=x1, y=y1, + dx=x2 - x1, dy=y2 - y1, color=color, flags=64, layer="breadboardWire") + link("breadboardView", w["id"], "connector0", "breadboardWire", ARD["id"], + ARD_PIN[pin][0], "breadboardbreadboard") + link("breadboardView", w["id"], "connector1", "breadboardWire", BOARD["id"], + hole_id(*hole_cr), "breadboardbreadboard") + wires.append(w) + +# couleurs de fils Fritzing +NU = "#a8a8a8" # fil nu étamé, côté soudure +ROUGE, VERT, BLEU = "#cc1414", "#47cc79", "#418dd9" +NOIR, JAUNE, ORANGE = "#404040", "#ffe24d", "#ff7033" + +# --- implantation ------------------------------------------------------------- +PAS = 6 # pas entre LED, en trous (6 × 2,54 = 15,24 mm) +X0, Y_LED = 2, (18, 24, 30) # LED (ligne r, colonne c) : pattes R K G B +COUL = {"R": 0, "G": 2, "B": 3} # décalage de la patte dans la LED +PIN_ANODE = {(c, k): f"D{2 + 3 * c + i}" for c in range(3) for i, k in enumerate("RGB")} +RES_VAL = {"R": "220Ω", "G": "150Ω", "B": "150Ω"} +COUL_FIL = {"R": ROUGE, "G": VERT, "B": BLEU} +ard_jobs = [] # (broche, trou, couleur) routés à la fin + +leds = {} +for r in range(3): + for c in range(3): + x = X0 + PAS * c + leds[(r, c)] = place_on_holes( + LED, f"LED L{r}C{c}", + {"connector0": (x, Y_LED[r]), "connector1": (x + 1, Y_LED[r]), + "connector2": (x + 2, Y_LED[r]), "connector3": (x + 3, Y_LED[r])}, + lift=0.6) + +res_anode = {} +for c in range(3): + for k, off in COUL.items(): + col = X0 + PAS * c + off + top, bot = (2, 6) if k in "RG" else (7, 11) + res_anode[(c, k)] = place_on_holes( + RES, f"R {k}{c}", {"connector0": (col, top), "connector1": (col, bot)}, + rot=90, props={"resistance": RES_VAL[k]}) + bb_wire((col, 1), (col, top), NU) + ard_jobs.append((PIN_ANODE[(c, k)], (col, 1), COUL_FIL[k])) + bb_wire((col, bot), (col, Y_LED[0]), NU) + bb_wire((col, Y_LED[0]), (col, Y_LED[1]), NU) + bb_wire((col, Y_LED[1]), (col, Y_LED[2]), NU) + +XC, XB, XE = 21, 22, 23 # pattes C, B, E +X_GND = 30 +trans, res_base = {}, {} +for r in range(3): + y = Y_LED[r] + 1 + for c in range(3): + xk = X0 + PAS * c + 1 + bb_wire((xk, Y_LED[r]), (xk, y), NU) + bb_wire((X0 + 1, y), (X0 + 1 + PAS, y), NOIR) + bb_wire((X0 + 1 + PAS, y), (X0 + 1 + 2 * PAS, y), NOIR) + bb_wire((X0 + 1 + 2 * PAS, y), (XC - 1, y), NOIR) + bb_wire((XC - 1, y), (XC, y), NU) + trans[r] = place_on_holes(NPN, f"T{r} (BC337)", + {"connector2": (XC, y), "connector1": (XB, y), + "connector0": (XE, y)}, lift=0.6) + res_base[r] = place_on_holes(RES, f"Rb{r}", + {"connector0": (XB, y + 1), "connector1": (XB + 4, y + 1)}, + lift=0.0, props={"resistance": "1kΩ"}) + bb_wire((XB, y), (XB, y + 1), NU) + xw = XB + 5 + r + bb_wire((XB + 4, y + 1), (xw, y + 1), NU) + ard_jobs.append((f"D{11 + r}", (xw, y + 1), JAUNE)) + bb_wire((XE, y), (X_GND, y), NU) +yg = Y_LED[0] - 1 +bb_wire((X_GND, yg), (X_GND, Y_LED[0] + 1), NU) +bb_wire((X_GND, Y_LED[0] + 1), (X_GND, Y_LED[1] + 1), NU) +bb_wire((X_GND, Y_LED[1] + 1), (X_GND, Y_LED[2] + 1), NU) +ard_jobs.append(("GND", (X_GND, yg), NOIR)) + +# routage des fils Arduino : descente, palier horizontal, descente (fils coudés) +def chain(points, color, pin, hole_cr): + ids = [] + for i in range(len(points) - 1): + (x1, y1), (x2, y2) = points[i], points[i + 1] + w = dict(id=nid(), title=f"Fil {pin}", view="breadboardView", x=x1, y=y1, + dx=x2 - x1, dy=y2 - y1, color=color, flags=64, layer="breadboardWire") + wires.append(w) + ids.append(w["id"]) + link("breadboardView", ids[0], "connector0", "breadboardWire", ARD["id"], + ARD_PIN[pin][0], "breadboardbreadboard") + link("breadboardView", ids[-1], "connector1", "breadboardWire", BOARD["id"], + hole_id(*hole_cr), "breadboardbreadboard") + for a_, b_ in zip(ids, ids[1:]): + link("breadboardView", a_, "connector1", "breadboardWire", b_, "connector0", + "breadboardWire") + +header_y = ard_pin_xy("D2")[1] +jobs = [(pin, h, col, ard_pin_xy(pin)[0], hole(*h)[0]) for pin, h, col in ard_jobs] +droite = sorted([j for j in jobs if j[4] > j[3] + 0.5], key=lambda j: j[3]) +gauche = sorted([j for j in jobs if j[4] < j[3] - 0.5], key=lambda j: -j[3]) +niveau = {} +for groupe in (droite, gauche): + n = len(groupe) + for i, j in enumerate(groupe): # le plus à l'extérieur descend le plus bas + niveau[j[0]] = n - i +for pin, h, col, xp, xh in jobs: + yp = header_y + if pin in niveau: + yl = header_y + 18 + 9 * niveau[pin] + chain([(xp, yp), (xp, yl), (xh, yl), hole(*h)], col, pin, h) + else: + chain([(xp, yp), hole(*h)], col, pin, h) + +# ---------------------------------------------------------------- vue schéma +K = 90 / 96 # px Inkscape -> px Fritzing (SVG en pouces) +SCH = { # terminaux dans le repère du symbole (px Fritzing) + "LED": {"connector0": (5.126, 35.457), "connector2": (23.172, 35.457), + "connector3": (41.297, 35.457), "connector1": (23.225, 1.5)}, + "NPN": {"connector1": (0.4375, 18.4375), "connector2": (18.4375, 0.4375), + "connector0": (18.4375, 36.4375)}, + "RES": {"connector0": (0.4375, 3.87), "connector1": (36.4375, 3.87)}, + "RES_SIZE": (0.409722 * 90, 0.086 * 90), +} +def ard_sch(name): + con = ARD_PIN[name][0] + if name == "GND": + return (72.0, 207.0) + n = {"D2": 0, "D3": 1, "D4": 2, "D5": 3, "D6": 4, "D7": 5, "D8": 6, "D9": 7, + "D10": 8, "D11": 9, "D12": 10, "D13": 11}[name] + return (135.0, 54.0 + 9 * n) + +def set_sch(inst, pos, rot=0, size=None): + m = rot_matrix(rot, *size) if rot else None + inst["views"]["schematicView"] = dict(layer="schematic", x=pos[0], y=pos[1], z=2.5, m=m) + inst["sch_pos"], inst["sch_m"] = pos, m + +def place_sch(inst, table, con, target, rot=0, size=None): + """Place le symbole pour que le terminal `con` tombe sur `target`.""" + m = rot_matrix(rot, *size) if rot else None + lx, ly = SCH[table][con] + if m: + lx, ly = apply(m, lx, ly) + set_sch(inst, (target[0] - lx, target[1] - ly), rot, size) + +def term(inst, table, con): + lx, ly = SCH[table][con] + if inst.get("sch_m"): + lx, ly = apply(inst["sch_m"], lx, ly) + return (inst["sch_pos"][0] + lx, inst["sch_pos"][1] + ly) + +ends = {} # point arrondi -> [(wire_id, connector)] +def sch_poly(points, start=None, end=None): + ids = [] + for (x1, y1), (x2, y2) in zip(points, points[1:]): + w = dict(id=nid(), title="Fil", view="schematicView", x=x1, y=y1, dx=x2 - x1, + dy=y2 - y1, color="#404040", flags=128, layer="schematicTrace", + mils="9.7222", z=5.5) + wires.append(w) + ids.append(w["id"]) + for a_, b_ in zip(ids, ids[1:]): + link("schematicView", a_, "connector1", "schematicTrace", b_, "connector0", + "schematicTrace") + if start: + link("schematicView", ids[0], "connector0", "schematicTrace", start[0]["id"], + start[1], "schematic") + else: + ends.setdefault((round(points[0][0], 2), round(points[0][1], 2)), []).append( + (ids[0], "connector0")) + if end: + link("schematicView", ids[-1], "connector1", "schematicTrace", end[0]["id"], + end[1], "schematic") + else: + ends.setdefault((round(points[-1][0], 2), round(points[-1][1], 2)), []).append( + (ids[-1], "connector1")) + +A_POS = (0.0, 450.0) +ARD["views"]["schematicView"] = dict(layer="schematic", x=A_POS[0], y=A_POS[1], z=2.5) +def apin(name): + x, y = ard_sch(name) + return (A_POS[0] + x, A_POS[1] + y) +ARDP = lambda name: (ARD, ARD_PIN[name][0]) + +MX, MY, PASS = 225.0, 60.0, 90.0 +BUS_OFF = {"R": -27.0, "G": -18.0, "B": -9.0} +PROF = {"R": 9.0, "G": 18.0, "B": 27.0} +LEDCON = {"R": "connector0", "G": "connector2", "B": "connector3"} +RES_TOP = {"R": 315.0, "G": 360.0, "B": 405.0} +for (r, c), led in leds.items(): + set_sch(led, (MX + PASS * c, MY + PASS * r)) +for c in range(3): + for k in "RGB": + bx = MX + PASS * c + BUS_OFF[k] + res = res_anode[(c, k)] + # patte 1 (connector0) côté Arduino, en bas ; patte 2 côté LED, en haut + place_sch(res, "RES", "connector1", (bx, RES_TOP[k]), rot=270, size=SCH["RES_SIZE"]) + top, bot = term(res, "RES", "connector1"), term(res, "RES", "connector0") + pin = PIN_ANODE[(c, k)] + px, py = apin(pin) + sch_poly([(px, py), (bx, py), bot], start=ARDP(pin), end=(res, "connector0")) + # bus : résistance -> jonctions des lignes 2, 1, 0 + pts = [top] + [(bx, MY + PASS * r + 35.457 + PROF[k]) for r in (2, 1, 0)] + for i, (a_, b_) in enumerate(zip(pts, pts[1:])): + sch_poly([a_, b_], start=(res, "connector1") if i == 0 else None) + for r in range(3): + led = leds[(r, c)] + tx, ty = term(led, "LED", LEDCON[k]) + jy = MY + PASS * r + 35.457 + PROF[k] + sch_poly([(tx, ty), (tx, jy), (bx, jy)], start=(led, LEDCON[k])) + +XT = 545.0 +XV = {0: 462.0, 1: 471.0, 2: 480.0} +X_GND_S, Y_GND_S = XT + 45.0, 690.0 +for r in range(3): + yrl = MY + PASS * r + 1.5 - 9.0 + ks = [term(leds[(r, c)], "LED", "connector1") for c in range(3)] + for c in range(3): + sch_poly([ks[c], (ks[c][0], yrl)], start=(leds[(r, c)], "connector1")) + t = trans[r] + set_sch(t, (XT, yrl + 18.0 - 0.4375)) + cpt, bpt, ept = term(t, "NPN", "connector2"), term(t, "NPN", "connector1"), term(t, "NPN", "connector0") + sch_poly([(ks[0][0], yrl), (ks[1][0], yrl)]) + sch_poly([(ks[1][0], yrl), (ks[2][0], yrl)]) + sch_poly([(ks[2][0], yrl), (cpt[0], yrl), cpt], end=(t, "connector2")) + rb = res_base[r] + # patte 1 (connector0) côté base, patte 2 côté Arduino + place_sch(rb, "RES", "connector0", (bpt[0] - 9.0, bpt[1]), rot=180, size=SCH["RES_SIZE"]) + rbase, rard = term(rb, "RES", "connector0"), term(rb, "RES", "connector1") + sch_poly([rbase, bpt], start=(rb, "connector0"), end=(t, "connector1")) + pin = f"D{11 + r}" + px, py = apin(pin) + sch_poly([(px, py), (XV[r], py), (XV[r], rard[1]), rard], start=ARDP(pin), end=(rb, "connector1")) + sch_poly([ept, (X_GND_S, ept[1])], start=(t, "connector0")) + yE = ept[1] + if r == 0: + y_e0 = yE + else: + sch_poly([(X_GND_S, prev_e), (X_GND_S, yE)]) + prev_e = yE +gx, gy = apin("GND") +sch_poly([(X_GND_S, prev_e), (X_GND_S, Y_GND_S), (gx, Y_GND_S), (gx, gy)], end=ARDP("GND")) + +for pt, lst in ends.items(): + for i in range(len(lst)): + for j in range(i + 1, len(lst)): + link("schematicView", lst[i][0], lst[i][1], "schematicTrace", lst[j][0], + lst[j][1], "schematicTrace") + +# ---------------------------------------------------------------- écriture XML +def fmt(v): + return f"{v:.4f}".rstrip("0").rstrip(".") if isinstance(v, float) else str(v) + +def connects_xml(inst_id, view, con, indent): + out = [] + for (oid, ocon, olayer) in conns.get((inst_id, view, con), []): + out.append(f'{indent}') + return "\n".join(out) + +def part_xml(inst): + L = [] + L.append(f'') + for k, v in inst["props"].items(): + L.append(f' ') + L.append(f' {escape(inst["title"])}') + L.append(' ') + for view, g in inst["views"].items(): + L.append(f' <{view} layer="{g["layer"]}">') + if g.get("m"): + m11, m12, m21, m22, m31, m32 = g["m"] + L.append(f' ') + L.append(f' ') + L.append(' ') + else: + L.append(f' ') + cons = sorted({c for (i, v, c) in conns if i == inst["id"] and v == view} + | (set(inst.get("legs", {})) if view == "breadboardView" else set())) + if cons: + L.append(' ') + for con in cons: + L.append(f' ') + if view == "breadboardView" and con in inst.get("legs", {}): + (sx, sy), (lx, ly) = inst["legs"][con] + L.append(f' ') + L.append(' ' + f'') + else: + L.append(' ') + L.append(' ') + L.append(connects_xml(inst["id"], view, con, " ")) + L.append(' ') + L.append(' ') + L.append(' ') + L.append(f' ') + L.append(' ') + L.append('') + return "\n".join(L) + +def wire_xml(w): + view, layer = w["view"], w["layer"] + L = [f'', + f' {escape(w["title"])}', ' ', + f' <{view} layer="{layer}">', + f' ', + f' ', + ' '] + for k in ("connector0", "connector1"): + L.append(f' ') + L.append(' ') + L.append(' ') + L.append(connects_xml(w["id"], view, k, " ")) + L.append(' ') + L.append(' ') + L += [' ', f' ', ' ', ''] + return "\n".join(L) + +def build(out_path, ino_path=None): + head = ''' + + + + + + + +''' + body = "\n".join(part_xml(i) for i in instances) + "\n" + "\n".join(wire_xml(w) for w in wires) + xml = head + body + "\n\n\n" + name = os.path.splitext(os.path.basename(out_path))[0] + with zipfile.ZipFile(out_path, "w", zipfile.ZIP_DEFLATED) as z: + z.writestr(name + ".fz", xml) + if ino_path: + z.write(ino_path, os.path.basename(ino_path)) + +if __name__ == "__main__" and sys.argv[1] != "/dev/null": + build(sys.argv[1], sys.argv[2] if len(sys.argv) > 2 else None) + print(len(instances), "pièces,", len(wires), "fils") diff --git a/matrice_3x3/outils/render_plaque.py b/matrice_3x3/outils/render_plaque.py new file mode 100644 index 0000000..e756217 --- /dev/null +++ b/matrice_3x3/outils/render_plaque.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 +"""Plan d'implantation vu de dessus : côté composants + côté soudures (miroir).""" +import runpy, sys +OUT = sys.argv[1] +sys.argv = ["x", "/dev/null"] +g = runpy.run_path(sys.path[0] + "/gen_fzz.py", run_name="gen") +COLS, ROWS = g["COLS"], g["ROWS"] +NU = g["NU"] +leds, res_anode, res_base, trans = g["leds"], g["res_anode"], g["res_base"], g["trans"] +board_wires, ard_jobs = g["board_wires"], g["ard_jobs"] + +PX = 16 # pas d'affichage +MARGE_X, MARGE_Y = 44, 120 +PANNEAU = COLS * PX + 2 * MARGE_X +LARG = 2 * PANNEAU + 40 +HAUT = MARGE_Y + ROWS * PX + 270 +out = [] +A = out.append + +def xy(c, r, panneau): + if panneau == 1: # côté soudures : plaque retournée gauche/droite + c = COLS - 1 - c + return (panneau * (PANNEAU + 40) + MARGE_X + c * PX + PX / 2, MARGE_Y + r * PX + PX / 2) + +def txt(x, y, s, size=11, anchor="middle", fill="#333", weight="normal", rot=None): + tr = f' transform="rotate({rot} {x:.1f} {y:.1f})"' if rot else "" + A(f'{s}') + +A(f'') +A('Implantation de la matrice 3x3 sur plaque à pastilles') +A(f'') + +for pan, titre, sous in ((0, "Côté composants (vu de dessus)", + "on pose les composants et les fils isolés"), + (1, "Côté soudures (plaque retournée)", + "on soude et on relie avec du fil nu. Attention : image en miroir !")): + x0 = pan * (PANNEAU + 40) + MARGE_X + txt(x0, 34, titre, 18, "start", "#222", "bold") + txt(x0, 54, sous, 13, "start", "#666") + # plaque + A(f'') + # numéros de colonnes et de lignes (comptés à partir de 1) + for c in range(COLS): + x, _ = xy(c, 0, pan) + if (c + 1) % 5 == 0 or c == 0: + txt(x, MARGE_Y + ROWS * PX + 22, str(c + 1), 10, fill="#8a6d3b") + for r in range(ROWS): + _, y = xy(0, r, pan) + if (r + 1) % 5 == 0 or r == 0: + txt(x0 - 14, y + 4, str(r + 1), 10, fill="#8a6d3b") + # pastilles + for c in range(COLS): + for r in range(ROWS): + x, y = xy(c, r, pan) + A(f'' + f'') + +# ---------------------------------------------------------------- côté composants +def contour_led(inst, pan, plein): + hs = list(inst["holes"].values()) + xs = [xy(c, r, pan)[0] for c, r in hs] + cx, cy = sum(xs) / 4, xy(*hs[0], pan)[1] + if plein: + A(f'') + for (c, r), lettre, coul in zip(hs, "RKGB", ("#cc1414", "#222", "#2a9d3a", "#2f6fc4")): + x, y = xy(c, r, pan) + A(f'') + txt(x, y - 6, lettre, 9, fill=coul, weight="bold") + else: + A(f'') + +def contour_res(inst, pan, plein, valeur): + (c1, r1), (c2, r2) = inst["holes"].values() + (xa, ya), (xb, yb) = xy(c1, r1, pan), xy(c2, r2, pan) + if plein: + A(f'') + vertical = abs(xa - xb) < 1 + L = 0.55 * (abs(ya - yb) if vertical else abs(xa - xb)) + mx, my = (xa + xb) / 2, (ya + yb) / 2 + w, h = (9, L) if vertical else (L, 9) + style = ('fill="#e8d3a8" stroke="#a88a52"' if plein + else 'fill="none" stroke="#9a7b4a" stroke-dasharray="3 3"') + A(f'') + if plein: + txt(mx + (0 if vertical else 0), my + (3 if not vertical else 3), valeur, 8, + fill="#5a4310", weight="bold", rot=(-90 if vertical else None)) + +def contour_npn(inst, pan, plein, nom): + hs = inst["holes"] + xc, y = xy(*hs["connector2"], pan) + xe, _ = xy(*hs["connector0"], pan) + cx = (xc + xe) / 2 + R = PX * 1.25 + # demi-cercle, face plate vers le bas de la plaque + d = f'M{cx - R:.1f} {y + 5:.1f} A{R:.1f} {R:.1f} 0 0 1 {cx + R:.1f} {y + 5:.1f} Z' + if plein: + A(f'') + for con, lettre in (("connector2", "C"), ("connector1", "B"), ("connector0", "E")): + x, yy = xy(*hs[con], pan) + A(f'') + txt(x, yy - 6, lettre, 9, fill="#fff", weight="bold") + txt(cx, y - R + 2, nom, 10, fill="#222", weight="bold") + else: + A(f'') + +def composants(pan, plein): + for k, inst in res_anode.items(): + contour_res(inst, pan, plein, inst["props"]["resistance"].replace("Ω", "")) + for r, inst in res_base.items(): + contour_res(inst, pan, plein, "1k") + for inst in leds.values(): + contour_led(inst, pan, plein) + for r, inst in trans.items(): + contour_npn(inst, pan, plein, f"T{r}") + +# fils isolés (côté composants) +for a, b, coul in board_wires: + if coul == NU: + continue + (xa, ya), (xb, yb) = xy(*a, 0), xy(*b, 0) + A(f'') +composants(0, True) + +# arrivées des fils de l'Arduino +for pin, (c, r), coul in ard_jobs: + x, y = xy(c, r, 0) + A(f'') + if r <= 2: # entrées en haut de la plaque : étiquette verticale au-dessus + A(f'') + txt(x + 4, MARGE_Y - 30, pin, 11, "start", coul if coul != "#ffe24d" else "#b39b00", + "bold", rot=-90) + else: + txt(x + 9, y + 4, pin, 11, "start", "#222", "bold") + +# ---------------------------------------------------------------- côté soudures +composants(1, False) +for a, b, coul in board_wires: + (xa, ya), (xb, yb) = xy(*a, 1), xy(*b, 1) + if coul == NU: + A(f'') +soudures = set() +for inst in list(leds.values()) + list(res_anode.values()) + list(res_base.values()) + list(trans.values()): + soudures.update(inst["holes"].values()) +for a, b, coul in board_wires: + soudures.update((a, b)) +for _, h, _ in ard_jobs: + soudures.add(h) +for c, r in soudures: + x, y = xy(c, r, 1) + A(f'') +for a, b, coul in board_wires: # bouts des fils isolés qui traversent + if coul != NU: + for h in (a, b): + x, y = xy(*h, 1) + A(f'') +for pin, h, coul in ard_jobs: + x, y = xy(*h, 1) + A(f'') + +# ---------------------------------------------------------------- légende +ly = MARGE_Y + ROWS * PX + 56 +txt(MARGE_X, ly, "Légende", 15, "start", "#222", "bold") +items = [ + ('', + "LED RGB vue de dessus : pattes R, K (cathode, la plus longue), G, B"), + ('', + "Résistance (valeur en ohms : 220, 150, 1k = 1000)"), + ('', + "Transistor BC337, face plate vers le bas : pattes C, B, E de gauche à droite"), + ('', + "Fil isolé, côté composants (cathodes des lignes)"), + ('', + "Fil nu (ou patte repliée), côté soudures"), + ('', + "Arrivée d'un fil vers l'Arduino (D2 à D13, GND)"), +] +for i, (forme, texte) in enumerate(items): + col, lig = i % 2, i // 2 + bx = MARGE_X + col * (LARG / 2) + by = ly + 30 + lig * 30 + A(forme.format(x=bx + 11, y=by - 4, xm=bx, ym=by - 8, xl=bx, xr=bx + 22, y4=by + 2)) + txt(bx + 34, by, texte, 12, "start", "#333") +txt(MARGE_X, ly + 140, "Pas entre LED : 6 trous = 15,24 mm. Les numéros de colonnes et de " + "lignes sont ceux du côté composants ; côté soudures, gauche et droite sont inversées.", + 12, "start", "#666") +txt(MARGE_X, ly + 160, "Correspondance avec le code : colonne c de la matrice = LED Lr Cc ; " + "ligne r = transistor Tr (broche D11 + r).", 12, "start", "#666") +A("") +with open(OUT, "w") as f: + f.write("\n".join(out)) diff --git a/matrice_3x3/outils/schema_electrique.py b/matrice_3x3/outils/schema_electrique.py new file mode 100644 index 0000000..1e85dee --- /dev/null +++ b/matrice_3x3/outils/schema_electrique.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python3 +"""Dessine schemas/schema_electrique.svg : schéma de la maquette 3 x 3, +avec les symboles normalisés (résistances rectangulaires) et des fils colorés. +Usage : python3 outils/schema_electrique.py schemas/schema_electrique.svg""" +import sys + +ROUGE, VERT, BLEU = "#d62828", "#2a9d3a", "#2f6fc4" +TEINTE = {ROUGE: "#fbd5d5", VERT: "#d3f0d8", BLEU: "#d6e3f7"} +LIGNE, BASE, MASSE, TEXTE = "#3a3a3a", "#c98a00", "#222222", "#333333" + +LX = [170, 370, 570] # x de la diode rouge de chaque colonne +LY = [330, 480, 630] # y des anodes de chaque ligne +DX = 48 # écart entre les diodes R, G, B d'une LED +COULEURS = [("R", ROUGE, "220 Ω"), ("G", VERT, "150 Ω"), ("B", BLEU, "150 Ω")] +XT = 790 # centre des transistors +XD = {0: 900, 1: 930, 2: 960} # descentes D11, D12, D13 +X_GND = 1000 +Y_PIN = 170 # bas du bloc Arduino + +out = [] +A = out.append + +def ligne(x1, y1, x2, y2, c, w=2.2): + A(f'') + +def chemin(d, c, w=2.2): + A(f'') + +def point(x, y, c): + A(f'') + +def texte(x, y, s, taille=13, ancre="middle", c=TEXTE, gras=False, rot=None, italique=False): + tr = f' transform="rotate({rot} {x} {y})"' if rot else "" + A(f'{s}') + +def resistance_v(x, y1, y2, c, valeur): + """Résistance verticale entre y1 et y2 (rectangle de 50 px au milieu).""" + m = (y1 + y2) / 2 + ligne(x, y1, x, m - 25, c) + ligne(x, m + 25, x, y2, c) + A(f'') + texte(x + 19, m, valeur, 12, c=c, rot=-90) + +def resistance_h(x1, x2, y, c, valeur): + m = (x1 + x2) / 2 + ligne(x1, y, m - 25, y, c) + ligne(m + 25, y, x2, y, c) + A(f'') + texte(m, y - 13, valeur, 12, c=c) + +def diode(x, ya, c): + """Diode verticale : anode en haut (ya), cathode en bas (ya + 32).""" + ligne(x, ya, x, ya + 8, c) + A(f'') + ligne(x - 9, ya + 22, x + 9, ya + 22, c, 2.4) + ligne(x, ya + 22, x, ya + 32, LIGNE) + for dy in (0, 7): # lumière émise + x1, y1, x2, y2 = x + 9, ya + 15 + dy, x + 18, ya + 6 + dy + ligne(x1, y1, x2, y2, c, 1.4) + A(f'') + +def masse(x, y): + ligne(x - 11, y, x + 11, y, MASSE, 2.4) + ligne(x - 7, y + 5, x + 7, y + 5, MASSE, 2.4) + ligne(x - 3, y + 10, x + 3, y + 10, MASSE, 2.4) + +def transistor(r, yt): + """NPN dessiné en miroir : base à droite, collecteur en haut à gauche, + émetteur en bas à gauche (vers la masse).""" + xb = XT + 8 + A(f'') + ligne(xb, yt - 13, xb, yt + 13, TEXTE, 3) + ligne(xb, yt - 6, XT - 12, yt - 20, LIGNE) # collecteur + ligne(xb, yt + 6, XT - 12, yt + 20, MASSE) # émetteur + A(f'') + ligne(xb, yt, XT + 40, yt, BASE) # base + texte(XT - 44, yt - 2, f"T{r}", 14, gras=True) + texte(XT - 44, yt + 14, "BC337", 11, c="#666") + texte(XT + 22, yt + 18, "B", 10, c="#888") + texte(XT - 22, yt - 26, "C", 10, c="#888") + texte(XT - 26, yt + 28, "E", 10, c="#888") + +W, H = 1080, 970 +A(f'') +A('Schéma électrique de la maquette 3 x 3') +A(f'') +texte(40, 38, "Maquette 3 × 3 : schéma électrique", 22, "start", "#111", True) +texte(40, 60, "9 LED RGB à cathode commune, allumées ligne par ligne (multiplexage)", + 14, "start", "#666") + +# ---------------------------------------------------------------- Arduino +A(f'') +texte(565, 112, "Arduino Uno", 20, gras=True, c="#00666d") +texte(565, 132, "alimenté par le câble USB", 12, c="#4d8f94") +broches = {} +for c in range(3): + for k, (_, coul, _) in enumerate(COULEURS): + broches[f"D{2 + 3 * c + k}"] = (LX[c] + DX * k - 22, coul) +for r in range(3): + broches[f"D{11 + r}"] = (XD[r], BASE) +broches["GND"] = (X_GND, MASSE) +for nom, (x, coul) in broches.items(): + ligne(x, Y_PIN, x, Y_PIN + 20, coul) + texte(x, Y_PIN - 10, nom, 12, gras=True, c=coul if coul != BASE else "#9a6a00") +masse(X_GND, Y_PIN + 20) + +# ---------------------------------------------------------------- colonnes (anodes) +for c in range(3): + for k, (_, coul, val) in enumerate(COULEURS): + xb = LX[c] + DX * k - 22 + resistance_v(xb, Y_PIN + 20, 265, coul, val) + y_fin = LY[2] - 12 + ligne(xb, 265, xb, y_fin, coul) + for r in range(3): + xd, ys = LX[c] + DX * k, LY[r] - 12 + chemin(f"M{xb} {ys} H{xd} V{LY[r]}", coul) + if r < 2: + point(xb, ys, coul) + texte(LX[c] + DX, LY[2] + 100, f"colonne {c}", 14, c="#555", gras=True) + +# ---------------------------------------------------------------- LED +for r in range(3): + for c in range(3): + x0 = LX[c] + A(f'') + texte(x0 + DX + 2, LY[r] - 22, f"L{r}C{c}", 11, c="#888", gras=True) +for r in range(3): + for c in range(3): + for k, (_, coul, _) in enumerate(COULEURS): + diode(LX[c] + DX * k, LY[r], coul) + +# ---------------------------------------------------------------- lignes (cathodes) +bus_x = sorted(LX[c] + DX * k - 22 for c in range(3) for k in range(3)) +for r in range(3): + y = LY[r] + 60 + for c in range(3): + for k in range(3): + xd = LX[c] + DX * k + ligne(xd, LY[r] + 32, xd, y, LIGNE) + if not (c == 0 and k == 0): + point(xd, y, LIGNE) + # ligne horizontale avec des « ponts » là où elle croise une colonne + d = f"M{LX[0]} {y}" + if r < 2: + for xb in bus_x: + if LX[0] < xb < XT - 12: + d += f" H{xb - 6} a6 6 0 0 1 12 0" + d += f" H{XT - 12} V{y + 40 - 20}" + chemin(d, LIGNE) + texte(XT - 56, y - 8, f"ligne {r}", 13, "end", c="#555", gras=True) + yt = y + 40 + transistor(r, yt) + ligne(XT - 12, yt + 20, XT - 12, yt + 36, MASSE) + masse(XT - 12, yt + 36) + resistance_h(XT + 40, XD[r], yt, BASE, "1 kΩ") + ligne(XD[r], yt, XD[r], Y_PIN + 20, BASE) + +# ---------------------------------------------------------------- légende +ly = 840 +A(f'') +texte(60, ly, "Légende", 15, "start", "#111", True) +items = [ + (lambda x, y: (ligne(x, y, x + 30, y, ROUGE), ligne(x + 34, y, x + 64, y, VERT), + ligne(x + 68, y, x + 98, y, BLEU)), + "colonnes : anodes R, G, B (D2 à D10)"), + (lambda x, y: ligne(x, y, x + 98, y, LIGNE), "lignes : cathodes communes"), + (lambda x, y: ligne(x, y, x + 98, y, BASE), "commande des lignes (D11 à D13)"), + (lambda x, y: masse(x + 49, y - 6), "masse (GND) : symboles tous reliés"), + (lambda x, y: point(x + 49, y, LIGNE), "point = fils connectés"), + (lambda x, y: chemin(f"M{x} {y} H{x + 43} a6 6 0 0 1 12 0 H{x + 98}", LIGNE), + "pont = croisement sans contact"), +] +for i, (dessin, legende) in enumerate(items): + col, lig = i % 3, i // 3 + x, y = (60, 400, 700)[col], ly + 30 + lig * 32 + dessin(x, y) + texte(x + 110, y + 4, legende, 12, "start") +texte(60, ly + 98, "Un pixel s'allume quand sa ligne est active (D11, D12 ou D13 à HIGH : le " + "transistor relie les cathodes à la masse) et que sa couleur l'est aussi (D2 à D10).", + 12, "start", "#555", italique=True) +A("") + +with open(sys.argv[1], "w") as f: + f.write("\n".join(out)) diff --git a/matrice_3x3/schemas/implantation_plaque.svg b/matrice_3x3/schemas/implantation_plaque.svg new file mode 100644 index 0000000..9ba611c --- /dev/null +++ b/matrice_3x3/schemas/implantation_plaque.svg @@ -0,0 +1,2626 @@ + +Implantation de la matrice 3x3 sur plaque à pastilles + +Côté composants (vu de dessus) +on pose les composants et les fils isolés + +1 +5 +10 +15 +20 +25 +30 +1 +5 +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Côté soudures (plaque retournée) +on soude et on relie avec du fil nu. Attention : image en miroir ! + +1 +5 +10 +15 +20 +25 +30 +1 +5 +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +220 + + +150 + + +150 + + +220 + + +150 + + +150 + + +220 + + +150 + + +150 + + +1k + + +1k + + +1k + + +R + +K + +G + +B + + +R + +K + +G + +B + + +R + +K + +G + +B + + +R + +K + +G + +B + + +R + +K + +G + +B + + +R + +K + +G + +B + + +R + +K + +G + +B + + +R + +K + +G + +B + + +R + +K + +G + +B + + +C + +B + +E +T0 + + +C + +B + +E +T1 + + +C + +B + +E +T2 + + +D2 + + +D3 + + +D4 + + +D5 + + +D6 + + +D7 + + +D8 + + +D9 + + +D10 + +D11 + +D12 + +D13 + +GND + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Légende + +LED RGB vue de dessus : pattes R, K (cathode, la plus longue), G, B + +Résistance (valeur en ohms : 220, 150, 1k = 1000) + +Transistor BC337, face plate vers le bas : pattes C, B, E de gauche à droite + +Fil isolé, côté composants (cathodes des lignes) + +Fil nu (ou patte repliée), côté soudures + +Arrivée d'un fil vers l'Arduino (D2 à D13, GND) +Pas entre LED : 6 trous = 15,24 mm. Les numéros de colonnes et de lignes sont ceux du côté composants ; côté soudures, gauche et droite sont inversées. +Correspondance avec le code : colonne c de la matrice = LED Lr Cc ; ligne r = transistor Tr (broche D11 + r). + \ No newline at end of file diff --git a/matrice_3x3/schemas/plaque_fritzing.png b/matrice_3x3/schemas/plaque_fritzing.png new file mode 100644 index 0000000..9e346af Binary files /dev/null and b/matrice_3x3/schemas/plaque_fritzing.png differ diff --git a/matrice_3x3/schemas/schema_electrique.svg b/matrice_3x3/schemas/schema_electrique.svg new file mode 100644 index 0000000..09be2ec --- /dev/null +++ b/matrice_3x3/schemas/schema_electrique.svg @@ -0,0 +1,501 @@ + +Schéma électrique de la maquette 3 x 3 + +Maquette 3 × 3 : schéma électrique +9 LED RGB à cathode commune, allumées ligne par ligne (multiplexage) + +Arduino Uno +alimenté par le câble USB + +D2 + +D3 + +D4 + +D5 + +D6 + +D7 + +D8 + +D9 + +D10 + +D11 + +D12 + +D13 + +GND + + + + + + +220 Ω + + + + + + + + + +150 Ω + + + + + + + + + +150 Ω + + + + + + +colonne 0 + + + +220 Ω + + + + + + + + + +150 Ω + + + + + + + + + +150 Ω + + + + + + +colonne 1 + + + +220 Ω + + + + + + + + + +150 Ω + + + + + + + + + +150 Ω + + + + + + +colonne 2 + +L0C0 + +L0C1 + +L0C2 + +L1C0 + +L1C1 + +L1C2 + +L2C0 + +L2C1 + +L2C2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +ligne 0 + + + + + + +T0 +BC337 +B +C +E + + + + + + + +1 kΩ + + + + + + + + + + + + + + + + + + + +ligne 1 + + + + + + +T1 +BC337 +B +C +E + + + + + + + +1 kΩ + + + + + + + + + + + + + + + + + + + +ligne 2 + + + + + + +T2 +BC337 +B +C +E + + + + + + + +1 kΩ + + +Légende + + + +colonnes : anodes R, G, B (D2 à D10) + +lignes : cathodes communes + +commande des lignes (D11 à D13) + + + +masse (GND) : symboles tous reliés + +point = fils connectés + +pont = croisement sans contact +Un pixel s'allume quand sa ligne est active (D11, D12 ou D13 à HIGH : le transistor relie les cathodes à la masse) et que sa couleur l'est aussi (D2 à D10). + \ No newline at end of file diff --git a/matrice_3x3/schemas/schema_fritzing.png b/matrice_3x3/schemas/schema_fritzing.png new file mode 100644 index 0000000..7464cdc Binary files /dev/null and b/matrice_3x3/schemas/schema_fritzing.png differ