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:
committed by
Wayne Stambaugh
parent
964b5a1830
commit
7311f07eaf
@@ -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
|
||||
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user