diff options
Diffstat (limited to 'pcbnew/scripting/plugins')
-rw-r--r-- | pcbnew/scripting/plugins/FPC_(SMD_type)_footprintwizard.py | 156 | ||||
-rw-r--r-- | pcbnew/scripting/plugins/FootprintWizardDrawingAids.py | 523 | ||||
-rw-r--r-- | pcbnew/scripting/plugins/HelpfulFootprintWizardPlugin.py | 330 | ||||
-rw-r--r-- | pcbnew/scripting/plugins/PadArray.py | 275 | ||||
-rw-r--r-- | pcbnew/scripting/plugins/__init__.py | 1 | ||||
-rw-r--r-- | pcbnew/scripting/plugins/bga_wizard.py | 98 | ||||
-rw-r--r-- | pcbnew/scripting/plugins/circular_pad_array_wizard.py | 83 | ||||
-rw-r--r-- | pcbnew/scripting/plugins/qfp_wizard.py | 125 | ||||
-rw-r--r-- | pcbnew/scripting/plugins/sdip_wizard.py | 237 | ||||
-rw-r--r-- | pcbnew/scripting/plugins/touch_slider_wizard.py | 205 | ||||
-rw-r--r-- | pcbnew/scripting/plugins/uss39_barcode.py | 149 | ||||
-rw-r--r-- | pcbnew/scripting/plugins/zip_wizard.py | 201 |
12 files changed, 2383 insertions, 0 deletions
diff --git a/pcbnew/scripting/plugins/FPC_(SMD_type)_footprintwizard.py b/pcbnew/scripting/plugins/FPC_(SMD_type)_footprintwizard.py new file mode 100644 index 0000000..8526b45 --- /dev/null +++ b/pcbnew/scripting/plugins/FPC_(SMD_type)_footprintwizard.py @@ -0,0 +1,156 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +from __future__ import division +import pcbnew + +import HelpfulFootprintWizardPlugin as HFPW + + +class FPC_FootprintWizard(HFPW.HelpfulFootprintWizardPlugin): + + def GetName(self): + return "FPC (SMT connector)" + + def GetDescription(self): + return "FPC (SMT connector) Footprint Wizard" + + def GetValue(self): + pins = self.parameters["Pads"]["*n"] + return "FPC_%d" % pins + + def GenerateParameterList(self): + self.AddParam( "Pads", "n", self.uNatural, 40 ) + self.AddParam( "Pads", "pitch", self.uMM, 0.5 ) + self.AddParam( "Pads", "width", self.uMM, 0.25 ) + self.AddParam( "Pads", "height", self.uMM, 1.6) + self.AddParam( "Shield", "shield_to_pad", self.uMM, 1.6 ) + self.AddParam( "Shield", "from_top", self.uMM, 1.3 ) + self.AddParam( "Shield", "width", self.uMM, 1.5 ) + self.AddParam( "Shield", "height", self.uMM, 2 ) + + + # build a rectangular pad + def smdRectPad(self,module,size,pos,name): + pad = pcbnew.D_PAD(module) + pad.SetSize(size) + pad.SetShape(pcbnew.PAD_SHAPE_RECT) + pad.SetAttribute(pcbnew.PAD_ATTRIB_SMD) + pad.SetLayerSet( pad.SMDMask() ) + pad.SetPos0(pos) + pad.SetPosition(pos) + pad.SetPadName(name) + return pad + + def CheckParameters(self): + p = self.parameters + self.CheckParamInt( "Pads", "*n" ) # not internal units preceded by "*" + + + def BuildThisFootprint(self): + p = self.parameters + pad_count = int(p["Pads"]["*n"]) + pad_width = p["Pads"]["width"] + pad_height = p["Pads"]["height"] + pad_pitch = p["Pads"]["pitch"] + shl_width = p["Shield"]["width"] + shl_height = p["Shield"]["height"] + shl_to_pad = p["Shield"]["shield_to_pad"] + shl_from_top = p["Shield"]["from_top"] + + offsetX = pad_pitch * ( pad_count-1 ) / 2 + size_pad = pcbnew.wxSize( pad_width, pad_height ) + size_shld = pcbnew.wxSize(shl_width, shl_height) + size_text = self.GetTextSize() # IPC nominal + + # Gives a position and size to ref and value texts: + textposy = pad_height/2 + pcbnew.FromMM(1) + self.GetTextThickness() + self.draw.Reference( 0, textposy, size_text ) + + textposy = textposy + size_text + self.GetTextThickness() + self.draw.Value( 0, textposy, size_text ) + + # create a pad array and add it to the module + for n in range ( 0, pad_count ): + xpos = pad_pitch*n - offsetX + pad = self.smdRectPad(self.module,size_pad, pcbnew.wxPoint(xpos,0),str(n+1)) + self.module.Add(pad) + + + # Mechanical shield pads: left pad and right pad + xpos = -shl_to_pad-offsetX + pad_s0_pos = pcbnew.wxPoint(xpos,shl_from_top) + pad_s0 = self.smdRectPad(self.module, size_shld, pad_s0_pos, "0") + xpos = (pad_count-1) * pad_pitch+shl_to_pad - offsetX + pad_s1_pos = pcbnew.wxPoint(xpos,shl_from_top) + pad_s1 = self.smdRectPad(self.module, size_shld, pad_s1_pos, "0") + + self.module.Add(pad_s0) + self.module.Add(pad_s1) + + # add footprint outline + linewidth = self.draw.GetLineTickness() + margin = linewidth + + # upper line + posy = -pad_height/2 - linewidth/2 - margin + xstart = - pad_pitch*0.5-offsetX + xend = pad_pitch * pad_count + xstart; + self.draw.Line( xstart, posy, xend, posy ) + + # lower line + posy = pad_height/2 + linewidth/2 + margin + self.draw.Line(xstart, posy, xend, posy) + + # around left mechanical pad (the outline around right pad is mirrored/y axix) + yend = pad_s0_pos.y + shl_height/2 + margin + self.draw.Line(xstart, posy, xstart, yend) + self.draw.Line(-xstart, posy, -xstart, yend) + + posy = yend + xend = pad_s0_pos.x - (shl_width/2 + linewidth + margin*2) + self.draw.Line(xstart, posy, xend, posy) + + # right pad side + self.draw.Line(-xstart, posy, -xend, yend) + + # vertical segment at left of the pad + xstart = xend + yend = posy - (shl_height + linewidth + margin*2) + self.draw.Line(xstart, posy, xend, yend) + + # right pad side + self.draw.Line(-xstart, posy, -xend, yend) + + # horizontal segment above the pad + xstart = xend + xend = - pad_pitch*0.5-offsetX + posy = yend + self.draw.Line(xstart, posy, xend, yend) + + # right pad side + self.draw.Line(-xstart, posy,-xend, yend) + + # vertical segment above the pad + xstart = xend + yend = -pad_height/2 - linewidth/2 - margin + self.draw.Line(xstart, posy, xend, yend) + + # right pad side + self.draw.Line(-xstart, posy, -xend, yend) + + +FPC_FootprintWizard().register() diff --git a/pcbnew/scripting/plugins/FootprintWizardDrawingAids.py b/pcbnew/scripting/plugins/FootprintWizardDrawingAids.py new file mode 100644 index 0000000..058fe1e --- /dev/null +++ b/pcbnew/scripting/plugins/FootprintWizardDrawingAids.py @@ -0,0 +1,523 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +from __future__ import division + +import pcbnew +import math + + +class FootprintWizardDrawingAids: + """ + Collection of handy functions to simplify drawing shapes from within + footprint wizards + + A "drawing context" is provided which can be used to set and retain + settings such as line tickness and layer + """ + + # directions (in degrees, compass-like) + dirN = 0 + dirNE = 45 + dirE = 90 + dirSE = 135 + dirS = 180 + dirSW = 225 + dirW = 270 + dirNW = 315 + + # flip constants + flipNone = 0 + flipX = 1 # flip X values, i.e. about Y + flipY = 2 # flip Y valuersabout X + flipBoth = 3 + + xfrmIDENTITY = [1, 0, 0, 0, 1, 0] # no transform + + # these values come from our KiCad Library Convention 0.11 + defaultLineThickness = pcbnew.FromMM(0.15) + + def DefaultGraphicLayer(self): + return pcbnew.F_SilkS + + def DefaultTextValueLayer(self): + return pcbnew.F_Fab + + def __init__(self, module): + self.module = module + # drawing context defaults + self.dc = { + 'layer': self.DefaultGraphicLayer(), + 'lineThickness': self.defaultLineThickness, + 'transforms': [], + 'transform': self.xfrmIDENTITY + } + + def PushTransform(self, mat): + """ + Add a transform to the top of the stack and recompute the + overall transform + """ + self.dc['transforms'].append(mat) + self.RecomputeTransforms() + + def PopTransform(self, num=1): + """ + Remove a transform from the top of the stack and recompute the + overall transform + """ + + for i in range(num): + mat = self.dc['transforms'].pop() + self.RecomputeTransforms() + return mat + + def ResetTransform(self): + """ + Reset the transform stack to the identity matrix + """ + self.dc['transforms'] = [] + self.RecomputeTransforms() + + def _ComposeMatricesWithIdentity(self, mats): + """ + Compose a sequence of matrices together by sequential + pre-mutiplciation with the identity matrix + """ + + x = self.xfrmIDENTITY + + for mat in mats: + #precompose with each transform in turn + x = [ + x[0] * mat[0] + x[1] * mat[3], + x[0] * mat[1] + x[1] * mat[4], + x[0] * mat[2] + x[1] * mat[5] + x[2], + x[3] * mat[0] + x[4] * mat[3], + x[3] * mat[1] + x[4] * mat[4], + x[3] * mat[2] + x[4] * mat[5] + x[5]] + + return x + + def RecomputeTransforms(self): + """ + Re-compute the transform stack into a single transform and + store in the DC + """ + self.dc['transform'] = self._ComposeMatricesWithIdentity( + self.dc['transforms']) + + def TransformTranslate(self, x, y, push=True): + """ + Set up and return a transform matrix representing a translartion + optionally pushing onto the stack + + ( 1 0 x ) + ( 0 1 y ) + """ + mat = [1, 0, x, 0, 1, y] + + if push: + self.PushTransform(mat) + return mat + + def TransformFlipOrigin(self, flip, push=True): + """ + Set up and return a transform matrix representing a horizontal, + vertical or both flip about the origin + """ + mat = None + if flip == self.flipX: + mat = [-1, 0, 0, 0, 1, 0] + elif flip == self.flipY: + mat = [1, 0, 0, 0, -1, 0] + elif flip == self.flipBoth: + mat = [-1, 0, 0, 0, -1, 0] + elif flip == self.flipNone: + mat = self.xfrmIDENTITY + else: + raise ValueError + + if push: + self.PushTransform(mat) + return mat + + def TransformFlip(self, x, y, flip=flipNone, push=True): + """ + Set up and return a transform matrix representing a horizontal, + vertical or both flip about a point (x,y) + + This is performed by a translate-to-origin, flip, translate- + back sequence + """ + mats = [self.TransformTranslate(x, y, push=False), + self.TransformFlipOrigin(flip, push=False), + self.TransformTranslate(-x, -y, push=False)] + + #distill into a single matrix + mat = self._ComposeMatricesWithIdentity(mats) + + if push: + self.PushTransform(mat) + return mat + + def TransformRotationOrigin(self, rot, push=True): + """ + Set up and return a transform matrix representing a rotation + about the origin, and optionally push onto the stack + + ( cos(t) -sin(t) 0 ) + ( sin(t) cos(t) 0 ) + """ + rads = rot * math.pi / 180 + mat = [math.cos(rads), -math.sin(rads), 0, + math.sin(rads), math.cos(rads), 0] + + if push: + self.PushTransform(mat) + return mat + + def TransformRotation(self, x, y, rot, push=True): + """ + Set up and return a transform matrix representing a rotation + about the point (x,y), and optionally push onto the stack + + This is performed by a translate-to-origin, rotate, translate- + back sequence + """ + + mats = [self.TransformTranslate(x, y, push=False), + self.TransformRotationOrigin(rot, push=False), + self.TransformTranslate(-x, -y, push=False)] + + #distill into a single matrix + mat = self._ComposeMatricesWithIdentity(mats) + + if push: + self.PushTransform(mat) + return mat + + def TransformScaleOrigin(self, sx, sy=None, push=True): + """ + Set up and return a transform matrix representing a scale about + the origin, and optionally push onto the stack + + ( sx 0 0 ) + ( 0 sy 0 ) + """ + + if sy is None: + sy = sx + + mat = [sx, 0, 0, 0, sy, 0] + + if push: + self.PushTransform(mat) + return mat + + def TransformPoint(self, x, y, mat=None): + """ + Return a point (x, y) transformed by the given matrix, or if + that is not given, the drawing context transform + """ + + if not mat: + mat = self.dc['transform'] + + return pcbnew.wxPoint(x * mat[0] + y * mat[1] + mat[2], + x * mat[3] + y * mat[4] + mat[5]) + + def SetLineTickness(self, lineThickness): + """ + Set the current pen lineThickness used for subsequent drawing + operations + """ + self.dc['lineThickness'] = lineThickness + + def GetLineTickness(self): + """ + Get the current drawing context line tickness + """ + return self.dc['lineThickness'] + + def SetLayer(self, layer): + """ + Set the current drawing layer, used for subsequent drawing + operations + """ + self.dc['layer'] = layer + + def GetLayer(self): + """ + return the current drawing layer, used drawing operations + """ + return self.dc['layer'] + + def Line(self, x1, y1, x2, y2): + """ + Draw a line from (x1, y1) to (x2, y2) + """ + outline = pcbnew.EDGE_MODULE(self.module) + outline.SetWidth(self.GetLineTickness()) + outline.SetLayer(self.GetLayer()) + outline.SetShape(pcbnew.S_SEGMENT) + start = self.TransformPoint(x1, y1) + end = self.TransformPoint(x2, y2) + outline.SetStartEnd(start, end) + self.module.Add(outline) + + def Circle(self, x, y, r, filled=False): + """ + Draw a circle at (x,y) of radius r + If filled is true, the thickness and radius of the line will be set + such that the circle appears filled + """ + circle = pcbnew.EDGE_MODULE(self.module) + start = self.TransformPoint(x, y) + + if filled: + circle.SetWidth(r) + end = self.TransformPoint(x, y + r/2) + else: + circle.SetWidth(self.dc['lineThickness']) + end = self.TransformPoint(x, y + r) + + circle.SetLayer(self.dc['layer']) + circle.SetShape(pcbnew.S_CIRCLE) + circle.SetStartEnd(start, end) + self.module.Add(circle) + + def Arc(self, cx, cy, sx, sy, a): + """ + Draw an arc based on centre, start and angle + + The transform matrix is applied + + Note that this won't work properly if the result is not a + circular arc (eg a horzontal scale) + """ + circle = pcbnew.EDGE_MODULE(self.module) + circle.SetWidth(self.dc['lineThickness']) + + center = self.TransformPoint(cx, cy) + start = self.TransformPoint(sx, sy) + + circle.SetLayer(self.dc['layer']) + circle.SetShape(pcbnew.S_ARC) + + # check if the angle needs to be reverse (a flip scaling) + if cmp(self.dc['transform'][0], 0) != cmp(self.dc['transform'][4], 0): + a = -a + + circle.SetAngle(a) + circle.SetStartEnd(center, start) + self.module.Add(circle) + + # extends from (x1,y1) right + def HLine(self, x, y, l): + """ + Draw a horizontal line from (x,y), rightwards + """ + self.Line(x, y, x + l, y) + + def VLine(self, x, y, l): + """ + Draw a vertical line from (x1,y1), downwards + """ + self.Line(x, y, x, y + l) + + def Polyline(self, pts, mirrorX=None, mirrorY=None): + """ + Draw a polyline, optinally mirroring around the given points + """ + + def _PolyLineInternal(pts): + if len(pts) < 2: + return + + for i in range(0, len(pts) - 1): + self.Line(pts[i][0], pts[i][1], pts[i+1][0], pts[i+1][1]) + + _PolyLineInternal(pts) # original + + if mirrorX is not None: + self.TransformFlip(mirrorX, 0, self.flipX) + _PolyLineInternal(pts) + self.PopTransform() + + if mirrorY is not None: + self.TransformFlipOrigin(0, mirrorY, self.flipY) + _PolyLineInternal(pts) + self.PopTransform() + + if mirrorX is not None and mirrorY is not None: + self.TransformFlip(mirrorX, mirrorY, self.flipBoth) # both + _PolyLineInternal(pts) + self.PopTransform() + + def Reference(self, x, y, size, orientation_degree = 0): + """ + Draw the module's reference as the given point. + + The actual setting of the reference is not done in this drawing + aid - that is up to the wizard + """ + + text_size = pcbnew.wxSize(size, size) + + self.module.Reference().SetPos0(self.TransformPoint(x, y)) + self.module.Reference().SetTextPosition( + self.module.Reference().GetPos0()) + self.module.Reference().SetSize(text_size) + self.module.Reference().SetOrientation(orientation_degree*10) # internal angles are in 0.1 deg + + def Value(self, x, y, size, orientation_degree = 0): + """ + As for references, draw the module's value + """ + text_size = pcbnew.wxSize(size, size) + + self.module.Value().SetPos0(self.TransformPoint(x, y)) + self.module.Value().SetTextPosition(self.module.Value().GetPos0()) + self.module.Value().SetSize(text_size) + self.module.Value().SetLayer(self.DefaultTextValueLayer()) + self.module.Value().SetOrientation(orientation_degree*10) # internal angles are in 0.1 deg + + def Box(self, x, y, w, h): + """ + Draw a rectangular box, centred at (x,y), with given width and + height + """ + + pts = [[x - w/2, y - h/2], # left + [x + w/2, y - h/2], # right + [x + w/2, y + h/2], # bottom + [x - w/2, y + h/2], # top + [x - w/2, y - h/2]] # close + + self.Polyline(pts) + + def NotchedCircle(self, x, y, r, notch_w, notch_h, rotate=0): + """ + Circle radus r centred at (x, y) with a raised or depressed notch + at the top + Notch height is measured from the top of the circle radius + """ + + self.TransformRotation(x, y, rotate) + + # find the angle where the notch vertical meets the circle + angle_intercept = math.asin(notch_w/(2 * r)) + + # and find the co-ords of this point + sx = math.sin(angle_intercept) * r + sy = -math.cos(angle_intercept) * r + + # NOTE: this may be out by a factor of ten one day + arc_angle = (math.pi * 2 - angle_intercept * 2) * (1800/math.pi) + + self.Arc(x,y, sx, sy, arc_angle) + + pts = [[sx, sy], + [sx, -r - notch_h], + [-sx, -r - notch_h], + [-sx, sy]] + + self.Polyline(pts) + self.PopTransform() + + def NotchedBox(self, x, y, w, h, notchW, notchH, rotate=0): + """ + Draw a box with a notch in the top edge + """ + + self.TransformRotation(x, y, rotate) + + # limit to half the overall width + notchW = min(x + w/2, notchW) + + # draw notch + self.Polyline([ # three sides of box + (x - w/2, y - h/2), + (x - w/2, y + h/2), + (x + w/2, y + h/2), + (x + w/2, y - h/2), + # the notch + (notchW/2, y - h/2), + (notchW/2, y - h/2 + notchH), + (-notchW/2, y - h/2 + notchH), + (-notchW/2, y - h/2), + (x - w/2, y - h/2) + ]) + + self.PopTransform() + + def BoxWithDiagonalAtCorner(self, x, y, w, h, + setback=pcbnew.FromMM(1.27), flip=flipNone): + """ + Draw a box with a diagonal at the top left corner + """ + + self.TransformFlip(x, y, flip, push=True) + + pts = [[x - w/2 + setback, y - h/2], + [x - w/2, y - h/2 + setback], + [x - w/2, y + h/2], + [x + w/2, y + h/2], + [x + w/2, y - h/2], + [x - w/2 + setback, y - h/2]] + + self.Polyline(pts) + + self.PopTransform() + + def BoxWithOpenCorner(self, x, y, w, h, + setback=pcbnew.FromMM(1.27), flip=flipNone): + """ + Draw a box with an opening at the top left corner + """ + + self.TransformTranslate(x, y) + self.TransformFlipOrigin(flip) + + pts = [[- w/2, - h/2 + setback], + [- w/2, + h/2], + [+ w/2, + h/2], + [+ w/2, - h/2], + [- w/2 + setback, - h/2]] + + self.Polyline(pts) + + self.PopTransform(num=2) + + def MarkerArrow(self, x, y, direction=dirN, width=pcbnew.FromMM(1)): + """ + Draw a marker arrow facing in the given direction, with the + point at (x,y) + + Direction of 0 is north + """ + + self.TransformTranslate(x, y) + self.TransformRotationOrigin(direction) + + pts = [[0, 0], + [width / 2, width / 2], + [-width / 2, width / 2], + [0, 0]] + + self.Polyline(pts) + self.PopTransform(2) diff --git a/pcbnew/scripting/plugins/HelpfulFootprintWizardPlugin.py b/pcbnew/scripting/plugins/HelpfulFootprintWizardPlugin.py new file mode 100644 index 0000000..90d2498 --- /dev/null +++ b/pcbnew/scripting/plugins/HelpfulFootprintWizardPlugin.py @@ -0,0 +1,330 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +import pcbnew +import math +import FootprintWizardDrawingAids + + +class FootprintWizardParameterManager: + """ + Functions for helpfully managing parameters to a KiCAD Footprint + Wizard. + + Abstracts away from whatever structure is used by pcbnew's footprint + wizard class + """ + + def __init__(self): + self.parameters = {} + self.GenerateParameterList() + + def GenerateParameterList(self): + """ + Construct parameters here, or leave out to have no parameters + """ + pass + + def CheckParameters(self): + """ + Implement this to make checks on parameter values, filling + parameter_errors (or using the checker routines) + + Subclasses can implment their own and override the parent + defaults and add new ones + """ + pass + + uMM = 1 + uMils = 2 + uNatural = 3 + uBool = 4 + uString = 5 + + def AddParam(self, section, param, unit, default, hint=''): + """ + Add a parameter with some properties. + + TODO: Hints are not supported, as there is as yet nowhere to + put them in the KiCAD interface + """ + error = "" + val = None + if unit == self.uMM: + val = pcbnew.FromMM(default) + elif unit == self.uMils: + val = pcbnew.FromMils(default) + elif unit == self.uNatural: + val = default + elif unit == self.uString: + val = str(default) + elif unit == self.uBool: + val = "True" if default else "False" # ugly stringing + else: + error = "Warning: Unknown unit type: %s" % unit + return error + + if unit in [self.uNatural, self.uBool, self.uString]: + param = "*%s" % param # star prefix for natural + + if section not in self.parameters: + if not hasattr(self, 'page_order'): + self.page_order = [] + self.page_order.append(section) + self.parameters[section] = {} + if not hasattr(self, 'parameter_order'): + self.parameter_order = {} + self.parameter_order[section] = [] + + self.parameters[section][param] = val + self.parameter_order[section].append(param) + + return error + + + def _PrintParameterTable(self): + """ + Pretty-print the parameters we have + """ + message = "" + + for name, section in self.parameters.iteritems(): + message += " %s:\n" % name + + for key, value in section.iteritems(): + unit = "" + if ((type(value) is int or type(value) is float) + and not "*" in key): + unit = "mm" + + if "*" in key: + key = key[1:] + else: + value = pcbnew.ToMM(value) + + message += " %s: %s%s\n" % (key, value, unit) + + return message + + + def _ParametersHaveErrors(self): + """ + Return true if we discovered errors during parameter processing + """ + + for name, section in self.parameter_errors.iteritems(): + for k, v in section.iteritems(): + if v: + return True + + return False + + def _PrintParameterErrors(self): + """ + Pretty-print parameters with errors + """ + errors = "" + + for name, section in self.parameter_errors.iteritems(): + printed_section = False + + for key, value in section.iteritems(): + if value: + if not printed_section: + errors += " %s:" % name + + errors += " %s: %s (have %s)\n" % ( + key, value, self.parameters[name][key]) + + return errors + + def ProcessParameters(self): + """ + Make sure the parameters we have meet whatever expectations the + footprint wizard has of them + """ + + self.ClearErrors() + self.CheckParameters() + + if self._ParametersHaveErrors(): + return False + + return True + + ################################################################# + # PARAMETER CHECKERS + ################################################################# + + def CheckParamInt(self, section, param, min_value=1, + max_value=None, is_multiple_of=1): + """ + Make sure a parameter can be made into an int, and enforce + limits if required + """ + + try: + self.parameters[section][param] = ( + int(self.parameters[section][param])) + except ValueError: + self.parameter_errors[section][param] = ( + "Must be a valid integer") + return + + if min_value is not None and ( + self.parameters[section][param] < min_value): + self.parameter_errors[section][param] = ( + "Must be greater than or equal to %d" % (min_value)) + return + + if max_value is not None and ( + self.parameters[section][param] > max_value): + self.parameter_errors[section][param] = ( + "Must be less than or equal to %d" % (max_value)) + return + + if is_multiple_of > 1 and ( + self.parameters[section][param] % is_multiple_of) > 0: + self.parameter_errors[section][param] = ( + "Must be a multiple of %d" % is_multiple_of) + return + + return + + def CheckParamBool(self, section, param): + """ + Make sure a parameter looks like a boolean, convert to native + boolean type if so + """ + if str(self.parameters[section][param]).lower() in [ + "true", "t", "y", "yes", "on", "1", "1.0"]: + self.parameters[section][param] = True + return + elif str(self.parameters[section][param]).lower() in [ + "false", "f", "n", "no", "off", "0", "0.0"]: + self.parameters[section][param] = False + return + + self.parameter_errors[section][param] = "Must be boolean (true/false)" + return + + +class HelpfulFootprintWizardPlugin(pcbnew.FootprintWizardPlugin, + FootprintWizardParameterManager): + """ + A class to simplify many aspects of footprint creation, leaving only + the foot-print specific routines to the wizards themselves + + Generally, you need to implement: + GetReference() + GetValue() + GenerateParameterList() + CheckParameters() + BuildThisFootprint() + GetName() + GetDescription() + """ + def __init__(self): + pcbnew.FootprintWizardPlugin.__init__(self) + FootprintWizardParameterManager.__init__(self) + + self.name = self.GetName() + self.decription = self.GetDescription() + self.image = self.GetImage() + + def GetValue(self): + raise NotImplementedError + + # this value come from our KiCad Library Convention 0.11 + def GetReferencePrefix(self): + return "REF" + + def GetImage(self): + return "" + + def GetTextSize(self): + """ + IPC nominal + """ + return pcbnew.FromMM(1.2) + + def GetTextThickness(self): + """ + Thicker than IPC guidelines (10% of text height = 0.12mm) + as 5 wires/mm is a common silk screen limitation + """ + return pcbnew.FromMM(0.15) + + def SetModule3DModel(self): + """ + Set a 3D model for the module + + Default is to do nothing, you need to implement this if you have + a model to set + + FIXME: This doesn't seem to be enabled yet? + """ + pass + + def BuildThisFootprint(self): + """ + Draw the footprint. + + This is specific to each footprint class, you need to implment + this to draw what you want + """ + raise NotImplementedError + + def BuildFootprint( self ): + """ + Actually make the footprint. We defer all but the setup to + the implementing class + """ + + self.buildmessages = "" + + self.module = pcbnew.MODULE(None) # create a new module + # do it first, so if we return early, we don't segfault KiCad + + if not self.ProcessParameters(): + self.buildmessages = "Cannot build footprint: Parameters have errors:\n" + self.buildmessages += self._PrintParameterErrors() + return + + self.buildmessages = ("Building new %s footprint with the following parameters:\n" + % self.name) + + self.buildmessages += self._PrintParameterTable() + + self.draw = FootprintWizardDrawingAids.FootprintWizardDrawingAids( + self.module) + + self.module.SetValue(self.GetValue()) + self.module.SetReference("%s**" % self.GetReferencePrefix()) + + fpid = pcbnew.FPID(self.module.GetValue()) # the name in library + self.module.SetFPID(fpid) + + self.SetModule3DModel() # add a 3d module if specified + + thick = self.GetTextThickness() + + self.module.Reference().SetThickness(thick) + self.module.Value().SetThickness(thick) + + self.BuildThisFootprint() # implementer's build function + + return diff --git a/pcbnew/scripting/plugins/PadArray.py b/pcbnew/scripting/plugins/PadArray.py new file mode 100644 index 0000000..5908d38 --- /dev/null +++ b/pcbnew/scripting/plugins/PadArray.py @@ -0,0 +1,275 @@ +# PadArray.py +# +# Copyright 2014 john <john@johndev> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# +# + +from __future__ import division + +import math +import pcbnew + +class PadMaker: + """ + Useful construction functions for common types of pads + """ + + def __init__(self, module): + self.module = module + + def THPad(self, Vsize, Hsize, drill, shape=pcbnew.PAD_SHAPE_OVAL, rot_degree = 0): + pad = pcbnew.D_PAD(self.module) + pad.SetSize(pcbnew.wxSize(Hsize, Vsize)) + pad.SetShape(shape) + pad.SetAttribute(pcbnew.PAD_ATTRIB_STANDARD) + pad.SetLayerSet(pad.StandardMask()) + pad.SetDrillSize(pcbnew.wxSize(drill, drill)) + pad.SetOrientation(rot_degree*10) # rotation is in 0.1 degrees + + return pad + + def THRoundPad(self, size, drill): + pad = self.THPad(size, size, drill, shape=pcbnew.PAD_SHAPE_CIRCLE) + return pad + + def NPTHRoundPad(self, drill): + pad = pcbnew.D_PAD(self.module) + pad.SetSize(pcbnew.wxSize(drill, drill)) + pad.SetShape(pcbnew.PAD_SHAPE_CIRCLE) + pad.SetAttribute(pcbnew.PAD_ATTRIB_HOLE_NOT_PLATED) + pad.SetLayerSet(pad.UnplatedHoleMask()) + pad.SetDrillSize(pcbnew.wxSize(drill, drill)) + return pad + + def SMDPad(self, Vsize, Hsize, shape=pcbnew.PAD_SHAPE_RECT, rot_degree=0): + pad = pcbnew.D_PAD(self.module) + pad.SetSize(pcbnew.wxSize(Hsize, Vsize)) + pad.SetShape(shape) + pad.SetAttribute(pcbnew.PAD_ATTRIB_SMD) + pad.SetLayerSet(pad.SMDMask()) + pad.SetOrientation(rot_degree*10) # rotation is in 0.1 degrees + + return pad + + def SMTRoundPad(self, size): + pad = self.SMDPad(size, size, shape=pcbnew.PAD_SHAPE_CIRCLE) + return pad + + +class PadArray: + + def __init__(self): + self.firstPadNum = 1 + self.pinNames = None + self.firstPad = None + + def SetPinNames(self, pinNames): + """ + Set a name for all the pins + """ + self.pinNames = pinNames + + def SetFirstPadType(self, firstPad): + self.firstPad = firstPad + + def SetFirstPadInArray(self, fpNum): + self.firstPadNum = fpNum + + def AddPad(self, pad): + self.pad.GetParent().Add(pad) + + def GetPad(self, is_first_pad, pos): + if (self.firstPad and is_first_pad): + pad = self.firstPad + else: + pad = self.pad + + # create a new pad with same characteristics + pad = pad.Duplicate() + pad.SetPos0(pos) + pad.SetPosition(pos) + + return pad + + def GetName(self, *args, **kwargs): + + if self.pinNames is None: + return self.NamingFunction(*args, **kwargs) + + return self.pinNames + + def NamingFunction(self, *args, **kwargs): + """ + Implement this as needed for each array type + """ + raise NotImplementedError; + + +class PadGridArray(PadArray): + + def __init__(self, pad, nx, ny, px, py, centre=pcbnew.wxPoint(0, 0)): + PadArray.__init__(self) + # this pad is more of a "context", we will use it as a source of + # pad data, but not actually add it + self.pad = pad + self.nx = int(nx) + self.ny = int(ny) + self.px = px + self.py = py + self.centre = centre + + # handy utility function 1 - A, 2 - B, 26 - AA, etc + # aIndex = 0 for 0 - A + # alphabet = set of allowable chars if not A-Z, + # eg ABCDEFGHJKLMNPRTUVWY for BGA + def AlphaNameFromNumber(self, n, aIndex=1, + alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ"): + + div, mod = divmod(n - aIndex, len(alphabet)) + alpha = alphabet[mod] + + if div > 0: + return self.AlphaNameFromNumber(div, aIndex, alphabet) + alpha + + return alpha + + # right to left, top to bottom + def NamingFunction(self, x, y): + return self.firstPadNum + (self.nx * y + x) + + #relocate the pad and add it as many times as we need + def AddPadsToModule(self, dc): + + pin1posX = self.centre.x - self.px * (self.nx - 1) / 2 + pin1posY = self.centre.y - self.py * (self.ny - 1) / 2 + + for x in range(0, self.nx): + posX = pin1posX + (x * self.px) + + for y in range(self.ny): + posY = pin1posY + (self.py * y) + pos = dc.TransformPoint(posX, posY) + pad = self.GetPad(x == 0 and y == 0, pos) + pad.SetPadName(self.GetName(x,y)) + self.AddPad(pad) + + +class PadZGridArray(PadArray): + + def __init__(self, pad, pad_count, line_count, line_pitch, + pad_pitch, centre=pcbnew.wxPoint(0, 0)): + PadArray.__init__(self) + # this pad is more of a "context", we will use it as a source of + # pad data, but not actually add it + self.pad = pad + self.pad_count = int(pad_count) + self.line_count = int(line_count) + self.line_pitch = line_pitch + self.pad_pitch = pad_pitch + self.centre = centre + + + # right to left, top to bottom + def NamingFunction(self, pad_pos): + return self.firstPadNum + pad_pos + + #relocate the pad and add it as many times as we need + def AddPadsToModule(self, dc): + + pin1posX = self.centre.x - self.pad_pitch * (self.pad_count - 1) / 2 + pin1posY = self.centre.y + self.line_pitch * (self.line_count - 1) / 2 + line = 0 + + for padnum in range(0, self.pad_count): + posX = pin1posX + (padnum * self.pad_pitch) + posY = pin1posY - (self.line_pitch * line) + + pos = dc.TransformPoint(posX, posY) + pad = self.GetPad(padnum == 0, pos) + pad.SetPadName(self.GetName(padnum)) + self.AddPad(pad) + + line += 1 + + if line >= self.line_count: + line = 0 + +class PadLineArray(PadGridArray): + + def __init__(self, pad, n, pitch, isVertical, + centre=pcbnew.wxPoint(0, 0)): + + if isVertical: + PadGridArray.__init__(self, pad, 1, n, 0, pitch, centre) + else: + PadGridArray.__init__(self, pad, n, 1, pitch, 0, centre) + +class PadCircleArray(PadArray): + + def __init__(self, pad, n, r, angle_offset=0, centre=pcbnew.wxPoint(0, 0), + clockwise=True): + PadArray.__init__(self) + # this pad is more of a "context", we will use it as a source of + # pad data, but not actually add it + self.pad = pad + self.n = int(n) + self.r = r + self.angle_offset = angle_offset + self.centre = centre + self.clockwise = clockwise + + # around the circle, CW or CCW according to the flag + def NamingFunction(self, n): + return str(self.firstPadNum + n) + + #relocate the pad and add it as many times as we need + def AddPadsToModule(self, dc): + for pin in range(0, self.n): + angle = self.angle_offset + (360 / self.n) * pin + + if not self.clockwise: + angle = -angle + + pos_x = math.sin(angle * math.pi / 180) * self.r + pos_y = -math.cos(angle * math.pi / 180) * self.r + pos = dc.TransformPoint(pos_x, pos_y) + pad = self.GetPad(pin == 0, pos) + pad.SetPadName(self.GetName(pin)) + self.AddPad(pad) + +class PadCustomArray(PadArray): + """ + Layout pads according to a custom array of [x,y] data + """ + + def __init__(self, pad, array): + PadArray.__init__(self) + self.pad = pad + self.array = array + + def NamingFunction(self, n): + return str(self.firstPadNum + n) + + #relocate the pad and add it as many times as we need + def AddPadsToModule(self, dc): + + for i in range(len(self.array)): + pos = dc.TransformPoint(self.array[i][0], self.array[i][1]) + pad = self.GetPad(i == 0, pos) + pad.SetPadName(self.GetName(i)) + self.AddPad(pad) diff --git a/pcbnew/scripting/plugins/__init__.py b/pcbnew/scripting/plugins/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/pcbnew/scripting/plugins/__init__.py @@ -0,0 +1 @@ + diff --git a/pcbnew/scripting/plugins/bga_wizard.py b/pcbnew/scripting/plugins/bga_wizard.py new file mode 100644 index 0000000..ed3500f --- /dev/null +++ b/pcbnew/scripting/plugins/bga_wizard.py @@ -0,0 +1,98 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +from __future__ import division +import pcbnew + +import HelpfulFootprintWizardPlugin as HFPW +import PadArray as PA + + +class BGAPadGridArray(PA.PadGridArray): + + def NamingFunction(self, n_x, n_y): + return "%s%d" % ( + self.AlphaNameFromNumber(n_y + 1, alphabet="ABCDEFGHJKLMNPRTUVWY"), + n_x + 1) + + +class BGAWizard(HFPW.HelpfulFootprintWizardPlugin): + + def GetName(self): + return "BGA" + + def GetDescription(self): + return "Ball Grid Array Footprint Wizard" + + def GenerateParameterList(self): + self.AddParam("Pads", "pad pitch", self.uMM, 1) + self.AddParam("Pads", "pad size", self.uMM, 0.5) + self.AddParam("Pads", "row count", self.uNatural, 5) + self.AddParam("Pads", "column count", self.uNatural, 5) + self.AddParam("Pads", "outline x margin", self.uMM, 1) + self.AddParam("Pads", "outline y margin", self.uMM, 1) + + def CheckParameters(self): + self.CheckParamInt("Pads", "*row count") + self.CheckParamInt("Pads", "*column count") + + def GetValue(self): + pins = (self.parameters["Pads"]["*row count"] + * self.parameters["Pads"]["*column count"]) + + return "BGA_%d" % pins + + def BuildThisFootprint(self): + + pads = self.parameters["Pads"] + + rows = pads["*row count"] + cols = pads["*column count"] + pad_size = pads["pad size"] + pad_size = pcbnew.wxSize(pad_size, pad_size) + pad_pitch = pads["pad pitch"] + + # add in the pads + pad = PA.PadMaker(self.module).SMTRoundPad(pads["pad size"]) + + pin1_pos = pcbnew.wxPoint(-((cols - 1) * pad_pitch) / 2, + -((rows - 1) * pad_pitch) / 2) + + array = BGAPadGridArray(pad, cols, rows, pad_pitch, pad_pitch) + array.AddPadsToModule(self.draw) + + #box + ssx = -pin1_pos.x + pads["outline x margin"] + ssy = -pin1_pos.y + pads["outline y margin"] + + self.draw.BoxWithDiagonalAtCorner(0, 0, ssx*2, ssy*2, + pads["outline x margin"]) + + # Courtyard + cmargin = self.draw.GetLineTickness() + self.draw.SetLayer(pcbnew.F_CrtYd) + sizex = (ssx + cmargin) * 2 + sizey = (ssy + cmargin) * 2 + self.draw.Box(0, 0, sizex, sizey) + + #reference and value + text_size = self.GetTextSize() # IPC nominal + ypos = ssy + text_size + self.draw.Value(0, ypos, text_size) + self.draw.Reference(0, -ypos, text_size) + + +BGAWizard().register() diff --git a/pcbnew/scripting/plugins/circular_pad_array_wizard.py b/pcbnew/scripting/plugins/circular_pad_array_wizard.py new file mode 100644 index 0000000..0b2a280 --- /dev/null +++ b/pcbnew/scripting/plugins/circular_pad_array_wizard.py @@ -0,0 +1,83 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# +from __future__ import division + +import math + +import pcbnew +import HelpfulFootprintWizardPlugin as HFPW +import PadArray as PA + + +class circular_pad_array_wizard(HFPW.HelpfulFootprintWizardPlugin): + + def GetName(self): + return "Circular Pad Array" + + def GetDescription(self): + return "Circular array of pads" + + def GenerateParameterList(self): + + self.AddParam("Pads", "n", self.uNatural, 6) + self.AddParam("Pads", "pad width", self.uMM, 1.5) + self.AddParam("Pads", "drill", self.uMM, 1) + self.AddParam("Pads", "circle diameter", self.uMM, 5) + self.AddParam("Pads", "first pad angle", self.uNatural, 0) + self.AddParam("Pads", "number clockwise", self.uBool, True) + self.AddParam("Pads", "first pad number", self.uNatural, 1) + + def CheckParameters(self): + + self.CheckParamInt("Pads", "*n") + self.CheckParamInt("Pads", "*first pad number") + self.CheckParamBool("Pads", "*number clockwise") + + def GetValue(self): + pins = self.parameters["Pads"]["*n"] + return "CPA_%d" % pins + + def BuildThisFootprint(self): + + prm = self.parameters['Pads'] + + pad_size = prm['pad width'] + + pad = PA.PadMaker(self.module).THPad( + prm['pad width'], prm['pad width'], prm['drill']) + + array = PA.PadCircleArray( + pad, prm['*n'], prm['circle diameter'] / 2, + angle_offset=prm["*first pad angle"], + centre=pcbnew.wxPoint(0, 0), + clockwise=prm["*number clockwise"]) + + array.SetFirstPadInArray(prm["*first pad number"]) + + array.AddPadsToModule(self.draw) + + body_radius = (prm['circle diameter'] + prm['pad width'])/2 + self.draw.GetLineTickness() + self.draw.Circle(0, 0, body_radius) + + text_size = self.GetTextSize() # IPC nominal + thickness = self.GetTextThickness() + textposy = body_radius + self.draw.GetLineTickness()/2 + self.GetTextSize()/2 + thickness + self.draw.Value( 0, textposy, text_size ) + self.draw.Reference( 0, -textposy, text_size ) + + + +circular_pad_array_wizard().register() diff --git a/pcbnew/scripting/plugins/qfp_wizard.py b/pcbnew/scripting/plugins/qfp_wizard.py new file mode 100644 index 0000000..3cd1eda --- /dev/null +++ b/pcbnew/scripting/plugins/qfp_wizard.py @@ -0,0 +1,125 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +from __future__ import division +import pcbnew + +import HelpfulFootprintWizardPlugin +import PadArray as PA + + +class QFPWizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin): + + def GetName(self): + return "QFP" + + def GetDescription(self): + return "Quad Flat Package footprint wizard" + + def GenerateParameterList(self): + self.AddParam("Pads", "n", self.uNatural, 100) + self.AddParam("Pads", "pad pitch", self.uMM, 0.5) + self.AddParam("Pads", "pad width", self.uMM, 0.25) + self.AddParam("Pads", "pad length", self.uMM, 1.5) + self.AddParam("Pads", "vertical pitch", self.uMM, 15) + self.AddParam("Pads", "horizontal pitch", self.uMM, 15) + self.AddParam("Pads", "oval", self.uBool, True) + + self.AddParam("Package", "package width", self.uMM, 14) + self.AddParam("Package", "package height", self.uMM, 14) + self.AddParam("Package", "courtyard margin", self.uMM, 1) + + def CheckParameters(self): + self.CheckParamInt("Pads", "*n", is_multiple_of=4) + self.CheckParamBool("Pads", "*oval") + + def GetValue(self): + return "QFP_%d" % self.parameters["Pads"]["*n"] + + def BuildThisFootprint(self): + pads = self.parameters["Pads"] + + pad_pitch = pads["pad pitch"] + pad_length = self.parameters["Pads"]["pad length"] + pad_width = self.parameters["Pads"]["pad width"] + + v_pitch = pads["vertical pitch"] + h_pitch = pads["horizontal pitch"] + + pads_per_row = pads["*n"] // 4 + + row_len = (pads_per_row - 1) * pad_pitch + + pad_shape = pcbnew.PAD_SHAPE_OVAL if pads["*oval"] else pcbnew.PAD_SHAPE_RECT + + h_pad = PA.PadMaker(self.module).SMDPad( pad_length, pad_width, + shape=pad_shape, rot_degree=90.0) + v_pad = PA.PadMaker(self.module).SMDPad( pad_length, pad_width, shape=pad_shape) + + #left row + pin1Pos = pcbnew.wxPoint(-h_pitch / 2, 0) + array = PA.PadLineArray(h_pad, pads_per_row, pad_pitch, True, pin1Pos) + array.SetFirstPadInArray(1) + array.AddPadsToModule(self.draw) + + #bottom row + pin1Pos = pcbnew.wxPoint(0, v_pitch / 2) + array = PA.PadLineArray(v_pad, pads_per_row, pad_pitch, False, pin1Pos) + array.SetFirstPadInArray(pads_per_row + 1) + array.AddPadsToModule(self.draw) + + #right row + pin1Pos = pcbnew.wxPoint(h_pitch / 2, 0) + array = PA.PadLineArray(h_pad, pads_per_row, -pad_pitch, True, + pin1Pos) + array.SetFirstPadInArray(2*pads_per_row + 1) + array.AddPadsToModule(self.draw) + + #top row + pin1Pos = pcbnew.wxPoint(0, -v_pitch / 2) + array = PA.PadLineArray(v_pad, pads_per_row, -pad_pitch, False, + pin1Pos) + array.SetFirstPadInArray(3*pads_per_row + 1) + array.AddPadsToModule(self.draw) + + lim_x = self.parameters["Package"]["package width"] / 2 + lim_y = self.parameters["Package"]["package height"] / 2 + inner = (row_len / 2) + pad_pitch + + #top left - diagonal + self.draw.Line(-lim_x, -inner, -inner, -lim_y) + # top right + self.draw.Polyline([(inner, -lim_y), (lim_x, -lim_y), (lim_x, -inner)]) + # bottom left + self.draw.Polyline([(-inner, lim_y), (-lim_x, lim_y), (-lim_x, inner)]) + # bottom right + self.draw.Polyline([(inner, lim_y), (lim_x, lim_y), (lim_x, inner)]) + + # Courtyard + cmargin = self.parameters["Package"]["courtyard margin"] + self.draw.SetLayer(pcbnew.F_CrtYd) + sizex = (lim_x + cmargin) * 2 + pad_length + sizey = (lim_y + cmargin) * 2 + pad_length + self.draw.Box(0, 0, sizex, sizey) + + #reference and value + text_size = self.GetTextSize() # IPC nominal + text_offset = v_pitch / 2 + text_size + pad_length / 2 + + self.draw.Value(0, text_offset, text_size) + self.draw.Reference(0, -text_offset, text_size) + +QFPWizard().register() diff --git a/pcbnew/scripting/plugins/sdip_wizard.py b/pcbnew/scripting/plugins/sdip_wizard.py new file mode 100644 index 0000000..31f53cd --- /dev/null +++ b/pcbnew/scripting/plugins/sdip_wizard.py @@ -0,0 +1,237 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +from __future__ import division +import pcbnew + +import HelpfulFootprintWizardPlugin as HFPW +import PadArray as PA + + +class RowedGridArray(PA.PadGridArray): + + def NamingFunction(self, x, y): + pad_cnt = self.nx*self.ny + + if self.ny == 1: + return x+1 + + if (y % 2) == 0: # upper row, count down + return pad_cnt-x + else: # lower row, count up + return x+1 + + +class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin): + + pad_count_key = 'pas count' + row_count_key = 'row count' + row_spacing_key = 'row spacing' + pad_length_key = 'pad length' + pad_width_key = 'pad width' + pad_pitch_key = 'pad pitch' + + silkscreen_inside_key = 'silk screen inside' + outline_x_margin_key = 'outline x margin' + outline_y_margin_key = 'outline y margin' + + def GenerateParameterList(self): + # defaults for a DIP package + self.AddParam("Pads", self.pad_count_key, self.uNatural, 24) + self.AddParam("Pads", self.row_count_key, self.uNatural, 2) + + self.AddParam("Body", self.silkscreen_inside_key, self.uBool, False) + self.AddParam("Body", self.outline_x_margin_key, self.uMM, 0.5) + self.AddParam("Body", self.outline_y_margin_key, self.uMM, 0.5) + + def CheckParameters(self): + self.CheckParamInt("Pads", '*' + self.row_count_key, min_value=1, max_value=2) + self.CheckParamInt( + "Pads", '*' + self.pad_count_key, + is_multiple_of=self.parameters["Pads"]['*' + self.row_count_key]) + + # can do this internally to parameter manager? + self.CheckParamBool("Body", '*' + self.silkscreen_inside_key) + + def BuildThisFootprint(self): + pads = self.parameters["Pads"] + body = self.parameters["Body"] + num_pads = pads['*' + self.pad_count_key] + pad_length = pads[self.pad_length_key] + pad_width = pads[self.pad_width_key] + row_pitch = pads[self.row_spacing_key] + pad_pitch = pads[self.pad_pitch_key] + num_rows = pads['*' + self.row_count_key] + + pads_per_row = num_pads // num_rows + + # add in the pads + pad = self.GetPad() + + array = RowedGridArray(pad, pads_per_row, num_rows, pad_pitch, row_pitch) + array.AddPadsToModule(self.draw) + + # draw the Silk Screen + Hsize = pad_pitch * (num_pads / num_rows - 1) + Vsize = row_pitch * (num_rows - 1) + pin1_posY = -Vsize / 2 + pin1_posX = -Hsize / 2 + + pad_length = pads[self.pad_length_key] + pad_width = pads[self.pad_width_key] + + ssx_offset = -pad_width / 2 - body[self.outline_x_margin_key] + ssy_offset = -pad_length / 2 - body[self.outline_y_margin_key] + + if body['*' + self.silkscreen_inside_key]: + ssy_offset *= -1 + + ssx = -pin1_posX - ssx_offset + ssy = -pin1_posY - ssy_offset + + self.DrawBox(ssx, ssy) + + #reference and value + text_size = self.GetTextSize() # IPC nominal + + if num_rows == 1: + text_py = ssy + text_size + self.draw.Value(0, -text_py, text_size) + self.draw.Reference(0, text_py, text_size) + else: + text_px = ssx + text_size + # self.draw.Value(text_px, 0, text_size, orientation_degree=90) + self.draw.Value(0, 0, text_size) + self.draw.Reference(-text_px, 0, text_size, orientation_degree=90) + + +class SDIPWizard(RowedFootprint): + + def GetName(self): + return "S-DIP" + + def GetDescription(self): + return "Single/Dual Inline Package Footprint Wizard" + + def GenerateParameterList(self): + RowedFootprint.GenerateParameterList(self) + + self.AddParam("Pads", self.pad_pitch_key, self.uMM, 2.54) + self.AddParam("Pads", self.pad_width_key, self.uMM, 1.2) + self.AddParam("Pads", self.pad_length_key, self.uMM, 2) + self.AddParam("Pads", self.row_spacing_key, self.uMM, 7.52) + self.AddParam("Pads", "drill size", self.uMM, 0.8) + + def GetValue(self): + pads = self.parameters["Pads"] + rows = pads['*' + self.row_count_key] + pad_count = pads['*' + self.pad_count_key] + row_dist_mil = pcbnew.Iu2Mils(int(self.parameters["Pads"][self.row_spacing_key])) #int(self.parameters["Pads"][self.row_spacing_key] / 2.54 * 100) + pad_shape = "" + + if pads[self.pad_width_key] != pads[self.pad_length_key]: + pad_shape = '_ELL' + + if rows == 1: + name = "SIP" + return "%s-%d" % (name, pad_count) + + name = "DIP" + return "%s-%d_%d%s" % (name, pad_count, row_dist_mil, pad_shape) + + def GetPad(self): + pad_length = self.parameters["Pads"][self.pad_length_key] + pad_width = self.parameters["Pads"][self.pad_width_key] + drill = self.parameters["Pads"]["drill size"] + shape = pcbnew.PAD_SHAPE_CIRCLE + + if pad_length != pad_width: + shape = pcbnew.PAD_SHAPE_OVAL + + return PA.PadMaker(self.module).THPad( + pad_length, pad_width, drill, shape=shape) + + def DrawBox(self, ssx, ssy): + + if self.parameters["Pads"]['*' + self.row_count_key] == 2: + + # ---------- + # |8 7 6 5 | + # > | + # |1 2 3 4 | + # ---------- + + # draw the notch + notchWidth = ssy/1.5 + notchHeight = self.draw.GetLineTickness()*3 + + # NotchedBox draws the notch on top. Rotate the box 90 degrees + # to have it on the left + self.draw.NotchedBox(0, 0, ssy*2, ssx*2, notchWidth, notchHeight, -90) + else: + # ----------------- + # |1|2 3 4 5 6 7 8| + # ----------------- + self.draw.Box(0, 0, ssx*2, ssy*2) + + #line between pin1 and pin2 + pad_pitch = self.parameters["Pads"][self.pad_pitch_key] + pad_cnt = self.parameters["Pads"]['*' + self.pad_count_key] + line_x = ( pad_cnt/2 - 1) * pad_pitch + self.draw.VLine(-line_x, -ssy, ssy * 2) + + return ssx, ssy + +SDIPWizard().register() + + +class SOICWizard(RowedFootprint): + + def GetName(self): + return "SOIC" + + def GetDescription(self): + return "SOIC, MSOP, SSOP, TSSOP, etc, footprint wizard" + + def GetValue(self): + pad_count = self.parameters["Pads"]['*' + self.pad_count_key] + return "%s-%d" % ("SOIC", pad_count) + + def GenerateParameterList(self): + RowedFootprint.GenerateParameterList(self) + + #and override some of them + self.AddParam("Pads", self.pad_pitch_key, self.uMM, 1.27) + self.AddParam("Pads", self.pad_width_key, self.uMM, 0.6) + self.AddParam("Pads", self.pad_length_key, self.uMM, 2.2) + self.AddParam("Pads", self.row_spacing_key, self.uMM, 5.2) + + def GetPad(self): + pad_length = self.parameters["Pads"][self.pad_length_key] + pad_width = self.parameters["Pads"][self.pad_width_key] + return PA.PadMaker(self.module).SMDPad( + pad_length, pad_width, shape=pcbnew.PAD_SHAPE_RECT) + + def DrawBox(self, ssx, ssy): + + # ---------- + # |8 7 6 5 | + # |1 2 3 4 | + # \--------- + + self.draw.BoxWithDiagonalAtCorner(0, 0, ssx*2, ssy*2, pcbnew.FromMM(1)) + +SOICWizard().register() diff --git a/pcbnew/scripting/plugins/touch_slider_wizard.py b/pcbnew/scripting/plugins/touch_slider_wizard.py new file mode 100644 index 0000000..ee2ae53 --- /dev/null +++ b/pcbnew/scripting/plugins/touch_slider_wizard.py @@ -0,0 +1,205 @@ +#!/usr/bin/python + +# +# This program source code file is part of KiCad, a free EDA CAD application. +# +# Copyright (C) 2012-2014 KiCad Developers, see change_log.txt for contributors. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, you may find one here: +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# or you may search the http://www.gnu.org website for the version 2 license, +# or you may write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# + +from pcbnew import * +import HelpfulFootprintWizardPlugin as HFPW + + +class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin): + + def GetName(self): + """ + Return footprint name. + This is specific to each footprint class, you need to implement this + """ + return 'Touch Slider' + + def GetDescription(self): + """ + Return footprint description. + This is specific to each footprint class, you need to implement this + """ + return 'Capacitive Touch Slider wizard' + + def GetValue(self): + steps = int(self.parameters["Pads"]["*steps"]) + return "TS"+str(steps) + + def GenerateParameterList(self): + self.AddParam("Pads", "steps", self.uNatural, 4) + self.AddParam("Pads", "bands", self.uNatural, 2) + self.AddParam("Pads", "width", self.uMM, 10) + self.AddParam("Pads", "length", self.uMM, 50) + self.AddParam("Pads", "clearance", self.uMM, 1) + + # build a rectangular pad + def smdRectPad(self,module,size,pos,name): + pad = D_PAD(module) + pad.SetSize(size) + pad.SetShape(PAD_SHAPE_RECT) + pad.SetAttribute(PAD_ATTRIB_SMD) + pad.SetLayerSet(pad.ConnSMDMask()) + pad.SetPos0(pos) + pad.SetPosition(pos) + pad.SetPadName(name) + return pad + + + def smdTrianglePad(self,module,size,pos,name,up_down=1,left_right=0): + pad = D_PAD(module) + pad.SetSize(wxSize(size[0],size[1])) + pad.SetShape(PAD_SHAPE_TRAPEZOID) + pad.SetAttribute(PAD_ATTRIB_SMD) + pad.SetLayerSet(pad.ConnSMDMask()) + pad.SetPos0(pos) + pad.SetPosition(pos) + pad.SetPadName(name) + pad.SetDelta(wxSize(left_right*size[1],up_down*size[0])) + return pad + + + # This method checks the parameters provided to wizard and set errors + def CheckParameters(self): + prms = self.parameters["Pads"] + steps = prms["*steps"] + bands = prms["*bands"] + + if steps < 1: + self.parameter_errors["Pads"]["*steps"]="steps must be positive" + if bands < 1: + self.parameter_errors["Pads"]["*bands"]="bands must be positive" + + touch_width = prms["width"] + touch_length = prms["length"] + touch_clearance = prms["clearance"] + + # The start pad is made of a rectangular pad plus a couple of + # triangular pads facing tips on the middle/right of the first + # rectangular pad + def AddStartPad(self,position,touch_width,step_length,clearance,name): + module = self.module + step_length = step_length - clearance + size_pad = wxSize(step_length/2.0+(step_length/3),touch_width) + pad = self.smdRectPad(module,size_pad,position-wxPoint(step_length/6,0),name) + module.Add(pad) + + size_pad = wxSize(step_length/2.0,touch_width) + + tp = self.smdTrianglePad(module,wxSize(size_pad[0],size_pad[1]/2), + position+wxPoint(size_pad[0]/2,size_pad[1]/4), + name) + module.Add(tp) + tp = self.smdTrianglePad(module,wxSize(size_pad[0],size_pad[1]/2), + position+wxPoint(size_pad[0]/2,-size_pad[1]/4), + name + ,-1) + module.Add(tp) + + # compound a "start pad" shape plus a triangle on the left, pointing to + # the previous touch-pad + def AddMiddlePad(self,position,touch_width,step_length,clearance,name): + module = self.module + step_length = step_length - clearance + size_pad = wxSize(step_length/2.0,touch_width) + + size_pad = wxSize(step_length/2.0,touch_width) + pad = self.smdRectPad(module,size_pad,position,name) + module.Add(pad) + + tp = self.smdTrianglePad(module,wxSize(size_pad[0],size_pad[1]/2), + position+wxPoint(size_pad[0]/2,size_pad[1]/4), + name) + module.Add(tp) + tp = self.smdTrianglePad(module,wxSize(size_pad[0],size_pad[1]/2), + position+wxPoint(size_pad[0]/2,-size_pad[1]/4), + name + ,-1) + module.Add(tp) + + tp = self.smdTrianglePad(module,wxSize(size_pad[0],size_pad[1]/2), + position+wxPoint(-size_pad[0],0), + name, + 0, + -1) + module.Add(tp) + + + def AddFinalPad(self,position,touch_width,step_length,clearance,name): + module = self.module + step_length = step_length - clearance + size_pad = wxSize(step_length/2.0,touch_width) + + pad = self.smdRectPad(module, + wxSize(size_pad[0]+(step_length/3),size_pad[1]), + position+wxPoint(step_length/6,0), + name) + module.Add(pad) + + tp = self.smdTrianglePad(module,wxSize(size_pad[0],size_pad[1]/2), + position+wxPoint(-size_pad[0],0), + name, + 0, + -1) + module.Add(tp) + + def AddStrip(self,pos,steps,touch_width,step_length,touch_clearance): + self.AddStartPad(pos,touch_width,step_length,touch_clearance,"1") + + for n in range(2,steps): + pos = pos + wxPoint(step_length,0) + self.AddMiddlePad(pos,touch_width,step_length,touch_clearance,str(n)) + + pos = pos + wxPoint(step_length,0) + self.AddFinalPad(pos,touch_width,step_length,touch_clearance,str(steps)) + + # build the footprint from parameters + # FIX ME: the X and Y position of the footprint can be better. + def BuildThisFootprint(self): + prm = self.parameters["Pads"] + steps = int(prm["*steps"]) + bands = int(prm["*bands"]) + touch_width = prm["width"] + touch_length = prm["length"] + touch_clearance = prm["clearance"] + + step_length = float(touch_length) / float(steps) + + t_size = self.GetTextSize() + w_text = self.draw.GetLineTickness() + ypos = touch_width/(bands*2) + t_size/2 + w_text + self.draw.Value(0, -ypos, t_size) + ypos += t_size + w_text*2 + self.draw.Reference(0, -ypos, t_size) + + # starting pad + pos = wxPointMM(0,0) + band_width = touch_width/bands + + for b in range(bands): + self.AddStrip(pos,steps,band_width,step_length,touch_clearance) + pos += wxPoint(0,band_width) + +TouchSliderWizard().register() + diff --git a/pcbnew/scripting/plugins/uss39_barcode.py b/pcbnew/scripting/plugins/uss39_barcode.py new file mode 100644 index 0000000..846a2bc --- /dev/null +++ b/pcbnew/scripting/plugins/uss39_barcode.py @@ -0,0 +1,149 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. + +from __future__ import division +import pcbnew as B + +import HelpfulFootprintWizardPlugin + +''' +Created on Jan 16, 2015 + +@author: ejohns +Rewritten by LordBlick +''' +ptd = { + '0': '000110100', '1': '100100001', '2': '001100001', '3': '101100000', + '4': '000110001', '5': '100110000', '6': '001110000', '7': '000100101', + '8': '100100100', '9': '001100100', 'A': '100001001', 'B': '001001001', + 'C': '101001000', 'D': '000011001', 'E': '100011000', 'F': '001011000', + 'G': '000001101', 'H': '100001100', 'I': '001001100', 'J': '000011100', + 'K': '100000011', 'L': '001000011', 'M': '101000010', 'N': '000010011', + 'O': '100010010', 'P': '001010010', 'Q': '000000111', 'R': '100000110', + 'S': '001000110', 'T': '000010110', 'U': '110000001', 'V': '011000001', + 'W': '111000000', 'X': '010010001', 'Y': '110010000', 'Z': '011010000', + '-': '010000101', '.': '110000100', ' ': '011000100', '*': '010010100', + '$': '010101000', '/': '010100010', '+': '010001010', '%': '000101010'} + +class Uss39: + def __init__(self, text): + self.Text = self.makePrintable(text) + + __str__ = lambda self: self.Text + makePrintable = lambda self, text: ''.join((c for c in text.upper() if ptd.has_key(c))) + + def getBarCodePattern(self, text = None): + text = text if not(text is None) else self.Text + # Reformated text with start and end characters + return reduce(lambda a1, a2: a1 + [0] + a2, [map(int, ptd[c]) for c in ("*%s*" % self.makePrintable(text))]) + +class Uss39Wizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin): + GetName = lambda self: 'BARCODE USS-39' + GetDescription = lambda self: 'USS-39 Barcode' + GetReferencePrefix = lambda self: 'BARCODE' + GetValue = lambda self: self.module.Value().GetText() + + def GenerateParameterList(self): + # Silkscreen parameters + self.AddParam("Barcode", "Pixel Width", self.uMM, 0.20) + self.AddParam("Barcode", "Height", self.uMM, 3.0) + self.AddParam("Barcode", "Margin", self.uMM, 2.0) + self.AddParam("Barcode", "Contents", self.uString, 'BARCODE') + self.AddParam("Caption", "Enabled", self.uBool, True) + self.AddParam("Caption", "Height", self.uMM, 1.2) + self.AddParam("Caption", "Thickness", self.uMM, 0.12) + + def CheckParameters(self): + # Reset constants + self.CourtyardLineWidth = B.FromMM(0.05) + # Set bar height to the greater of 6.35mm or 0.15*L + # Set quiet width to 10*X + # User-defined parameters + # Create barcode object + self.Barcode = Uss39('=' + str(self.parameters['Barcode']['*Contents'])) + self.X = int(self.parameters['Barcode']['Pixel Width']) + self.module.Value().SetText( str(self.Barcode) ) + self.C = len(str(self.Barcode)) + # Inter-character gap + if self.X < 0.250: + self.I = B.FromMM(3.15) + else: + self.I = (2 * self.X) if (2*self.X) > B.FromMM(1.35) else B.FromMM(1.35) + # Wide to narrow ratio + if self.X >= B.FromMM(0.508): + self.N = B.FromMM(int((2.0+3.0)/2)) + else: + self.N = B.FromMM(int((2.2+3.0)/2)) + self.H = self.parameters['Barcode']['Height'] + self.Q = (10 * self.X) if (10 * self.X) > B.FromMM(6.35) else B.FromMM(6.35) + self.L = self.I * (1 + self.C) + (self.C + 2) * (6 * self.X + 3 * self.N * self.X) + 2 * self.Q + + + def __drawBar__(self, bit, x): + offset = (bit + 1) * self.X + return x + offset + + def __drawSpace__(self, bit, x): + self.draw.SetLayer(B.F_SilkS) + self.draw.SetLineTickness(self.X) + self.draw.Line(x, 0, x, self.H) + if (bit == 1): + self.draw.Line(x + self.X, 0, x + self.X, self.H) + self.draw.Line(x + self.X/2, 0, x + self.X/2, self.H) + self.draw.Line(x, 0, x + self.X, 0) + self.draw.Line(x, self.H, x + self.X, self.H) + offset = (bit + 1) * self.X + return x + offset + + def drawBars(self): + x = 0 + bars = self.Barcode.getBarCodePattern() + for index in range(0, len(bars), 2): + # Draw bar + barBit = bars[index] + x = self.__drawBar__(barBit, x) + # Draw space + if index < len(bars)-1: + spaceBit = bars[index + 1] + x = self.__drawSpace__(spaceBit, x) + return x + + def drawQuietZone(self, x0, y0, width, height): + self.draw.SetLayer(B.F_SilkS) + self.draw.SetLineTickness(self.X) + + for offset in range(0, int(self.Q), int(self.X/2)): + xoffset = offset + self.X + yoffset = offset + self.X/2 + self.draw.Line(x0 - xoffset, -yoffset, width + xoffset, -yoffset) + self.draw.Line(x0 - xoffset, self.H+yoffset, width + xoffset, self.H+yoffset) + self.draw.Line(x0 - xoffset, -yoffset, x0-xoffset, self.H+yoffset) + self.draw.Line(width + xoffset, -yoffset, width+xoffset, self.H+yoffset) + + def BuildThisFootprint(self): + # Draw bars + x = self.drawBars() + # Draw quiet zone + self.drawQuietZone(0, 0, x, self.H) + # Draw courtyard origin + self.draw.SetLayer(B.F_CrtYd) + self.draw.SetLineTickness(self.CourtyardLineWidth) + ch_lim = B.FromMM(0.35) + self.draw.Line(-ch_lim, 0, ch_lim, 0) + self.draw.Line(0, -ch_lim, 0, ch_lim) + self.draw.Circle(0, 0, B.FromMM(0.25)) + self.module.Value().SetLayer(B.F_Fab) + +Uss39Wizard().register() diff --git a/pcbnew/scripting/plugins/zip_wizard.py b/pcbnew/scripting/plugins/zip_wizard.py new file mode 100644 index 0000000..4e0f477 --- /dev/null +++ b/pcbnew/scripting/plugins/zip_wizard.py @@ -0,0 +1,201 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +from __future__ import division +import pcbnew + +import HelpfulFootprintWizardPlugin as HFPW +import PadArray as PA + + +class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin): + + pad_count_key = '#pad count' + line_count_key = '#line count' + pad_vertical_size_key = 'pad vertical size' + pad_horizontal_size_key = 'pad horizontal size' + line_spacing_key = 'line spacing' + pad_pitch_key = 'pad pitch' + drill_size_key = 'drill size' + + courtyard_x_margin_key = 'courtyard x margin' + courtyard_y_margin_key = 'courtyard y margin' + outline_x_margin_key = 'outline x margin' + outline_y_margin_key = 'outline y margin' + silkscreen_inside_key = 'silk screen inside' + + def GenerateParameterList(self): + + # defaults for a ZIP package + self.AddParam("Pads", self.pad_count_key, self.uNatural, 24) + self.AddParam("Pads", self.line_count_key, self.uNatural, 2) + self.AddParam("Body", self.silkscreen_inside_key, self.uBool, False) + self.AddParam("Body", self.courtyard_x_margin_key, self.uMM, 1) + self.AddParam("Body", self.courtyard_y_margin_key, self.uMM, 1) + + def CheckParameters(self): + self.CheckParamInt("Pads", '*' + self.pad_count_key) + self.CheckParamInt("Pads", '*' + self.line_count_key) + + # can do this internally to parameter manager? + self.CheckParamBool("Body", '*' + self.silkscreen_inside_key) + + def BuildThisFootprint(self): + pads = self.parameters["Pads"] + body = self.parameters["Body"] + + pad_count = pads['*' + self.pad_count_key] + pad_Vsize = pads[self.pad_vertical_size_key] + pad_Hsize = pads[self.pad_horizontal_size_key] + line_pitch = pads[self.line_spacing_key] + pad_pitch = pads[self.pad_pitch_key] + line_count = pads['*' + self.line_count_key] + + if line_count == 1: + singleline = True + else: + singleline = False + + # add in the pads + pad = self.GetPad() + + array = PA.PadZGridArray(pad, pad_count, line_count, line_pitch, pad_pitch) + array.AddPadsToModule(self.draw) + + # draw the Silk Screen + pads_per_line = pad_count // line_count + row_length = pad_pitch * (pads_per_line - 1) # fenceposts + ssx_offset = pad_Hsize / 2 + body[self.outline_x_margin_key] + ssy_offset = pad_Vsize / 2 + body[self.outline_y_margin_key] + + pin1posX = pad_pitch * (pad_count - 1) / 2 + pin1posY = line_pitch * (line_count - 1) / 2 + leftx = pin1posX + ssx_offset + lowy = pin1posY + ssy_offset + + cornery = lowy + + # body inside pads is possible only for 2 rows. + # for other values, there is no room + linew = self.draw.GetLineTickness() + if body['*'+self.silkscreen_inside_key] and line_count == 2: + cornery = pin1posY - ssy_offset + if cornery < linew: + cornery = linew + + self.DrawBox(leftx*2, cornery*2) + + # Courtyard + cmarginx = body[self.courtyard_x_margin_key] + cmarginy = body[self.courtyard_y_margin_key] + self.draw.SetLayer(pcbnew.F_CrtYd) + sizex = (pin1posX + cmarginx) * 2 + pad_Hsize + sizey = (pin1posY + cmarginy) * 2 + pad_Vsize + self.draw.Box(0, 0, sizex, sizey) + + #reference and value + text_size = self.GetTextSize() # IPC nominal + t_posy = lowy + text_size + + self.draw.Value(0, t_posy, text_size) + self.draw.Reference(0, -t_posy, text_size) + + def DrawBox(self, sizex, sizey): + + # ---------- + # | 2 4 6 8| + # |1 3 5 7 | + # \--------- + setback = pcbnew.FromMM(1) + + if setback > sizey/2: + setback = sizey/2 + + self.draw.BoxWithDiagonalAtCorner(0, 0, sizex, sizey, setback, self.draw.flipY) + + +class ZIPWizard(RowedFootprint): + + def GetName(self): + return "ZIP" + + def GetDescription(self): + return "N lines Zip Package Footprint Wizard" + + def GenerateParameterList(self): + RowedFootprint.GenerateParameterList(self) + + self.AddParam("Pads", self.pad_pitch_key, self.uMM, 1.27) + self.AddParam("Pads", self.pad_horizontal_size_key, self.uMM, 1.2) + self.AddParam("Pads", self.pad_vertical_size_key, self.uMM, 2) + self.AddParam("Pads", self.line_spacing_key, self.uMM, 2.54) + self.AddParam("Pads", self.drill_size_key, self.uMM, 0.8) + self.AddParam("Body", self.outline_x_margin_key, self.uMM, 1) + self.AddParam("Body", self.outline_y_margin_key, self.uMM, 0.5) + + def GetValue(self): + rows = self.parameters["Pads"]['*' + self.line_count_key] + pad_cnt = self.parameters["Pads"]['*' + self.pad_count_key] + + if rows == 1: + name = "SIP" + elif rows == 2: + name = "ZIP" + else: # triple and up aren't really a thing, but call it something! + name = "xIP" + + return "%s-%d" % (name, pad_cnt) + + def GetPad(self): + pad_Vsize = self.parameters["Pads"][self.pad_vertical_size_key] + pad_Hsize = self.parameters["Pads"][self.pad_horizontal_size_key] + drill = self.parameters["Pads"][self.drill_size_key] + return PA.PadMaker(self.module).THPad( + pad_Vsize, pad_Hsize, drill, shape=pcbnew.PAD_SHAPE_OVAL) + +ZIPWizard().register() + + +class ZOICWizard(RowedFootprint): + + def GetName(self): + return "ZOIC" + + def GetDescription(self): + return "ZOIC, etc, Footprint Wizard" + + def GetValue(self): + return "%s-%d" % ("ZOIC", self.parameters["Pads"]['*' + self.pad_count_key]) + + def GenerateParameterList(self): + RowedFootprint.GenerateParameterList(self) + + #and override some of them + self.AddParam("Pads", self.pad_pitch_key, self.uMM, 0.6) + self.AddParam("Pads", self.pad_horizontal_size_key, self.uMM, 0.6) + self.AddParam("Pads", self.pad_vertical_size_key, self.uMM, 1.8) + self.AddParam("Pads", self.line_spacing_key, self.uMM, 5.2) + + self.AddParam("Body", self.outline_x_margin_key, self.uMM, 0.5) + self.AddParam("Body", self.outline_y_margin_key, self.uMM, 1) + + def GetPad(self): + pad_Vsize = self.parameters["Pads"][self.pad_vertical_size_key] + pad_Hsize = self.parameters["Pads"][self.pad_horizontal_size_key] + return PA.PadMaker(self.module).SMDPad( + pad_Vsize, pad_Hsize, shape=pcbnew.PAD_SHAPE_RECT) + +ZOICWizard().register() |