From c5a14f28f1e282c654557a2471b3e862759bd6b9 Mon Sep 17 00:00:00 2001 From: John Beard Date: Wed, 23 Apr 2014 18:36:31 +0200 Subject: [PATCH] Add John Beard's patch: new and useful footprint wizards (SIP/SIP, SOIC/SSOP/TSSOP/MSOP and BGA). Add Python utility tools to make wizard scripts more easy to write. --- .../plugins/FootprintWizardDrawingAids.py | 134 +++++++++ .../plugins/HelpfulFootprintWizardPlugin.py | 250 ++++++++++++++++ pcbnew/scripting/plugins/PadArray.py | 148 ++++++++++ pcbnew/scripting/plugins/bga_wizard.py | 98 +++++++ pcbnew/scripting/plugins/qfp_wizard.py | 272 ++++-------------- pcbnew/scripting/plugins/sdip_wizard.py | 200 +++++++++++++ 6 files changed, 893 insertions(+), 209 deletions(-) create mode 100644 pcbnew/scripting/plugins/FootprintWizardDrawingAids.py create mode 100644 pcbnew/scripting/plugins/HelpfulFootprintWizardPlugin.py create mode 100644 pcbnew/scripting/plugins/PadArray.py create mode 100644 pcbnew/scripting/plugins/bga_wizard.py create mode 100644 pcbnew/scripting/plugins/sdip_wizard.py diff --git a/pcbnew/scripting/plugins/FootprintWizardDrawingAids.py b/pcbnew/scripting/plugins/FootprintWizardDrawingAids.py new file mode 100644 index 0000000000..d1046bdafb --- /dev/null +++ b/pcbnew/scripting/plugins/FootprintWizardDrawingAids.py @@ -0,0 +1,134 @@ +# 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 + +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 width and layer + """ + def __init__(self, module): + self.module = module + #drawing context defaults + self.dc = { + 'layer': pcbnew.SILKSCREEN_N_FRONT, + 'width': pcbnew.FromMM(0.2) + } + + def SetWidth(self, width): + self.dc['width'] = width + + def SetLayer(self, layer): + self.dc['layer'] = layer + + def Line(self, x1, y1, x2, y2): + + outline = pcbnew.EDGE_MODULE(self.module) + outline.SetWidth(self.dc['width']) + outline.SetLayer(self.dc['layer']) + outline.SetShape(pcbnew.S_SEGMENT) + start = pcbnew.wxPoint(x1, y1) + end = pcbnew.wxPoint(x2, y2) + outline.SetStartEnd(start, end) + self.module.Add(outline) + + # 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): + + 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]) + + def Reference(self, x, y, size): + """ + 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(pcbnew.wxPoint(x, y)) + self.module.Reference().SetTextPosition(self.module.Reference().GetPos0()) + self.module.Reference().SetSize(text_size) + + def Value(self, x, y, size): + """ + As for references, draw the module's value + """ + text_size = pcbnew.wxSize(size, size) + + self.module.Value().SetPos0(pcbnew.wxPoint(x, y)) + self.module.Value().SetTextPosition(self.module.Value().GetPos0()) + self.module.Value().SetSize(text_size) + + def Box(self, x, y, w, h): + """ + Draw a rectangular box, centred at (x,y), with given width and + height + """ + self.VLine(x - w/2, y - h/2, h) # left + self.VLine(x + w/2, y - h/2, h) # right + self.HLine(x - w/2, y + h/2, w) # bottom + self.HLine(x - w/2, y - h/2, w) # top + + def NotchedBox(self, x, y, w, h, notchW, notchH): + """ + Draw a box with a notch in the top edge + """ + #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) + ]) + + def BoxWithDiagonalAtCorner(self, x, y, w, h, diagSetback): + + self.Box(x, y, w, h) + + #diagonal corner + self.Line(x - w/2 + diagSetback, x - h/2, x - w/2, + x - h/2 + diagSetback) diff --git a/pcbnew/scripting/plugins/HelpfulFootprintWizardPlugin.py b/pcbnew/scripting/plugins/HelpfulFootprintWizardPlugin.py new file mode 100644 index 0000000000..de93cfda03 --- /dev/null +++ b/pcbnew/scripting/plugins/HelpfulFootprintWizardPlugin.py @@ -0,0 +1,250 @@ +# 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 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 + + 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 + """ + + 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.uBool: + val = "True" if default else "False" #ugly stringing + else: + print "Warning: Unknown unit type: %s" % unit + return + + if unit in [self.uNatural, self.uBool]: + param = "*%s" % param #star prefix for natural + + if section not in self.parameters: + self.parameters[section] = {} + + self.parameters[section][param] = val + + def _PrintParameterTable(self): + """ + Pretty-print the parameters we have + """ + for name, section in self.parameters.iteritems(): + print " %s:" % 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) + + print " %s: %s%s" % (key, value, unit) + + def _ParametersHaveErrors(self): + """ + Return true if we discovered errors suring 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 + """ + + for name, section in self.parameter_errors.iteritems(): + printed_section = False + + for key, value in section.iteritems(): + if value: + if not printed_section: + print " %s:" % name + + print " %s: %s (have %s)" % (key, value, + self.parameters[name][key]) + + 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(): + print "Cannot build footprint: Parameters have errors:" + self._PrintParameterErrors() + return False + + print "Building new %s footprint with the following parameters:" % self.name + + self._PrintParameterTable() + return True + + ################################################################# + # PARAMETER CHECKERS + ################################################################# + + def CheckParamPositiveInt(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] > min_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 GetReference(self): + raise NotImplementedError + + def GetValuePrefix(self): + return "U" # footprints needing wizards of often ICs + + def GetImage(self): + return "" + + def BuildThisFootprint(self): + raise NotImplementedError + + def BuildFootprint(self): + """ + Actually make the footprint. We defer all but the setup to + the implmenting class + """ + + if not self.ProcessParameters(): + return + + self.module = pcbnew.MODULE(None) # create a new module + + self.draw = FootprintWizardDrawingAids.FootprintWizardDrawingAids(self.module) + + self.module.SetReference(self.GetReference()) + self.module.SetValue("%s**" % self.GetValuePrefix()) + + fpid = pcbnew.FPID(self.module.GetReference()) #the name in library + self.module.SetFPID( fpid ) + + self.BuildThisFootprint() # implementer's build function diff --git a/pcbnew/scripting/plugins/PadArray.py b/pcbnew/scripting/plugins/PadArray.py new file mode 100644 index 0000000000..99fa70e0dc --- /dev/null +++ b/pcbnew/scripting/plugins/PadArray.py @@ -0,0 +1,148 @@ + +import pcbnew + +class PadMaker: + """ + Useful construction functions for common types of pads + """ + + def __init__(self, module): + self.module = module + + def THPad(self, w, l, drill, shape = pcbnew.PAD_OVAL): + pad = pcbnew.D_PAD(self.module) + + pad.SetSize(pcbnew.wxSize(l, w)) + + pad.SetShape(shape) + + pad.SetAttribute(pcbnew.PAD_STANDARD) + pad.SetLayerMask(pcbnew.PAD_STANDARD_DEFAULT_LAYERS) + pad.SetDrillSize(pcbnew.wxSize(drill, drill)) + + return pad + + def SMDPad(self, w, l, shape = pcbnew.PAD_RECT): + pad = pcbnew.D_PAD(self.module) + pad.SetSize(pcbnew.wxSize(l, w)) + + pad.SetShape(shape) + + pad.SetAttribute(pcbnew.PAD_SMD) + pad.SetLayerMask(pcbnew.PAD_SMD_DEFAULT_LAYERS) + + return pad + + def SMTRoundPad(self, size): + pad = self.SMDPad(size, size, shape = pcbnew.PAD_CIRCLE) + return pad + +class PadArray: + + def __init__(self): + self.firstPad = 1; + + def SetFirstPadInArray(self, fpNum): + self.firstPad = fpNum + + # HACK! pad should one day have its own clone method + def ClonePad(self): + + pad = pcbnew.D_PAD(self.pad.GetParent()) + + pad.SetSize(self.pad.GetSize()) + pad.SetShape(self.pad.GetShape()) + pad.SetAttribute(self.pad.GetAttribute()) + pad.SetLayerMask(self.pad.GetLayerMask()) + pad.SetDrillSize(self.pad.GetDrillSize()) + + return pad + + def AddPad(self, pad): + self.pad.GetParent().Add(pad) + +class PadGridArray(PadArray): + + def __init__(self, pad, nx, ny, px, py, pin1Pos): + # 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.pin1Pos = pin1Pos + + # handy utility function 1 - A, 2 - B, 26 - AA, etc + # aIndex = 0 for 0 - A + def AlphaNameFromNumber(self, n, aIndex = 1): + + div, mod = divmod(n - aIndex, 26) + alpha = chr(65 + mod) + + if div > 0: + return self.AlphaNameFromNumber(div) + alpha; + + return alpha; + + # right to left, top to bottom + def NamingFunction(self, x, y): + return self.firstPad + (self.nx * y + x) + + #relocate the pad and add it as many times as we need + def AddPadsToModule(self): + + for x in range(0, self.nx): + for y in range(self.ny): + posX = self.pin1Pos.x + (self.px * x) + posY = self.pin1Pos.y + (self.py * y) + + pos = pcbnew.wxPoint(posX, posY) + + # THIS DOESN'T WORK yet! + #pad = self.pad.Clone() + pad = self.ClonePad() + + pad.SetPos0(pos) + pad.SetPosition(pos) + + pad.SetPadName(str(self.NamingFunction(x,y))) + + self.AddPad(pad) + +class PadLineArray(PadGridArray): + + def __init__(self, pad, n, pitch, isVertical, pin1Pos): + + if isVertical: + PadGridArray.__init__(self, pad, 1, n, 0, pitch, pin1Pos) + else: + PadGridArray.__init__(self, pad, n, 1, pitch, 0, pin1Pos) + +class RectPadArray(PadArray): + + def __init__(self, nx, ny, pitch, xpitch, ypitch, pin1Pos): + + #left row + pin1Pos = pcbnew.wxPoint(-h_pitch / 2, -row_len / 2) + array = PadLineArray(h_pad, pads_per_row, pad_pitch, True, pin1Pos) + array.SetFirstPadInArray(1) + array.AddPadsToModule() + + #bottom row + pin1Pos = pcbnew.wxPoint(-row_len / 2, v_pitch / 2) + array = PA.PadLineArray(v_pad, pads_per_row, pad_pitch, False, pin1Pos) + array.SetFirstPadInArray(pads_per_row + 1) + array.AddPadsToModule() + + #right row + pin1Pos = pcbnew.wxPoint(h_pitch / 2, row_len / 2) + array = PadLineArray(h_pad, pads_per_row, -pad_pitch, True, pin1Pos) + array.SetFirstPadInArray(2*pads_per_row + 1) + array.AddPadsToModule() + + #top row + pin1Pos = pcbnew.wxPoint(row_len / 2, -v_pitch / 2) + array = PadLineArray(v_pad, pads_per_row, -pad_pitch, False, pin1Pos) + array.SetFirstPadInArray(3*pads_per_row + 1) + array.AddPadsToModule() diff --git a/pcbnew/scripting/plugins/bga_wizard.py b/pcbnew/scripting/plugins/bga_wizard.py new file mode 100644 index 0000000000..7fd6258f3e --- /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, x, y): + return "%s%d" % (self.AlphaNameFromNumber(y + 1), 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.CheckParamPositiveInt("Pads", "*row count") + self.CheckParamPositiveInt("Pads", "*column count") + + + def GetReference(self): + + pins = self.parameters["Pads"]["*row count"] * self.parameters["Pads"]["*column count"] + + return "BGA %d" % pins + + + def GetValuePrefix(self): + return "U" + + + 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"]) + + pin1Pos = pcbnew.wxPoint(-((rows - 1) * pad_pitch) / 2, + -((cols - 1) * pad_pitch) / 2) + + array = BGAPadGridArray(pad, rows, cols, pad_pitch, pad_pitch, pin1Pos) + array.AddPadsToModule() + + #box + ssX = -pin1Pos.x + pads["outline x margin"] + ssY = -pin1Pos.y + pads["outline y margin"] + + self.draw.BoxWithDiagonalAtCorner(0, 0, ssX*2, ssY*2, pads["outline x margin"]) + + #reference and value + textSize = pcbnew.FromMM(0.8) + + self.draw.Value(0, - ssY - textSize, textSize) + self.draw.Reference(0, ssY + textSize, textSize) + + +BGAWizard().register() diff --git a/pcbnew/scripting/plugins/qfp_wizard.py b/pcbnew/scripting/plugins/qfp_wizard.py index 2df89e113b..0034c1fadb 100644 --- a/pcbnew/scripting/plugins/qfp_wizard.py +++ b/pcbnew/scripting/plugins/qfp_wizard.py @@ -1,235 +1,89 @@ +from __future__ import division import pcbnew -def abs(x): - if x < 0: - return -x +import HelpfulFootprintWizardPlugin +import PadArray as PA - return x +class QFPWizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin): -class QFPWizard(pcbnew.FootprintWizardPlugin): - def __init__(self): - pcbnew.FootprintWizardPlugin.__init__(self) - self.name = "QFP" - self.description = "QFP Footprint Wizard" - self.parameters = { - "Pads": { - "*n": 100, - "pitch": pcbnew.FromMM(0.5), - "width": pcbnew.FromMM(0.25), - "length": pcbnew.FromMM(1.5), - "horizontal pitch": pcbnew.FromMM(15), - "vertical pitch": pcbnew.FromMM(15), - "*oval": "True" - }, - "Package": { - "width": pcbnew.FromMM(14), - "height": pcbnew.FromMM(14) - } - } + def GetName(self): + return "QFP" - self.ClearErrors() + def GetDescription(self): + return "QFP Footprint Wizard" - def smd_rect_pad(self, module, size, pos, name): - pad = pcbnew.D_PAD(module) + 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) - pad.SetSize(size) - - if self.parameters['Pads'].get('*oval', "true").lower() == "true": - pad.SetShape(pcbnew.PAD_OVAL) - else: - pad.SetShape(pcbnew.PAD_RECT) - - pad.SetAttribute(pcbnew.PAD_SMD) - pad.SetLayerMask(pcbnew.PAD_SMD_DEFAULT_LAYERS) - pad.SetPos0(pos) - pad.SetPosition(pos) - pad.SetPadName(name) - - return pad + self.AddParam("Pads", "package width", self.uMM, 14) + self.AddParam("Pads", "package height", self.uMM, 14) def CheckParameters(self): - errors = "" - pads = self.parameters - num_pads = pads["Pads"]["*n"] - if (num_pads < 1): - self.parameter_errors["Pads"]["*n"] = "Must be positive" - errors +="Pads/n has wrong value, " - pads["Pads"]["*n"] = int(num_pads) # Reset to int instead of float + self.CheckParamPositiveInt("Pads", "*n", is_multiple_of = 4) - return errors + def GetReference(self): + return "QFP %d" % self.parameters["Pads"]["*n"] - def BuildFootprint(self): - if self.has_errors(): - print "Cannot build footprint: Parameters have errors:" - print self.parameter_errors - return + def BuildThisFootprint(self): - print "Building new QFP footprint with the following parameters:" - self.print_parameter_table() + pads = self.parameters["Pads"] - self.module = pcbnew.MODULE(None) # create a new module + pad_pitch = pads["pad pitch"] + pad_length = self.parameters["Pads"]["pad length"] + pad_width = self.parameters["Pads"]["pad width"] - pads = self.parameters - num_pads = int(pads["Pads"]["*n"]) - pad_width = pads["Pads"]["width"] - pad_length = pads["Pads"]["length"] - pad_pitch = pads["Pads"]["pitch"] - pad_horizontal_pitch = pads["Pads"]["horizontal pitch"] - pad_vertical_pitch = pads["Pads"]["vertical pitch"] + v_pitch = pads["vertical pitch"] + h_pitch = pads["horizontal pitch"] - package_width = pads["Package"]["width"] - package_height = pads["Package"]["height"] + pads_per_row = pads["*n"] // 4 - side_length = pad_pitch * ((num_pads / 4) - 1) + row_len = (pads_per_row - 1) * pad_pitch - offsetX = pad_pitch * ((num_pads / 4) - 1) / 2 - text_size = pcbnew.wxSize(pcbnew.FromMM(0.8), pcbnew.FromMM(0.8)) + h_pad = PA.PadMaker(self.module).SMDPad(pad_width, pad_length, shape = pcbnew.PAD_OVAL) + v_pad = PA.PadMaker(self.module).SMDPad(pad_length, pad_width, shape = pcbnew.PAD_OVAL) - self.module.SetReference("QFP %d" % int(num_pads)) - self.module.Reference().SetPos0(pcbnew.wxPoint(0, pcbnew.FromMM(-0.8))) - self.module.Reference().SetTextPosition(self.module.Reference().GetPos0()) - self.module.Reference().SetSize(text_size) + #left row + pin1Pos = pcbnew.wxPoint(-h_pitch / 2, -row_len / 2) + array = PA.PadLineArray(h_pad, pads_per_row, pad_pitch, True, pin1Pos) + array.SetFirstPadInArray(1) + array.AddPadsToModule() - self.module.SetValue("U**") - self.module.Value().SetPos0(pcbnew.wxPoint(0, pcbnew.FromMM(+0.8))) - self.module.Value().SetTextPosition(self.module.Value().GetPos0()) - self.module.Value().SetSize(text_size) + #bottom row + pin1Pos = pcbnew.wxPoint(-row_len / 2, v_pitch / 2) + array = PA.PadLineArray(v_pad, pads_per_row, pad_pitch, False, pin1Pos) + array.SetFirstPadInArray(pads_per_row + 1) + array.AddPadsToModule() - fpid = pcbnew.FPID(self.module.GetReference()) #the name in library - self.module.SetFPID( fpid ) + #right row + pin1Pos = pcbnew.wxPoint(h_pitch / 2, row_len / 2) + array = PA.PadLineArray(h_pad, pads_per_row, -pad_pitch, True, pin1Pos) + array.SetFirstPadInArray(2*pads_per_row + 1) + array.AddPadsToModule() - pad_size_left_right = pcbnew.wxSize(pad_length, pad_width) - pad_size_bottom_top = pcbnew.wxSize(pad_width, pad_length) + #top row + pin1Pos = pcbnew.wxPoint(row_len / 2, -v_pitch / 2) + array = PA.PadLineArray(v_pad, pads_per_row, -pad_pitch, False, pin1Pos) + array.SetFirstPadInArray(3*pads_per_row + 1) + array.AddPadsToModule() - for cur_pad in range(0, num_pads): - side = int(cur_pad / (num_pads / 4)) # 0 -> left, 1 -> bottom, 2 -> right, 3 -> top + limX = pads["package width"] / 2 + limY = pads["package height"] / 2 + inner = (row_len / 2) + pad_pitch - if side == 0 or side == 2: - pad_size = pad_size_left_right - - pad_pos_x = -(pad_horizontal_pitch / 2) - pad_pos_y = (cur_pad % (num_pads / 4)) * pad_pitch - (side_length / 2) - - if side == 2: - pad_pos_x = -pad_pos_x - pad_pos_y = -pad_pos_y - - else: - pad_size = pad_size_bottom_top - - pad_pos_x = (cur_pad % (num_pads / 4)) * pad_pitch - (side_length / 2) - pad_pos_y = -(pad_vertical_pitch / 2) - - if side == 1: - pad_pos_y = -pad_pos_y - else: - pad_pos_x = -pad_pos_x - - pad_pos = pcbnew.wxPoint(pad_pos_x, pad_pos_y) - - pad = self.smd_rect_pad(self.module, pad_size, pad_pos, str(cur_pad + 1)) - - self.module.Add(pad) - - half_package_width = package_width / 2 - half_package_height = package_height / 2 - - package_pad_height_offset = abs(package_height - side_length) / 2 - pad_pitch - package_pad_width_offset = abs(package_width - side_length) / 2 - pad_pitch - - # Bottom Left Edge, vertical line - outline = pcbnew.EDGE_MODULE(self.module) - outline.SetWidth(pcbnew.FromMM(0.2)) - outline.SetLayer(pcbnew.SILKSCREEN_N_FRONT) - outline.SetShape(pcbnew.S_SEGMENT) - start = pcbnew.wxPoint(-half_package_width, half_package_height - package_pad_height_offset) - end = pcbnew.wxPoint(-half_package_width, half_package_height) - outline.SetStartEnd(start, end) - self.module.Add(outline) - - # Bottom Left Edge, horizontal line - outline = pcbnew.EDGE_MODULE(self.module) - outline.SetWidth(pcbnew.FromMM(0.2)) - outline.SetLayer(pcbnew.SILKSCREEN_N_FRONT) - outline.SetShape(pcbnew.S_SEGMENT) - start = pcbnew.wxPoint(-half_package_width, half_package_height) - end = pcbnew.wxPoint(-half_package_width + package_pad_width_offset, half_package_height) - outline.SetStartEnd(start, end) - self.module.Add(outline) - - # Bottom Right Edge, vertical line - outline = pcbnew.EDGE_MODULE(self.module) - outline.SetWidth(pcbnew.FromMM(0.2)) - outline.SetLayer(pcbnew.SILKSCREEN_N_FRONT) - outline.SetShape(pcbnew.S_SEGMENT) - start = pcbnew.wxPoint(half_package_width, half_package_height - package_pad_height_offset) - end = pcbnew.wxPoint(half_package_width, half_package_height) - outline.SetStartEnd(start, end) - self.module.Add(outline) - - # Bottom Right Edge, horizontal line - outline = pcbnew.EDGE_MODULE(self.module) - outline.SetWidth(pcbnew.FromMM(0.2)) - outline.SetLayer(pcbnew.SILKSCREEN_N_FRONT) - outline.SetShape(pcbnew.S_SEGMENT) - start = pcbnew.wxPoint(half_package_width, half_package_height) - end = pcbnew.wxPoint(half_package_width - package_pad_width_offset, half_package_height) - outline.SetStartEnd(start, end) - self.module.Add(outline) - - # Top Right Edge, vertical line - outline = pcbnew.EDGE_MODULE(self.module) - outline.SetWidth(pcbnew.FromMM(0.2)) - outline.SetLayer(pcbnew.SILKSCREEN_N_FRONT) - outline.SetShape(pcbnew.S_SEGMENT) - start = pcbnew.wxPoint(half_package_width, -half_package_height + package_pad_height_offset) - end = pcbnew.wxPoint(half_package_width, -half_package_height) - outline.SetStartEnd(start, end) - self.module.Add(outline) - - # Top Right Edge, horizontal line - outline = pcbnew.EDGE_MODULE(self.module) - outline.SetWidth(pcbnew.FromMM(0.2)) - outline.SetLayer(pcbnew.SILKSCREEN_N_FRONT) - outline.SetShape(pcbnew.S_SEGMENT) - start = pcbnew.wxPoint(half_package_width, -half_package_height) - end = pcbnew.wxPoint(half_package_width - package_pad_width_offset, -half_package_height) - outline.SetStartEnd(start, end) - self.module.Add(outline) - - # Top Left Edge, straight line - outline = pcbnew.EDGE_MODULE(self.module) - outline.SetWidth(pcbnew.FromMM(0.2)) - outline.SetLayer(pcbnew.SILKSCREEN_N_FRONT) - outline.SetShape(pcbnew.S_SEGMENT) - start = pcbnew.wxPoint(-half_package_width, -half_package_height + package_pad_height_offset) - end = pcbnew.wxPoint(-half_package_width + package_pad_width_offset, -half_package_height) - outline.SetStartEnd(start, end) - self.module.Add(outline) - - def print_parameter_table(self): - for name, section in self.parameters.iteritems(): - print " %s:" % 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) - - print " %s: %s%s" % (key, value, unit) - - def has_errors(self): - for name, section in self.parameter_errors.iteritems(): - for k, v in section.iteritems(): - if v: - return True - - return False + #top left - diagonal + self.draw.Line(-limX, -inner, -inner, -limY) + # top right + self.draw.Polyline([(inner, -limY), (limX, -limY), (limX, -inner)]) + # bottom left + self.draw.Polyline([(-inner, limY), (-limX, limY), (-limX, inner)]) + # bottom right + self.draw.Polyline([(inner, limY), (limX, limY), (limX, inner)]) QFPWizard().register() diff --git a/pcbnew/scripting/plugins/sdip_wizard.py b/pcbnew/scripting/plugins/sdip_wizard.py new file mode 100644 index 0000000000..c2a37653cb --- /dev/null +++ b/pcbnew/scripting/plugins/sdip_wizard.py @@ -0,0 +1,200 @@ +# 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): + if (x % 2) == 0: # even row, count up + return (x * self.ny) + y + 1; + else: # odd row, count down + return (self.ny * (x + 1)) - y; + +class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin): + + def GenerateParameterList(self): + + # defaults for a DIP package + self.AddParam("Pads", "n", self.uNatural, 24) + self.AddParam("Pads", "silk screen inside", self.uBool, False) + self.AddParam("Pads", "row count", self.uNatural, 2) + + def CheckParameters(self): + self.CheckParamPositiveInt("Pads", "*row count") + self.CheckParamPositiveInt("Pads", "*n", is_multiple_of = self.parameters["Pads"]["*row count"]) + self.CheckParamBool("Pads", "*silk screen inside") #can do this internally to parameter manager? + + def BuildThisFootprint(self): + + pads = self.parameters["Pads"] + + num_pads = pads["*n"] + + pad_length = pads["pad length"] + pad_width = pads["pad width"] + row_pitch = pads["row spacing"] + pad_pitch = pads["pad pitch"] + num_rows = pads["*row count"] + + pads_per_row = num_pads // num_rows + + row_length = pad_pitch * (pads_per_row - 1) #fenceposts + + # add in the pads + pad = self.GetPad() + + pin1Pos = pcbnew.wxPoint(-((num_rows - 1) * row_pitch) / 2, -row_length / 2) + + array = RowedGridArray(pad, num_rows, pads_per_row, row_pitch, pad_pitch, pin1Pos) + array.AddPadsToModule() + + # draw the Silk Screen + + pad_length = pads["pad length"] + pad_width = pads["pad width"] + + ssXOffset = -pad_length / 2 - pads["outline x margin"] + ssYOffset = -pad_width / 2 - pads["outline y margin"] + + + if pads["*silk screen inside"]: + ssXOffset *= -1 + + ssX = -pin1Pos.x - ssXOffset + ssY = -pin1Pos.y - ssYOffset + + + self.DrawBox(ssX, ssY) + + #reference and value + textSize = pcbnew.FromMM(0.8) + + self.draw.Value(0, - ssY - textSize, textSize) + self.draw.Reference(0, ssY + textSize, textSize) + + +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", "pad pitch", self.uMils, 100) + self.AddParam("Pads", "pad width", self.uMils, 60) + self.AddParam("Pads", "pad length", self.uMils, 150) + self.AddParam("Pads", "row spacing", self.uMils, 300) + self.AddParam("Pads", "drill size", self.uMM, 1) + self.AddParam("Pads", "outline x margin", self.uMM, 0.5) + self.AddParam("Pads", "outline y margin", self.uMM, 1) + + def GetReference(self): + + rows = self.parameters["Pads"]["*row count"] + + if rows == 1: + name = "SIP" + elif rows == 2: + name = "DIP" + else: # triple and up aren't really a thing, but call it something! + name = "xIP" + + return "%s %d" % (name, self.parameters["Pads"]["*n"]) + + def GetPad(self): + pad_length = self.parameters["Pads"]["pad length"] + pad_width = self.parameters["Pads"]["pad width"] + drill = self.parameters["Pads"]["drill size"] + return PA.PadMaker(self.module).THPad(pad_width, pad_length, drill, shape = pcbnew.PAD_OVAL) + + def DrawBox(self, ssX, ssY): + + if self.parameters["Pads"]["*row count"] == 2: + + # ---------- + # |8 7 6 5 | + # > | + # |1 2 3 4 | + # ---------- + + # draw the notch + notchWidth = pcbnew.FromMM(3) + notchHeight = pcbnew.FromMM(1) + + self.draw.NotchedBox(0, 0, ssX*2, ssY*2, notchWidth, notchHeight) + else: + # ----------------- + # |1|2 3 4 5 6 7 8| + # ----------------- + self.draw.Box(ssX*2, ssY*2) + + #line between pin1 and pin2 + pad_pitch = self.parameters["Pads"]["pad pitch"]; + self.draw.HLine(-ssX, pin1Pos.y + pad_pitch/2, ssX * 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 GetReference(self): + return "%s %d" % ("SOIC", self.parameters["Pads"]["*n"]) + + def GenerateParameterList(self): + RowedFootprint.GenerateParameterList(self) + + #and override some of them + self.AddParam("Pads", "pad pitch", self.uMM, 1.27) + self.AddParam("Pads", "pad width", self.uMM, 0.6) + self.AddParam("Pads", "pad length", self.uMM, 2.2) + self.AddParam("Pads", "row spacing", self.uMM, 5.2) + + self.AddParam("Pads", "outline x margin", self.uMM, 0.5) + self.AddParam("Pads", "outline y margin", self.uMM, 0.5) + + def GetPad(self): + pad_length = self.parameters["Pads"]["pad length"] + pad_width = self.parameters["Pads"]["pad width"] + return PA.PadMaker(self.module).SMDPad(pad_width, pad_length, shape = pcbnew.PAD_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()