SWIG Improvements

*) Extend SWIG support deeper into the BOARD class.
*) Move swig *.i files into a directory identified for SWIG, in preparation
   for a parallel universe involving Phoenix's SIP.
*) Move swig files which will be common to both eeschema and pcbnew into
   common/swig.
*) Sketch out a "common" python module, and plan on dovetailing that into a
   libkicad_shared.{dll,so}
*) Add common/swig/ki_exceptions.i and define a macro HANDLE_EXCEPTIONS()
   which is to be applied to any function which needs C++ to python
   exception translation.
*) Move the test for SWIG tool into top level CMakeLists.txt file for use
   in all python modules beyond pcbnew, i.e. eeschema and common.
*) Add SWIG_MODULE_pcbnew_EXTRA_DEPS which generates a better Makefile, one
   which rebuilds the swig generated *.cxx file when one of its dependencies
   change.
*) Re-architect the board.i file so that it can be split into multiple *.i
   files easily.
*) Make some KIWAY from python progress, in preparation for Modular KiCad
   phase III.
This commit is contained in:
Dick Hollenbeck
2016-09-20 11:59:43 -04:00
committed by Wayne Stambaugh
parent 964b5a1830
commit 7311f07eaf
66 changed files with 1151 additions and 317 deletions
@@ -0,0 +1,159 @@
# 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.GetLineThickness()
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)
# set SMD attribute
self.module.SetAttributes(pcbnew.MOD_CMS)
# 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()
@@ -0,0 +1,532 @@
# 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 thickness 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 SetLineThickness(self, lineThickness):
"""
Set the current pen lineThickness used for subsequent drawing
operations
"""
self.dc['lineThickness'] = lineThickness
def SetLineTickness(self, lineThickness):
"""
Old version of SetLineThickness.
Does the same thing, but is is only here for compatibility with old scripts
Set the current pen lineThickness used for subsequent drawing
operations
"""
self.dc['lineThickness'] = lineThickness
def GetLineThickness(self):
"""
Get the current drawing context line thickness
"""
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.GetLineThickness())
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)
@@ -0,0 +1,348 @@
# 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 1.0
def GetReferencePrefix(self):
return "REF"
def GetImage(self):
return ""
def GetTextSize(self):
"""
IPC nominal
"""
return pcbnew.FromMM(1.0)
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 PutOnGridMM(self, value, gridSizeMM=0.05):
"""
Round the value (in KiCAD internal units 1nm) according to the
provided gridSize in mm.
"""
thresh = pcbnew.FromMM(gridSizeMM)
res = round(value/thresh)*thresh
return res
def PutOnGridMils(self, value, gridSizeMil=2):
"""
Round the value (in KiCAD internal units 1nm) according to the
provided gridSize in mil.
"""
thresh = pcbnew.FromMils(gridSizeMil)
res = round(value/thresh)*thresh
return res
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
+281
View File
@@ -0,0 +1,281 @@
# 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 EPADGridArray(PadGridArray):
def NamingFunction(self, nx, ny):
return self.firstPadNum
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)
+1
View File
@@ -0,0 +1 @@
+107
View File
@@ -0,0 +1,107 @@
# 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.GetLineThickness()
self.draw.SetLayer(pcbnew.F_CrtYd)
sizex = (ssx + cmargin) * 2
sizey = (ssy + cmargin) * 2
# round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
sizex = self.PutOnGridMM(sizex, 0.1)
sizey = self.PutOnGridMM(sizey, 0.1)
# set courtyard line thickness to the one defined in KLC
self.draw.SetLineThickness(pcbnew.FromMM(0.05))
self.draw.Box(0, 0, sizex, sizey)
# restore line thickness to previous value
self.draw.SetLineThickness(pcbnew.FromMM(cmargin))
#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)
# set SMD attribute
self.module.SetAttributes(pcbnew.MOD_CMS)
BGAWizard().register()
@@ -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.GetLineThickness()
self.draw.Circle(0, 0, body_radius)
text_size = self.GetTextSize() # IPC nominal
thickness = self.GetTextThickness()
textposy = body_radius + self.draw.GetLineThickness()/2 + self.GetTextSize()/2 + thickness
self.draw.Value( 0, textposy, text_size )
self.draw.Reference( 0, -textposy, text_size )
circular_pad_array_wizard().register()
+192
View File
@@ -0,0 +1,192 @@
# 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 QFNWizard(HFPW.HelpfulFootprintWizardPlugin):
def GetName(self):
return "QFN"
def GetDescription(self):
return "Quad Flat No-lead with Exposed Pad 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", "oval", self.uBool, True)
self.AddParam("Pads", "thermal vias", self.uBool, True)
self.AddParam("Pads", "thermal vias drill", self.uMM, 0.3)
self.AddParam("Pads", "epad subdiv x", self.uNatural, 2)
self.AddParam("Pads", "epad subdiv y", self.uNatural, 2)
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")
self.CheckParamBool("Pads", "*thermal vias")
def GetValue(self):
return "QFN_%d" % self.parameters["Pads"]["*n"]
def BuildThisFootprint(self):
pads = self.parameters["Pads"]
pad_pitch = pads["pad pitch"]
pad_length = pads["pad length"]
pad_width = pads["pad width"]
v_pitch = self.parameters["Package"]["package height"]
h_pitch = self.parameters["Package"]["package width"]
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
# epad
epad_width = self.parameters["Package"]["package height"] - (2*pad_length)
epad_length = self.parameters["Package"]["package width"] - (2*pad_length)
epad_subdv_x = pads["*epad subdiv x"]
epad_subdv_y = pads["*epad subdiv y"]
epad_via_drill = pads["thermal vias drill"]
if (epad_subdv_y != 0 and epad_subdv_x != 0) and (epad_subdv_y != 1 or epad_subdv_x != 1):
# Create the master pad (one area) on front solder mask, and perhaps of front copper layer
# at location 0,0
emasterpad = PA.PadMaker(self.module).SMDPad( epad_length, epad_width,
shape=pcbnew.PAD_SHAPE_RECT, rot_degree=0.0)
emasterpad.SetLayerSet(pcbnew.LSET(pcbnew.F_Mask)) # currently, only on solder mask
emasterpad.SetPadName(pads["*n"]+1)
self.module.Add(emasterpad)
px = pcbnew.FromMM(0.1); py = pcbnew.FromMM(0.1)
esubpad_size_x = epad_length / epad_subdv_x - px
esubpad_size_y = epad_width / epad_subdv_y - py
epad1_pos = pcbnew.wxPoint(-(esubpad_size_x*(epad_subdv_x-1)/2), -esubpad_size_y*(epad_subdv_y-1)/2)
epad = PA.PadMaker(self.module).SMDPad( esubpad_size_y, esubpad_size_x,
shape=pcbnew.PAD_SHAPE_RECT, rot_degree=0.0)
array = PA.EPADGridArray(epad, epad_subdv_x, epad_subdv_y, esubpad_size_x + px, esubpad_size_y + py, pcbnew.wxPoint(0,0))
array.SetFirstPadInArray(pads["*n"]+1)
array.AddPadsToModule(self.draw)
if pads["*thermal vias"]:
via_diam = min(esubpad_size_y, esubpad_size_x)/3.
thpad = PA.PadMaker(self.module).THRoundPad(via_diam, min(via_diam/2, epad_via_drill))
layerset = pcbnew.LSET.AllCuMask()
layerset.AddLayer(pcbnew.B_Mask)
layerset.AddLayer(pcbnew.F_Mask)
thpad.SetLayerSet(layerset)
array2 = PA.EPADGridArray(thpad, epad_subdv_x, epad_subdv_y, esubpad_size_x + px, esubpad_size_y + py, pcbnew.wxPoint(0,0))
array2.SetFirstPadInArray(pads["*n"]+1)
array2.AddPadsToModule(self.draw)
else:
epad = PA.PadMaker(self.module).SMDPad(epad_length, epad_width)
epad_pos = pcbnew.wxPoint(0,0)
array = PA.PadLineArray(epad, 1, 1, False, epad_pos)
array.SetFirstPadInArray(pads["*n"]+1)
array.AddPadsToModule(self.draw)
if pads["*thermal vias"]:
via_diam = min(epad_length, epad_width)/3.
thpad = PA.PadMaker(self.module).THRoundPad( via_diam, min(via_diam/2, epad_via_drill))
layerset = pcbnew.LSET.AllCuMask()
layerset.AddLayer(pcbnew.B_Mask)
layerset.AddLayer(pcbnew.F_Mask)
thpad.SetLayerSet(layerset)
array2 = PA.PadLineArray(thpad, 1, 1, False, epad_pos)
array2.SetFirstPadInArray(pads["*n"]+1)
array2.AddPadsToModule(self.draw)
#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/2.
sizey = (lim_y + cmargin) * 2 + pad_length/2.
# round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
sizex = self.PutOnGridMM(sizex, 0.1)
sizey = self.PutOnGridMM(sizey, 0.1)
# set courtyard line thickness to the one defined in KLC
thick = self.draw.GetLineThickness()
self.draw.SetLineThickness(pcbnew.FromMM(0.05))
self.draw.Box(0, 0, sizex, sizey)
# restore line thickness to previous value
self.draw.SetLineThickness(pcbnew.FromMM(thick))
#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)
# set SMD attribute
self.module.SetAttributes(pcbnew.MOD_CMS)
QFNWizard().register()
+136
View File
@@ -0,0 +1,136 @@
# 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
# round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
sizex = self.PutOnGridMM(sizex, 0.1)
sizey = self.PutOnGridMM(sizey, 0.1)
# set courtyard line thickness to the one defined in KLC
thick = self.draw.GetLineThickness()
self.draw.SetLineThickness(pcbnew.FromMM(0.05))
self.draw.Box(0, 0, sizex, sizey)
# restore line thickness to previous value
self.draw.SetLineThickness(pcbnew.FromMM(thick))
#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)
# set SMD attribute
self.module.SetAttributes(pcbnew.MOD_CMS)
QFPWizard().register()
+261
View File
@@ -0,0 +1,261 @@
# 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 = 'pad 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)
# Courtyard
cmargin = self.draw.GetLineThickness()
self.draw.SetLayer(pcbnew.F_CrtYd)
sizex = (ssx + cmargin) * 2
sizey = (ssy + cmargin) * 2
# round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
sizex = self.PutOnGridMM(sizex, 0.1)
sizey = self.PutOnGridMM(sizey, 0.1)
# set courtyard line thickness to the one defined in KLC
self.draw.SetLineThickness(pcbnew.FromMM(0.05))
self.draw.Box(0, 0, sizex, sizey)
# restore line thickness to previous value
self.draw.SetLineThickness(pcbnew.FromMM(cmargin))
#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)
# set the attribute
if self.GetName() == "S-DIP":
self.module.SetAttributes(pcbnew.MOD_DEFAULT)
elif self.GetName() == "SOIC":
self.module.SetAttributes(pcbnew.MOD_CMS)
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.GetLineThickness()*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 |
# \---------
setback = pcbnew.FromMM(0.8)
if setback > ssy:
setback = ssy
self.draw.BoxWithDiagonalAtCorner(0, 0, ssx*2, ssy*2, setback, self.draw.flipY)
SOICWizard().register()
@@ -0,0 +1,208 @@
#!/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.GetLineThickness()
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)
# set SMD attribute
self.module.SetAttributes(MOD_CMS)
# 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()
+149
View File
@@ -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.SetLineThickness(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.SetLineThickness(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.SetLineThickness(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()
+215
View File
@@ -0,0 +1,215 @@
# 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.GetLineThickness()
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)
thick = self.draw.GetLineThickness()
sizex = (pin1posX + cmarginx) * 2 + pad_Hsize + thick
sizey = (pin1posY + cmarginy) * 2 + pad_Vsize + thick
# round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
sizex = self.PutOnGridMM(sizex, 0.1)
sizey = self.PutOnGridMM(sizey, 0.1)
# set courtyard line thickness to the one defined in KLC
self.draw.SetLineThickness(pcbnew.FromMM(0.05))
self.draw.Box(0, 0, sizex, sizey)
# restore line thickness to previous value
self.draw.SetLineThickness(pcbnew.FromMM(thick))
#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)
# set SMD attribute
if self.GetName() == "ZIP":
self.module.SetAttributes(pcbnew.MOD_DEFAULT)
elif self.GetName() == "ZOIC":
self.module.SetAttributes(pcbnew.MOD_CMS)
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()