¿Es posible hacer una huella de un conjunto de almohadillas en un diseño de PCB gEDA?

1

He pasado horas montando cuidadosamente un par de almohadillas para formar la huella de un codificador rotativo Bourns PEC11S . Ahora estoy atrapado en el procedimiento lo sé, es decir, « Cortar en búfer » , « Dividir elementos de búfer en piezas ». Por supuesto, el procedimiento asume que estamos comenzando con una huella existente, pero en mi caso opté por los paneles de diseño en una PCB ... con la esperanza de poder salir adelante, la razón detrás es hacer huellas SMD desde cero es una pesadilla completa en gEDA ya que las almohadillas se vuelven a convertir en pistas, que están redondeadas. Eso es muy, muy inquietante. Al menos a mí.

Así que creé archivos de huella SMD para almohadillas rectangulares de dimensiones dadas y las coloqué como si fueran componentes en una PCB. He intentado combinar todos los trucos que conozco, pero en vano: o bien obtengo un búfer vacío o no tengo ninguna huella. Creo que la razón es que todas mis almohadillas aún se ven como componentes separados y me gustaría fusionar todas las almohadillas en un solo componente, manteniendo todas las distancias relativas.

Aquí está el diseño del pad:

¿Hay alguna manera de convertir rápidamente el ensamblaje de las almohadillas en una huella?

    
pregunta

1 respuesta

0

Sí, es posible. No nativamente sin embargo, necesitaba un guión. Como he adivinado, debe agregar las coordenadas de las almohadillas con las coordenadas del elemento; las coordenadas de la almohadilla son relativas a su elemento que contiene.

Aquí hay un ejemplo:

Element["" "3.7x4-pad" "" "3.7 x 4 pad" 17.1500mm 11.8500mm 10.00mil 2.6540mm 0 100 ""]
(
    Pad[0.0000 -0.1500mm 0.0000 0.1500mm 3.7000mm 30.00mil 3.7000mm "" "1" "square"]

    )

Element["" "3.7x4-pad" "" "3.7 x 4 pad" 4.3500mm 11.8500mm 3.3540mm 1.5540mm 0 100 ""]
(
    Pad[0.0000 -0.1500mm 0.0000 0.1500mm 3.7000mm 30.00mil 3.7000mm "" "1" "square"]

    )

Para hacer una huella de esta PCB, debe desplazar todos los pads con el origen de su elemento contenedor. No me molesté en hacerlo a mano, especialmente si es probable que esto se convierta en una tarea recurrente. Python scripting al rescate:

#!/usr/bin/env python

"""
This script parses a PCB file and outputs a comoponent footprint. This is
to be used whenever designers need to build SMD footprints for parts not
yet distributed with gEDA or registered at gedafootprints.org.

Note: the source PCB is assumed valid!
"""
import re
from sys import stdin, stdout, version
from contextlib import contextmanager

# Included in Python 3.4: ignore certain exceptions while doing something
if version < '3.4':

    @contextmanager
    def ignored(*exceptions):
        try:
            yield
        except exceptions:
            pass

# Add numbers with their respective units, forcing millimeter
def add( *items ):
    return "{:.4f}mm".format( sum(items) )


