Merge lp:~john-j-beard/kicad/fpwizards into lp:kicad/product

Proposed by John Beard
Status: Merged
Merge reported by: John Beard
Merged at revision: not available
Proposed branch: lp:~john-j-beard/kicad/fpwizards
Merge into: lp:kicad/product
Diff against target: 1177 lines (+913/-229)
6 files modified
pcbnew/scripting/plugins/FootprintWizardDrawingAids.py (+134/-0)
pcbnew/scripting/plugins/HelpfulFootprintWizardPlugin.py (+250/-0)
pcbnew/scripting/plugins/PadArray.py (+148/-0)
pcbnew/scripting/plugins/bga_wizard.py (+98/-0)
pcbnew/scripting/plugins/qfp_wizard.py (+83/-229)
pcbnew/scripting/plugins/sdip_wizard.py (+200/-0)
To merge this branch: bzr merge lp:~john-j-beard/kicad/fpwizards
Reviewer Review Type Date Requested Status
KiCad Lead Developers Pending
Review via email: mp+216560@code.launchpad.net

Description of the change

Add a conveneience layer under the Python foortprint wizard interface, along with some new examples of new wizards (SIP/SIP, SOIC/SSOP/TSSOP/MSOP and BGA) and a reworking of an existing wizard (QFN).

For now, this is just a wrapper around the pcbnew classes (and you can write wizards without it, or with only bits of it), but there is no reason it (or parts of it) couldn't be moved into the pcbnew module if wanted.

To post a comment you must log in.
Revision history for this message
jean-pierre charras (jp-charras) wrote :

Thanks.
I committed your patch.

