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.
This commit is contained in:
John Beard 2014-04-23 18:36:31 +02:00 committed by jean-pierre charras
parent 61fe97e666
commit 38f36ae45c
6 changed files with 893 additions and 209 deletions

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()