class ComponentFactory(object):

    # Enclose captured matches with parentheses
    descriptors = {
        'PAD': '\s*',
        'SEP': '\s+',
        'DIM': '(-?[0-9.]+\w+)',
        'TXT': '"([^"]*)"',
        'DIG': '(\d)',
        'INT': '(-?\d+)',
        'UINT': '(\d+)',
    }

    # List of fields for a given component
    components = {
        'PCB[]': [
            ('name', 'TXT'),
            ('width', 'DIM'),
            ('height', 'DIM'),
        ],
        'Element[]': [
            ('flags', 'TXT'),
            ('desc', 'TXT'),
            ('name', 'TXT'),
            ('value', 'TXT'),
            ('x' , 'DIM'),
            ('y' , 'DIM'),
            ('tx' , 'DIM'),
            ('ty' , 'DIM'),
            ('dir', 'DIG'),
            ('scale', 'UINT'),
            ('tflags', 'TXT'),
        ],
        'Pad[]': [
            ('x1', 'DIM'),
            ('y1', 'DIM'),
            ('x2', 'DIM'),
            ('y2', 'DIM'),
            ('thick', 'DIM'),
            ('clear', 'DIM'),
            ('mask', 'DIM'),
            ('name', 'TXT'),
            ('number', 'TXT'),
            ('flags', 'TXT'),
        ],
        'Layer()': [
            ('id', 'UINT'),
            ('name', 'TXT'),
        ],
        'Line[]': [
            ('x1', 'DIM'),
            ('y1', 'DIM'),
            ('x2', 'DIM'),
            ('y2', 'DIM'),
            ('thick', 'DIM'),
            ('clear', 'DIM'),
            ('flags', 'TXT'),
        ],
        'Arc[]': [
            ('x' , 'DIM'),
            ('y' , 'DIM'),
            ('width', 'DIM'),
            ('height', 'DIM'),
            ('thick', 'DIM'),
            ('clear', 'DIM'),
            ('angle', 'INT'),
            ('delta', 'INT'),
            ('flags', 'TXT')
        ]
    }

    def __init__(self, name):
        # Figure out the pattern given the name
        pattern = [ n for n in self.components.keys() if n[:-2] == name ][0]

        # Store name and fields for later use
        self.name = name
        self.fields, types = zip(*self.components[pattern])

        # Then compile a regular expression for each matching line
        self.re = re.compile(
            '{}\{}\s*{}\s*\{}'.format(name, pattern[-2], self.descriptors['SEP'].join(self.descriptors[t] for t in types), pattern[-1])
        )

    class Component(dict):
        def __getitem__(self, name):
            """ Returns a signed float with fields that have units. Returns
            the raw (text) value otherwise """
            value = super(self.__class__, self).__getitem__(name)
            m = re.match('(-?[0-9.]+)(mil|mm)?', value)
            if not m:
                return value

            if m.group(2) == 'mil':
                return float(m.group(1)) * 0.0254

            return float(m.group(1))

        def __getattr__(self, name):
            """ Return a dict item as if it were an attribute """
            return self.__getitem__(name)

        def __setattr__(self, name, value):
            """ Careful while setting attributes though: check attribute doesn't
            exist before passing it to the dict! """
            try:
                self.__getitem__(name)
                self.__setitem__(name, value)
            except AttributeError:
                super(self.__class__, self).__setattr__(name, value)

    def parse(self, text):
        try:
            return ComponentFactory.Component( zip( self.fields, re.search(self.re, text).groups() ) )

        except AttributeError:
            raise ValueError


# Component parsers
PCB = ComponentFactory('PCB')
Element = ComponentFactory('Element')
Pad = ComponentFactory('Pad')
Layer = ComponentFactory('Layer')
Line = ComponentFactory('Line')
Arc = ComponentFactory('Arc')

# State variables
description = ''
number = 1
origin = ()
offset = ()
silk = False

while 1:
    try:
        text = stdin.readline()
    except KeyboardInterrupt:
        break

    if not text:
        break

    # Strip carriage return
    text = text.strip("\r\n");

    # Use PCB name as element name
    with ignored(ValueError):
        description = PCB.parse(text).name

    # Try parsing an element
    with ignored(ValueError):
        e = Element.parse(text)
        offset = ( e.x, e.y )

        # Mark origin at the middle of the first encountered element
        if not origin:
            origin = -offset[0], -offset[1]

            # Write element header (not sure about usefulness of x and y though)
            stdout.write( '\nElement["" "{name}" "" "" {x}mm {y}mm 10mil 10mil 0 100 ""]\n(\n'.format(name=description, x=offset[0], y=offset[1]) )


    # Try parsing a pad, use last offset to compute pad position
    # Note: first pad by design is always centered around (0, 0)
    with ignored(ValueError):
        p = Pad.parse(text)

        # Offset the pad to the origin
        p.x1, p.y1, p.x2, p.y2 = add(origin[0], p.x1, offset[0]), add(origin[1], p.y1, offset[1]), add(origin[0], p.x2, offset[0]), add(origin[1], p.y2, offset[1])

        # Set pad number
        p.number = number
        number = number + 1
        stdout.write( '\tPad[{x1} {y1} {x2} {y2} {thick} {clear} {mask} "{name}" "{number}" "{flags}"]\n'.format(**p) )

    # Match silk layers to output offset ElementLines
    with ignored(ValueError):
        lay = Layer.parse(text)
        silk = lay.name in ['silk']

    if silk:
        # Transform each line and arc inside the silk layer
        with ignored(ValueError):
            ln = Line.parse(text)
            ln.x1, ln.y1, ln.x2, ln.y2 = add(origin[0], ln.x1), add(origin[1], ln.y1), add(origin[0], ln.x2), add(origin[1], ln.y2)
            stdout.write( '\tElementLine[{x1} {y1} {x2} {y2} {thick}]\n'.format(**ln) )

        with ignored(ValueError):
            arc = Arc.parse(text)
            arc.x, arc.y = add(origin[0], arc.x), add(origin[1], arc.y)
            stdout.write( '\tElementArc[{x} {y} {width} {height} {angle} {delta} {thick}]\n'.format(**arc) )

