2014-04-23 16:36:31 +00:00
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software
|
|
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
|
|
# MA 02110-1301, USA.
|
|
|
|
#
|
|
|
|
|
|
|
|
from __future__ import division
|
|
|
|
import pcbnew
|
|
|
|
|
2017-01-03 15:01:47 +00:00
|
|
|
import FootprintWizardBase
|
2014-04-23 16:36:31 +00:00
|
|
|
import PadArray as PA
|
|
|
|
|
|
|
|
|
|
|
|
class BGAPadGridArray(PA.PadGridArray):
|
|
|
|
|
2014-09-14 16:29:10 +00:00
|
|
|
def NamingFunction(self, n_x, n_y):
|
|
|
|
return "%s%d" % (
|
|
|
|
self.AlphaNameFromNumber(n_y + 1, alphabet="ABCDEFGHJKLMNPRTUVWY"),
|
|
|
|
n_x + 1)
|
2014-04-23 16:36:31 +00:00
|
|
|
|
|
|
|
|
2017-01-03 15:01:47 +00:00
|
|
|
class BGAWizard(FootprintWizardBase.FootprintWizard):
|
2014-04-23 16:36:31 +00:00
|
|
|
|
|
|
|
def GetName(self):
|
|
|
|
return "BGA"
|
|
|
|
|
|
|
|
def GetDescription(self):
|
|
|
|
return "Ball Grid Array Footprint Wizard"
|
|
|
|
|
|
|
|
def GenerateParameterList(self):
|
2017-01-03 15:01:47 +00:00
|
|
|
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")
|
2014-04-23 16:36:31 +00:00
|
|
|
|
|
|
|
def CheckParameters(self):
|
2017-01-03 15:01:47 +00:00
|
|
|
|
|
|
|
# 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))
|
2014-04-23 16:36:31 +00:00
|
|
|
|
2014-09-14 16:29:10 +00:00
|
|
|
def GetValue(self):
|
2017-01-03 15:01:47 +00:00
|
|
|
pins = (self.parameters["Pads"]["rows"] * self.parameters["Pads"]["columns"])
|
2014-04-23 16:36:31 +00:00
|
|
|
|
2017-01-03 15:01:47 +00:00
|
|
|
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'])
|
|
|
|
)
|
2014-04-23 16:36:31 +00:00
|
|
|
|
|
|
|
def BuildThisFootprint(self):
|
|
|
|
|
|
|
|
pads = self.parameters["Pads"]
|
|
|
|
|
2017-01-03 15:01:47 +00:00
|
|
|
rows = pads["rows"]
|
|
|
|
cols = pads["columns"]
|
|
|
|
pad_size = pads["size"]
|
2014-04-23 16:36:31 +00:00
|
|
|
pad_size = pcbnew.wxSize(pad_size, pad_size)
|
2017-01-03 15:01:47 +00:00
|
|
|
pad_pitch = pads["pitch"]
|
2014-04-23 16:36:31 +00:00
|
|
|
|
|
|
|
# add in the pads
|
2017-01-03 15:01:47 +00:00
|
|
|
pad = PA.PadMaker(self.module).SMTRoundPad(pads["size"])
|
2014-04-23 16:36:31 +00:00
|
|
|
|
2014-09-14 16:29:10 +00:00
|
|
|
pin1_pos = pcbnew.wxPoint(-((cols - 1) * pad_pitch) / 2,
|
|
|
|
-((rows - 1) * pad_pitch) / 2)
|
2014-04-23 16:36:31 +00:00
|
|
|
|
2014-09-14 16:29:10 +00:00
|
|
|
array = BGAPadGridArray(pad, cols, rows, pad_pitch, pad_pitch)
|
|
|
|
array.AddPadsToModule(self.draw)
|
2014-04-23 16:36:31 +00:00
|
|
|
|
2017-01-03 15:01:47 +00:00
|
|
|
# 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
|
|
|
|
|
|
|
|
# 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
|
2018-12-10 11:13:12 +00:00
|
|
|
self.draw.SetLineThickness( pcbnew.FromMM( 0.1 ) ) #Default per KLC F5.2 as of 12/2018
|
2017-01-03 15:01:47 +00:00
|
|
|
self.draw.BoxWithDiagonalAtCorner(0, 0, ssx*2, ssy*2, bevel)
|
|
|
|
|
|
|
|
# Add IPC markings to F_Silk layer
|
2018-12-10 11:13:12 +00:00
|
|
|
self.draw.SetLayer( pcbnew.F_SilkS )
|
|
|
|
self.draw.SetLineThickness( pcbnew.FromMM( 0.12 ) ) #Default per KLC F5.1 as of 12/2018
|
|
|
|
offset = self.draw.GetLineThickness()
|
2017-01-03 15:01:47 +00:00
|
|
|
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)
|
2014-04-23 16:36:31 +00:00
|
|
|
|
2015-10-31 11:54:48 +00:00
|
|
|
# Courtyard
|
2017-01-03 15:01:47 +00:00
|
|
|
cmargin = self.parameters['Package']['margin']
|
2015-10-31 11:54:48 +00:00
|
|
|
self.draw.SetLayer(pcbnew.F_CrtYd)
|
|
|
|
sizex = (ssx + cmargin) * 2
|
|
|
|
sizey = (ssy + cmargin) * 2
|
2017-01-03 15:01:47 +00:00
|
|
|
|
2016-02-18 17:53:39 +00:00
|
|
|
# round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
|
2017-01-03 15:01:47 +00:00
|
|
|
sizex = pcbnew.PutOnGridMM(sizex, 0.1)
|
|
|
|
sizey = pcbnew.PutOnGridMM(sizey, 0.1)
|
|
|
|
|
2016-02-09 09:10:17 +00:00
|
|
|
# set courtyard line thickness to the one defined in KLC
|
2016-02-11 15:02:37 +00:00
|
|
|
self.draw.SetLineThickness(pcbnew.FromMM(0.05))
|
2015-10-31 11:54:48 +00:00
|
|
|
self.draw.Box(0, 0, sizex, sizey)
|
2016-02-09 09:10:17 +00:00
|
|
|
# restore line thickness to previous value
|
2016-02-11 15:02:37 +00:00
|
|
|
self.draw.SetLineThickness(pcbnew.FromMM(cmargin))
|
2015-10-31 11:54:48 +00:00
|
|
|
|
2014-04-23 16:36:31 +00:00
|
|
|
#reference and value
|
2015-10-29 17:35:52 +00:00
|
|
|
text_size = self.GetTextSize() # IPC nominal
|
|
|
|
ypos = ssy + text_size
|
|
|
|
self.draw.Value(0, ypos, text_size)
|
|
|
|
self.draw.Reference(0, -ypos, text_size)
|
2014-04-23 16:36:31 +00:00
|
|
|
|
2016-02-18 14:29:30 +00:00
|
|
|
# set SMD attribute
|
2020-11-14 12:38:44 +00:00
|
|
|
self.module.SetAttributes(pcbnew.PAD_ATTRIB_SMD)
|
2014-04-23 16:36:31 +00:00
|
|
|
|
|
|
|
BGAWizard().register()
|