For this kind of change, I prefer a patch instead of a merge request.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'pcbnew/scripting/plugins/FootprintWizardDrawingAids.py'
2--- pcbnew/scripting/plugins/FootprintWizardDrawingAids.py 1970-01-01 00:00:00 +0000
3+++ pcbnew/scripting/plugins/FootprintWizardDrawingAids.py 2014-04-20 22:23:21 +0000
4@@ -0,0 +1,134 @@
5+# This program is free software; you can redistribute it and/or modify
6+# it under the terms of the GNU General Public License as published by
7+# the Free Software Foundation; either version 2 of the License, or
8+# (at your option) any later version.
9+#
10+# This program is distributed in the hope that it will be useful,
11+# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+# GNU General Public License for more details.
14+#
15+# You should have received a copy of the GNU General Public License
16+# along with this program; if not, write to the Free Software
17+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18+# MA 02110-1301, USA.
19+#
20+
21+import pcbnew
22+
23+class FootprintWizardDrawingAids:
24+ """
25+ Collection of handy functions to simplify drawing shapes from within
26+ footprint wizards
27+
28+ A "drawing context" is provided which can be used to set and retain
29+ settings such as line width and layer
30+ """
31+ def __init__(self, module):
32+ self.module = module
33+ #drawing context defaults
34+ self.dc = {
35+ 'layer': pcbnew.SILKSCREEN_N_FRONT,
36+ 'width': pcbnew.FromMM(0.2)
37+ }
38+
39+ def SetWidth(self, width):
40+ self.dc['width'] = width
41+
42+ def SetLayer(self, layer):
43+ self.dc['layer'] = layer
44+
45+ def Line(self, x1, y1, x2, y2):
46+
47+ outline = pcbnew.EDGE_MODULE(self.module)
48+ outline.SetWidth(self.dc['width'])
49+ outline.SetLayer(self.dc['layer'])
50+ outline.SetShape(pcbnew.S_SEGMENT)
51+ start = pcbnew.wxPoint(x1, y1)
52+ end = pcbnew.wxPoint(x2, y2)
53+ outline.SetStartEnd(start, end)
54+ self.module.Add(outline)
55+
56+ # extends from (x1,y1) right
57+ def HLine(self, x, y, l):
58+ """
59+ Draw a horizontal line from (x,y), rightwards
60+ """
61+ self.Line(x, y, x + l, y)
62+
63+ def VLine(self, x, y, l):
64+ """
65+ Draw a vertical line from (x1,y1), downwards
66+ """
67+ self.Line(x, y, x, y + l)
68+
69+ def Polyline(self, pts):
70+
71+ if len(pts) < 2:
72+ return
73+
74+ for i in range(0, len(pts) - 1):
75+ self.Line(pts[i][0], pts[i][1], pts[i+1][0], pts[i+1][1])
76+
77+ def Reference(self, x, y, size):
78+ """
79+ Draw the module's reference as the given point.
80+
81+ The actual setting of the reference is not done in this drawing
82+ aid - that is up to the wizard
83+ """
84+
85+ text_size = pcbnew.wxSize(size, size)
86+
87+ self.module.Reference().SetPos0(pcbnew.wxPoint(x, y))
88+ self.module.Reference().SetTextPosition(self.module.Reference().GetPos0())
89+ self.module.Reference().SetSize(text_size)
90+
91+ def Value(self, x, y, size):
92+ """
93+ As for references, draw the module's value
94+ """
95+ text_size = pcbnew.wxSize(size, size)
96+
97+ self.module.Value().SetPos0(pcbnew.wxPoint(x, y))
98+ self.module.Value().SetTextPosition(self.module.Value().GetPos0())
99+ self.module.Value().SetSize(text_size)
100+
101+ def Box(self, x, y, w, h):
102+ """
103+ Draw a rectangular box, centred at (x,y), with given width and
104+ height
105+ """
106+ self.VLine(x - w/2, y - h/2, h) # left
107+ self.VLine(x + w/2, y - h/2, h) # right
108+ self.HLine(x - w/2, y + h/2, w) # bottom
109+ self.HLine(x - w/2, y - h/2, w) # top
110+
111+ def NotchedBox(self, x, y, w, h, notchW, notchH):
112+ """
113+ Draw a box with a notch in the top edge
114+ """
115+ #limit to half the overall width
116+ notchW = min(x + w/2, notchW)
117+
118+ # draw notch
119+ self.Polyline([ #three sides of box
120+ (x - w/2, y - h/2),
121+ (x - w/2, y + h/2),
122+ (x + w/2, y + h/2),
123+ (x + w/2, y - h/2),
124+ #the notch
125+ (notchW/2, y - h/2),
126+ (notchW/2, y - h/2 + notchH),
127+ (-notchW/2, y - h/2 + notchH),
128+ (-notchW/2, y - h/2),
129+ (x - w/2, y - h/2)
130+ ])
131+
132+ def BoxWithDiagonalAtCorner(self, x, y, w, h, diagSetback):
133+
134+ self.Box(x, y, w, h)
135+
136+ #diagonal corner
137+ self.Line(x - w/2 + diagSetback, x - h/2, x - w/2,
138+ x - h/2 + diagSetback)
139
140=== added file 'pcbnew/scripting/plugins/HelpfulFootprintWizardPlugin.py'
141--- pcbnew/scripting/plugins/HelpfulFootprintWizardPlugin.py 1970-01-01 00:00:00 +0000
142+++ pcbnew/scripting/plugins/HelpfulFootprintWizardPlugin.py 2014-04-20 22:23:21 +0000
143@@ -0,0 +1,250 @@
144+# This program is free software; you can redistribute it and/or modify
145+# it under the terms of the GNU General Public License as published by
146+# the Free Software Foundation; either version 2 of the License, or
147+# (at your option) any later version.
148+#
149+# This program is distributed in the hope that it will be useful,
150+# but WITHOUT ANY WARRANTY; without even the implied warranty of
151+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
152+# GNU General Public License for more details.
153+#
154+# You should have received a copy of the GNU General Public License
155+# along with this program; if not, write to the Free Software
156+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
157+# MA 02110-1301, USA.
158+#
159+
160+import pcbnew
161+import FootprintWizardDrawingAids
162+
163+class FootprintWizardParameterManager:
164+ """
165+ Functions for helpfully managing parameters to a KiCAD Footprint
166+ Wizard.
167+
168+ Abstracts away from whatever structure is used by pcbnew's footprint
169+ wizard class
170+ """
171+
172+ def __init__(self):
173+ self.parameters = {}
174+ self.GenerateParameterList()
175+
176+ def GenerateParameterList(self):
177+ """
178+ Construct parameters here, or leave out to have no parameters
179+ """
180+ pass
181+
182+ def CheckParameters(self):
183+ """
184+ Implement this to make checks on parameter values, filling
185+ parameter_errors (or using the checker routines)
186+
187+ Subclasses can implment their own and override the parent
188+ defaults and add new ones
189+ """
190+ pass
191+
192+ uMM = 1
193+ uMils = 2
194+ uNatural = 3
195+ uBool = 4
196+
197+ def AddParam(self, section, param, unit, default, hint = ''):
198+ """
199+ Add a parameter with some properties.
200+
201+ TODO: Hints are not supported, as there is as yet nowhere to
202+ put them in the KiCAD interface
203+ """
204+
205+ val = None
206+ if unit == self.uMM:
207+ val = pcbnew.FromMM(default)
208+ elif unit == self.uMils:
209+ val = pcbnew.FromMils(default)
210+ elif unit == self.uNatural:
211+ val = default
212+ elif unit == self.uBool:
213+ val = "True" if default else "False" #ugly stringing
214+ else:
215+ print "Warning: Unknown unit type: %s" % unit
216+ return
217+
218+ if unit in [self.uNatural, self.uBool]:
219+ param = "*%s" % param #star prefix for natural
220+
221+ if section not in self.parameters:
222+ self.parameters[section] = {}
223+
224+ self.parameters[section][param] = val
225+
226+ def _PrintParameterTable(self):
227+ """
228+ Pretty-print the parameters we have
229+ """
230+ for name, section in self.parameters.iteritems():
231+ print " %s:" % name
232+
233+ for key, value in section.iteritems():
234+ unit = ""
235+ if (type(value) is int or type(value) is float) and not "*" in key:
236+ unit = "mm"
237+
238+ if "*" in key:
239+ key = key[1:]
240+ else:
241+ value = pcbnew.ToMM(value)
242+
243+ print " %s: %s%s" % (key, value, unit)
244+
245+ def _ParametersHaveErrors(self):
246+ """
247+ Return true if we discovered errors suring parameter processing
248+ """
249+
250+ for name, section in self.parameter_errors.iteritems():
251+ for k, v in section.iteritems():
252+ if v:
253+ return True
254+
255+ return False
256+
257+ def _PrintParameterErrors(self):
258+ """
259+ Pretty-print parameters with errors
260+ """
261+
262+ for name, section in self.parameter_errors.iteritems():
263+ printed_section = False
264+
265+ for key, value in section.iteritems():
266+ if value:
267+ if not printed_section:
268+ print " %s:" % name
269+
270+ print " %s: %s (have %s)" % (key, value,
271+ self.parameters[name][key])
272+
273+ def ProcessParameters(self):
274+ """
275+ Make sure the parameters we have meet whatever expectations the
276+ footprint wizard has of them
277+ """
278+
279+ self.ClearErrors()
280+ self.CheckParameters();
281+
282+ if self._ParametersHaveErrors():
283+ print "Cannot build footprint: Parameters have errors:"
284+ self._PrintParameterErrors()
285+ return False
286+
287+ print "Building new %s footprint with the following parameters:" % self.name
288+
289+ self._PrintParameterTable()
290+ return True
291+
292+ #################################################################
293+ # PARAMETER CHECKERS
294+ #################################################################
295+
296+ def CheckParamPositiveInt(self, section, param, min_value = 1,
297+ max_value = None, is_multiple_of = 1):
298+ """
299+ Make sure a parameter can be made into an int, and enforce
300+ limits if required
301+ """
302+
303+ try:
304+ self.parameters[section][param] = int(self.parameters[section][param])
305+ except ValueError:
306+ self.parameter_errors[section][param] = "Must be a valid integer"
307+ return
308+
309+ if min_value is not None and (self.parameters[section][param] < min_value):
310+ self.parameter_errors[section][param] = "Must be greater than or equal to %d" % (min_value)
311+ return
312+
313+ if max_value is not None and (self.parameters[section][param] > min_value):
314+ self.parameter_errors[section][param] = "Must be less than or equal to %d" % (max_value)
315+ return
316+
317+ if is_multiple_of > 1 and (self.parameters[section][param] % is_multiple_of) > 0:
318+ self.parameter_errors[section][param] = "Must be a multiple of %d" % is_multiple_of
319+ return
320+
321+ return
322+
323+ def CheckParamBool(self, section, param):
324+ """
325+ Make sure a parameter looks like a boolean, convert to native
326+ boolean type if so
327+ """
328+ if str(self.parameters[section][param]).lower() in ["true", "t", "y", "yes", "on", "1", "1.0"]:
329+ self.parameters[section][param] = True;
330+ return
331+ elif str(self.parameters[section][param]).lower() in ["false", "f", "n", "no", "off", "0", "0.0"]:
332+ self.parameters[section][param] = False;
333+ return
334+
335+ self.parameter_errors[section][param] = "Must be boolean (true/false)"
336+ return
337+
338+
339+class HelpfulFootprintWizardPlugin(pcbnew.FootprintWizardPlugin,
340+ FootprintWizardParameterManager):
341+ """
342+ A class to simplify many aspects of footprint creation, leaving only
343+ the foot-print specific routines to the wizards themselves
344+
345+ Generally, you need to implement:
346+ GetReference()
347+ GetValue()
348+ GenerateParameterList()
349+ CheckParameters()
350+ BuildThisFootprint()
351+ GetName()
352+ GetDescription()
353+ """
354+ def __init__(self):
355+ pcbnew.FootprintWizardPlugin.__init__(self)
356+ FootprintWizardParameterManager.__init__(self)
357+
358+ self.name = self.GetName()
359+ self.decription = self.GetDescription()
360+ self.image = self.GetImage()
361+
362+ def GetReference(self):
363+ raise NotImplementedError
364+
365+ def GetValuePrefix(self):
366+ return "U" # footprints needing wizards of often ICs
367+
368+ def GetImage(self):
369+ return ""
370+
371+ def BuildThisFootprint(self):
372+ raise NotImplementedError
373+
374+ def BuildFootprint(self):
375+ """
376+ Actually make the footprint. We defer all but the setup to
377+ the implmenting class
378+ """
379+
380+ if not self.ProcessParameters():
381+ return
382+
383+ self.module = pcbnew.MODULE(None) # create a new module
384+
385+ self.draw = FootprintWizardDrawingAids.FootprintWizardDrawingAids(self.module)
386+
387+ self.module.SetReference(self.GetReference())
388+ self.module.SetValue("%s**" % self.GetValuePrefix())
389+
390+ fpid = pcbnew.FPID(self.module.GetReference()) #the name in library
391+ self.module.SetFPID( fpid )
392+
393+ self.BuildThisFootprint() # implementer's build function
394
395=== added file 'pcbnew/scripting/plugins/PadArray.py'
396--- pcbnew/scripting/plugins/PadArray.py 1970-01-01 00:00:00 +0000
397+++ pcbnew/scripting/plugins/PadArray.py 2014-04-20 22:23:21 +0000
398@@ -0,0 +1,148 @@
399+
400+import pcbnew
401+
402+class PadMaker:
403+ """
404+ Useful construction functions for common types of pads
405+ """
406+
407+ def __init__(self, module):
408+ self.module = module
409+
410+ def THPad(self, w, l, drill, shape = pcbnew.PAD_OVAL):
411+ pad = pcbnew.D_PAD(self.module)
412+
413+ pad.SetSize(pcbnew.wxSize(l, w))
414+
415+ pad.SetShape(shape)
416+
417+ pad.SetAttribute(pcbnew.PAD_STANDARD)
418+ pad.SetLayerMask(pcbnew.PAD_STANDARD_DEFAULT_LAYERS)
419+ pad.SetDrillSize(pcbnew.wxSize(drill, drill))
420+
421+ return pad
422+
423+ def SMDPad(self, w, l, shape = pcbnew.PAD_RECT):
424+ pad = pcbnew.D_PAD(self.module)
425+ pad.SetSize(pcbnew.wxSize(l, w))
426+
427+ pad.SetShape(shape)
428+
429+ pad.SetAttribute(pcbnew.PAD_SMD)
430+ pad.SetLayerMask(pcbnew.PAD_SMD_DEFAULT_LAYERS)
431+
432+ return pad
433+
434+ def SMTRoundPad(self, size):
435+ pad = self.SMDPad(size, size, shape = pcbnew.PAD_CIRCLE)
436+ return pad
437+
438+class PadArray:
439+
440+ def __init__(self):
441+ self.firstPad = 1;
442+
443+ def SetFirstPadInArray(self, fpNum):
444+ self.firstPad = fpNum
445+
446+ # HACK! pad should one day have its own clone method
447+ def ClonePad(self):
448+
449+ pad = pcbnew.D_PAD(self.pad.GetParent())
450+
451+ pad.SetSize(self.pad.GetSize())
452+ pad.SetShape(self.pad.GetShape())
453+ pad.SetAttribute(self.pad.GetAttribute())
454+ pad.SetLayerMask(self.pad.GetLayerMask())
455+ pad.SetDrillSize(self.pad.GetDrillSize())
456+
457+ return pad
458+
459+ def AddPad(self, pad):
460+ self.pad.GetParent().Add(pad)
461+
462+class PadGridArray(PadArray):
463+
464+ def __init__(self, pad, nx, ny, px, py, pin1Pos):
465+ # this pad is more of a "context", we will use it as a source of
466+ # pad data, but not actually add it
467+ self.pad = pad
468+ self.nx = int(nx)
469+ self.ny = int(ny)
470+ self.px = px
471+ self.py = py
472+ self.pin1Pos = pin1Pos
473+
474+ # handy utility function 1 - A, 2 - B, 26 - AA, etc
475+ # aIndex = 0 for 0 - A
476+ def AlphaNameFromNumber(self, n, aIndex = 1):
477+
478+ div, mod = divmod(n - aIndex, 26)
479+ alpha = chr(65 + mod)
480+
481+ if div > 0:
482+ return self.AlphaNameFromNumber(div) + alpha;
483+
484+ return alpha;
485+
486+ # right to left, top to bottom
487+ def NamingFunction(self, x, y):
488+ return self.firstPad + (self.nx * y + x)
489+
490+ #relocate the pad and add it as many times as we need
491+ def AddPadsToModule(self):
492+
493+ for x in range(0, self.nx):
494+ for y in range(self.ny):
495+ posX = self.pin1Pos.x + (self.px * x)
496+ posY = self.pin1Pos.y + (self.py * y)
497+
498+ pos = pcbnew.wxPoint(posX, posY)
499+
500+ # THIS DOESN'T WORK yet!
501+ #pad = self.pad.Clone()
502+ pad = self.ClonePad()
503+
504+ pad.SetPos0(pos)
505+ pad.SetPosition(pos)
506+
507+ pad.SetPadName(str(self.NamingFunction(x,y)))
508+
509+ self.AddPad(pad)
510+
511+class PadLineArray(PadGridArray):
512+
513+ def __init__(self, pad, n, pitch, isVertical, pin1Pos):
514+
515+ if isVertical:
516+ PadGridArray.__init__(self, pad, 1, n, 0, pitch, pin1Pos)
517+ else:
518+ PadGridArray.__init__(self, pad, n, 1, pitch, 0, pin1Pos)
519+
520+class RectPadArray(PadArray):
521+
522+ def __init__(self, nx, ny, pitch, xpitch, ypitch, pin1Pos):
523+
524+ #left row
525+ pin1Pos = pcbnew.wxPoint(-h_pitch / 2, -row_len / 2)
526+ array = PadLineArray(h_pad, pads_per_row, pad_pitch, True, pin1Pos)
527+ array.SetFirstPadInArray(1)
528+ array.AddPadsToModule()
529+
530+ #bottom row
531+ pin1Pos = pcbnew.wxPoint(-row_len / 2, v_pitch / 2)
532+ array = PA.PadLineArray(v_pad, pads_per_row, pad_pitch, False, pin1Pos)
533+ array.SetFirstPadInArray(pads_per_row + 1)
534+ array.AddPadsToModule()
535+
536+ #right row
537+ pin1Pos = pcbnew.wxPoint(h_pitch / 2, row_len / 2)
538+ array = PadLineArray(h_pad, pads_per_row, -pad_pitch, True, pin1Pos)
539+ array.SetFirstPadInArray(2*pads_per_row + 1)
540+ array.AddPadsToModule()
541+
542+ #top row
543+ pin1Pos = pcbnew.wxPoint(row_len / 2, -v_pitch / 2)
544+ array = PadLineArray(v_pad, pads_per_row, -pad_pitch, False, pin1Pos)
545+ array.SetFirstPadInArray(3*pads_per_row + 1)
546+ array.AddPadsToModule()
547
548=== added file 'pcbnew/scripting/plugins/bga_wizard.py'
549--- pcbnew/scripting/plugins/bga_wizard.py 1970-01-01 00:00:00 +0000
550+++ pcbnew/scripting/plugins/bga_wizard.py 2014-04-20 22:23:21 +0000
551@@ -0,0 +1,98 @@
552+# This program is free software; you can redistribute it and/or modify
553+# it under the terms of the GNU General Public License as published by
554+# the Free Software Foundation; either version 2 of the License, or
555+# (at your option) any later version.
556+#
557+# This program is distributed in the hope that it will be useful,
558+# but WITHOUT ANY WARRANTY; without even the implied warranty of
559+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
560+# GNU General Public License for more details.
561+#
562+# You should have received a copy of the GNU General Public License
563+# along with this program; if not, write to the Free Software
564+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
565+# MA 02110-1301, USA.
566+#
567+
568+from __future__ import division
569+import pcbnew
570+
571+import HelpfulFootprintWizardPlugin as HFPW
572+import PadArray as PA
573+
574+
575+class BGAPadGridArray(PA.PadGridArray):
576+
577+ def NamingFunction(self, x, y):
578+ return "%s%d" % (self.AlphaNameFromNumber(y + 1), x + 1)
579+
580+
581+class BGAWizard(HFPW.HelpfulFootprintWizardPlugin):
582+
583+ def GetName(self):
584+ return "BGA"
585+
586+ def GetDescription(self):
587+ return "Ball Grid Array Footprint Wizard"
588+
589+ def GenerateParameterList(self):
590+
591+ self.AddParam("Pads", "pad pitch", self.uMM, 1)
592+ self.AddParam("Pads", "pad size", self.uMM, 0.5)
593+ self.AddParam("Pads", "row count", self.uNatural, 5)
594+ self.AddParam("Pads", "column count", self.uNatural, 5)
595+ self.AddParam("Pads", "outline x margin", self.uMM, 1)
596+ self.AddParam("Pads", "outline y margin", self.uMM, 1)
597+
598+ def CheckParameters(self):
599+
600+ self.CheckParamPositiveInt("Pads", "*row count")
601+ self.CheckParamPositiveInt("Pads", "*column count")
602+
603+
604+ def GetReference(self):
605+
606+ pins = self.parameters["Pads"]["*row count"] * self.parameters["Pads"]["*column count"]
607+
608+ return "BGA %d" % pins
609+
610+
611+ def GetValuePrefix(self):
612+ return "U"
613+
614+
615+ def BuildThisFootprint(self):
616+
617+ pads = self.parameters["Pads"]
618+
619+ rows = pads["*row count"]
620+ cols = pads["*column count"]
621+ pad_size = pads["pad size"]
622+
623+ pad_size = pcbnew.wxSize(pad_size, pad_size)
624+
625+ pad_pitch = pads["pad pitch"]
626+
627+ # add in the pads
628+ pad = PA.PadMaker(self.module).SMTRoundPad(pads["pad size"])
629+
630+ pin1Pos = pcbnew.wxPoint(-((rows - 1) * pad_pitch) / 2,
631+ -((cols - 1) * pad_pitch) / 2)
632+
633+ array = BGAPadGridArray(pad, rows, cols, pad_pitch, pad_pitch, pin1Pos)
634+ array.AddPadsToModule()
635+
636+ #box
637+ ssX = -pin1Pos.x + pads["outline x margin"]
638+ ssY = -pin1Pos.y + pads["outline y margin"]
639+
640+ self.draw.BoxWithDiagonalAtCorner(0, 0, ssX*2, ssY*2, pads["outline x margin"])
641+
642+ #reference and value
643+ textSize = pcbnew.FromMM(0.8)
644+
645+ self.draw.Value(0, - ssY - textSize, textSize)
646+ self.draw.Reference(0, ssY + textSize, textSize)
647+
648+
649+BGAWizard().register()
650
651=== modified file 'pcbnew/scripting/plugins/qfp_wizard.py'
652--- pcbnew/scripting/plugins/qfp_wizard.py 2014-02-21 10:05:28 +0000
653+++ pcbnew/scripting/plugins/qfp_wizard.py 2014-04-20 22:23:21 +0000
654@@ -1,235 +1,89 @@
655+from __future__ import division
656 import pcbnew
657
658-def abs(x):
659- if x < 0:
660- return -x
661-
662- return x
663-
664-class QFPWizard(pcbnew.FootprintWizardPlugin):
665- def __init__(self):
666- pcbnew.FootprintWizardPlugin.__init__(self)
667- self.name = "QFP"
668- self.description = "QFP Footprint Wizard"
669- self.parameters = {
670- "Pads": {
671- "*n": 100,
672- "pitch": pcbnew.FromMM(0.5),
673- "width": pcbnew.FromMM(0.25),
674- "length": pcbnew.FromMM(1.5),
675- "horizontal pitch": pcbnew.FromMM(15),
676- "vertical pitch": pcbnew.FromMM(15),
677- "*oval": "True"
678- },
679- "Package": {
680- "width": pcbnew.FromMM(14),
681- "height": pcbnew.FromMM(14)
682- }
683- }
684-
685- self.ClearErrors()
686-
687- def smd_rect_pad(self, module, size, pos, name):
688- pad = pcbnew.D_PAD(module)
689-
690- pad.SetSize(size)
691-
692- if self.parameters['Pads'].get('*oval', "true").lower() == "true":
693- pad.SetShape(pcbnew.PAD_OVAL)
694- else:
695- pad.SetShape(pcbnew.PAD_RECT)
696-
697- pad.SetAttribute(pcbnew.PAD_SMD)
698- pad.SetLayerMask(pcbnew.PAD_SMD_DEFAULT_LAYERS)
699- pad.SetPos0(pos)
700- pad.SetPosition(pos)
701- pad.SetPadName(name)
702-
703- return pad
704+import HelpfulFootprintWizardPlugin
705+import PadArray as PA
706+
707+class QFPWizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin):
708+
709+ def GetName(self):
710+ return "QFP"
711+
712+ def GetDescription(self):
713+ return "QFP Footprint Wizard"
714+
715+ def GenerateParameterList(self):
716+ self.AddParam("Pads", "n", self.uNatural, 100)
717+ self.AddParam("Pads", "pad pitch", self.uMM, 0.5)
718+ self.AddParam("Pads", "pad width", self.uMM, 0.25)
719+ self.AddParam("Pads", "pad length", self.uMM, 1.5)
720+ self.AddParam("Pads", "vertical pitch", self.uMM, 15)
721+ self.AddParam("Pads", "horizontal pitch", self.uMM, 15)
722+ self.AddParam("Pads", "oval", self.uBool, True)
723+
724+ self.AddParam("Pads", "package width", self.uMM, 14)
725+ self.AddParam("Pads", "package height", self.uMM, 14)
726
727 def CheckParameters(self):
728- errors = ""
729- pads = self.parameters
730-
731- num_pads = pads["Pads"]["*n"]
732- if (num_pads < 1):
733- self.parameter_errors["Pads"]["*n"] = "Must be positive"
734- errors +="Pads/n has wrong value, "
735- pads["Pads"]["*n"] = int(num_pads) # Reset to int instead of float
736-
737- return errors
738-
739- def BuildFootprint(self):
740- if self.has_errors():
741- print "Cannot build footprint: Parameters have errors:"
742- print self.parameter_errors
743- return
744-
745- print "Building new QFP footprint with the following parameters:"
746- self.print_parameter_table()
747-
748- self.module = pcbnew.MODULE(None) # create a new module
749-
750- pads = self.parameters
751- num_pads = int(pads["Pads"]["*n"])
752- pad_width = pads["Pads"]["width"]
753- pad_length = pads["Pads"]["length"]
754- pad_pitch = pads["Pads"]["pitch"]
755- pad_horizontal_pitch = pads["Pads"]["horizontal pitch"]
756- pad_vertical_pitch = pads["Pads"]["vertical pitch"]
757-
758- package_width = pads["Package"]["width"]
759- package_height = pads["Package"]["height"]
760-
761- side_length = pad_pitch * ((num_pads / 4) - 1)
762-
763- offsetX = pad_pitch * ((num_pads / 4) - 1) / 2
764- text_size = pcbnew.wxSize(pcbnew.FromMM(0.8), pcbnew.FromMM(0.8))
765-
766- self.module.SetReference("QFP %d" % int(num_pads))
767- self.module.Reference().SetPos0(pcbnew.wxPoint(0, pcbnew.FromMM(-0.8)))
768- self.module.Reference().SetTextPosition(self.module.Reference().GetPos0())
769- self.module.Reference().SetSize(text_size)
770-
771- self.module.SetValue("U**")
772- self.module.Value().SetPos0(pcbnew.wxPoint(0, pcbnew.FromMM(+0.8)))
773- self.module.Value().SetTextPosition(self.module.Value().GetPos0())
774- self.module.Value().SetSize(text_size)
775-
776- fpid = pcbnew.FPID(self.module.GetReference()) #the name in library
777- self.module.SetFPID( fpid )
778-
779- pad_size_left_right = pcbnew.wxSize(pad_length, pad_width)
780- pad_size_bottom_top = pcbnew.wxSize(pad_width, pad_length)
781-
782- for cur_pad in range(0, num_pads):
783- side = int(cur_pad / (num_pads / 4)) # 0 -> left, 1 -> bottom, 2 -> right, 3 -> top
784-
785- if side == 0 or side == 2:
786- pad_size = pad_size_left_right
787-
788- pad_pos_x = -(pad_horizontal_pitch / 2)
789- pad_pos_y = (cur_pad % (num_pads / 4)) * pad_pitch - (side_length / 2)
790-
791- if side == 2:
792- pad_pos_x = -pad_pos_x
793- pad_pos_y = -pad_pos_y
794-
795- else:
796- pad_size = pad_size_bottom_top
797-
798- pad_pos_x = (cur_pad % (num_pads / 4)) * pad_pitch - (side_length / 2)
799- pad_pos_y = -(pad_vertical_pitch / 2)
800-
801- if side == 1:
802- pad_pos_y = -pad_pos_y
803- else:
804- pad_pos_x = -pad_pos_x
805-
806- pad_pos = pcbnew.wxPoint(pad_pos_x, pad_pos_y)
807-
808- pad = self.smd_rect_pad(self.module, pad_size, pad_pos, str(cur_pad + 1))
809-
810- self.module.Add(pad)
811-
812- half_package_width = package_width / 2
813- half_package_height = package_height / 2
814-
815- package_pad_height_offset = abs(package_height - side_length) / 2 - pad_pitch
816- package_pad_width_offset = abs(package_width - side_length) / 2 - pad_pitch
817-
818- # Bottom Left Edge, vertical line
819- outline = pcbnew.EDGE_MODULE(self.module)
820- outline.SetWidth(pcbnew.FromMM(0.2))
821- outline.SetLayer(pcbnew.SILKSCREEN_N_FRONT)
822- outline.SetShape(pcbnew.S_SEGMENT)
823- start = pcbnew.wxPoint(-half_package_width, half_package_height - package_pad_height_offset)
824- end = pcbnew.wxPoint(-half_package_width, half_package_height)
825- outline.SetStartEnd(start, end)
826- self.module.Add(outline)
827-
828- # Bottom Left Edge, horizontal line
829- outline = pcbnew.EDGE_MODULE(self.module)
830- outline.SetWidth(pcbnew.FromMM(0.2))
831- outline.SetLayer(pcbnew.SILKSCREEN_N_FRONT)
832- outline.SetShape(pcbnew.S_SEGMENT)
833- start = pcbnew.wxPoint(-half_package_width, half_package_height)
834- end = pcbnew.wxPoint(-half_package_width + package_pad_width_offset, half_package_height)
835- outline.SetStartEnd(start, end)
836- self.module.Add(outline)
837-
838- # Bottom Right Edge, vertical line
839- outline = pcbnew.EDGE_MODULE(self.module)
840- outline.SetWidth(pcbnew.FromMM(0.2))
841- outline.SetLayer(pcbnew.SILKSCREEN_N_FRONT)
842- outline.SetShape(pcbnew.S_SEGMENT)
843- start = pcbnew.wxPoint(half_package_width, half_package_height - package_pad_height_offset)
844- end = pcbnew.wxPoint(half_package_width, half_package_height)
845- outline.SetStartEnd(start, end)
846- self.module.Add(outline)
847-
848- # Bottom Right Edge, horizontal line
849- outline = pcbnew.EDGE_MODULE(self.module)
850- outline.SetWidth(pcbnew.FromMM(0.2))
851- outline.SetLayer(pcbnew.SILKSCREEN_N_FRONT)
852- outline.SetShape(pcbnew.S_SEGMENT)
853- start = pcbnew.wxPoint(half_package_width, half_package_height)
854- end = pcbnew.wxPoint(half_package_width - package_pad_width_offset, half_package_height)
855- outline.SetStartEnd(start, end)
856- self.module.Add(outline)
857-
858- # Top Right Edge, vertical line
859- outline = pcbnew.EDGE_MODULE(self.module)
860- outline.SetWidth(pcbnew.FromMM(0.2))
861- outline.SetLayer(pcbnew.SILKSCREEN_N_FRONT)
862- outline.SetShape(pcbnew.S_SEGMENT)
863- start = pcbnew.wxPoint(half_package_width, -half_package_height + package_pad_height_offset)
864- end = pcbnew.wxPoint(half_package_width, -half_package_height)
865- outline.SetStartEnd(start, end)
866- self.module.Add(outline)
867-
868- # Top Right Edge, horizontal line
869- outline = pcbnew.EDGE_MODULE(self.module)
870- outline.SetWidth(pcbnew.FromMM(0.2))
871- outline.SetLayer(pcbnew.SILKSCREEN_N_FRONT)
872- outline.SetShape(pcbnew.S_SEGMENT)
873- start = pcbnew.wxPoint(half_package_width, -half_package_height)
874- end = pcbnew.wxPoint(half_package_width - package_pad_width_offset, -half_package_height)
875- outline.SetStartEnd(start, end)
876- self.module.Add(outline)
877-
878- # Top Left Edge, straight line
879- outline = pcbnew.EDGE_MODULE(self.module)
880- outline.SetWidth(pcbnew.FromMM(0.2))
881- outline.SetLayer(pcbnew.SILKSCREEN_N_FRONT)
882- outline.SetShape(pcbnew.S_SEGMENT)
883- start = pcbnew.wxPoint(-half_package_width, -half_package_height + package_pad_height_offset)
884- end = pcbnew.wxPoint(-half_package_width + package_pad_width_offset, -half_package_height)
885- outline.SetStartEnd(start, end)
886- self.module.Add(outline)
887-
888- def print_parameter_table(self):
889- for name, section in self.parameters.iteritems():
890- print " %s:" % name
891-
892- for key, value in section.iteritems():
893- unit = ""
894- if (type(value) is int or type(value) is float) and not "*" in key:
895- unit = "mm"
896-
897- if "*" in key:
898- key = key[1:]
899- else:
900- value = pcbnew.ToMM(value)
901-
902- print " %s: %s%s" % (key, value, unit)
903-
904- def has_errors(self):
905- for name, section in self.parameter_errors.iteritems():
906- for k, v in section.iteritems():
907- if v:
908- return True
909-
910- return False
911+
912+ self.CheckParamPositiveInt("Pads", "*n", is_multiple_of = 4)
913+
914+ def GetReference(self):
915+ return "QFP %d" % self.parameters["Pads"]["*n"]
916+
917+ def BuildThisFootprint(self):
918+
919+ pads = self.parameters["Pads"]
920+
921+ pad_pitch = pads["pad pitch"]
922+ pad_length = self.parameters["Pads"]["pad length"]
923+ pad_width = self.parameters["Pads"]["pad width"]
924+
925+ v_pitch = pads["vertical pitch"]
926+ h_pitch = pads["horizontal pitch"]
927+
928+ pads_per_row = pads["*n"] // 4
929+
930+ row_len = (pads_per_row - 1) * pad_pitch
931+
932+ h_pad = PA.PadMaker(self.module).SMDPad(pad_width, pad_length, shape = pcbnew.PAD_OVAL)
933+ v_pad = PA.PadMaker(self.module).SMDPad(pad_length, pad_width, shape = pcbnew.PAD_OVAL)
934+
935+ #left row
936+ pin1Pos = pcbnew.wxPoint(-h_pitch / 2, -row_len / 2)
937+ array = PA.PadLineArray(h_pad, pads_per_row, pad_pitch, True, pin1Pos)
938+ array.SetFirstPadInArray(1)
939+ array.AddPadsToModule()
940+
941+ #bottom row
942+ pin1Pos = pcbnew.wxPoint(-row_len / 2, v_pitch / 2)
943+ array = PA.PadLineArray(v_pad, pads_per_row, pad_pitch, False, pin1Pos)
944+ array.SetFirstPadInArray(pads_per_row + 1)
945+ array.AddPadsToModule()
946+
947+ #right row
948+ pin1Pos = pcbnew.wxPoint(h_pitch / 2, row_len / 2)
949+ array = PA.PadLineArray(h_pad, pads_per_row, -pad_pitch, True, pin1Pos)
950+ array.SetFirstPadInArray(2*pads_per_row + 1)
951+ array.AddPadsToModule()
952+
953+ #top row
954+ pin1Pos = pcbnew.wxPoint(row_len / 2, -v_pitch / 2)
955+ array = PA.PadLineArray(v_pad, pads_per_row, -pad_pitch, False, pin1Pos)
956+ array.SetFirstPadInArray(3*pads_per_row + 1)
957+ array.AddPadsToModule()
958+
959+ limX = pads["package width"] / 2
960+ limY = pads["package height"] / 2
961+ inner = (row_len / 2) + pad_pitch
962+
963+ #top left - diagonal
964+ self.draw.Line(-limX, -inner, -inner, -limY)
965+ # top right
966+ self.draw.Polyline([(inner, -limY), (limX, -limY), (limX, -inner)])
967+ # bottom left
968+ self.draw.Polyline([(-inner, limY), (-limX, limY), (-limX, inner)])
969+ # bottom right
970+ self.draw.Polyline([(inner, limY), (limX, limY), (limX, inner)])
971
972 QFPWizard().register()
973
974=== added file 'pcbnew/scripting/plugins/sdip_wizard.py'
975--- pcbnew/scripting/plugins/sdip_wizard.py 1970-01-01 00:00:00 +0000
976+++ pcbnew/scripting/plugins/sdip_wizard.py 2014-04-20 22:23:21 +0000
977@@ -0,0 +1,200 @@
978+# This program is free software; you can redistribute it and/or modify
979+# it under the terms of the GNU General Public License as published by
980+# the Free Software Foundation; either version 2 of the License, or
981+# (at your option) any later version.
982+#
983+# This program is distributed in the hope that it will be useful,
984+# but WITHOUT ANY WARRANTY; without even the implied warranty of
985+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
986+# GNU General Public License for more details.
987+#
988+# You should have received a copy of the GNU General Public License
989+# along with this program; if not, write to the Free Software
990+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
991+# MA 02110-1301, USA.
992+#
993+
994+from __future__ import division
995+import pcbnew
996+
997+import HelpfulFootprintWizardPlugin as HFPW
998+import PadArray as PA
999+
1000+
1001+class RowedGridArray(PA.PadGridArray):
1002+
1003+ def NamingFunction(self, x, y):
1004+ if (x % 2) == 0: # even row, count up
1005+ return (x * self.ny) + y + 1;
1006+ else: # odd row, count down
1007+ return (self.ny * (x + 1)) - y;
1008+
1009+class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
1010+
1011+ def GenerateParameterList(self):
1012+
1013+ # defaults for a DIP package
1014+ self.AddParam("Pads", "n", self.uNatural, 24)
1015+ self.AddParam("Pads", "silk screen inside", self.uBool, False)
1016+ self.AddParam("Pads", "row count", self.uNatural, 2)
1017+
1018+ def CheckParameters(self):
1019+ self.CheckParamPositiveInt("Pads", "*row count")
1020+ self.CheckParamPositiveInt("Pads", "*n", is_multiple_of = self.parameters["Pads"]["*row count"])
1021+ self.CheckParamBool("Pads", "*silk screen inside") #can do this internally to parameter manager?
1022+
1023+ def BuildThisFootprint(self):
1024+
1025+ pads = self.parameters["Pads"]
1026+
1027+ num_pads = pads["*n"]
1028+
1029+ pad_length = pads["pad length"]
1030+ pad_width = pads["pad width"]
1031+ row_pitch = pads["row spacing"]
1032+ pad_pitch = pads["pad pitch"]
1033+ num_rows = pads["*row count"]
1034+
1035+ pads_per_row = num_pads // num_rows
1036+
1037+ row_length = pad_pitch * (pads_per_row - 1) #fenceposts
1038+
1039+ # add in the pads
1040+ pad = self.GetPad()
1041+
1042+ pin1Pos = pcbnew.wxPoint(-((num_rows - 1) * row_pitch) / 2, -row_length / 2)
1043+
1044+ array = RowedGridArray(pad, num_rows, pads_per_row, row_pitch, pad_pitch, pin1Pos)
1045+ array.AddPadsToModule()
1046+
1047+ # draw the Silk Screen
1048+
1049+ pad_length = pads["pad length"]
1050+ pad_width = pads["pad width"]
1051+
1052+ ssXOffset = -pad_length / 2 - pads["outline x margin"]
1053+ ssYOffset = -pad_width / 2 - pads["outline y margin"]
1054+
1055+
1056+ if pads["*silk screen inside"]:
1057+ ssXOffset *= -1
1058+
1059+ ssX = -pin1Pos.x - ssXOffset
1060+ ssY = -pin1Pos.y - ssYOffset
1061+
1062+
1063+ self.DrawBox(ssX, ssY)
1064+
1065+ #reference and value
1066+ textSize = pcbnew.FromMM(0.8)
1067+
1068+ self.draw.Value(0, - ssY - textSize, textSize)
1069+ self.draw.Reference(0, ssY + textSize, textSize)
1070+
1071+
1072+class SDIPWizard(RowedFootprint):
1073+
1074+ def GetName(self):
1075+ return "S/DIP"
1076+
1077+ def GetDescription(self):
1078+ return "Single/Dual Inline Package Footprint Wizard"
1079+
1080+ def GenerateParameterList(self):
1081+ RowedFootprint.GenerateParameterList(self)
1082+
1083+ self.AddParam("Pads", "pad pitch", self.uMils, 100)
1084+ self.AddParam("Pads", "pad width", self.uMils, 60)
1085+ self.AddParam("Pads", "pad length", self.uMils, 150)
1086+ self.AddParam("Pads", "row spacing", self.uMils, 300)
1087+ self.AddParam("Pads", "drill size", self.uMM, 1)
1088+ self.AddParam("Pads", "outline x margin", self.uMM, 0.5)
1089+ self.AddParam("Pads", "outline y margin", self.uMM, 1)
1090+
1091+ def GetReference(self):
1092+
1093+ rows = self.parameters["Pads"]["*row count"]
1094+
1095+ if rows == 1:
1096+ name = "SIP"
1097+ elif rows == 2:
1098+ name = "DIP"
1099+ else: # triple and up aren't really a thing, but call it something!
1100+ name = "xIP"
1101+
1102+ return "%s %d" % (name, self.parameters["Pads"]["*n"])
1103+
1104+ def GetPad(self):
1105+ pad_length = self.parameters["Pads"]["pad length"]
1106+ pad_width = self.parameters["Pads"]["pad width"]
1107+ drill = self.parameters["Pads"]["drill size"]
1108+ return PA.PadMaker(self.module).THPad(pad_width, pad_length, drill, shape = pcbnew.PAD_OVAL)
1109+
1110+ def DrawBox(self, ssX, ssY):
1111+
1112+ if self.parameters["Pads"]["*row count"] == 2:
1113+
1114+ # ----------
1115+ # |8 7 6 5 |
1116+ # > |
1117+ # |1 2 3 4 |
1118+ # ----------
1119+
1120+ # draw the notch
1121+ notchWidth = pcbnew.FromMM(3)
1122+ notchHeight = pcbnew.FromMM(1)
1123+
1124+ self.draw.NotchedBox(0, 0, ssX*2, ssY*2, notchWidth, notchHeight)
1125+ else:
1126+ # -----------------
1127+ # |1|2 3 4 5 6 7 8|
1128+ # -----------------
1129+ self.draw.Box(ssX*2, ssY*2)
1130+
1131+ #line between pin1 and pin2
1132+ pad_pitch = self.parameters["Pads"]["pad pitch"];
1133+ self.draw.HLine(-ssX, pin1Pos.y + pad_pitch/2, ssX * 2)
1134+
1135+ return ssX, ssY
1136+
1137+SDIPWizard().register()
1138+
1139+
1140+class SOICWizard(RowedFootprint):
1141+
1142+ def GetName(self):
1143+ return "SOIC"
1144+
1145+ def GetDescription(self):
1146+ return "SOIC, MSOP, SSOP, TSSOP, etc, footprint wizard"
1147+
1148+ def GetReference(self):
1149+ return "%s %d" % ("SOIC", self.parameters["Pads"]["*n"])
1150+
1151+ def GenerateParameterList(self):
1152+ RowedFootprint.GenerateParameterList(self)
1153+
1154+ #and override some of them
1155+ self.AddParam("Pads", "pad pitch", self.uMM, 1.27)
1156+ self.AddParam("Pads", "pad width", self.uMM, 0.6)
1157+ self.AddParam("Pads", "pad length", self.uMM, 2.2)
1158+ self.AddParam("Pads", "row spacing", self.uMM, 5.2)
1159+
1160+ self.AddParam("Pads", "outline x margin", self.uMM, 0.5)
1161+ self.AddParam("Pads", "outline y margin", self.uMM, 0.5)
1162+
1163+ def GetPad(self):
1164+ pad_length = self.parameters["Pads"]["pad length"]
1165+ pad_width = self.parameters["Pads"]["pad width"]
1166+ return PA.PadMaker(self.module).SMDPad(pad_width, pad_length, shape = pcbnew.PAD_RECT)
1167+
1168+ def DrawBox(self, ssX, ssY):
1169+
1170+ # ----------
1171+ # |8 7 6 5 |
1172+ # |1 2 3 4 |
1173+ # \---------
1174+
1175+ self.draw.BoxWithDiagonalAtCorner(0, 0, ssX*2, ssY*2, pcbnew.FromMM(1))
1176+
1177+SOICWizard().register()