if description:
    stdout.write( '\n)\n' )

Guarda el script como build-element.py , por ejemplo. Para crear una huella de un PCB existente (y válido):

python build-element.py < your-circuit.pcb > element.fp

Usted tendrá que abrir la huella generada en pcb , opcionalmente cambiar la asignación de pin y guardar el archivo. Personalmente, siempre edito la huella en un editor de texto para eliminar los números del pin no utilizado, es más rápido.

Tenga en cuenta que la secuencia de comandos utiliza el nombre de PCB como la descripción de la huella. Así es como se ve la huella final:

Laimagenmuestralospinesrealesenunaversióneditadadelintentoquepubliquéenmipregunta.Aquíestáelarchivodelahuella,despuésdequeeditéelresultadoparaeliminarmanualmentelospinesnoutilizados:

Element["" "Bourns PEC11S SMD Incremental Encoder" "" "" 17.15mm 11.85mm 10mil 10mil 0 100 ""]
(
    Pad[0.0000mm -0.1500mm 0.0000mm 0.1500mm 3.7000mm 30.00mil 3.7000mm "" "" "square"]
    Pad[-12.8000mm -0.1500mm -12.8000mm 0.1500mm 3.7000mm 30.00mil 3.7000mm "" "" "square"]
    Pad[-11.4000mm -8.2500mm -11.4000mm -7.5500mm 2.5000mm 30.00mil 2.5000mm "" "" "square"]
    Pad[-1.4000mm -8.2500mm -1.4000mm -7.5500mm 2.5000mm 30.00mil 2.5000mm "" "" "square"]
    Pad[-3.9000mm -7.5500mm -3.9000mm -6.4500mm 1.3000mm 30.00mil 1.3000mm "E" "5" "square"]
    Pad[-8.9000mm -7.5500mm -8.9000mm -6.4500mm 1.3000mm 30.00mil 1.3000mm "D" "4" "square"]
    Pad[-8.9000mm 6.7500mm -8.9000mm 7.8500mm 1.3000mm 30.00mil 1.3000mm "A" "1" "square"]
    Pad[-6.4000mm 6.7500mm -6.4000mm 7.8500mm 1.3000mm 30.00mil 1.3000mm "C" "2" "square"]
    Pad[-3.9000mm 6.7500mm -3.9000mm 7.8500mm 1.3000mm 30.00mil 1.3000mm "B" "3" "square"]
    Pad[-1.4000mm 7.5500mm -1.4000mm 8.2500mm 2.5000mm 30.00mil 2.5000mm "" "" "square"]
    Pad[-11.4000mm 7.5500mm -11.4000mm 8.2500mm 2.5000mm 30.00mil 2.5000mm "" "" "square"]
    ElementLine[-7.8500mm -8.1500mm -4.9500mm -8.1500mm 6.00mil]
    ElementLine[-7.8500mm 8.4500mm -7.4500mm 8.4500mm 6.00mil]
    ElementLine[-5.3500mm 8.4500mm -5.0500mm 8.4500mm 6.00mil]
    ElementLine[-12.6500mm 2.3500mm -12.6500mm 5.9500mm 6.00mil]
    ElementLine[-0.1500mm 5.9500mm -0.1500mm 2.3500mm 6.00mil]
    ElementLine[-12.6500mm -2.3500mm -12.6500mm -5.9500mm 6.00mil]
    ElementLine[-0.1500mm -5.9500mm -0.1500mm -2.3500mm 6.00mil]

)
    
respondido por el user59864

Lea otras preguntas en las etiquetas