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.

463 lines
14 KiB

4 years ago
  1. # PadArray.py
  2. #
  3. # Copyright 2014 john <john@johndev>
  4. #
  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. from __future__ import division
  22. import math
  23. import pcbnew
  24. class PadMaker(object):
  25. """!
  26. Useful construction functions for common types of pads, providing
  27. sensible defaults for common pads.
  28. """
  29. def __init__(self, module):
  30. """!
  31. @param module: the module the pads will be part of
  32. """
  33. self.module = module
  34. def THPad(self, Vsize, Hsize, drill, shape=pcbnew.PAD_SHAPE_OVAL,
  35. rot_degree = 0):
  36. """!
  37. A basic through-hole pad of the given size and shape
  38. @param Vsize: the vertical size of the pad
  39. @param Hsize: the horizontal size of the pad
  40. @param drill: the drill diameter
  41. @param shape: the shape of the pad
  42. @param rot_degree: the pad rotation, in degrees
  43. """
  44. pad = pcbnew.PAD(self.module)
  45. pad.SetSize(pcbnew.VECTOR2I( int(Hsize), int(Vsize) ))
  46. pad.SetShape(shape)
  47. pad.SetAttribute(pcbnew.PAD_ATTRIB_PTH)
  48. pad.SetLayerSet(pad.PTHMask())
  49. pad.SetDrillSize(pcbnew.VECTOR2I( int(drill), int(drill) ))
  50. pad.SetOrientation( pcbnew.EDA_ANGLE( rot_degree, pcbnew.DEGREES_T ) )
  51. return pad
  52. def THRoundPad(self, size, drill):
  53. """!
  54. A round though-hole pad. A shortcut for THPad()
  55. @param size: pad diameter
  56. @param drill: drill diameter
  57. """
  58. pad = self.THPad(size, size, drill, shape=pcbnew.PAD_SHAPE_CIRCLE)
  59. return pad
  60. def NPTHRoundPad(self, drill):
  61. """!
  62. A round non-plated though hole (NPTH)
  63. @param drill: the drill diameter (equals the NPTH diameter)
  64. """
  65. pad = pcbnew.PAD(self.module)
  66. pad.SetSize(pcbnew.VECTOR2I( int(drill), int(drill) ))
  67. pad.SetShape(pcbnew.PAD_SHAPE_CIRCLE)
  68. pad.SetAttribute(pcbnew.PAD_ATTRIB_NPTH)
  69. pad.SetLayerSet(pad.UnplatedHoleMask())
  70. pad.SetDrillSize(pcbnew.VECTOR2I( int(drill), int(drill) ))
  71. return pad
  72. def SMDPad(self, Vsize, Hsize, shape=pcbnew.PAD_SHAPE_RECT, rot_degree=0):
  73. """
  74. Create a surface-mount pad of the given size and shape
  75. @param Vsize: the vertical size of the pad
  76. @param Hsize: the horizontal size of the pad
  77. @param drill: the drill diameter
  78. @param shape: the shape of the pad
  79. @param rot_degree: the pad rotation, in degrees
  80. """
  81. pad = pcbnew.PAD(self.module)
  82. pad.SetSize(pcbnew.VECTOR2I( int(Hsize), int(Vsize) ) )
  83. pad.SetShape(shape)
  84. pad.SetAttribute(pcbnew.PAD_ATTRIB_SMD)
  85. pad.SetLayerSet(pad.SMDMask())
  86. pad.SetOrientation( pcbnew.EDA_ANGLE( rot_degree, pcbnew.DEGREES_T ) )
  87. return pad
  88. def SMTRoundPad(self, size):
  89. """!
  90. A round surface-mount pad. A shortcut for SMDPad()
  91. @param size: pad diameter
  92. """
  93. pad = self.SMDPad(size, size, shape=pcbnew.PAD_SHAPE_CIRCLE)
  94. return pad
  95. class PadArray(object):
  96. """!
  97. A class to assist in creating repetitive grids of pads
  98. Generally, PadArrays have an internal prototypical pad, and copy this
  99. for each pad in the array. They can also have a special pad for the
  100. first pad, and a custom function to name the pad.
  101. Generally, PadArray is used as a base class for more specific array
  102. types.
  103. """
  104. def __init__(self, pad):
  105. """!
  106. @param pad: the prototypical pad
  107. """
  108. self.firstPadNum = 1
  109. self.pinNames = None
  110. # this pad is more of a "context", we will use it as a source of
  111. # pad data, but not actually add it
  112. self.pad = pad
  113. self.firstPad = None
  114. def SetPinNames(self, pinNames):
  115. """!
  116. Set a name for all the pins. If given, this overrides the
  117. naming function.
  118. @param pinNames: the name to use for all pins
  119. """
  120. self.pinNames = pinNames
  121. def SetFirstPadType(self, firstPad):
  122. """!
  123. If the array has a different first pad, this is the pad that
  124. is used
  125. @param firstPad: the prototypical first pad
  126. """
  127. self.firstPad = firstPad
  128. def SetFirstPadInArray(self, fpNum):
  129. """!
  130. Set the numbering for the first pad in the array
  131. @param fpNum: the number for the first pad
  132. """
  133. self.firstPadNum = fpNum
  134. def AddPad(self, pad):
  135. """!
  136. Add a pad to the array, under the same moodule as the main
  137. prototype pad
  138. @param pad: pad to add
  139. """
  140. self.pad.GetParent().Add(pad)
  141. def GetPad(self, is_first_pad, pos):
  142. """!
  143. Get a pad in the array with the given position
  144. @param is_first_pad: use the special first pad if there is one
  145. @param pos: the pad position
  146. """
  147. if (self.firstPad and is_first_pad):
  148. pad = self.firstPad
  149. else:
  150. pad = self.pad
  151. # create a new pad with same characteristics
  152. pad = pad.Duplicate()
  153. pad.SetPos0(pos)
  154. pad.SetPosition(pos)
  155. return pad
  156. def GetName(self, *args, **kwargs):
  157. """!
  158. Get the pad name from the naming function, or the pre-set
  159. pinNames parameter (set with SetPinNames)
  160. """
  161. if self.pinNames is None:
  162. return self.NamingFunction(*args, **kwargs)
  163. return self.pinNames
  164. def NamingFunction(self, *args, **kwargs):
  165. """!
  166. Implement this as needed for each array type
  167. """
  168. raise NotImplementedError;
  169. class PadGridArray(PadArray):
  170. """!
  171. A basic grid of pads
  172. """
  173. def __init__(self, pad, nx, ny, px, py, centre=pcbnew.VECTOR2I(0, 0)):
  174. """!
  175. @param pad: the prototypical pad of the array
  176. @param nx: number of pads in x-direction
  177. @param ny: number of pads in y-direction
  178. @param px: pitch in x-direction
  179. @param py: pitch in y-direction
  180. @param centre: array centre point
  181. """
  182. try:
  183. super().__init__(pad)
  184. except TypeError:
  185. super(PadGridArray, self).__init__(pad)
  186. self.nx = int(nx)
  187. self.ny = int(ny)
  188. self.px = px
  189. self.py = py
  190. self.centre = centre
  191. def AlphaNameFromNumber(self, n, aIndex=1,
  192. alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ"):
  193. """!
  194. Utility function to generate an alphabetical name:
  195. eg. 1 - A, 2 - B, 26 - AA, etc
  196. @param aIndex: index of 'A': 0 for 0 - A
  197. @param n: the pad index
  198. @param alphabet: set of allowable chars if not A-Z,
  199. e.g. ABCDEFGHJKLMNPRTUVWY for BGA
  200. """
  201. div, mod = divmod(n - aIndex, len(alphabet))
  202. alpha = alphabet[mod]
  203. if div > 0:
  204. return self.AlphaNameFromNumber(div, aIndex, alphabet) + alpha
  205. return alpha
  206. def NamingFunction(self, x, y):
  207. """!
  208. Implementation of the naming function: right to left, top-to-bottom
  209. @param x: the pad x index
  210. @param y: the pad y index
  211. """
  212. return self.firstPadNum + (self.nx * y + x)
  213. #relocate the pad and add it as many times as we need
  214. def AddPadsToModule(self, dc):
  215. """!
  216. Create the pads and add them to the module in the correct positions
  217. @param dc: the drawing context
  218. """
  219. pin1posX = self.centre.x - self.px * (self.nx - 1) / 2
  220. pin1posY = self.centre.y - self.py * (self.ny - 1) / 2
  221. for x in range(0, self.nx):
  222. posX = pin1posX + (x * self.px)
  223. for y in range(self.ny):
  224. posY = pin1posY + (self.py * y)
  225. pos = dc.TransformPoint(posX, posY)
  226. pad = self.GetPad(x == 0 and y == 0, pos)
  227. pad.SetName(self.GetName(x,y))
  228. self.AddPad(pad)
  229. class EPADGridArray(PadGridArray):
  230. """!
  231. A pad grid array with a fixed name, used for things like thermal
  232. pads and via grids.
  233. """
  234. def NamingFunction(self, nx, ny):
  235. """!
  236. Simply return the firstPadNum
  237. @param nx: not used
  238. @param ny: not used
  239. """
  240. return self.firstPadNum
  241. class PadZGridArray(PadArray):
  242. """!
  243. A staggered pin array
  244. """
  245. def __init__(self, pad, pad_count, line_count, line_pitch,
  246. pad_pitch, centre=pcbnew.VECTOR2I(0, 0)):
  247. """!
  248. @param pad: the prototypical pad
  249. @param pad_count: total pad count
  250. @param line_count: number of staggered lines
  251. @param line_pitch: distance between lines
  252. @param pad_pitch: distance between pads in a line
  253. @param centre: array centre point
  254. """
  255. super(PadZGridArray, self).__init__(pad)
  256. self.pad_count = int(pad_count)
  257. self.line_count = int(line_count)
  258. self.line_pitch = line_pitch
  259. self.pad_pitch = pad_pitch
  260. self.centre = centre
  261. def NamingFunction(self, pad_pos):
  262. """!
  263. Naming just increased with pad index in array
  264. """
  265. return self.firstPadNum + pad_pos
  266. def AddPadsToModule(self, dc):
  267. """!
  268. Create the pads and add them to the module in the correct positions
  269. @param dc: the drawing context
  270. """
  271. pin1posX = self.centre.x - self.pad_pitch * (self.pad_count - 1) / 2
  272. pin1posY = self.centre.y + self.line_pitch * (self.line_count - 1) / 2
  273. line = 0
  274. for padnum in range(0, self.pad_count):
  275. posX = pin1posX + (padnum * self.pad_pitch)
  276. posY = pin1posY - (self.line_pitch * line)
  277. pos = dc.TransformPoint(posX, posY)
  278. pad = self.GetPad(padnum == 0, pos)
  279. pad.SetName(self.GetName(padnum))
  280. self.AddPad(pad)
  281. line += 1
  282. if line >= self.line_count:
  283. line = 0
  284. class PadLineArray(PadGridArray):
  285. """!
  286. Shortcut cases for a single-row grid array. Can be used for
  287. constructing sections of larger footprints.
  288. """
  289. def __init__(self, pad, n, pitch, isVertical,
  290. centre=pcbnew.VECTOR2I(0, 0)):
  291. """!
  292. @param pad: the prototypical pad
  293. @param n: number of pads in array
  294. @param pitch: distance between pad centres
  295. @param isVertical: horizontal or vertical array (can also use the
  296. drawing contexts transforms for more control)
  297. @param centre: array centre
  298. """
  299. if isVertical:
  300. super(PadLineArray, self).__init__(pad, 1, n, 0, pitch, centre)
  301. else:
  302. super(PadLineArray, self).__init__(pad, n, 1, pitch, 0, centre)
  303. class PadCircleArray(PadArray):
  304. """!
  305. Circular pad array
  306. """
  307. def __init__(self, pad, n, r, angle_offset=0, centre=pcbnew.VECTOR2I(0, 0),
  308. clockwise=True, padRotationEnable=False, padRotationOffset=0):
  309. """!
  310. @param pad: the prototypical pad
  311. @param n: number of pads in array
  312. @param r: the circle radius
  313. @param angle_offset: angle of the first pad
  314. @param centre: array centre point
  315. @param clockwise: array increases in a clockwise direction
  316. @param padRotationEnable: also rotate pads when placing
  317. @param padRotationOffset: rotation of first pad
  318. """
  319. super(PadCircleArray, self).__init__(pad)
  320. self.n = int(n)
  321. self.r = r
  322. self.angle_offset = angle_offset
  323. self.centre = centre
  324. self.clockwise = clockwise
  325. self.padRotationEnable = padRotationEnable
  326. self.padRotationOffset = padRotationOffset
  327. def NamingFunction(self, n):
  328. """!
  329. Naming around the circle, CW or CCW according to the clockwise flag
  330. """
  331. return str(self.firstPadNum + n)
  332. def AddPadsToModule(self, dc):
  333. """!
  334. Create the pads and add them to the module in the correct positions
  335. @param dc: the drawing context
  336. """
  337. for pin in range(0, self.n):
  338. angle = self.angle_offset + (360 / self.n) * pin
  339. if not self.clockwise:
  340. angle = -angle
  341. pos_x = math.sin(angle * math.pi / 180) * self.r
  342. pos_y = -math.cos(angle * math.pi / 180) * self.r
  343. pos = dc.TransformPoint(pos_x, pos_y)
  344. pad = self.GetPad(pin == 0, pos)
  345. padAngle = self.padRotationOffset
  346. if self.padRotationEnable:
  347. padAngle -=angle
  348. pad.SetOrientation( pcbnew.EDA_ANGLE( padAngle, pcbnew.DEGREES_T ) )
  349. pad.SetName(self.GetName(pin))
  350. self.AddPad(pad)
  351. class PadCustomArray(PadArray):
  352. """!
  353. Layout pads according to a custom array of [x,y] data
  354. """
  355. def __init__(self, pad, array):
  356. """!
  357. @param pad: the prototypical pad
  358. @param array: the position data array
  359. """
  360. super(PadCustomArray, self).__init__(pad)
  361. self.array = array
  362. def NamingFunction(self, n):
  363. """!
  364. Simple increment along the given array
  365. @param n: the pad index in the array
  366. """
  367. return str(self.firstPadNum + n)
  368. def AddPadsToModule(self, dc):
  369. """!
  370. Create the pads and add them to the module in the correct positions
  371. @param dc: the drawing context
  372. """
  373. for i in range(len(self.array)):
  374. pos = dc.TransformPoint(self.array[i][0], self.array[i][1])
  375. pad = self.GetPad(i == 0, pos)
  376. pad.SetName(self.GetName(i))
  377. self.AddPad(pad)