You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

234 lines
8.7 KiB

  1. # This program is free software; you can redistribute it and/or modify
  2. # it under the terms of the GNU General Public License as published by
  3. # the Free Software Foundation; either version 2 of the License, or
  4. # (at your option) any later version.
  5. #
  6. # This program is distributed in the hope that it will be useful,
  7. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. # GNU General Public License for more details.
  10. #
  11. # You should have received a copy of the GNU General Public License
  12. # along with this program; if not, write to the Free Software
  13. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  14. # MA 02110-1301, USA.
  15. #
  16. from __future__ import division
  17. import pcbnew
  18. import pcbnew
  19. import FootprintWizardBase
  20. import PadArray as PA
  21. class QFNWizard(FootprintWizardBase.FootprintWizard):
  22. def GetName(self):
  23. return "QFN"
  24. def GetDescription(self):
  25. return "Quad Flat No-lead (QFN) footprint wizard"
  26. def GenerateParameterList(self):
  27. #TODO - Allow different number of pads in x and y directions
  28. self.AddParam("Pads", "n", self.uInteger, 100, multiple=4, min_value=4)
  29. self.AddParam("Pads", "pitch", self.uMM, 0.4, designator='e')
  30. self.AddParam("Pads", "width", self.uMM, 0.2, designator='X1')
  31. self.AddParam("Pads", "length", self.uMM, 0.75, designator='Y1')
  32. self.AddParam("Pads", "offset", self.uMM, 0.0)
  33. self.AddParam("Pads", "oval", self.uBool, True)
  34. self.AddParam("EPad", "epad", self.uBool, True)
  35. self.AddParam("EPad", "width", self.uMM, 10, designator="E2")
  36. self.AddParam("EPad", "length", self.uMM, 10, designator="D2")
  37. self.AddParam("EPad", "thermal vias", self.uBool, False)
  38. self.AddParam("EPad", "thermal vias drill", self.uMM, 1, min_value=0.1)
  39. self.AddParam("EPad", "x divisions", self.uInteger, 4, min_value=1)
  40. self.AddParam("EPad", "y divisions", self.uInteger, 4, min_value=1)
  41. self.AddParam("EPad", "paste margin", self.uMM, 0.75)
  42. self.AddParam("Package", "width", self.uMM, 14, designator='E')
  43. self.AddParam("Package", "height", self.uMM, 14, designator='D')
  44. self.AddParam("Package", "margin", self.uMM, 0.25, minValue=0.2)
  45. @property
  46. def pads(self):
  47. return self.parameters['Pads']
  48. @property
  49. def epad(self):
  50. return self.parameters['EPad']
  51. @property
  52. def package(self):
  53. return self.parameters['Package']
  54. def CheckParameters(self):
  55. pass
  56. def GetValue(self):
  57. return "QFN-{n}_{ep}{x:g}x{y:g}_Pitch{p:g}mm".format(
  58. n = self.pads['n'],
  59. ep = "EP_" if self.epad['epad'] else '',
  60. x = pcbnew.ToMM(self.package['width']),
  61. y = pcbnew.ToMM(self.package['height']),
  62. p = pcbnew.ToMM(self.pads['pitch'])
  63. )
  64. def BuildThisFootprint(self):
  65. pad_pitch = self.pads["pitch"]
  66. pad_length = self.pads["length"]
  67. # offset allows to define how much of the pad is outside of the package
  68. pad_offset = self.pads["offset"]
  69. pad_width = self.pads["width"]
  70. v_pitch = self.package["height"]
  71. h_pitch = self.package["width"]
  72. pads_per_row = int(self.pads["n"] // 4)
  73. row_len = (pads_per_row - 1) * pad_pitch
  74. pad_shape = pcbnew.PAD_SHAPE_OVAL if self.pads["oval"] else pcbnew.PAD_SHAPE_RECT
  75. h_pad = PA.PadMaker(self.module).SMDPad( pad_length, pad_width,
  76. shape=pad_shape, rot_degree=90.0)
  77. v_pad = PA.PadMaker(self.module).SMDPad( pad_length, pad_width, shape=pad_shape)
  78. h_pitch = h_pitch / 2 - pad_length + pad_offset + pad_length/2
  79. v_pitch = v_pitch / 2 - pad_length +pad_offset + pad_length/2
  80. #left row
  81. pin1Pos = pcbnew.wxPoint(-h_pitch, 0)
  82. array = PA.PadLineArray(h_pad, pads_per_row, pad_pitch, True, pin1Pos)
  83. array.SetFirstPadInArray(1)
  84. array.AddPadsToModule(self.draw)
  85. #bottom row
  86. pin1Pos = pcbnew.wxPoint(0, v_pitch)
  87. array = PA.PadLineArray(v_pad, pads_per_row, pad_pitch, False, pin1Pos)
  88. array.SetFirstPadInArray(pads_per_row + 1)
  89. array.AddPadsToModule(self.draw)
  90. #right row
  91. pin1Pos = pcbnew.wxPoint(h_pitch, 0)
  92. array = PA.PadLineArray(h_pad, pads_per_row, -pad_pitch, True,
  93. pin1Pos)
  94. array.SetFirstPadInArray(2*pads_per_row + 1)
  95. array.AddPadsToModule(self.draw)
  96. #top row
  97. pin1Pos = pcbnew.wxPoint(0, -v_pitch)
  98. array = PA.PadLineArray(v_pad, pads_per_row, -pad_pitch, False,
  99. pin1Pos)
  100. array.SetFirstPadInArray(3*pads_per_row + 1)
  101. array.AddPadsToModule(self.draw)
  102. lim_x = self.package["width"] / 2
  103. lim_y = self.package["height"] / 2
  104. inner = (row_len / 2) + pad_pitch
  105. # epad
  106. epad_width = self.epad["width"]
  107. epad_length = self.epad["length"]
  108. epad_ny = self.epad["x divisions"]
  109. epad_nx = self.epad["y divisions"]
  110. epad_via_drill = self.epad["thermal vias drill"]
  111. # Create a central exposed pad?
  112. if self.epad['epad'] == True:
  113. epad_num = self.pads['n'] + 1
  114. epad_w = epad_length / epad_nx
  115. epad_l = epad_width / epad_ny
  116. # Create the epad
  117. epad = PA.PadMaker(self.module).SMDPad( epad_w, epad_l, shape=pcbnew.PAD_SHAPE_RECT )
  118. epad.SetLocalSolderPasteMargin( -1 * self.epad['paste margin'] )
  119. # set pad layers
  120. layers = pcbnew.LSET(pcbnew.F_Mask)
  121. layers.AddLayer(pcbnew.F_Cu)
  122. layers.AddLayer(pcbnew.F_Paste)
  123. epad.SetName(epad_num)
  124. array = PA.EPADGridArray( epad, epad_ny, epad_nx, epad_l, epad_w, pcbnew.wxPoint(0,0) )
  125. array.SetFirstPadInArray(epad_num)
  126. array.AddPadsToModule(self.draw)
  127. if self.epad['thermal vias']:
  128. # create the thermal via
  129. via_diam = min(epad_w, epad_l) / 2
  130. via_drill = min(via_diam / 2, epad_via_drill)
  131. via = PA.PadMaker(self.module).THRoundPad(via_diam, via_drill)
  132. layers = pcbnew.LSET.AllCuMask()
  133. layers.AddLayer(pcbnew.B_Mask)
  134. layers.AddLayer(pcbnew.F_Mask)
  135. via.SetLayerSet(layers)
  136. via_array = PA.EPADGridArray(via, epad_ny, epad_nx, epad_l, epad_w, pcbnew.wxPoint(0,0) )
  137. via_array.SetFirstPadInArray(epad_num)
  138. via_array.AddPadsToModule(self.draw)
  139. # Draw the package outline on the F.Fab layer
  140. bevel = min( pcbnew.FromMM(1.0), self.package['width']/2, self.package['height']/2 )
  141. self.draw.SetLayer(pcbnew.F_Fab)
  142. w = self.package['width']
  143. h = self.package['height']
  144. self.draw.BoxWithDiagonalAtCorner(0, 0, w, h, bevel)
  145. # Silkscreen
  146. self.draw.SetLayer( pcbnew.F_SilkS )
  147. offset = self.draw.GetLineThickness()
  148. clip = row_len / 2 + self.pads['pitch']
  149. self.draw.SetLineThickness( pcbnew.FromMM( 0.12 ) ) #Default per KLC F5.1 as of 12/2018
  150. self.draw.Polyline( [ [ clip, -h/2-offset], [ w/2+offset,-h/2-offset], [ w/2+offset, -clip] ] ) # top right
  151. self.draw.Polyline( [ [ clip, h/2+offset], [ w/2+offset, h/2+offset], [ w/2+offset, clip] ] ) # bottom right
  152. self.draw.Polyline( [ [-clip, h/2+offset], [-w/2-offset, h/2+offset], [-w/2-offset, clip] ] ) # bottom left
  153. # Add pin-1 indication as per IPC-7351C
  154. self.draw.Line( -clip, -h/2-offset, -w/2-pad_length/2, -h/2-offset )
  155. self.draw.SetLineThickness( offset ) #Restore default
  156. # Courtyard
  157. cmargin = self.package["margin"]
  158. self.draw.SetLayer(pcbnew.F_CrtYd)
  159. sizex = (lim_x + cmargin) * 2 + pad_length
  160. sizey = (lim_y + cmargin) * 2 + pad_length
  161. # round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
  162. sizex = pcbnew.PutOnGridMM(sizex, 0.1)
  163. sizey = pcbnew.PutOnGridMM(sizey, 0.1)
  164. # set courtyard line thickness to the one defined in KLC
  165. thick = self.draw.GetLineThickness()
  166. self.draw.SetLineThickness(pcbnew.FromMM(0.05))
  167. self.draw.Box(0, 0, sizex, sizey)
  168. # restore line thickness to previous value
  169. self.draw.SetLineThickness(pcbnew.FromMM(thick))
  170. #reference and value
  171. text_size = self.GetTextSize() # IPC nominal
  172. text_offset = sizey / 2 + text_size
  173. self.draw.Value(0, text_offset, text_size)
  174. self.draw.Reference(0, -text_offset, text_size)
  175. # set SMD attribute
  176. self.module.SetAttributes(pcbnew.MOD_CMS)
  177. QFNWizard().register()