Footprint Wizards Update

This commit is contained in:
Oliver Walters
2017-01-03 16:01:47 +01:00
committed by jean-pierre charras
parent d1278a48b6
commit 66ee44cb1f
23 changed files with 2063 additions and 844 deletions
@@ -17,10 +17,9 @@
from __future__ import division
import pcbnew
import HelpfulFootprintWizardPlugin as HFPW
import FootprintWizardBase
class FPC_FootprintWizard(HFPW.HelpfulFootprintWizardPlugin):
class FPC_FootprintWizard(FootprintWizardBase.FootprintWizard):
def GetName(self):
return "FPC (SMT connector)"
@@ -29,11 +28,11 @@ class FPC_FootprintWizard(HFPW.HelpfulFootprintWizardPlugin):
return "FPC (SMT connector) Footprint Wizard"
def GetValue(self):
pins = self.parameters["Pads"]["*n"]
pins = self.parameters["Pads"]["n"]
return "FPC_%d" % pins
def GenerateParameterList(self):
self.AddParam( "Pads", "n", self.uNatural, 40 )
self.AddParam( "Pads", "n", self.uInteger, 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)
@@ -56,13 +55,12 @@ class FPC_FootprintWizard(HFPW.HelpfulFootprintWizardPlugin):
return pad
def CheckParameters(self):
p = self.parameters
self.CheckParamInt( "Pads", "*n" ) # not internal units preceded by "*"
#TODO implement custom parameter checking
pass
def BuildThisFootprint(self):
p = self.parameters
pad_count = int(p["Pads"]["*n"])
pad_count = int(p["Pads"]["n"])
pad_width = p["Pads"]["width"]
pad_height = p["Pads"]["height"]
pad_pitch = p["Pads"]["pitch"]
@@ -15,10 +15,150 @@
#
from __future__ import division
import pcbnew
import math
# Base class for creating footprint wizards
# Inherit this class to make a new wizard
class FootprintWizard(pcbnew.FootprintWizardPlugin):
# Copy units from pcbnew
uMM = pcbnew.uMM
uMils = pcbnew.uMils
uFloat = pcbnew.uFloat
uInteger = pcbnew.uInteger
uBool = pcbnew.uBool
uRadians = pcbnew.uRadians
uDegrees = pcbnew.uDegrees
uPercent = pcbnew.uPercent
uString = pcbnew.uString
"""
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:
GetValue()
GenerateParameterList()
CheckParameters()
BuildThisFootprint()
GetName()
GetDescription()
"""
def __init__(self):
pcbnew.FootprintWizardPlugin.__init__(self)
self.GenerateParameterList()
def GetName(self):
"""
Retun the name of the footprint wizard
"""
raise NotImplementedError
def GetDescription(self):
"""
Return the footprint wizard description
"""
raise NotImplementedError
def GetValue(self):
"""
Return the value (name) of the generated footprint
"""
raise NotImplementedError
def GenerateParameterList(self):
"""
Footprint parameter specification is done here
"""
raise NotImplementedError
def CheckParameters(self):
"""
Any custom parameter checking should be performed here
"""
raise NotImplementedError
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
# Do not override this method!
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
# Perform default checks on all params
for p in self.params:
p.ClearErrors()
p.Check() # use defaults
self.CheckParameters() # User error checks
if self.AnyErrors(): # Errors were detected!
self.buildmessages = "Cannot build footprint: Parameters have errors:\n"
for p in self.params:
if len(p.error_list) > 0:
self.buildmessages +="['{page}']['{name}']:\n".format(page=p.page,name=p.name)
for error in p.error_list:
self.buildmessages += "\t" + error + "\n"
return
self.buildmessages = ("Building new {name} footprint with the following parameters:\n".format(name=self.name))
self.buildmessages += self.Show()
self.draw = FootprintWizardDrawingAids(
self.module)
self.module.SetValue(self.GetValue())
self.module.SetReference("%s**" % self.GetReferencePrefix())
fpid = pcbnew.LIB_ID(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
def SetModule3DModel(self):
pass
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)
class FootprintWizardDrawingAids:
"""
@@ -294,6 +434,7 @@ class FootprintWizardDrawingAids:
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)
@@ -362,21 +503,22 @@ class FootprintWizardDrawingAids:
_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()
elif mirrorX is not None:
self.TransformFlip(mirrorX, 0, self.flipX)
_PolyLineInternal(pts)
self.PopTransform()
elif mirrorY is not None:
self.TransformFlip(0, mirrorY, self.flipY)
_PolyLineInternal(pts)
self.PopTransform()
def Reference(self, x, y, size, orientation_degree = 0):
"""
Draw the module's reference as the given point.
@@ -529,4 +671,4 @@ class FootprintWizardDrawingAids:
[0, 0]]
self.Polyline(pts)
self.PopTransform(2)
self.PopTransform(2)
@@ -1,348 +0,0 @@
# 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.LIB_ID(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
+86 -26
View File
@@ -17,7 +17,7 @@
from __future__ import division
import pcbnew
import HelpfulFootprintWizardPlugin as HFPW
import FootprintWizardBase
import PadArray as PA
@@ -29,7 +29,7 @@ class BGAPadGridArray(PA.PadGridArray):
n_x + 1)
class BGAWizard(HFPW.HelpfulFootprintWizardPlugin):
class BGAWizard(FootprintWizardBase.FootprintWizard):
def GetName(self):
return "BGA"
@@ -38,35 +38,48 @@ class BGAWizard(HFPW.HelpfulFootprintWizardPlugin):
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)
self.AddParam("Pads", "pitch", self.uMM, 1, designator='p')
self.AddParam("Pads", "size", self.uMM, 0.5)
self.AddParam("Pads", "columns", self.uInteger, 5, designator="nx")
self.AddParam("Pads", "rows", self.uInteger, 5, designator="ny")
self.AddParam("Package", "width", self.uMM, 6, designator='X')
self.AddParam("Package", "length", self.uMM, 6, designator='Y')
self.AddParam("Package", "margin", self.uMM, 0.25, min_value=0.2, hint="Courtyard margin")
def CheckParameters(self):
self.CheckParamInt("Pads", "*row count")
self.CheckParamInt("Pads", "*column count")
# check that the package is large enough
width = pcbnew.ToMM(self.parameters['Pads']['pitch'] * self.parameters['Pads']['columns'])
length = pcbnew.ToMM(self.parameters['Pads']['pitch'] * self.parameters['Pads']['rows'])
self.CheckParam('Package','width',min_value=width,info="Package width is too small (< {w}mm)".format(w=width))
self.CheckParam('Package','length',min_value=length,info="Package length is too small (< {l}mm".format(l=length))
def GetValue(self):
pins = (self.parameters["Pads"]["*row count"]
* self.parameters["Pads"]["*column count"])
pins = (self.parameters["Pads"]["rows"] * self.parameters["Pads"]["columns"])
return "BGA_%d" % pins
return "BGA-{n}_{a}x{b}_{x}x{y}mm".format(
n = pins,
a = self.parameters['Pads']['columns'],
b = self.parameters['Pads']['rows'],
x = pcbnew.ToMM(self.parameters['Package']['width']),
y = pcbnew.ToMM(self.parameters['Package']['length'])
)
def BuildThisFootprint(self):
pads = self.parameters["Pads"]
rows = pads["*row count"]
cols = pads["*column count"]
pad_size = pads["pad size"]
rows = pads["rows"]
cols = pads["columns"]
pad_size = pads["size"]
pad_size = pcbnew.wxSize(pad_size, pad_size)
pad_pitch = pads["pad pitch"]
pad_pitch = pads["pitch"]
# add in the pads
pad = PA.PadMaker(self.module).SMTRoundPad(pads["pad size"])
pad = PA.PadMaker(self.module).SMTRoundPad(pads["size"])
pin1_pos = pcbnew.wxPoint(-((cols - 1) * pad_pitch) / 2,
-((rows - 1) * pad_pitch) / 2)
@@ -74,21 +87,68 @@ class BGAWizard(HFPW.HelpfulFootprintWizardPlugin):
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"]
# Draw box outline on F.Fab layer
self.draw.SetLayer(pcbnew.F_Fab)
ssx = self.parameters['Package']['width'] / 2
ssy = self.parameters['Package']['length'] / 2
self.draw.BoxWithDiagonalAtCorner(0, 0, ssx*2, ssy*2,
pads["outline x margin"])
# Bevel should be 1mm nominal but we'll allow smaller values
if pcbnew.ToMM(ssx) < 1:
bevel = ssx
else:
bevel = pcbnew.FromMM(1)
# Box with 1mm bevel as per IPC7351C
self.draw.BoxWithDiagonalAtCorner(0, 0, ssx*2, ssy*2, bevel)
# Add IPC markings to F_Silk layer
self.draw.SetLayer(pcbnew.F_SilkS)
offset = pcbnew.FromMM(0.15)
len_x = 0.5 * ssx
len_y = 0.5 * ssy
edge = [
[ ssx + offset - len_x, -ssy - offset],
[ ssx + offset, -ssy - offset],
[ ssx + offset, -ssy - offset + len_y],
]
# Draw three square edges
self.draw.Polyline(edge)
self.draw.Polyline(edge, mirrorY=0)
self.draw.Polyline(edge, mirrorX=0, mirrorY=0)
# Draw pin-1 marker
bevel += offset
pin1 = [
[ -ssx - offset + len_x, -ssy - offset],
[ -ssx - offset + bevel, -ssy - offset],
[ -ssx - offset, -ssy - offset + bevel],
[ -ssx - offset, -ssy - offset + len_y],
]
# Remove lines if the package is too small
if bevel > len_x:
pin1 = pin1[1:]
if bevel > len_y:
pin1 = pin1[:-1]
self.draw.Polyline(pin1)
# Draw a circle in the bevel void
self.draw.Circle( -ssx, -ssy, pcbnew.FromMM(0.2), filled=True)
# Courtyard
cmargin = self.draw.GetLineThickness()
cmargin = self.parameters['Package']['margin']
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)
sizex = pcbnew.PutOnGridMM(sizex, 0.1)
sizey = pcbnew.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)
@@ -18,11 +18,11 @@ from __future__ import division
import math
import pcbnew
import HelpfulFootprintWizardPlugin as HFPW
import FootprintWizardBase
import PadArray as PA
class circular_pad_array_wizard(HFPW.HelpfulFootprintWizardPlugin):
class circular_pad_array_wizard(FootprintWizardBase.FootprintWizard):
def GetName(self):
return "Circular Pad Array"
@@ -32,52 +32,84 @@ class circular_pad_array_wizard(HFPW.HelpfulFootprintWizardPlugin):
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)
self.AddParam("Pads", "count", self.uInteger, 6, min_value=1, designator='n')
self.AddParam("Pads", "center diameter", self.uMM, 5, min_value=0, designator='r', hint="Centre distance between pads")
self.AddParam("Pads", "diameter", self.uMM, 1.5)
self.AddParam("Pads", "drill", self.uMM, 0.8)
self.AddParam("Pads", "angle", self.uDegrees, 0, designator='a')
self.AddParam("Numbering", "initial", self.uInteger, 1, min_value=1)
#self.AddParam("Numbering", "increment", self.uInteger, 1, min_value=1)
self.AddParam("Numbering", "clockwise", self.uBool, True)
self.AddParam("Outline", "diameter", self.uMM, 7, designator='D')
self.AddParam("Outline", "margin", self.uMM, 0.25, min_value=0.2)
def CheckParameters(self):
self.CheckParamInt("Pads", "*n")
self.CheckParamInt("Pads", "*first pad number")
self.CheckParamBool("Pads", "*number clockwise")
pads = self.parameters['Pads']
numbering = self.parameters['Numbering']
outline = self.parameters['Outline']
# Check that pads do not overlap
pad_dia = pcbnew.ToMM(pads['diameter'])
centres = pcbnew.ToMM(pads['center diameter'])
n_pads = pads['count']
self.CheckParam('Pads','diameter',max_value=centres*math.pi/n_pads,info="Pads overlap")
# Check that the pads fit inside the outline
d_min = pad_dia + centres
self.CheckParam("Outline","diameter",min_value=d_min, info="Outline diameter is too small")
def GetValue(self):
pins = self.parameters["Pads"]["*n"]
pins = self.parameters["Pads"]["count"]
return "CPA_%d" % pins
def BuildThisFootprint(self):
prm = self.parameters['Pads']
pads = self.parameters['Pads']
numbering = self.parameters['Numbering']
outline = self.parameters['Outline']
pad_size = prm['pad width']
pad_size = pads['diameter']
pad = PA.PadMaker(self.module).THPad(
prm['pad width'], prm['pad width'], prm['drill'])
pad = PA.PadMaker(self.module).THPad(pads['diameter'], pads['diameter'], pads['drill'])
array = PA.PadCircleArray(
pad, prm['*n'], prm['circle diameter'] / 2,
angle_offset=prm["*first pad angle"],
pad, pads['count'], pads['center diameter'] / 2,
angle_offset=pads["angle"],
centre=pcbnew.wxPoint(0, 0),
clockwise=prm["*number clockwise"])
clockwise=numbering["clockwise"])
array.SetFirstPadInArray(prm["*first pad number"])
array.SetFirstPadInArray(numbering["initial"])
array.AddPadsToModule(self.draw)
body_radius = (prm['circle diameter'] + prm['pad width'])/2 + self.draw.GetLineThickness()
# Draw the outline
body_radius = outline['diameter'] / 2
self.draw.SetLayer(pcbnew.F_Fab)
self.draw.GetLineThickness()
self.draw.Circle(0, 0, body_radius)
#silkscreen
body_radius += pcbnew.FromMM(0.15)
self.draw.SetLayer(pcbnew.F_SilkS)
self.draw.Circle(0, 0, body_radius)
# courtyard
self.draw.SetLayer(pcbnew.F_CrtYd)
self.draw.SetLineThickness(pcbnew.FromMM(0.05))
self.draw.Circle(0, 0, body_radius + outline['margin'])
# Text size
text_size = self.GetTextSize() # IPC nominal
thickness = self.GetTextThickness()
textposy = body_radius + self.draw.GetLineThickness()/2 + self.GetTextSize()/2 + thickness
textposy = body_radius + self.draw.GetLineThickness()/2 + self.GetTextSize()/2 + thickness + + outline['margin']
self.draw.Value( 0, textposy, text_size )
self.draw.Reference( 0, -textposy, text_size )
circular_pad_array_wizard().register()
+124 -90
View File
@@ -17,55 +17,80 @@
from __future__ import division
import pcbnew
import HelpfulFootprintWizardPlugin as HFPW
import pcbnew
import FootprintWizardBase
import PadArray as PA
class QFNWizard(HFPW.HelpfulFootprintWizardPlugin):
class QFNWizard(FootprintWizardBase.FootprintWizard):
def GetName(self):
return "QFN"
def GetDescription(self):
return "Quad Flat No-lead with Exposed Pad footprint wizard"
return "Quad Flat No-lead (QFN) 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)
#TODO - Allow different number of pads in x and y directions
self.AddParam("Pads", "n", self.uInteger, 100, multiple=4, min_value=4)
self.AddParam("Pads", "pitch", self.uMM, 0.5, designator='e')
self.AddParam("Pads", "width", self.uMM, 0.25, designator='X1')
self.AddParam("Pads", "length", self.uMM, 1.5, designator='Y1')
self.AddParam("Pads", "oval", self.uBool, True)
self.AddParam("EPad", "epad", self.uBool, True)
self.AddParam("EPad", "width", self.uMM, 10, designator="E2")
self.AddParam("EPad", "length", self.uMM, 10, designator="D2")
self.AddParam("EPad", "thermal vias", self.uBool, False)
self.AddParam("EPad", "thermal vias drill", self.uMM, 1, min_value=0.1)
self.AddParam("EPad", "x divisions", self.uInteger, 2, min_value=1)
self.AddParam("EPad", "y divisions", self.uInteger, 2, min_value=1)
self.AddParam("EPad", "paste margin", self.uMM, 0.1)
self.AddParam("Package", "width", self.uMM, 14, designator='E')
self.AddParam("Package", "height", self.uMM, 14, designator='D')
self.AddParam("Package", "margin", self.uMM, 0.25, minValue=0.2)
@property
def pads(self):
return self.parameters['Pads']
@property
def epad(self):
return self.parameters['EPad']
@property
def package(self):
return self.parameters['Package']
def CheckParameters(self):
self.CheckParamInt("Pads", "*n", is_multiple_of=4)
self.CheckParamBool("Pads", "*oval")
self.CheckParamBool("Pads", "*thermal vias")
pass
def GetValue(self):
return "QFN_%d" % self.parameters["Pads"]["*n"]
return "QFN-{n}_{ep}{x:g}x{y:g}_Pitch{p:g}mm".format(
n = self.pads['n'],
ep = "EP_" if self.epad['epad'] else '',
x = pcbnew.ToMM(self.package['width']),
y = pcbnew.ToMM(self.package['height']),
p = pcbnew.ToMM(self.pads['pitch'])
)
def BuildThisFootprint(self):
pads = self.parameters["Pads"]
pad_pitch = pads["pad pitch"]
pad_length = pads["pad length"]
pad_width = pads["pad width"]
pad_pitch = self.pads["pitch"]
pad_length = self.pads["length"]
pad_width = self.pads["width"]
v_pitch = self.parameters["Package"]["package height"]
h_pitch = self.parameters["Package"]["package width"]
v_pitch = self.package["height"]
h_pitch = self.package["width"]
pads_per_row = pads["*n"] // 4
pads_per_row = int(self.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
pad_shape = pcbnew.PAD_SHAPE_OVAL if self.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)
@@ -97,79 +122,88 @@ class QFNWizard(HFPW.HelpfulFootprintWizardPlugin):
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
lim_x = self.package["width"] / 2
lim_y = self.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"]
epad_width = self.epad["width"]
epad_length = self.epad["length"]
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)
epad_ny = self.epad["x divisions"]
epad_nx = self.epad["y divisions"]
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)
epad_via_drill = self.epad["thermal vias drill"]
#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)])
# Create a central exposed pad?
if self.epad['epad'] == True:
epad_num = self.pads['n'] + 1
epad_w = epad_length / epad_nx
epad_l = epad_width / epad_ny
# Create the epad
epad = PA.PadMaker(self.module).SMDPad( epad_w, epad_l, shape=pcbnew.PAD_SHAPE_RECT )
epad.SetLocalSolderPasteMargin( -1 * self.epad['paste margin'] )
# set pad layers
layers = pcbnew.LSET(pcbnew.F_Mask)
layers.AddLayer(pcbnew.F_Cu)
layers.AddLayer(pcbnew.F_Paste)
epad.SetPadName(epad_num)
array = PA.EPADGridArray( epad, epad_ny, epad_nx, epad_l, epad_w, pcbnew.wxPoint(0,0) )
array.SetFirstPadInArray(epad_num)
array.AddPadsToModule(self.draw)
if self.epad['thermal vias']:
# create the thermal via
via_diam = min(epad_w, epad_l) / 2
via_drill = min(via_diam / 2, epad_via_drill)
via = PA.PadMaker(self.module).THRoundPad(via_diam, via_drill)
layers = pcbnew.LSET.AllCuMask()
layers.AddLayer(pcbnew.B_Mask)
layers.AddLayer(pcbnew.F_Mask)
via.SetLayerSet(layers)
via_array = PA.EPADGridArray(via, epad_ny, epad_nx, epad_l, epad_w, pcbnew.wxPoint(0,0) )
via_array.SetFirstPadInArray(epad_num)
via_array.AddPadsToModule(self.draw)
# Draw the package outline on the F.Fab layer
bevel = min( pcbnew.FromMM(1.0), self.package['width']/2, self.package['height']/2 )
self.draw.SetLayer(pcbnew.F_Fab)
w = self.package['width']
h = self.package['height']
self.draw.BoxWithDiagonalAtCorner(0, 0, w, h, bevel)
# Silkscreen
self.draw.SetLayer(pcbnew.F_SilkS)
offset = self.draw.GetLineThickness()
clip = row_len / 2 + self.pads['pitch']
self.draw.Polyline( [ [ clip, -h/2-offset], [ w/2+offset,-h/2-offset], [ w/2+offset, -clip] ] ) # top right
self.draw.Polyline( [ [ clip, h/2+offset], [ w/2+offset, h/2+offset], [ w/2+offset, clip] ] ) # bottom right
self.draw.Polyline( [ [-clip, h/2+offset], [-w/2-offset, h/2+offset], [-w/2-offset, clip] ] ) # bottom left
# Add pin-1 indication as per IPC-7351C
self.draw.Line(-clip, -h/2-offset, -w/2-pad_length/2, -h/2-offset)
# Courtyard
cmargin = self.parameters["Package"]["courtyard margin"]
cmargin = self.package["margin"]
self.draw.SetLayer(pcbnew.F_CrtYd)
sizex = (lim_x + cmargin) * 2 + pad_length/2.
sizey = (lim_y + cmargin) * 2 + pad_length/2.
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)
sizex = pcbnew.PutOnGridMM(sizex, 0.1)
sizey = pcbnew.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))
+68 -35
View File
@@ -17,53 +17,64 @@
from __future__ import division
import pcbnew
import HelpfulFootprintWizardPlugin
import FootprintWizardBase
import PadArray as PA
class QFPWizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin):
class QFPWizard(FootprintWizardBase.FootprintWizard):
def GetName(self):
return "QFP"
def GetDescription(self):
return "Quad Flat Package footprint wizard"
return "Quad Flat Package (QFP) 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", "n", self.uInteger, 100, multiple=4, min_value=4)
self.AddParam("Pads", "pitch", self.uMM, 0.5, designator='e')
self.AddParam("Pads", "width", self.uMM, 0.25, designator='X1')
self.AddParam("Pads", "length", self.uMM, 1.5, designator='Y1')
self.AddParam("Pads", "horizontal spacing", self.uMM, 15, designator='C1')
self.AddParam("Pads", "vertical spacing", self.uMM, 15, designator='C2')
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)
self.AddParam("Package", "width", self.uMM, 14, designator='D1')
self.AddParam("Package", "height", self.uMM, 14, designator='E1')
self.AddParam("Package", "courtyard margin", self.uMM, 0.25, min_value=0.2)
@property
def pads(self):
return self.parameters['Pads']
@property
def package(self):
return self.parameters['Package']
def CheckParameters(self):
self.CheckParamInt("Pads", "*n", is_multiple_of=4)
self.CheckParamBool("Pads", "*oval")
# todo - custom checking
pass
def GetValue(self):
return "QFP_%d" % self.parameters["Pads"]["*n"]
return "QFP-{n}_{x:g}x{y:g}_Pitch{p:g}mm".format(
n = self.pads['n'],
x = pcbnew.ToMM(self.package['width']),
y = pcbnew.ToMM(self.package['height']),
p = pcbnew.ToMM(self.pads['pitch'])
)
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"]
pad_pitch = self.pads["pitch"]
pad_length = self.pads["length"]
pad_width = self.pads["width"]
v_pitch = pads["vertical pitch"]
h_pitch = pads["horizontal pitch"]
v_pitch = self.pads["vertical spacing"]
h_pitch = self.pads["horizontal spacing"]
pads_per_row = pads["*n"] // 4
pads_per_row = int(self.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
pad_shape = pcbnew.PAD_SHAPE_OVAL if self.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)
@@ -95,27 +106,49 @@ class QFPWizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin):
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
offset = pcbnew.FromMM(0.15)
x = self.parameters["Package"]["width"] / 2 + offset
y = self.parameters["Package"]["height"] / 2 + offset
inner = (row_len / 2) + pad_pitch
#top left - diagonal
self.draw.Line(-lim_x, -inner, -inner, -lim_y)
# Add outline to F_Fab layer
self.draw.SetLayer(pcbnew.F_Fab)
bevel = min( pcbnew.FromMM(1.0), self.package['width']/2, self.package['height']/2 )
w = self.package['width']
h = self.package['height']
# outermost limits of pins
right_edge = (h_pitch + pad_length) / 2
left_edge = -right_edge
bottom_edge = (v_pitch + pad_length) / 2
top_edge = -bottom_edge
self.draw.BoxWithDiagonalAtCorner(0, 0, w, h, bevel)
# Draw silkscreen
self.draw.SetLayer(pcbnew.F_SilkS)
#top left - as per IPC-7351C
self.draw.Polyline([(-inner, -y), (-x, -y), (-x, -inner), (left_edge, -inner)])
# top right
self.draw.Polyline([(inner, -lim_y), (lim_x, -lim_y), (lim_x, -inner)])
self.draw.Polyline([(inner, -y), (x, -y), (x, -inner)])
# bottom left
self.draw.Polyline([(-inner, lim_y), (-lim_x, lim_y), (-lim_x, inner)])
self.draw.Polyline([(-inner, y), (-x, y), (-x, inner)])
# bottom right
self.draw.Polyline([(inner, lim_y), (lim_x, lim_y), (lim_x, inner)])
self.draw.Polyline([(inner, y), (x, y), (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
sizex = (right_edge + cmargin) * 2
sizey = (bottom_edge + 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)
sizex = pcbnew.PutOnGridMM(sizex, 0.1)
sizey = pcbnew.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))
+827
View File
@@ -0,0 +1,827 @@
#
# QR Code Generator for Python
#
# Copyright (c) 2012 Kazuhiko Arase
#
# URL: http://www.d-project.com/
#
# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license.php
#
# The word 'QR Code' is registered trademark of
# DENSO WAVE INCORPORATED
# http://www.denso-wave.com/qrcode/faqpatent-e.html
#
"""QR Code Generator for Python
from qrcode import QRCode, ErrorCorrectLevel
# generate with explicit type number
qr = QRCode()
qr.setTypeNumber(4)
qr.setErrorCorrectLevel(ErrorCorrectLevel.M)
qr.addData('here comes qr!')
qr.make()
# generate with auto type number
# qr = QRCode.getMinimumQRCode('here comes qr!', ErrorCorrectLevel.M)
# create an image
for r in range(qr.getModuleCount() ):
for c in range(qr.getModuleCount() ):
color = black if qr.isDark(r, c) else white
# set pixel ...
"""
class QRCode:
PAD0 = 0xEC
PAD1 = 0x11
def __init__(self):
self.typeNumber = 1
self.errorCorrectLevel = ErrorCorrectLevel.H
self.qrDataList = []
self.modules = []
self.moduleCount = 0
def getTypeNumber(self):
return self.typeNumber
def setTypeNumber(self, typeNumber):
self.typeNumber = typeNumber
def getErrorCorrectLevel(self):
return self.errorCorrectLevel
def setErrorCorrectLevel(self, errorCorrectLevel):
self.errorCorrectLevel = errorCorrectLevel
def clearData(self):
self.qrDataList = []
def addData(self, data):
self.qrDataList.append(QR8BitByte(data) )
def getDataCount(self):
return len(self.qrDataList)
def getData(self, index):
return self.qrDataList[index]
def isDark(self, row, col):
return (self.modules[row][col] if self.modules[row][col] != None
else False)
def getModuleCount(self):
return self.moduleCount
def make(self):
self._make(False, self._getBestMaskPattern() )
def _getBestMaskPattern(self):
minLostPoint = 0
pattern = 0
for i in range(8):
self._make(True, i)
lostPoint = QRUtil.getLostPoint(self)
if i == 0 or minLostPoint > lostPoint:
minLostPoint = lostPoint
pattern = i
return pattern
def _make(self, test, maskPattern):
self.moduleCount = self.typeNumber * 4 + 17
self.modules = [[None] * self.moduleCount
for i in range(self.moduleCount)]
self._setupPositionProbePattern(0, 0)
self._setupPositionProbePattern(self.moduleCount - 7, 0)
self._setupPositionProbePattern(0, self.moduleCount - 7)
self._setupPositionAdjustPattern()
self._setupTimingPattern()
self._setupTypeInfo(test, maskPattern)
if self.typeNumber >= 7:
self._setupTypeNumber(test)
data = QRCode._createData(
self.typeNumber,
self.errorCorrectLevel,
self.qrDataList)
self._mapData(data, maskPattern)
def _mapData(self, data, maskPattern):
rows = list(range(self.moduleCount) )
cols = [col - 1 if col <= 6 else col
for col in range(self.moduleCount - 1, 0, -2)]
maskFunc = QRUtil.getMaskFunction(maskPattern)
byteIndex = 0
bitIndex = 7
for col in cols:
rows.reverse()
for row in rows:
for c in range(2):
if self.modules[row][col - c] == None:
dark = False
if byteIndex < len(data):
dark = ( (data[byteIndex] >> bitIndex) & 1) == 1
if maskFunc(row, col - c):
dark = not dark
self.modules[row][col - c] = dark
bitIndex -= 1
if bitIndex == -1:
byteIndex += 1
bitIndex = 7
def _setupPositionAdjustPattern(self):
pos = QRUtil.getPatternPosition(self.typeNumber)
for row in pos:
for col in pos:
if self.modules[row][col] != None:
continue
for r in range(-2, 3):
for c in range(-2, 3):
self.modules[row + r][col + c] = (
r == -2 or r == 2 or c == -2 or c == 2
or (r == 0 and c == 0) )
def _setupPositionProbePattern(self, row, col):
for r in range(-1, 8):
for c in range(-1, 8):
if (row + r <= -1 or self.moduleCount <= row + r
or col + c <= -1 or self.moduleCount <= col + c):
continue
self.modules[row + r][col + c] = (
(0 <= r and r <= 6 and (c == 0 or c == 6) )
or (0 <= c and c <= 6 and (r == 0 or r == 6) )
or (2 <= r and r <= 4 and 2 <= c and c <= 4) )
def _setupTimingPattern(self):
for r in range(8, self.moduleCount - 8):
if self.modules[r][6] != None:
continue
self.modules[r][6] = r % 2 == 0
for c in range(8, self.moduleCount - 8):
if self.modules[6][c] != None:
continue
self.modules[6][c] = c % 2 == 0
def _setupTypeNumber(self, test):
bits = QRUtil.getBCHTypeNumber(self.typeNumber)
for i in range(18):
self.modules[i // 3][i % 3 + self.moduleCount - 8 - 3] = (
not test and ( (bits >> i) & 1) == 1)
for i in range(18):
self.modules[i % 3 + self.moduleCount - 8 - 3][i // 3] = (
not test and ( (bits >> i) & 1) == 1)
def _setupTypeInfo(self, test, maskPattern):
data = (self.errorCorrectLevel << 3) | maskPattern
bits = QRUtil.getBCHTypeInfo(data)
# vertical
for i in range(15):
mod = not test and ( (bits >> i) & 1) == 1
if i < 6:
self.modules[i][8] = mod
elif i < 8:
self.modules[i + 1][8] = mod
else:
self.modules[self.moduleCount - 15 + i][8] = mod
# horizontal
for i in range(15):
mod = not test and ( (bits >> i) & 1) == 1
if i < 8:
self.modules[8][self.moduleCount - i - 1] = mod
elif i < 9:
self.modules[8][15 - i - 1 + 1] = mod
else:
self.modules[8][15 - i - 1] = mod
# fixed
self.modules[self.moduleCount - 8][8] = not test
@staticmethod
def _createData(typeNumber, errorCorrectLevel, dataArray):
rsBlocks = RSBlock.getRSBlocks(typeNumber, errorCorrectLevel)
buffer = BitBuffer()
for data in dataArray:
buffer.put(data.getMode(), 4)
buffer.put(data.getLength(), data.getLengthInBits(typeNumber) )
data.write(buffer)
totalDataCount = sum(rsBlock.getDataCount()
for rsBlock in rsBlocks)
if buffer.getLengthInBits() > totalDataCount * 8:
raise Exception('code length overflow. (%s > %s)' %
(buffer.getLengthInBits(), totalDataCount * 8) )
# end code
if buffer.getLengthInBits() + 4 <= totalDataCount * 8:
buffer.put(0, 4)
# padding
while buffer.getLengthInBits() % 8 != 0:
buffer.put(False)
# padding
while True:
if buffer.getLengthInBits() >= totalDataCount * 8:
break
buffer.put(QRCode.PAD0, 8)
if buffer.getLengthInBits() >= totalDataCount * 8:
break
buffer.put(QRCode.PAD1, 8)
return QRCode._createBytes(buffer, rsBlocks)
@staticmethod
def _createBytes(buffer, rsBlocks):
offset = 0
maxDcCount = 0
maxEcCount = 0
dcdata = [None] * len(rsBlocks)
ecdata = [None] * len(rsBlocks)
for r in range(len(rsBlocks) ):
dcCount = rsBlocks[r].getDataCount()
ecCount = rsBlocks[r].getTotalCount() - dcCount
maxDcCount = max(maxDcCount, dcCount)
maxEcCount = max(maxEcCount, ecCount)
dcdata[r] = [0] * dcCount
for i in range(len(dcdata[r] ) ):
dcdata[r][i] = 0xff & buffer.getBuffer()[i + offset]
offset += dcCount
rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount)
rawPoly = Polynomial(dcdata[r], rsPoly.getLength() - 1)
modPoly = rawPoly.mod(rsPoly)
ecdata[r] = [0] * (rsPoly.getLength() - 1)
for i in range(len(ecdata[r]) ):
modIndex = i + modPoly.getLength() - len(ecdata[r])
ecdata[r][i] = modPoly.get(modIndex) if modIndex >= 0 else 0
totalCodeCount = sum(rsBlock.getTotalCount()
for rsBlock in rsBlocks)
data = [0] * totalCodeCount
index = 0
for i in range(maxDcCount):
for r in range(len(rsBlocks) ):
if i < len(dcdata[r] ):
data[index] = dcdata[r][i]
index += 1
for i in range(maxEcCount):
for r in range(len(rsBlocks) ):
if i < len(ecdata[r] ):
data[index] = ecdata[r][i]
index += 1
return data
@staticmethod
def getMinimumQRCode(data, errorCorrectLevel):
mode = Mode.MODE_8BIT_BYTE # fixed to 8bit byte
qr = QRCode()
qr.setErrorCorrectLevel(errorCorrectLevel)
qr.addData(data)
length = qr.getData(0).getLength()
for typeNumber in range(1, 11):
if length <= QRUtil.getMaxLength(
typeNumber, mode, errorCorrectLevel):
qr.setTypeNumber(typeNumber)
break
qr.make()
return qr
class Mode:
MODE_NUMBER = 1 << 0
MODE_ALPHA_NUM = 1 << 1
MODE_8BIT_BYTE = 1 << 2
MODE_KANJI = 1 << 3
class ErrorCorrectLevel:
L = 1 # 7%
M = 0 # 15%
Q = 3 # 25%
H = 2 # 30%
class MaskPattern:
PATTERN000 = 0
PATTERN001 = 1
PATTERN010 = 2
PATTERN011 = 3
PATTERN100 = 4
PATTERN101 = 5
PATTERN110 = 6
PATTERN111 = 7
class QRUtil:
@staticmethod
def getPatternPosition(typeNumber):
return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1]
PATTERN_POSITION_TABLE = [
[],
[6, 18],
[6, 22],
[6, 26],
[6, 30],
[6, 34],
[6, 22, 38],
[6, 24, 42],
[6, 26, 46],
[6, 28, 50],
[6, 30, 54],
[6, 32, 58],
[6, 34, 62],
[6, 26, 46, 66],
[6, 26, 48, 70],
[6, 26, 50, 74],
[6, 30, 54, 78],
[6, 30, 56, 82],
[6, 30, 58, 86],
[6, 34, 62, 90],
[6, 28, 50, 72, 94],
[6, 26, 50, 74, 98],
[6, 30, 54, 78, 102],
[6, 28, 54, 80, 106],
[6, 32, 58, 84, 110],
[6, 30, 58, 86, 114],
[6, 34, 62, 90, 118],
[6, 26, 50, 74, 98, 122],
[6, 30, 54, 78, 102, 126],
[6, 26, 52, 78, 104, 130],
[6, 30, 56, 82, 108, 134],
[6, 34, 60, 86, 112, 138],
[6, 30, 58, 86, 114, 142],
[6, 34, 62, 90, 118, 146],
[6, 30, 54, 78, 102, 126, 150],
[6, 24, 50, 76, 102, 128, 154],
[6, 28, 54, 80, 106, 132, 158],
[6, 32, 58, 84, 110, 136, 162],
[6, 26, 54, 82, 110, 138, 166],
[6, 30, 58, 86, 114, 142, 170]
]
MAX_LENGTH = [
[ [41, 25, 17, 10], [34, 20, 14, 8], [27, 16, 11, 7], [17, 10, 7, 4] ],
[ [77, 47, 32, 20], [63, 38, 26, 16], [48, 29, 20, 12], [34, 20, 14, 8] ],
[ [127, 77, 53, 32], [101, 61, 42, 26], [77, 47, 32, 20], [58, 35, 24, 15] ],
[ [187, 114, 78, 48], [149, 90, 62, 38], [111, 67, 46, 28], [82, 50, 34, 21] ],
[ [255, 154, 106, 65], [202, 122, 84, 52], [144, 87, 60, 37], [106, 64, 44, 27] ],
[ [322, 195, 134, 82], [255, 154, 106, 65], [178, 108, 74, 45], [139, 84, 58, 36] ],
[ [370, 224, 154, 95], [293, 178, 122, 75], [207, 125, 86, 53], [154, 93, 64, 39] ],
[ [461, 279, 192, 118], [365, 221, 152, 93], [259, 157, 108, 66], [202, 122, 84, 52] ],
[ [552, 335, 230, 141], [432, 262, 180, 111], [312, 189, 130, 80], [235, 143, 98, 60] ],
[ [652, 395, 271, 167], [513, 311, 213, 131], [364, 221, 151, 93], [288, 174, 119, 74] ]
]
@staticmethod
def getMaxLength(typeNumber, mode, errorCorrectLevel):
t = typeNumber - 1
e = {
ErrorCorrectLevel.L: 0,
ErrorCorrectLevel.M: 1,
ErrorCorrectLevel.Q: 2,
ErrorCorrectLevel.H: 3
}[errorCorrectLevel]
m = {
Mode.MODE_NUMBER: 0,
Mode.MODE_ALPHA_NUM: 1,
Mode.MODE_8BIT_BYTE: 2,
Mode.MODE_KANJI: 3
}[mode]
return QRUtil.MAX_LENGTH[t][e][m]
@staticmethod
def getErrorCorrectPolynomial(errorCorrectLength):
a = Polynomial([1])
for i in range(errorCorrectLength):
a = a.multiply(Polynomial([1, QRMath.gexp(i)]) )
return a
@staticmethod
def getMaskFunction(maskPattern):
return {
MaskPattern.PATTERN000:
lambda i, j: (i + j) % 2 == 0,
MaskPattern.PATTERN001:
lambda i, j: i % 2 == 0,
MaskPattern.PATTERN010:
lambda i, j: j % 3 == 0,
MaskPattern.PATTERN011:
lambda i, j: (i + j) % 3 == 0,
MaskPattern.PATTERN100:
lambda i, j: (i // 2 + j // 3) % 2 == 0,
MaskPattern.PATTERN101:
lambda i, j: (i * j) % 2 + (i * j) % 3 == 0,
MaskPattern.PATTERN110:
lambda i, j: ( (i * j) % 2 + (i * j) % 3) % 2 == 0,
MaskPattern.PATTERN111:
lambda i, j: ( (i * j) % 3 + (i + j) % 2) % 2 == 0
}[maskPattern]
@staticmethod
def getLostPoint(qrcode):
moduleCount = qrcode.getModuleCount()
lostPoint = 0
# LEVEL1
for row in range(moduleCount):
for col in range(moduleCount):
sameCount = 0
dark = qrcode.isDark(row, col)
for r in range(-1, 2):
if row + r < 0 or moduleCount <= row + r:
continue
for c in range(-1, 2):
if col + c < 0 or moduleCount <= col + c:
continue
if r == 0 and c == 0:
continue
if dark == qrcode.isDark(row + r, col + c):
sameCount += 1
if sameCount > 5:
lostPoint += (3 + sameCount - 5)
# LEVEL2
for row in range(moduleCount - 1):
for col in range(moduleCount - 1):
count = 0
if qrcode.isDark(row, col):
count += 1
if qrcode.isDark(row + 1, col):
count += 1
if qrcode.isDark(row, col + 1):
count += 1
if qrcode.isDark(row + 1, col + 1):
count += 1
if count == 0 or count == 4:
lostPoint += 3
# LEVEL3
for row in range(moduleCount):
for col in range(moduleCount - 6):
if (qrcode.isDark(row, col)
and not qrcode.isDark(row, col + 1)
and qrcode.isDark(row, col + 2)
and qrcode.isDark(row, col + 3)
and qrcode.isDark(row, col + 4)
and not qrcode.isDark(row, col + 5)
and qrcode.isDark(row, col + 6) ):
lostPoint += 40
for col in range(moduleCount):
for row in range(moduleCount - 6):
if (qrcode.isDark(row, col)
and not qrcode.isDark(row + 1, col)
and qrcode.isDark(row + 2, col)
and qrcode.isDark(row + 3, col)
and qrcode.isDark(row + 4, col)
and not qrcode.isDark(row + 5, col)
and qrcode.isDark(row + 6, col) ):
lostPoint += 40
# LEVEL4
darkCount = 0
for col in range(moduleCount):
for row in range(moduleCount):
if qrcode.isDark(row, col):
darkCount += 1
ratio = abs(100 * darkCount // moduleCount // moduleCount - 50) // 5
lostPoint += ratio * 10
return lostPoint
G15 = ( (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) |
(1 << 2) | (1 << 1) | (1 << 0) )
G18 = ( (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) |
(1 << 8) | (1 << 5) | (1 << 2) | (1 << 0) )
G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1)
@staticmethod
def getBCHTypeInfo(data):
d = data << 10
while QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0:
d ^= (QRUtil.G15 << (QRUtil.getBCHDigit(d) -
QRUtil.getBCHDigit(QRUtil.G15) ) )
return ( (data << 10) | d) ^ QRUtil.G15_MASK
@staticmethod
def getBCHTypeNumber(data):
d = data << 12
while QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0:
d ^= (QRUtil.G18 << (QRUtil.getBCHDigit(d) -
QRUtil.getBCHDigit(QRUtil.G18) ) )
return (data << 12) | d
@staticmethod
def getBCHDigit(data):
digit = 0
while data != 0:
digit += 1
data >>= 1
return digit
@staticmethod
def stringToBytes(s):
return [ord(c) & 0xff for c in s]
class QR8BitByte:
def __init__(self, data):
self.mode = Mode.MODE_8BIT_BYTE
self.data = data
def getMode(self):
return self.mode
def getData(self):
return self.data
'''
def write(self, buffer): raise Exception('not implemented.')
def getLength(self): raise Exception('not implemented.')
'''
def write(self, buffer):
data = QRUtil.stringToBytes(self.getData() )
for d in data:
buffer.put(d, 8)
def getLength(self):
return len(QRUtil.stringToBytes(self.getData() ) )
def getLengthInBits(self, type):
if 1 <= type and type < 10: # 1 - 9
return {
Mode.MODE_NUMBER: 10,
Mode.MODE_ALPHA_NUM: 9,
Mode.MODE_8BIT_BYTE: 8,
Mode.MODE_KANJI: 8
}[self.mode]
elif type < 27: # 10 - 26
return {
Mode.MODE_NUMBER: 12,
Mode.MODE_ALPHA_NUM: 11,
Mode.MODE_8BIT_BYTE: 16,
Mode.MODE_KANJI: 10
}[self.mode]
elif type < 41: # 27 - 40
return {
Mode.MODE_NUMBER: 14,
Mode.MODE_ALPHA_NUM: 13,
Mode.MODE_8BIT_BYTE: 16,
Mode.MODE_KANJI: 12
}[self.mode]
else:
raise Exception('type:%s' % type)
class QRMath:
EXP_TABLE = None
LOG_TABLE = None
@staticmethod
def _init():
QRMath.EXP_TABLE = [0] * 256
for i in range(256):
QRMath.EXP_TABLE[i] = (1 << i if i < 8 else
QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^
QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8])
QRMath.LOG_TABLE = [0] * 256
for i in range(255):
QRMath.LOG_TABLE[QRMath.EXP_TABLE[i] ] = i
@staticmethod
def glog(n):
if n < 1:
raise Exception('log(%s)' % n)
return QRMath.LOG_TABLE[n]
@staticmethod
def gexp(n):
while n < 0:
n += 255
while n >= 256:
n -= 255
return QRMath.EXP_TABLE[n]
# initialize statics
QRMath._init()
class Polynomial:
def __init__(self, num, shift=0):
offset = 0
length = len(num)
while offset < length and num[offset] == 0:
offset += 1
self.num = num[offset:] + [0] * shift
def get(self, index):
return self.num[index]
def getLength(self):
return len(self.num)
def __repr__(self):
return ','.join( [str(self.get(i) )
for i in range(self.getLength() ) ] )
def toLogString(self):
return ','.join( [str(QRMath.glog(self.get(i) ) )
for i in range(self.getLength() ) ] )
def multiply(self, e):
num = [0] * (self.getLength() + e.getLength() - 1)
for i in range(self.getLength() ):
for j in range(e.getLength() ):
num[i + j] ^= QRMath.gexp(QRMath.glog(self.get(i) ) +
QRMath.glog(e.get(j) ) )
return Polynomial(num)
def mod(self, e):
if self.getLength() - e.getLength() < 0:
return self
ratio = QRMath.glog(self.get(0) ) - QRMath.glog(e.get(0) )
num = self.num[:]
for i in range(e.getLength() ):
num[i] ^= QRMath.gexp(QRMath.glog(e.get(i) ) + ratio)
return Polynomial(num).mod(e)
class RSBlock:
RS_BLOCK_TABLE = [
# L
# M
# Q
# H
# 1
[1, 26, 19],
[1, 26, 16],
[1, 26, 13],
[1, 26, 9],
# 2
[1, 44, 34],
[1, 44, 28],
[1, 44, 22],
[1, 44, 16],
# 3
[1, 70, 55],
[1, 70, 44],
[2, 35, 17],
[2, 35, 13],
# 4
[1, 100, 80],
[2, 50, 32],
[2, 50, 24],
[4, 25, 9],
# 5
[1, 134, 108],
[2, 67, 43],
[2, 33, 15, 2, 34, 16],
[2, 33, 11, 2, 34, 12],
# 6
[2, 86, 68],
[4, 43, 27],
[4, 43, 19],
[4, 43, 15],
# 7
[2, 98, 78],
[4, 49, 31],
[2, 32, 14, 4, 33, 15],
[4, 39, 13, 1, 40, 14],
# 8
[2, 121, 97],
[2, 60, 38, 2, 61, 39],
[4, 40, 18, 2, 41, 19],
[4, 40, 14, 2, 41, 15],
# 9
[2, 146, 116],
[3, 58, 36, 2, 59, 37],
[4, 36, 16, 4, 37, 17],
[4, 36, 12, 4, 37, 13],
# 10
[2, 86, 68, 2, 87, 69],
[4, 69, 43, 1, 70, 44],
[6, 43, 19, 2, 44, 20],
[6, 43, 15, 2, 44, 16]
]
def __init__(self, totalCount, dataCount):
self.totalCount = totalCount
self.dataCount = dataCount
def getDataCount(self):
return self.dataCount
def getTotalCount(self):
return self.totalCount
def __repr__(self):
return ('(total=%s,data=%s)' % (self.totalCount, self.dataCount) )
@staticmethod
def getRSBlocks(typeNumber, errorCorrectLevel):
rsBlock = RSBlock.getRsBlockTable(typeNumber, errorCorrectLevel)
length = len(rsBlock) // 3
list = []
for i in range(length):
count = rsBlock[i * 3 + 0]
totalCount = rsBlock[i * 3 + 1]
dataCount = rsBlock[i * 3 + 2]
list += [RSBlock(totalCount, dataCount)] * count
return list
@staticmethod
def getRsBlockTable(typeNumber, errorCorrectLevel):
return {
ErrorCorrectLevel.L:
RSBlock.RS_BLOCK_TABLE[ (typeNumber - 1) * 4 + 0],
ErrorCorrectLevel.M:
RSBlock.RS_BLOCK_TABLE[ (typeNumber - 1) * 4 + 1],
ErrorCorrectLevel.Q:
RSBlock.RS_BLOCK_TABLE[ (typeNumber - 1) * 4 + 2],
ErrorCorrectLevel.H:
RSBlock.RS_BLOCK_TABLE[ (typeNumber - 1) * 4 + 3]
}[errorCorrectLevel]
class BitBuffer:
def __init__(self, inclements=32):
self.inclements = inclements
self.buffer = [0] * self.inclements
self.length = 0
def getBuffer(self):
return self.buffer
def getLengthInBits(self):
return self.length
def get(self, index):
return ( (self.buffer[index // 8] >> (7 - index % 8) ) & 1) == 1
def putBit(self, bit):
if self.length == len(self.buffer) * 8:
self.buffer += [0] * self.inclements
if bit:
self.buffer[self.length // 8] |= (0x80 >> (self.length % 8) )
self.length += 1
def put(self, num, length):
for i in range(length):
self.putBit( ( (num >> (length - i - 1) ) & 1) == 1)
def __repr__(self):
return ''.join('1' if self.get(i) else '0'
for i in range(self.getLengthInBits() ) )
@@ -0,0 +1,120 @@
# 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 FootprintWizardBase
# Additional import for QRCode
# see https://github.com/kazuhikoarase/qrcode-generator/blob/master/python/qrcode.py
import qrcode
class QRCodeWizard(FootprintWizardBase.FootprintWizard):
GetName = lambda self: '2D Barcode QRCode'
GetDescription = lambda self: 'QR Code'
GetReferencePrefix = lambda self: 'QR***'
GetValue = lambda self: self.module.Value().GetText()
def GenerateParameterList(self):
self.AddParam("Barcode", "Pixel Width", self.uMM, 0.5, min_value=0.4)
self.AddParam("Barcode", "Border", self.uInteger, 0)
self.AddParam("Barcode", "Contents", self.uString, 'Example')
self.AddParam("Barcode", "Negative", self.uBool, False)
self.AddParam("Barcode", "Use SilkS layer", self.uBool, False)
self.AddParam("Barcode", "Use Cu layer", self.uBool, True)
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):
self.Barcode = str(self.parameters['Barcode']['Contents'])
self.X = self.parameters['Barcode']['Pixel Width']
self.negative = self.parameters['Barcode']['Negative']
self.UseSilkS = self.parameters['Barcode']['Use SilkS layer']
self.UseCu = self.parameters['Barcode']['Use Cu layer']
self.border = int(self.parameters['Barcode']['Border'])
self.textHeight = int(self.parameters['Caption']['Height'])
self.module.Value().SetText(str(self.Barcode) )
# Build Qrcode
self.qr = qrcode.QRCode()
self.qr.setTypeNumber(4)
# ErrorCorrectLevel: L = 7%, M = 15% Q = 25% H = 30%
self.qr.setErrorCorrectLevel(qrcode.ErrorCorrectLevel.M)
self.qr.addData(str(self.Barcode))
self.qr.make()
def _drawPixel(self, xposition, yposition):
# build a rectangular pad: as a dot
pad = pcbnew.D_PAD(self.module)
pad.SetSize(pcbnew.wxSize(self.X, self.X))
pad.SetShape(pcbnew.PAD_SHAPE_RECT)
pad.SetAttribute(pcbnew.PAD_ATTRIB_SMD)
layerset = pcbnew.LSET()
if self.UseCu:
layerset.AddLayer(pcbnew.F_Cu)
layerset.AddLayer(pcbnew.F_Mask)
if self.UseSilkS:
layerset.AddLayer(pcbnew.F_SilkS)
pad.SetLayerSet( layerset )
pad.SetPosition(pcbnew.wxPoint(xposition,yposition))
pad.SetPadName("1")
self.module.Add(pad)
def BuildThisFootprint(self):
if self.border >= 0:
# Adding border: Create a new array larger than the self.qr.modules
sz = self.qr.modules.__len__() + (self.border * 2)
arrayToDraw = [ [ 0 for a in range(sz) ] for b in range(sz) ]
lineposition = self.border
for i in self.qr.modules:
columnposition = self.border
for j in i:
arrayToDraw[lineposition][columnposition] = j
columnposition += 1
lineposition += 1
else:
# No border: using array as is
arrayToDraw = self.qr.modules
# used many times...
half_number_of_elements = arrayToDraw.__len__() / 2
# Center position of QrCode
yposition = - int(half_number_of_elements * self.X)
for line in arrayToDraw:
xposition = - int(half_number_of_elements * self.X)
for pixel in line:
# Trust table for drawing a pixel
# Negative is a boolean;
# each pixel is a boolean (need to draw of not)
# Negative | Pixel | Result
# 0 | 0 | 0
# 0 | 1 | 1
# 1 | 0 | 1
# 1 | 1 | 0
# => Draw as Xor
if self.negative != pixel: # Xor...
self._drawPixel(xposition, yposition)
xposition += self.X
yposition += self.X
#int((5 + half_number_of_elements) * self.X))
textPosition = int((self.textHeight) + ((1 + half_number_of_elements) * self.X))
self.module.Value().SetPosition(pcbnew.wxPoint(0, - textPosition))
self.module.Reference().SetPosition(pcbnew.wxPoint(0, textPosition))
self.module.Value().SetLayer(pcbnew.F_SilkS)
QRCodeWizard().register()
+15 -21
View File
@@ -17,7 +17,7 @@
from __future__ import division
import pcbnew
import HelpfulFootprintWizardPlugin as HFPW
import FootprintWizardBase
import PadArray as PA
@@ -35,7 +35,7 @@ class RowedGridArray(PA.PadGridArray):
return x+1
class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
class RowedFootprint(FootprintWizardBase.FootprintWizard):
pad_count_key = 'pad count'
row_count_key = 'row count'
@@ -50,31 +50,25 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
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("Pads", self.pad_count_key, self.uInteger, 24)
self.AddParam("Pads", self.row_count_key, self.uInteger, 2, min_value=1, max_value=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)
self.CheckParam("Pads", self.pad_count_key, multiple=self.parameters['Pads'][self.row_count_key], info='Pads must be multiple of row count')
def BuildThisFootprint(self):
pads = self.parameters["Pads"]
body = self.parameters["Body"]
num_pads = pads['*' + self.pad_count_key]
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]
num_rows = pads[self.row_count_key]
pads_per_row = num_pads // num_rows
@@ -96,7 +90,7 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
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]:
if body[self.silkscreen_inside_key]:
ssy_offset *= -1
ssx = -pin1_posX - ssx_offset
@@ -110,8 +104,8 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
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)
sizex = pcbnew.PutOnGridMM(sizex, 0.1)
sizey = pcbnew.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)
@@ -156,8 +150,8 @@ class SDIPWizard(RowedFootprint):
def GetValue(self):
pads = self.parameters["Pads"]
rows = pads['*' + self.row_count_key]
pad_count = pads['*' + self.pad_count_key]
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 = ""
@@ -185,7 +179,7 @@ class SDIPWizard(RowedFootprint):
def DrawBox(self, ssx, ssy):
if self.parameters["Pads"]['*' + self.row_count_key] == 2:
if self.parameters["Pads"][self.row_count_key] == 2:
# ----------
# |8 7 6 5 |
@@ -208,7 +202,7 @@ class SDIPWizard(RowedFootprint):
#line between pin1 and pin2
pad_pitch = self.parameters["Pads"][self.pad_pitch_key]
pad_cnt = self.parameters["Pads"]['*' + self.pad_count_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)
@@ -226,7 +220,7 @@ class SOICWizard(RowedFootprint):
return "SOIC, MSOP, SSOP, TSSOP, etc, footprint wizard"
def GetValue(self):
pad_count = self.parameters["Pads"]['*' + self.pad_count_key]
pad_count = self.parameters["Pads"][self.pad_count_key]
return "%s-%d" % ("SOIC", pad_count)
def GenerateParameterList(self):
+28 -27
View File
@@ -24,10 +24,10 @@
#
from pcbnew import *
import HelpfulFootprintWizardPlugin as HFPW
import FootprintWizardBase
import pcbnew
class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin):
class TouchSliderWizard(FootprintWizardBase.FootprintWizard):
def GetName(self):
"""
@@ -44,16 +44,23 @@ class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin):
return 'Capacitive Touch Slider wizard'
def GetValue(self):
steps = int(self.parameters["Pads"]["*steps"])
return "TS"+str(steps)
return "TouchSlider-{s}_{x:g}x{y:g}mm".format(
s = self.pads['steps'],
x = pcbnew.ToMM(self.pads['length']),
y = pcbnew.ToMM(self.pads['width'])
)
def GenerateParameterList(self):
self.AddParam("Pads", "steps", self.uNatural, 4)
self.AddParam("Pads", "bands", self.uNatural, 2)
self.AddParam("Pads", "steps", self.uInteger, 4, min_value=2)
self.AddParam("Pads", "bands", self.uInteger, 2, min_value=1)
self.AddParam("Pads", "width", self.uMM, 10)
self.AddParam("Pads", "length", self.uMM, 50)
self.AddParam("Pads", "clearance", self.uMM, 1)
@property
def pads(self):
return self.parameters['Pads']
# build a rectangular pad
def smdRectPad(self,module,size,pos,name):
pad = D_PAD(module)
@@ -82,18 +89,8 @@ class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin):
# 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"]
#TODO - implement custom checks
pass
# The start pad is made of a rectangular pad plus a couple of
# triangular pads facing tips on the middle/right of the first
@@ -177,18 +174,18 @@ class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin):
# 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"]
steps = self.pads["steps"]
bands = self.pads["bands"]
touch_width = self.pads["width"]
touch_length = self.pads["length"]
touch_clearance = self.pads["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
ypos = touch_width/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)
@@ -197,9 +194,13 @@ class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin):
self.module.SetAttributes(MOD_CMS)
# starting pad
pos = wxPointMM(0,0)
band_width = touch_width/bands
xpos = -0.5 * (steps - 1) * step_length
ypos = -0.5 * (bands - 1) * band_width
pos = wxPointMM(pcbnew.ToMM(xpos), pcbnew.ToMM(ypos))
for b in range(bands):
self.AddStrip(pos,steps,band_width,step_length,touch_clearance)
pos += wxPoint(0,band_width)
+6 -5
View File
@@ -15,8 +15,7 @@
from __future__ import division
import pcbnew as B
import HelpfulFootprintWizardPlugin
import FootprintWizardBase
'''
Created on Jan 16, 2015
@@ -49,7 +48,7 @@ class Uss39:
# 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):
class Uss39Wizard(FootprintWizardBase.FootprintWizard):
GetName = lambda self: 'BARCODE USS-39'
GetDescription = lambda self: 'USS-39 Barcode'
GetReferencePrefix = lambda self: 'BARCODE'
@@ -61,18 +60,20 @@ class Uss39Wizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin):
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.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))
@@ -146,4 +147,4 @@ class Uss39Wizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin):
self.draw.Circle(0, 0, B.FromMM(0.25))
self.module.Value().SetLayer(B.F_Fab)
Uss39Wizard().register()
Uss39Wizard().register()
+52 -63
View File
@@ -17,52 +17,41 @@
from __future__ import division
import pcbnew
import HelpfulFootprintWizardPlugin as HFPW
import FootprintWizardBase
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'
class RowedFootprint(FootprintWizardBase.FootprintWizard):
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)
self.AddParam("Pads", "pad count", self.uInteger, 24)
self.AddParam("Pads", "line count", self.uInteger, 2)
self.AddParam("Body", "silkscreen inside", self.uBool, False)
self.AddParam("Body", "courtyard margin", self.uMM, 0.5, min_value=0.2)
@property
def pads(self):
return self.parameters['Pads']
@property
def body(self):
return self.parameters['Body']
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)
# TODO - implement custom checks
pass
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]
pad_count = self.pads['pad count']
pad_Vsize = self.pads['pad height']
pad_Hsize = self.pads['pad width']
line_pitch = self.pads['line spacing']
pad_pitch = self.pads['pitch']
line_count = self.pads['line count']
if line_count == 1:
singleline = True
@@ -74,12 +63,12 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
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]
ssx_offset = pad_Hsize / 2 + self.body['outline x margin']
ssy_offset = pad_Vsize / 2 + self.body['outline y margin']
pin1posX = pad_pitch * (pad_count - 1) / 2
pin1posY = line_pitch * (line_count - 1) / 2
@@ -91,7 +80,7 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
# 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:
if self.body['silkscreen inside'] and line_count == 2:
cornery = pin1posY - ssy_offset
if cornery < linew:
cornery = linew
@@ -99,15 +88,15 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
self.DrawBox(leftx*2, cornery*2)
# Courtyard
cmarginx = body[self.courtyard_x_margin_key]
cmarginy = body[self.courtyard_y_margin_key]
cmarginx = self.body['courtyard margin']
cmarginy = cmarginx
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)
sizex = pcbnew.PutOnGridMM(sizex, 0.1)
sizey = pcbnew.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)
@@ -152,17 +141,17 @@ class ZIPWizard(RowedFootprint):
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)
self.AddParam("Pads", "pitch", self.uMM, 1.27)
self.AddParam("Pads", "pad width", self.uMM, 1.2)
self.AddParam("Pads", "pad height", self.uMM, 2)
self.AddParam("Pads", "line spacing", self.uMM, 2.54)
self.AddParam("Pads", "drill size", self.uMM, 0.8)
self.AddParam("Body", 'outline x margin', self.uMM, 1)
self.AddParam("Body", 'outline y margin', self.uMM, 0.5)
def GetValue(self):
rows = self.parameters["Pads"]['*' + self.line_count_key]
pad_cnt = self.parameters["Pads"]['*' + self.pad_count_key]
rows = self.pads['line count']
pad_cnt = self.pads['pad count']
if rows == 1:
name = "SIP"
@@ -174,9 +163,9 @@ class ZIPWizard(RowedFootprint):
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]
pad_Vsize = self.pads['pad height']
pad_Hsize = self.pads['pad width']
drill = self.pads['drill size']
return PA.PadMaker(self.module).THPad(
pad_Vsize, pad_Hsize, drill, shape=pcbnew.PAD_SHAPE_OVAL)
@@ -192,23 +181,23 @@ class ZOICWizard(RowedFootprint):
return "ZOIC, etc, Footprint Wizard"
def GetValue(self):
return "%s-%d" % ("ZOIC", self.parameters["Pads"]['*' + self.pad_count_key])
return "%s-%d" % ("ZOIC-", self.pads['pad count'])
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("Pads", "pitch", self.uMM, 0.6)
self.AddParam("Pads", "pad width", self.uMM, 0.6)
self.AddParam("Pads", "pad height", self.uMM, 1.8)
self.AddParam("Pads", "line spacing", 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)
self.AddParam("Body", "outline x margin", self.uMM, 0.5)
self.AddParam("Body", "outline y margin", 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]
pad_Vsize = self.pads['pad height']
pad_Hsize = self.pads['pad width']
return PA.PadMaker(self.module).SMDPad(
pad_Vsize, pad_Hsize, shape=pcbnew.PAD_SHAPE_RECT)