From bf83ec776503ead52d608db0615e662998b08946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=20P=C3=B6schl?= Date: Sun, 8 Sep 2019 15:00:57 +0200 Subject: [PATCH 01/19] Introduce more powerful round radius handler --- KicadModTree/nodes/base/Pad.py | 137 +++++++++++++++--- .../nodes/specialized/ChamferedPad.py | 12 +- KicadModTree/nodes/specialized/ExposedPad.py | 54 ++++--- .../ipc_gullwing_generator.py | 5 +- 4 files changed, 156 insertions(+), 52 deletions(-) diff --git a/KicadModTree/nodes/base/Pad.py b/KicadModTree/nodes/base/Pad.py index a501e9ae7..42967dd9d 100644 --- a/KicadModTree/nodes/base/Pad.py +++ b/KicadModTree/nodes/base/Pad.py @@ -23,6 +23,110 @@ from KicadModTree.nodes.base.Line import Line from KicadModTree.nodes.base.Polygon import Polygon +class RoundRadiusHandler(object): + r"""Handles round radius setting of a pad + + :param \**kwargs: + See below + + :Keyword Arguments: + * *radius_ratio* (``float``) -- + The radius ratio of the rounded rectangle. + Ignored for every shape except round rect. + * *maximum_radius* (``float``) -- + The maximum radius for the rounded rectangle. + If the radius produced by the radius_ratio parameter for the pad would + exceed the maximum radius, the ratio is reduced to limit the radius. + (This is useful for IPC-7351C compliance as it suggests 25% ratio with limit 0.25mm) + Ignored for every shape except round rect. + * *round_radius_exact* (``float``) -- + Set an exact round radius for a pad. + Ignored for every shape except round rect + """ + def __init__(self, **kwargs): + radius_ratio = kwargs.get('radius_ratio', 0) + if type(radius_ratio) not in [int, float]: + raise TypeError('radius ratio needs to be of type int or float') + if radius_ratio >= 0 and radius_ratio <= 0.5: + self.radius_ratio = radius_ratio + else: + raise ValueError('radius ratio out of allowed range (0 <= rr <= 0.5)') + + + self.maximum_radius = RoundRadiusHandler.__checkAbsoluteValue('maximum_radius', **kwargs) + self.round_radius_exact = RoundRadiusHandler.__checkAbsoluteValue('round_radius_exact', **kwargs) + + self.kicad4_compatible = kwargs.get('kicad4_compatible', False) + + @staticmethod + def __checkAbsoluteValue(name, **kwargs): + val = kwargs.get(name) + if val is not None: + if type(val) not in [int, float]: + raise TypeError('{} needs to be of type int or float'.format(name)) + return val + + def getRadiusRatio(self, shortest_sidelength): + r"""get the resulting round radius ratio + + :param shortest_sidelength: shortest sidelength of a pad + :return: the resulting round radius ratio to be used for the pad + """ + if self.kicad4_compatible: + return 0 + + if self.round_radius_exact: + if self.round_radius_exact > shortest_sidelength/2: + raise ValueError("requested round radius of {} is too large for pad size of {}".format(self.round_radius_exact, pad_size)) + if self.maximum_radius: + return min(self.round_radius_exact, self.maximum_radius)/shortest_sidelength + else: + return self.round_radius_exact/shortest_sidelength + if self.maximum_radius: + if self.radius_ratio*shortest_sidelength > self.maximum_radius: + return self.maximum_radius/shortest_sidelength + + return self.radius_ratio + + def getRoundRadius(self, shortest_sidelength): + r"""get the resulting round radius + + :param shortest_sidelength: shortest sidelength of a pad + :return: the resulting round radius to be used for the pad + """ + return self.getRadiusRatio(shortest_sidelength)*shortest_sidelength + + def roundingRequested(self): + r"""Check if the pad has a rounded corner + + :return: True if rounding corners is required + """ + if self.kicad4_compatible: + return False + + if self.round_radius_exact is not None: + if self.round_radius_exact > 0: + return True + else: + return False + + if self.radius_ratio > 0: + return True + else: + return False + + def limitMaxRadius(self, limit): + r"""Set a new maximum limit + + :param limit: the new limit. + """ + + if not self.roundingRequested(): + return + if self.maximum_radius is not None: + self.maximum_radius = min(self.maximum_radius, limit) + else: + self.maximum_radius = limit class Pad(Node): r"""Add a Pad to the render tree @@ -50,13 +154,20 @@ class Pad(Node): drill-size of the pad * *radius_ratio* (``float``) -- The radius ratio of the rounded rectangle. - Ignored for every other shape. + Ignored for every shape except round rect. * *maximum_radius* (``float``) -- The maximum radius for the rounded rectangle. If the radius produced by the radius_ratio parameter for the pad would - exceed the maximum radius, the ratio is reduced to limit the ratio. + exceed the maximum radius, the ratio is reduced to limit the radius. (This is useful for IPC-7351C compliance as it suggests 25% ratio with limit 0.25mm) - Ignored for every other shape. + Ignored for every shape except round rect. + * *round_radius_exact* (``float``) -- + Set an exact round radius for a pad. + Ignored for every shape except round rect + * *round_radius_handler* (``RoundRadiusHandler``) -- + An instance of the RoundRadiusHandler class + If this is given then all other round radius specifiers are Ignored + Ignored for every shape except round rect * *solder_paste_margin_ratio* (``float``) -- solder paste margin ratio of the pad (default: 0) * *solder_paste_margin* (``float``) -- @@ -210,22 +321,12 @@ def _initLayers(self, **kwargs): self.layers = kwargs.get('layers') def _initRadiusRatio(self, **kwargs): - radius_ratio = kwargs.get('radius_ratio', 0) - if type(radius_ratio) not in [int, float]: - raise TypeError('radius ratio needs to be of type int or float') - if radius_ratio >= 0 and radius_ratio <= 0.5: - self.radius_ratio = radius_ratio + if('round_radius_handler' in kwargs): + self.round_radius_handler = kwargs['round_radius_handler'] else: - raise ValueError('radius ratio out of allowed range (0 <= rr <= 0.5)') - - if kwargs.get('maximum_radius') is not None: - maximum_radius = kwargs.get('maximum_radius') - if type(maximum_radius) not in [int, float]: - raise TypeError('maximum radius needs to be of type int or float') + self.round_radius_handler = RoundRadiusHandler(**kwargs) - shortest_sidelength = min(self.size) - if self.radius_ratio*shortest_sidelength > maximum_radius: - self.radius_ratio = maximum_radius/shortest_sidelength + self.radius_ratio = self.round_radius_handler.getRadiusRatio(min(self.size)) if self.radius_ratio == 0: self.shape = Pad.SHAPE_RECT @@ -275,4 +376,4 @@ def getRoundRadius(self): if r > r_max: r_max = r return r_max - return self.radius_ratio*min(self.size) + return self.round_radius_handler.getRoundRadius(min(self.size)) diff --git a/KicadModTree/nodes/specialized/ChamferedPad.py b/KicadModTree/nodes/specialized/ChamferedPad.py index 960da4e7e..4c4e55652 100644 --- a/KicadModTree/nodes/specialized/ChamferedPad.py +++ b/KicadModTree/nodes/specialized/ChamferedPad.py @@ -19,7 +19,7 @@ from KicadModTree.util.paramUtil import * from KicadModTree.Vector import * from KicadModTree.nodes.base.Polygon import * -from KicadModTree.nodes.base.Pad import Pad +from KicadModTree.nodes.base.Pad import Pad, RoundRadiusHandler from math import sqrt @@ -225,8 +225,7 @@ def __init__(self, **kwargs): self._initSize(**kwargs) self._initMirror(**kwargs) self._initPadSettings(**kwargs) - self.radius_ratio = kwargs.get('radius_ratio', 0) - self.maximum_radius = kwargs.get('maximum_radius') + self.round_radius_handler = RoundRadiusHandler(**kwargs) self.pad = self._generatePad() def _initSize(self, **kwargs): @@ -271,10 +270,7 @@ def _generatePad(self): if self.corner_selection.isAnySelected() and self.chamfer_size[0] > 0 and self.chamfer_size[1] > 0: is_chamfered = True - shortest_sidlength = min(self.size) - radius = shortest_sidlength*self.radius_ratio - if self.maximum_radius and radius > self.maximum_radius: - radius = self.maximum_radius + radius = self.round_radius_handler.getRoundRadius(min(self.size)) if is_chamfered: outside = Vector2D(self.size.x/2, self.size.y/2) @@ -284,7 +280,7 @@ def _generatePad(self): ] polygon_width = 0 - if self.radius_ratio > 0: + if self.round_radius_handler.roundingRequested(): if self.chamfer_size[0] != self.chamfer_size[1]: raise NotImplementedError('rounded chamfered pads are only supported for 45 degree chamfers') # We prefer the use of rounded rectangle over chamfered pads. diff --git a/KicadModTree/nodes/specialized/ExposedPad.py b/KicadModTree/nodes/specialized/ExposedPad.py index 242fb4f9b..c1789d6a1 100644 --- a/KicadModTree/nodes/specialized/ExposedPad.py +++ b/KicadModTree/nodes/specialized/ExposedPad.py @@ -65,6 +65,7 @@ class ExposedPad(Node): * *paste_coverage* (``float``) -- how much of the mask free area is covered with paste. (default: 0.65) + * *via_layout* (``int``, ``[int, int]``) -- thermal via layout specification. How many vias in x and y direction. @@ -105,11 +106,20 @@ class ExposedPad(Node): 0 means no rounding * *radius_ratio* (``float``) -- - The radius ratio for all pads (copper, paste and mask) - Default: 0 means pads do not included rounded corners (normal rectangles are used) + The radius ratio of the main pads. * *maximum_radius* (``float``) -- - Only used if a radius ratio is given. - Limits the radius. + The maximum radius for the main pads. + If the radius produced by the radius_ratio parameter for the pad would + exceed the maximum radius, the ratio is reduced to limit the radius. + * *round_radius_exact* (``float``) -- + Set an exact round radius for the main pads. + + * *paste_radius_ratio* (``float``) -- + The radius ratio of the paste pads. + * *paste_maximum_radius* (``float``) -- + The maximum radius for the paste pads. + If the radius produced by the paste_radius_ratio parameter for the paste pad would + exceed the maximum radius, the ratio is reduced to limit the radius. * *kicad4_compatible* (``bool``) -- Makes sure the resulting pad is compatible with kicad 4. default False @@ -126,13 +136,14 @@ def __init__(self, **kwargs): self.size_round_base = kwargs.get('size_round_base', 0.01) self.grid_round_base = kwargs.get('grid_round_base', 0.01) - self.radius_ratio = kwargs.get('radius_ratio', 0) - self.maximum_radius = kwargs.get('maximum_radius') + self.round_radius_handler = RoundRadiusHandler(**kwargs) self.kicad4_compatible = kwargs.get('kicad4_compatible', False) - if self.kicad4_compatible: - self.radius_ratio = 0 - self.maximum_radius = None + self.paste_round_radius_handler = RoundRadiusHandler( + radius_ratio=kwargs.get('paste_round_radius_ratio', 0), + maximum_radius=kwargs.get('paste_maximum_radius', None), + kicad4_compatible=self.kicad4_compatible + ) self._initNumber(**kwargs) self._initSize(**kwargs) @@ -268,7 +279,7 @@ def __createPasteIgnoreVia(self): center=self.at, size=paste_size, layers=['F.Paste'], chamfer_size=0, chamfer_selection=0, pincount=self.paste_layout, grid=paste_grid, - radius_ratio=self.radius_ratio, maximum_radius=self.maximum_radius + round_radius_handler=self.paste_round_radius_handler )] @staticmethod @@ -312,7 +323,7 @@ def __createPasteAvoidViasInside(self): center=[0, 0], size=self.inner_size, layers=['F.Paste'], chamfer_size=0, chamfer_selection=corner, pincount=self.paste_between_vias, grid=self.inner_grid, - radius_ratio=self.radius_ratio, maximum_radius=self.maximum_radius + round_radius_handler=self.paste_round_radius_handler ) if not self.kicad4_compatible: @@ -342,7 +353,7 @@ def __createPasteOutsideX(self): chamfer_size=0, chamfer_selection=corner, pincount=[self.paste_rings_outside[0], self.paste_between_vias[1]], grid=[self.outer_paste_grid[0], self.inner_grid[1]], - radius_ratio=self.radius_ratio, maximum_radius=self.maximum_radius + round_radius_handler=self.paste_round_radius_handler ) if not self.kicad4_compatible: @@ -388,7 +399,7 @@ def __createPasteOutsideY(self): chamfer_size=0, chamfer_selection=corner, pincount=[self.paste_between_vias[0], self.paste_rings_outside[1]], grid=[self.inner_grid[0], self.outer_paste_grid[1]], - radius_ratio=self.radius_ratio, maximum_radius=self.maximum_radius + round_radius_handler=self.paste_round_radius_handler ) if not self.kicad4_compatible: @@ -436,7 +447,7 @@ def __createPasteOutsideCorners(self): chamfer_size=0, chamfer_selection=0, pincount=self.paste_rings_outside, grid=self.outer_paste_grid, - radius_ratio=self.radius_ratio, maximum_radius=self.maximum_radius + round_radius_handler=self.paste_round_radius_handler ) if not self.kicad4_compatible: @@ -498,13 +509,13 @@ def __createMainPad(self): pads.append(Pad( number="", at=self.at, size=self.mask_size, shape=Pad.SHAPE_ROUNDRECT, type=Pad.TYPE_SMT, layers=['F.Mask'], - radius_ratio=self.radius_ratio, maximum_radius=self.main_max_radius + round_radius_handler=self.round_radius_handler )) pads.append(Pad( number=self.number, at=self.at, size=self.size, shape=Pad.SHAPE_ROUNDRECT, type=Pad.TYPE_SMT, layers=layers_main, - radius_ratio=self.radius_ratio, maximum_radius=self.main_max_radius + round_radius_handler=self.round_radius_handler )) return pads @@ -533,7 +544,7 @@ def __createVias(self): number=self.number, at=self.at, size=self.bottom_size, shape=Pad.SHAPE_ROUNDRECT, type=Pad.TYPE_SMT, layers=self.bottom_pad_Layers, - radius_ratio=self.radius_ratio, maximum_radius=self.main_max_radius + round_radius_handler=self.round_radius_handler )) return pads @@ -541,12 +552,7 @@ def __createVias(self): def getVirtualChilds(self): # traceback.print_stack() if self.has_vias: - if self.maximum_radius: - self.main_max_radius = min(self.maximum_radius, self.via_size/2) - else: - self.main_max_radius = self.via_size/2 - else: - self.main_max_radius = self.maximum_radius + self.round_radius_handler.limitMaxRadius(self.via_size/2) pads = [] pads += self.__createMainPad() @@ -556,4 +562,4 @@ def getVirtualChilds(self): return pads def getRoundRadius(self): - return min(self.radius_ratio*min(self.size), self.maximum_radius) + return self.round_radius_handler.getRoundRadius(min(self.size)) diff --git a/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/ipc_gullwing_generator.py b/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/ipc_gullwing_generator.py index 05f958521..42801301e 100755 --- a/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/ipc_gullwing_generator.py +++ b/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/ipc_gullwing_generator.py @@ -257,8 +257,11 @@ def __createFootprintVariant(self, device_params, header, dimensions, with_therm if 'round_rect_max_radius' in configuration: pad_shape_details['maximum_radius'] = configuration['round_rect_max_radius'] + pad_radius = add_dual_or_quad_pad_border(kicad_mod, configuration, pad_details, device_params) + EP_round_radius = 0 if dimensions['has_EP']: + EP_round_radius = device_params.get('EP_round_radius_overwrite', pad_radius) EP_mask_size = EP_mask_size if EP_mask_size['x'] > 0 else None if with_thermal_vias: @@ -292,8 +295,6 @@ def __createFootprintVariant(self, device_params, header, dimensions, with_therm kicad_mod.append(EP) EP_round_radius = EP.getRoundRadius() - pad_radius = add_dual_or_quad_pad_border(kicad_mod, configuration, pad_details, device_params) - body_edge = { 'left': -dimensions['body_size_x'].nominal/2, 'right': dimensions['body_size_x'].nominal/2, From 9241a3ddecdf85de58da887c0ec904991d2d5f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=20P=C3=B6schl?= Date: Sun, 8 Sep 2019 16:47:10 +0200 Subject: [PATCH 02/19] EP round radius update Using a fixed round radius ratio of 0.25 with maximum 0.25mm radius does not make sense for the exposed pad as it is created without positive fillet and therefore such a large radius would interfere with the "lead". Our research showed that using the same radius as the normal pads is a much better option. There also has been a parameter added to directly control the round radius from the size definition. --- KicadModTree/nodes/base/Pad.py | 8 ++++++-- KicadModTree/nodes/specialized/ExposedPad.py | 5 ++++- .../ipc_gullwing_generator.py | 14 ++++++++++++-- .../ipc_noLead_generator.py | 14 ++++++++++++-- 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/KicadModTree/nodes/base/Pad.py b/KicadModTree/nodes/base/Pad.py index 42967dd9d..a73fb80f2 100644 --- a/KicadModTree/nodes/base/Pad.py +++ b/KicadModTree/nodes/base/Pad.py @@ -23,6 +23,7 @@ from KicadModTree.nodes.base.Line import Line from KicadModTree.nodes.base.Polygon import Polygon + class RoundRadiusHandler(object): r"""Handles round radius setting of a pad @@ -52,7 +53,6 @@ def __init__(self, **kwargs): else: raise ValueError('radius ratio out of allowed range (0 <= rr <= 0.5)') - self.maximum_radius = RoundRadiusHandler.__checkAbsoluteValue('maximum_radius', **kwargs) self.round_radius_exact = RoundRadiusHandler.__checkAbsoluteValue('round_radius_exact', **kwargs) @@ -77,7 +77,10 @@ def getRadiusRatio(self, shortest_sidelength): if self.round_radius_exact: if self.round_radius_exact > shortest_sidelength/2: - raise ValueError("requested round radius of {} is too large for pad size of {}".format(self.round_radius_exact, pad_size)) + raise ValueError( + "requested round radius of {} is too large for pad size of {}" + .format(self.round_radius_exact, pad_size) + ) if self.maximum_radius: return min(self.round_radius_exact, self.maximum_radius)/shortest_sidelength else: @@ -128,6 +131,7 @@ def limitMaxRadius(self, limit): else: self.maximum_radius = limit + class Pad(Node): r"""Add a Pad to the render tree diff --git a/KicadModTree/nodes/specialized/ExposedPad.py b/KicadModTree/nodes/specialized/ExposedPad.py index c1789d6a1..069c77533 100644 --- a/KicadModTree/nodes/specialized/ExposedPad.py +++ b/KicadModTree/nodes/specialized/ExposedPad.py @@ -120,6 +120,8 @@ class ExposedPad(Node): The maximum radius for the paste pads. If the radius produced by the paste_radius_ratio parameter for the paste pad would exceed the maximum radius, the ratio is reduced to limit the radius. + * *paste_round_radius_exact* (``float``) -- + Set an exact round radius for the paste pads. * *kicad4_compatible* (``bool``) -- Makes sure the resulting pad is compatible with kicad 4. default False @@ -140,8 +142,9 @@ def __init__(self, **kwargs): self.kicad4_compatible = kwargs.get('kicad4_compatible', False) self.paste_round_radius_handler = RoundRadiusHandler( - radius_ratio=kwargs.get('paste_round_radius_ratio', 0), + radius_ratio=kwargs.get('paste_radius_ratio', 0), maximum_radius=kwargs.get('paste_maximum_radius', None), + round_radius_exact=kwargs.get('paste_round_radius_exact', None), kicad4_compatible=self.kicad4_compatible ) diff --git a/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/ipc_gullwing_generator.py b/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/ipc_gullwing_generator.py index 42801301e..9f323b84e 100755 --- a/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/ipc_gullwing_generator.py +++ b/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/ipc_gullwing_generator.py @@ -34,8 +34,14 @@ def __init__(self, configuration): self.ipc_defintions = yaml.safe_load(ipc_stream) self.configuration['min_ep_to_pad_clearance'] = 0.2 + + #ToDo: find a settings file that can contain these. + self.configuration['paste_radius_ratio'] = 0.25 + self.configuration['paste_maximum_radius'] = 0.25 + if 'ipc_generic_rules' in self.ipc_defintions: self.configuration['min_ep_to_pad_clearance'] = self.ipc_defintions['ipc_generic_rules'].get('min_ep_to_pad_clearance', 0.2) + except yaml.YAMLError as exc: print(exc) @@ -251,17 +257,21 @@ def __createFootprintVariant(self, device_params, header, dimensions, with_therm ).lstrip()) kicad_mod.setAttribute('smd') + pad_radius = add_dual_or_quad_pad_border(kicad_mod, configuration, pad_details, device_params) + pad_shape_details = {} pad_shape_details['shape'] = Pad.SHAPE_ROUNDRECT pad_shape_details['radius_ratio'] = configuration.get('round_rect_radius_ratio', 0) if 'round_rect_max_radius' in configuration: pad_shape_details['maximum_radius'] = configuration['round_rect_max_radius'] - pad_radius = add_dual_or_quad_pad_border(kicad_mod, configuration, pad_details, device_params) + pad_shape_details['round_radius_exact'] = device_params.get( + 'EP_round_radius_overwrite', pad_radius) + pad_shape_details['paste_radius_ratio'] = self.configuration['paste_radius_ratio'] + pad_shape_details['paste_maximum_radius'] = self.configuration['paste_maximum_radius'] EP_round_radius = 0 if dimensions['has_EP']: - EP_round_radius = device_params.get('EP_round_radius_overwrite', pad_radius) EP_mask_size = EP_mask_size if EP_mask_size['x'] > 0 else None if with_thermal_vias: diff --git a/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/ipc_noLead_generator.py b/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/ipc_noLead_generator.py index 3ad4ac173..f07aba697 100755 --- a/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/ipc_noLead_generator.py +++ b/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/ipc_noLead_generator.py @@ -41,6 +41,11 @@ def __init__(self, configuration): self.ipc_defintions = yaml.safe_load(ipc_stream) self.configuration['min_ep_to_pad_clearance'] = 0.2 + + #ToDo: find a settings file that can contain these. + self.configuration['paste_radius_ratio'] = 0.25 + self.configuration['paste_maximum_radius'] = 0.25 + if 'ipc_generic_rules' in self.ipc_defintions: self.configuration['min_ep_to_pad_clearance'] = self.ipc_defintions['ipc_generic_rules'].get('min_ep_to_pad_clearance', 0.2) except yaml.YAMLError as exc: @@ -319,12 +324,19 @@ def __createFootprintVariant(self, device_params, device_dimensions, with_therma ).lstrip()) kicad_mod.setAttribute('smd') + pad_radius = add_dual_or_quad_pad_border(kicad_mod, configuration, pad_details, device_params) + pad_shape_details = {} pad_shape_details['shape'] = Pad.SHAPE_ROUNDRECT pad_shape_details['radius_ratio'] = configuration.get('round_rect_radius_ratio', 0) if 'round_rect_max_radius' in configuration: pad_shape_details['maximum_radius'] = configuration['round_rect_max_radius'] + pad_shape_details['round_radius_exact'] = device_params.get( + 'EP_round_radius_overwrite', pad_radius) + pad_shape_details['paste_radius_ratio'] = self.configuration['paste_radius_ratio'] + pad_shape_details['paste_maximum_radius'] = self.configuration['paste_maximum_radius'] + if device_dimensions['has_EP']: if with_thermal_vias: thermals = device_params['thermal_vias'] @@ -358,8 +370,6 @@ def __createFootprintVariant(self, device_params, device_dimensions, with_therma **pad_shape_details )) - add_dual_or_quad_pad_border(kicad_mod, configuration, pad_details, device_params) - body_edge = { 'left': -size_x/2, 'right': size_x/2, From 688d3ba787636f5df9b4f00f5fa2bedf2bd60817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=20P=C3=B6schl?= Date: Sun, 8 Sep 2019 18:18:29 +0200 Subject: [PATCH 03/19] fix duplicate old style dimension --- .../size_definitions/qfn/qfn-24.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/qfn/qfn-24.yaml b/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/qfn/qfn-24.yaml index ff4056f52..4a1736d47 100644 --- a/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/qfn/qfn-24.yaml +++ b/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/qfn/qfn-24.yaml @@ -352,7 +352,6 @@ QFN-24-1EP_4x4mm_P0.5mm_EP2.7x2.7mm: ipc_class: 'qfn' # 'qfn_pull_back' #ipc_density: 'least' #overwrite global value for this device. # custom_name_format: - body_size_x_min: 3.9 body_size_x: nominal: 4 tolerance: 0.1 From 08a4cf395b0848c577f19ee4fb8fcd221a1fb1d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=20P=C3=B6schl?= Date: Sun, 8 Sep 2019 19:05:02 +0200 Subject: [PATCH 04/19] fix rounding of chamfered pad if handler is given --- KicadModTree/nodes/base/Pad.py | 6 ++++++ KicadModTree/nodes/specialized/ChamferedPad.py | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/KicadModTree/nodes/base/Pad.py b/KicadModTree/nodes/base/Pad.py index a73fb80f2..a759b7a16 100644 --- a/KicadModTree/nodes/base/Pad.py +++ b/KicadModTree/nodes/base/Pad.py @@ -131,6 +131,12 @@ def limitMaxRadius(self, limit): else: self.maximum_radius = limit + def __str__(self): + return "ratio {}, max {}, exact {}, v4 compatible {}".format( + self.radius_ratio, self.maximum_radius, + self.round_radius_exact, self.kicad4_compatible + ) + class Pad(Node): r"""Add a Pad to the render tree diff --git a/KicadModTree/nodes/specialized/ChamferedPad.py b/KicadModTree/nodes/specialized/ChamferedPad.py index 4c4e55652..627a9ff00 100644 --- a/KicadModTree/nodes/specialized/ChamferedPad.py +++ b/KicadModTree/nodes/specialized/ChamferedPad.py @@ -225,7 +225,12 @@ def __init__(self, **kwargs): self._initSize(**kwargs) self._initMirror(**kwargs) self._initPadSettings(**kwargs) - self.round_radius_handler = RoundRadiusHandler(**kwargs) + + if('round_radius_handler' in kwargs): + self.round_radius_handler = kwargs['round_radius_handler'] + else: + self.round_radius_handler = RoundRadiusHandler(**kwargs) + self.pad = self._generatePad() def _initSize(self, **kwargs): From 343f887e1a2f3c0705f444ea94f86f48387130a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=20P=C3=B6schl?= Date: Sun, 8 Sep 2019 19:06:39 +0200 Subject: [PATCH 05/19] update docstring --- KicadModTree/nodes/specialized/ChamferedPad.py | 14 ++++++++++---- KicadModTree/nodes/specialized/ChamferedPadGrid.py | 14 ++++++++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/KicadModTree/nodes/specialized/ChamferedPad.py b/KicadModTree/nodes/specialized/ChamferedPad.py index 627a9ff00..55d13c369 100644 --- a/KicadModTree/nodes/specialized/ChamferedPad.py +++ b/KicadModTree/nodes/specialized/ChamferedPad.py @@ -212,11 +212,17 @@ class ChamferedPad(Node): mirror y direction around offset "point" * *radius_ratio* (``float``) -- - The radius ratio used if the pad has no chamfer - Default: 0 means pads do not included rounded corners (normal rectangles are used) + The radius ratio of the rounded rectangle. * *maximum_radius* (``float``) -- - Only used if a radius ratio is given. - Limits the radius. + The maximum radius for the rounded rectangle. + If the radius produced by the radius_ratio parameter for the pad would + exceed the maximum radius, the ratio is reduced to limit the radius. + (This is useful for IPC-7351C compliance as it suggests 25% ratio with limit 0.25mm) + * *round_radius_exact* (``float``) -- + Set an exact round radius for a pad. + * *round_radius_handler* (``RoundRadiusHandler``) -- + An instance of the RoundRadiusHandler class + If this is given then all other round radius specifiers are Ignored """ def __init__(self, **kwargs): diff --git a/KicadModTree/nodes/specialized/ChamferedPadGrid.py b/KicadModTree/nodes/specialized/ChamferedPadGrid.py index 36758d55a..cbdf31d65 100644 --- a/KicadModTree/nodes/specialized/ChamferedPadGrid.py +++ b/KicadModTree/nodes/specialized/ChamferedPadGrid.py @@ -180,11 +180,17 @@ class ChamferedPadGrid(Node): mirror y direction around offset "point" * *radius_ratio* (``float``) -- - The radius ratio used if the pad has no chamfer - Default: 0 means pads do not included rounded corners (normal rectangles are used) + The radius ratio of the rounded rectangle. * *maximum_radius* (``float``) -- - Only used if a radius ratio is given. - Limits the radius. + The maximum radius for the rounded rectangle. + If the radius produced by the radius_ratio parameter for the pad would + exceed the maximum radius, the ratio is reduced to limit the radius. + (This is useful for IPC-7351C compliance as it suggests 25% ratio with limit 0.25mm) + * *round_radius_exact* (``float``) -- + Set an exact round radius for a pad. + * *round_radius_handler* (``RoundRadiusHandler``) -- + An instance of the RoundRadiusHandler class + If this is given then all other round radius specifiers are Ignored """ def __init__(self, **kwargs): From 24f55e89a6891c756018739a9fdfccbb76361384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=20P=C3=B6schl?= Date: Thu, 12 Sep 2019 21:22:05 +0200 Subject: [PATCH 06/19] better int/float param checking --- KicadModTree/nodes/base/Pad.py | 22 +++++-------------- KicadModTree/util/paramUtil.py | 39 ++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/KicadModTree/nodes/base/Pad.py b/KicadModTree/nodes/base/Pad.py index a759b7a16..bce67092c 100644 --- a/KicadModTree/nodes/base/Pad.py +++ b/KicadModTree/nodes/base/Pad.py @@ -45,27 +45,15 @@ class RoundRadiusHandler(object): Ignored for every shape except round rect """ def __init__(self, **kwargs): - radius_ratio = kwargs.get('radius_ratio', 0) - if type(radius_ratio) not in [int, float]: - raise TypeError('radius ratio needs to be of type int or float') - if radius_ratio >= 0 and radius_ratio <= 0.5: - self.radius_ratio = radius_ratio - else: - raise ValueError('radius ratio out of allowed range (0 <= rr <= 0.5)') + self.radius_ratio = getOptionalNumberTypeParam( + kwargs, 'radius_ratio', default_value=0, + low_limit=0, high_limit=0.5) - self.maximum_radius = RoundRadiusHandler.__checkAbsoluteValue('maximum_radius', **kwargs) - self.round_radius_exact = RoundRadiusHandler.__checkAbsoluteValue('round_radius_exact', **kwargs) + self.maximum_radius = getOptionalNumberTypeParam(kwargs, 'maximum_radius') + self.round_radius_exact = getOptionalNumberTypeParam(kwargs, 'round_radius_exact') self.kicad4_compatible = kwargs.get('kicad4_compatible', False) - @staticmethod - def __checkAbsoluteValue(name, **kwargs): - val = kwargs.get(name) - if val is not None: - if type(val) not in [int, float]: - raise TypeError('{} needs to be of type int or float'.format(name)) - return val - def getRadiusRatio(self, shortest_sidelength): r"""get the resulting round radius ratio diff --git a/KicadModTree/util/paramUtil.py b/KicadModTree/util/paramUtil.py index 837508a47..164771538 100644 --- a/KicadModTree/util/paramUtil.py +++ b/KicadModTree/util/paramUtil.py @@ -164,3 +164,42 @@ def toVectorUseCopyIfNumber(value, length=2, low_limit=None, must_be_larger=True raise ValueError("One value in ({}) too small. Limit is {}.".format(result, low_limit)) return result + +def getOptionalNumberTypeParam(kwargs, param_name, default_value=None, + low_limit=None, high_limit=None, allow_equal_limit=True): + r""" Get a named parameter from packed dict and guarantee it is a number (float or int) + + :param param_name: + The name of the parameter (=key) + + :param default_value: -- default: None + The value to be used if the parameter is not in the dict + + :param low_limit: -- default: None + The minimum allowable value + + :param high_limit: -- default: None + The maximum allowable value + + :param allow_equal_limit: -- default: True + Limits are included in range of allowable values + (min <= x <= max if true else min < x < max) + + :param **kwargs: + The parameters as packed dict + """ + val = kwargs.get(param_name, default_value) + if val is not None: + if type(val) not in [int, float]: + raise TypeError('{} needs to be of type int or float'.format(param_name)) + if low_limit is not None: + if val < low_limit or (val == low_limit and not allow_equal_limit): + raise ValueError( + '{} with value {} violates the low limit of {}' + .format(param_name, val, low_limit)) + if high_limit is not None: + if val > high_limit or (val == low_limit and not allow_equal_limit): + raise ValueError( + '{} with value {} violates the high limit of {}' + .format(param_name, val, high_limit)) + return val From bb831c409b012f47ad7dbcdc732560c057055882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=20P=C3=B6schl?= Date: Sun, 15 Sep 2019 10:04:06 +0200 Subject: [PATCH 07/19] docstring --- KicadModTree/nodes/base/Pad.py | 3 --- KicadModTree/nodes/specialized/ExposedPad.py | 1 - 2 files changed, 4 deletions(-) diff --git a/KicadModTree/nodes/base/Pad.py b/KicadModTree/nodes/base/Pad.py index bce67092c..e01ac973b 100644 --- a/KicadModTree/nodes/base/Pad.py +++ b/KicadModTree/nodes/base/Pad.py @@ -33,16 +33,13 @@ class RoundRadiusHandler(object): :Keyword Arguments: * *radius_ratio* (``float``) -- The radius ratio of the rounded rectangle. - Ignored for every shape except round rect. * *maximum_radius* (``float``) -- The maximum radius for the rounded rectangle. If the radius produced by the radius_ratio parameter for the pad would exceed the maximum radius, the ratio is reduced to limit the radius. (This is useful for IPC-7351C compliance as it suggests 25% ratio with limit 0.25mm) - Ignored for every shape except round rect. * *round_radius_exact* (``float``) -- Set an exact round radius for a pad. - Ignored for every shape except round rect """ def __init__(self, **kwargs): self.radius_ratio = getOptionalNumberTypeParam( diff --git a/KicadModTree/nodes/specialized/ExposedPad.py b/KicadModTree/nodes/specialized/ExposedPad.py index 069c77533..05c99eca9 100644 --- a/KicadModTree/nodes/specialized/ExposedPad.py +++ b/KicadModTree/nodes/specialized/ExposedPad.py @@ -65,7 +65,6 @@ class ExposedPad(Node): * *paste_coverage* (``float``) -- how much of the mask free area is covered with paste. (default: 0.65) - * *via_layout* (``int``, ``[int, int]``) -- thermal via layout specification. How many vias in x and y direction. From 6709c4809c59c4cbb880ce9fd82d64c563c8fad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=20P=C3=B6schl?= Date: Thu, 26 Sep 2019 20:39:41 +0200 Subject: [PATCH 08/19] fix minor grammer spelling and formating mistakes --- KicadModTree/nodes/base/Pad.py | 4 ++-- .../Package_NoLead__DFN_QFN_LGA_SON/ipc_noLead_generator.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/KicadModTree/nodes/base/Pad.py b/KicadModTree/nodes/base/Pad.py index e01ac973b..6e20c03a0 100644 --- a/KicadModTree/nodes/base/Pad.py +++ b/KicadModTree/nodes/base/Pad.py @@ -87,7 +87,7 @@ def getRoundRadius(self, shortest_sidelength): def roundingRequested(self): r"""Check if the pad has a rounded corner - :return: True if rounding corners is required + :return: True are rounding corners is required """ if self.kicad4_compatible: return False @@ -161,7 +161,7 @@ class Pad(Node): Ignored for every shape except round rect * *round_radius_handler* (``RoundRadiusHandler``) -- An instance of the RoundRadiusHandler class - If this is given then all other round radius specifiers are Ignored + If this is given then all other round radius specifiers are ignored Ignored for every shape except round rect * *solder_paste_margin_ratio* (``float``) -- solder paste margin ratio of the pad (default: 0) diff --git a/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/ipc_noLead_generator.py b/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/ipc_noLead_generator.py index f07aba697..d9ddce2e3 100755 --- a/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/ipc_noLead_generator.py +++ b/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/ipc_noLead_generator.py @@ -48,6 +48,7 @@ def __init__(self, configuration): if 'ipc_generic_rules' in self.ipc_defintions: self.configuration['min_ep_to_pad_clearance'] = self.ipc_defintions['ipc_generic_rules'].get('min_ep_to_pad_clearance', 0.2) + except yaml.YAMLError as exc: print(exc) From 7d48698328414c8471d5a01a2d7e2b2e2ed8ca8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=20P=C3=B6schl?= Date: Fri, 27 Sep 2019 07:49:47 +0200 Subject: [PATCH 09/19] Better formating of format string --- KicadModTree/nodes/base/Pad.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/KicadModTree/nodes/base/Pad.py b/KicadModTree/nodes/base/Pad.py index 6e20c03a0..53198ed7d 100644 --- a/KicadModTree/nodes/base/Pad.py +++ b/KicadModTree/nodes/base/Pad.py @@ -137,6 +137,9 @@ class Pad(Node): * *shape* (``Pad.SHAPE_CIRCLE``, ``Pad.SHAPE_OVAL``, ``Pad.SHAPE_RECT``, ``SHAPE_ROUNDRECT``, ``Pad.SHAPE_TRAPEZE``, ``SHAPE_CUSTOM``) -- shape of the pad + * *layers* (``Pad.LAYERS_SMT``, ``Pad.LAYERS_THT``, ``Pad.LAYERS_NPTH``) -- + layers on which are used for the pad + * *at* (``Vector2D``) -- center position of the pad * *rotation* (``float``) -- @@ -147,6 +150,7 @@ class Pad(Node): offset of the pad * *drill* (``float``, ``Vector2D``) -- drill-size of the pad + * *radius_ratio* (``float``) -- The radius ratio of the rounded rectangle. Ignored for every shape except round rect. @@ -163,14 +167,14 @@ class Pad(Node): An instance of the RoundRadiusHandler class If this is given then all other round radius specifiers are ignored Ignored for every shape except round rect + * *solder_paste_margin_ratio* (``float``) -- solder paste margin ratio of the pad (default: 0) * *solder_paste_margin* (``float``) -- solder paste margin of the pad (default: 0) * *solder_mask_margin* (``float``) -- solder mask margin of the pad (default: 0) - * *layers* (``Pad.LAYERS_SMT``, ``Pad.LAYERS_THT``, ``Pad.LAYERS_NPTH``) -- - layers on which are used for the pad + * *x_mirror* (``[int, float](mirror offset)``) -- mirror x direction around offset "point" * *y_mirror* (``[int, float](mirror offset)``) -- From baaddfae6406b2575f5ed70685cc0881f3df7924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=20P=C3=B6schl?= Date: Wed, 9 Oct 2019 17:14:09 +0200 Subject: [PATCH 10/19] correct exclude pin list for SOIC-4_4.55x3.7mm_P2.54mm --- .../Package_Gullwing__QFP_SOIC_SO/size_definitions/soic.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/size_definitions/soic.yaml b/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/size_definitions/soic.yaml index f3238b7d0..a71fa723c 100644 --- a/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/size_definitions/soic.yaml +++ b/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/size_definitions/soic.yaml @@ -24,7 +24,7 @@ SOIC-4_4.55x3.7mm_P2.54mm: pitch: 1.27 num_pins_x: 0 num_pins_y: 3 - exclude_pin_list: [2, 4] #not currently implemented so hand modification is necessary + exclude_pin_list: [2, 5] SOIC-4_4.55x2.6mm_P1.27mm: size_source: 'https://toshiba.semicon-storage.com/info/docget.jsp?did=12884&prodName=TLP291' From c8e7299132c230061226b3264dd0530a725c2cc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=20P=C3=B6schl?= Date: Thu, 10 Oct 2019 21:09:00 +0200 Subject: [PATCH 11/19] VSON-10 has no place in the qfn/dfn lib --- .../size_definitions/dfn.yaml | 57 +------------------ .../size_definitions/vson.yaml | 53 +++++++++++++++++ 2 files changed, 54 insertions(+), 56 deletions(-) create mode 100644 scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/vson.yaml diff --git a/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/dfn.yaml b/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/dfn.yaml index f53826ece..1681cc578 100644 --- a/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/dfn.yaml +++ b/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/dfn.yaml @@ -333,7 +333,7 @@ DFN-8-1EP_4x4mm_P0.8mm_EP2.3x3.24mm: EP_size_x_overwrite: 2.30 EP_size_y_overwrite: 3.24 - + # EP_paste_coverage: 0.65 EP_num_paste_pads: [2, 2] # heel_reduction: 0.1 #for relatively large EP pads (increase clearance) @@ -560,59 +560,6 @@ WDFN-12-1EP_3x3mm_P0.45mm_EP1.7x2.5mm: #suffix: '_Pad{pad_x:.2f}x{pad_y:.2f}mm_HandSolder' #include_suffix_in_3dpath: 'False' -VSON-10_3x3mm_P0.5mm_EP1.2x2mm: - device_type: 'VSON' - #manufacturer: 'man' - #part_number: 'mpn' - size_source: 'http://rohmfs.rohm.com/en/products/databook/datasheet/ic/power/switching_regulator/bd8314nuv-e.pdf (Page 20)' - ipc_class: 'qfn' # 'qfn_pull_back' - #ipc_density: 'least' #overwrite global value for this device. - # custom_name_format: - body_size_x: - nominal: 3 - tolerance: 0.1 - body_size_y: - nominal: 3 - tolerance: 0.1 - lead_width_min: 0.21 - lead_width_max: 0.3 - lead_len_min: 0.3 - lead_len_max: 0.5 - - # heel_reduction: 0.05 #for relatively large EP pads (increase clearance) - - EP_size_x_min: 1.1 - EP_size_x: 1.2 - EP_size_x_max: 1.3 - EP_size_y_min: 1.9 - EP_size_y: 2.0 - EP_size_y_max: 2.2 - # EP_paste_coverage: 0.65 - EP_num_paste_pads: [2, 2] - #heel_reduction: 0.05 #for relatively large EP pads (increase clearance) - - thermal_vias: - count: [1, 2] - drill: 0.3 - # min_annular_ring: 0.15 - paste_via_clearance: 0.1 - EP_num_paste_pads: [2, 2] - #paste_between_vias: 1 - #paste_rings_outside: 1 - EP_paste_coverage: 0.75 - #grid: [1, 1] - # bottom_pad_size: - paste_avoid_via: False - - pitch: 0.5 - num_pins_x: 0 - num_pins_y: 5 - #chamfer_edge_pins: 0.25 - #pin_count_grid: - #pad_length_addition: 0.5 - #suffix: '_Pad{pad_x:.2f}x{pad_y:.2f}mm_HandSolder' - #include_suffix_in_3dpath: 'False' - Broadcom_DFN-6_2.0x2.0mm: device_type: 'DFN' size_source: 'https://docs.broadcom.com/docs/AV02-4755EN' @@ -696,5 +643,3 @@ TDFN-6-1EP_2.5x2.5mm_P0.65mm_EP1.3x2mm: suffix: '' #include_suffix_in_3dpath: 'False' #include_pad_size: 'fp_name_only' # 'both' | 'none' (default) - - diff --git a/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/vson.yaml b/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/vson.yaml new file mode 100644 index 000000000..85bd3315d --- /dev/null +++ b/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/vson.yaml @@ -0,0 +1,53 @@ +VSON-10_3x3mm_P0.5mm_EP1.2x2mm: + device_type: 'VSON' + library: Package_SON + #manufacturer: 'man' + #part_number: 'mpn' + size_source: 'http://rohmfs.rohm.com/en/products/databook/datasheet/ic/power/switching_regulator/bd8314nuv-e.pdf (Page 20)' + ipc_class: 'qfn' # 'qfn_pull_back' + #ipc_density: 'least' #overwrite global value for this device. + # custom_name_format: + body_size_x: + nominal: 3 + tolerance: 0.1 + body_size_y: + nominal: 3 + tolerance: 0.1 + lead_width_min: 0.21 + lead_width_max: 0.3 + lead_len_min: 0.3 + lead_len_max: 0.5 + + # heel_reduction: 0.05 #for relatively large EP pads (increase clearance) + + EP_size_x_min: 1.1 + EP_size_x: 1.2 + EP_size_x_max: 1.3 + EP_size_y_min: 1.9 + EP_size_y: 2.0 + EP_size_y_max: 2.2 + # EP_paste_coverage: 0.65 + EP_num_paste_pads: [2, 2] + #heel_reduction: 0.05 #for relatively large EP pads (increase clearance) + + thermal_vias: + count: [1, 2] + drill: 0.3 + # min_annular_ring: 0.15 + paste_via_clearance: 0.1 + EP_num_paste_pads: [2, 2] + #paste_between_vias: 1 + #paste_rings_outside: 1 + EP_paste_coverage: 0.75 + #grid: [1, 1] + # bottom_pad_size: + paste_avoid_via: False + + pitch: 0.5 + num_pins_x: 0 + num_pins_y: 5 + #chamfer_edge_pins: 0.25 + #pin_count_grid: + #pad_length_addition: 0.5 + #suffix: '_Pad{pad_x:.2f}x{pad_y:.2f}mm_HandSolder' + #include_suffix_in_3dpath: 'False' From 13969f52758664710b9e7c3b5ae27c3945c25ba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=20P=C3=B6schl?= Date: Thu, 10 Oct 2019 21:17:14 +0200 Subject: [PATCH 12/19] fix spelling mistakes --- KicadModTree/nodes/base/Pad.py | 8 ++++---- KicadModTree/nodes/specialized/ChamferedPad.py | 2 +- KicadModTree/nodes/specialized/ChamferedPadGrid.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/KicadModTree/nodes/base/Pad.py b/KicadModTree/nodes/base/Pad.py index 3b23187e6..dfbcd40e9 100644 --- a/KicadModTree/nodes/base/Pad.py +++ b/KicadModTree/nodes/base/Pad.py @@ -87,7 +87,7 @@ def getRoundRadius(self, shortest_sidelength): def roundingRequested(self): r"""Check if the pad has a rounded corner - :return: True are rounding corners is required + :return: True if rounded corners are required """ if self.kicad4_compatible: return False @@ -139,7 +139,7 @@ class Pad(Node): shape of the pad * *layers* (``Pad.LAYERS_SMT``, ``Pad.LAYERS_THT``, ``Pad.LAYERS_NPTH``) -- layers on which are used for the pad - + * *at* (``Vector2D``) -- center position of the pad * *rotation* (``float``) -- @@ -150,7 +150,7 @@ class Pad(Node): offset of the pad * *drill* (``float``, ``Vector2D``) -- drill-size of the pad - + * *radius_ratio* (``float``) -- The radius ratio of the rounded rectangle. Ignored for every shape except round rect. @@ -167,7 +167,7 @@ class Pad(Node): An instance of the RoundRadiusHandler class If this is given then all other round radius specifiers are ignored Ignored for every shape except round rect - + * *solder_paste_margin_ratio* (``float``) -- solder paste margin ratio of the pad (default: 0) * *solder_paste_margin* (``float``) -- diff --git a/KicadModTree/nodes/specialized/ChamferedPad.py b/KicadModTree/nodes/specialized/ChamferedPad.py index 55d13c369..153f32ed8 100644 --- a/KicadModTree/nodes/specialized/ChamferedPad.py +++ b/KicadModTree/nodes/specialized/ChamferedPad.py @@ -222,7 +222,7 @@ class ChamferedPad(Node): Set an exact round radius for a pad. * *round_radius_handler* (``RoundRadiusHandler``) -- An instance of the RoundRadiusHandler class - If this is given then all other round radius specifiers are Ignored + If this is given then all other round radius specifiers are ignored """ def __init__(self, **kwargs): diff --git a/KicadModTree/nodes/specialized/ChamferedPadGrid.py b/KicadModTree/nodes/specialized/ChamferedPadGrid.py index cbdf31d65..ddee3c557 100644 --- a/KicadModTree/nodes/specialized/ChamferedPadGrid.py +++ b/KicadModTree/nodes/specialized/ChamferedPadGrid.py @@ -190,7 +190,7 @@ class ChamferedPadGrid(Node): Set an exact round radius for a pad. * *round_radius_handler* (``RoundRadiusHandler``) -- An instance of the RoundRadiusHandler class - If this is given then all other round radius specifiers are Ignored + If this is given then all other round radius specifiers are ignored """ def __init__(self, **kwargs): From 53693b8735c427f581a064c55489eb14bf809440 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=20P=C3=B6schl?= Date: Sat, 9 Nov 2019 00:06:11 +0100 Subject: [PATCH 13/19] more options for ep rounding and default is now 0 --- KicadModTree/nodes/base/Pad.py | 24 ++++++------ .../ipc_gullwing_generator.py | 15 ++------ .../ipc_noLead_generator.py | 20 ++++------ scripts/Packages/utils/ep_handling_utils.py | 37 +++++++++++++++++++ 4 files changed, 60 insertions(+), 36 deletions(-) create mode 100644 scripts/Packages/utils/ep_handling_utils.py diff --git a/KicadModTree/nodes/base/Pad.py b/KicadModTree/nodes/base/Pad.py index dfbcd40e9..453012eb2 100644 --- a/KicadModTree/nodes/base/Pad.py +++ b/KicadModTree/nodes/base/Pad.py @@ -43,7 +43,7 @@ class RoundRadiusHandler(object): """ def __init__(self, **kwargs): self.radius_ratio = getOptionalNumberTypeParam( - kwargs, 'radius_ratio', default_value=0, + kwargs, 'radius_ratio', default_value=0.25, low_limit=0, high_limit=0.5) self.maximum_radius = getOptionalNumberTypeParam(kwargs, 'maximum_radius') @@ -60,17 +60,17 @@ def getRadiusRatio(self, shortest_sidelength): if self.kicad4_compatible: return 0 - if self.round_radius_exact: + if self.round_radius_exact is not None: if self.round_radius_exact > shortest_sidelength/2: raise ValueError( "requested round radius of {} is too large for pad size of {}" .format(self.round_radius_exact, pad_size) ) - if self.maximum_radius: + if self.maximum_radius is not None: return min(self.round_radius_exact, self.maximum_radius)/shortest_sidelength else: return self.round_radius_exact/shortest_sidelength - if self.maximum_radius: + if self.maximum_radius is not None: if self.radius_ratio*shortest_sidelength > self.maximum_radius: return self.maximum_radius/shortest_sidelength @@ -92,17 +92,17 @@ def roundingRequested(self): if self.kicad4_compatible: return False - if self.round_radius_exact is not None: - if self.round_radius_exact > 0: - return True - else: - return False + if self.maximum_radius == 0: + return False - if self.radius_ratio > 0: - return True - else: + if self.round_radius_exact == 0: return False + if self.radius_ratio == 0: + return False + + return True + def limitMaxRadius(self, limit): r"""Set a new maximum limit diff --git a/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/ipc_gullwing_generator.py b/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/ipc_gullwing_generator.py index 488861d12..52a3ba51b 100755 --- a/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/ipc_gullwing_generator.py +++ b/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/ipc_gullwing_generator.py @@ -16,6 +16,9 @@ from quad_dual_pad_border import add_dual_or_quad_pad_border from drawing_tools import nearestSilkPointOnOrthogonalLine +sys.path.append(os.path.join(sys.path[0], "..", "utils")) +from ep_handling_utils import getEpRoundRadiusParams + ipc_density = 'nominal' ipc_doc_file = '../ipc_definitions.yaml' @@ -259,19 +262,9 @@ def __createFootprintVariant(self, device_params, header, dimensions, with_therm pad_radius = add_dual_or_quad_pad_border(kicad_mod, configuration, pad_details, device_params) - pad_shape_details = {} - pad_shape_details['shape'] = Pad.SHAPE_ROUNDRECT - pad_shape_details['radius_ratio'] = configuration.get('round_rect_radius_ratio', 0) - if 'round_rect_max_radius' in configuration: - pad_shape_details['maximum_radius'] = configuration['round_rect_max_radius'] - - pad_shape_details['round_radius_exact'] = device_params.get( - 'EP_round_radius_overwrite', pad_radius) - pad_shape_details['paste_radius_ratio'] = self.configuration['paste_radius_ratio'] - pad_shape_details['paste_maximum_radius'] = self.configuration['paste_maximum_radius'] - EP_round_radius = 0 if dimensions['has_EP']: + pad_shape_details = getEpRoundRadiusParams(device_params, self.configuration, pad_radius) EP_mask_size = EP_mask_size if EP_mask_size['x'] > 0 else None if with_thermal_vias: diff --git a/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/ipc_noLead_generator.py b/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/ipc_noLead_generator.py index 5cdc9a681..6144a12e1 100755 --- a/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/ipc_noLead_generator.py +++ b/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/ipc_noLead_generator.py @@ -17,6 +17,9 @@ from ipc_pad_size_calculators import * from quad_dual_pad_border import add_dual_or_quad_pad_border +sys.path.append(os.path.join(sys.path[0], "..", "utils")) +from ep_handling_utils import getEpRoundRadiusParams + ipc_density = 'nominal' ipc_doc_file = '../ipc_definitions.yaml' category = 'NoLead' @@ -102,7 +105,7 @@ def calcPadDetails(self, device_dimensions, EP_size, ipc_data, ipc_round_base): pull_back=pull_back ) - min_ep_to_pad_clearance = configuration['min_ep_to_pad_clearance'] + min_ep_to_pad_clearance = self.configuration['min_ep_to_pad_clearance'] heel_reduction_max = 0 @@ -325,20 +328,11 @@ def __createFootprintVariant(self, device_params, device_dimensions, with_therma ).lstrip()) kicad_mod.setAttribute('smd') - pad_radius = add_dual_or_quad_pad_border(kicad_mod, configuration, pad_details, device_params) - - pad_shape_details = {} - pad_shape_details['shape'] = Pad.SHAPE_ROUNDRECT - pad_shape_details['radius_ratio'] = configuration.get('round_rect_radius_ratio', 0) - if 'round_rect_max_radius' in configuration: - pad_shape_details['maximum_radius'] = configuration['round_rect_max_radius'] - - pad_shape_details['round_radius_exact'] = device_params.get( - 'EP_round_radius_overwrite', pad_radius) - pad_shape_details['paste_radius_ratio'] = self.configuration['paste_radius_ratio'] - pad_shape_details['paste_maximum_radius'] = self.configuration['paste_maximum_radius'] + pad_radius = add_dual_or_quad_pad_border(kicad_mod, self.configuration, pad_details, device_params) if device_dimensions['has_EP']: + pad_shape_details = getEpRoundRadiusParams(device_params, self.configuration, pad_radius) + if with_thermal_vias: thermals = device_params['thermal_vias'] paste_coverage = thermals.get('EP_paste_coverage', diff --git a/scripts/Packages/utils/ep_handling_utils.py b/scripts/Packages/utils/ep_handling_utils.py new file mode 100644 index 000000000..db3ade413 --- /dev/null +++ b/scripts/Packages/utils/ep_handling_utils.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 + +from KicadModTree.nodes.base.Pad import Pad + +def getEpRoundRadiusParams(device_params, configuration, pad_radius): + pad_shape_details = {} + pad_shape_details['shape'] = Pad.SHAPE_ROUNDRECT + + pad_shape_details['paste_radius_ratio'] = configuration['paste_radius_ratio'] + pad_shape_details['paste_maximum_radius'] = configuration['paste_maximum_radius'] + + if 'EP_round_radius' in device_params: + if type(device_params['EP_round_radius']) in [float, int]: + pad_shape_details['round_radius_exact'] = device_params['EP_round_radius'] + elif device_params['EP_round_radius'] == "pad": + pad_shape_details['round_radius_exact'] = pad_radius + else: + raise TypeError( + "round radius must be a number or 'pad', is {}" + .format(type(device_params['EP_round_radius'])) + ) + elif 'EP_round_radius_ratio' in device_params: + pad_shape_details['radius_ratio'] = device_params['EP_round_radius_ratio'] + elif 'EP_round_radius_ratio' in configuration: + pad_shape_details['radius_ratio'] = configuration['EP_round_radius_ratio'] + else: + pad_shape_details['radius_ratio'] = 0 + + if 'radius_ratio' in pad_shape_details and pad_shape_details['radius_ratio'] > 0: + if 'EP_maximum_radius' in device_params: + pad_shape_details['maximum_radius'] = device_params['EP_maximum_radius'] + elif 'EP_maximum_radius' in configuration: + pad_shape_details['maximum_radius'] = configuration['EP_maximum_radius'] + else: + pad_shape_details['maximum_radius'] = 0.25 + + return pad_shape_details From a2f9bb55a4adda54dbd3152bdd65c27bf6e387a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=20P=C3=B6schl?= Date: Sat, 9 Nov 2019 11:55:57 +0100 Subject: [PATCH 14/19] update readme with new ep rounded corner handling --- .../Package_Gullwing__QFP_SOIC_SO/Readme.md | 12 + .../Packages/documentation/ep_handling.svg | 32 +- .../documentation/ep_handling_rounded.svg | 301 ++++++++++++++++++ .../Packages/documentation/thermal_vias.svg | 40 +-- 4 files changed, 349 insertions(+), 36 deletions(-) create mode 100644 scripts/Packages/documentation/ep_handling_rounded.svg diff --git a/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/Readme.md b/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/Readme.md index 716ffceb0..8aa10c959 100644 --- a/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/Readme.md +++ b/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/Readme.md @@ -77,6 +77,18 @@ _Note: Contributions intended for the official library shall not include the man - Paste is split into a regular grid with (`EP_num_paste_pads`) {[int (x), int (y)]} - The optional paste coverage multiplier determines how much of the exposed copper area is covered by paste. (`EP_paste_coverage`) {float (0..1), default=0.65} +### Rounding of exposed pad features +IPC excludes exposed pads from the requirement for rounding its corners. By default the exposed pad does therefore not use rounded corners. Some datasheets do however suggest the use of rounded corners either specified to a specific value or implicitly shown to be equal to the normal pads. + +![rounded exposed pad example](../documentation/ep_handling_rounded.svg) + +- Paste corner rounding is controlled by global config parameters `paste_radius_ratio` and +`paste_maximum_radius` {float} +- The round radius of the exposed pad can be directly set with `EP_round_radius` {float/"pad"} + - The string "pad" can be used to use the same radius for the exposed pad as for the normal pads. + - Alternatively the round radius ratio and max radius can be set using `EP_round_radius_ratio` and `EP_maximum_radius` {float} + - Both these can be set per footprint or in the global config file. + ### Thermal vias A package with exposed pad can generate a version with thermal vias. This will be generated in addition to the normal version. diff --git a/scripts/Packages/documentation/ep_handling.svg b/scripts/Packages/documentation/ep_handling.svg index 71069264f..438917554 100644 --- a/scripts/Packages/documentation/ep_handling.svg +++ b/scripts/Packages/documentation/ep_handling.svg @@ -181,8 +181,8 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="1.979899" - inkscape:cx="511.11796" - inkscape:cy="97.439044" + inkscape:cx="249.64405" + inkscape:cy="97.380678" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" @@ -221,9 +221,9 @@ + sodipodi:nodetypes="ccccc" /> + sodipodi:nodetypes="ccccc" /> @@ -260,7 +260,7 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path20433" - d="M 59.197211,245.28455 V 227.10061" + d="M 59.197211,243.53059 V 227.10061" style="color:#000000;overflow:visible;fill:none;fill-opacity:0.72030652;fill-rule:evenodd;stroke:#000000;stroke-width:0.10583334;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:5.090909;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -342,7 +342,7 @@ transform="rotate(-90,43.989695,259.54979)"> @@ -350,7 +350,7 @@ sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path5937" - d="M 59.197211,245.28455 V 227.10061" + d="M 59.197211,243.63082 V 227.10061" style="color:#000000;overflow:visible;fill:none;fill-opacity:0.72030652;fill-rule:evenodd;stroke:#000000;stroke-width:0.10583334;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:5.090909;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> @@ -481,9 +481,9 @@ inkscape:connector-curvature="0" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + global config parameterspaste_radius_ratio andpaste_maximum_radius + + + + + + + + EP_round_radius or(EP_round_radius_ratioand EP_maximum_radius) + + + diff --git a/scripts/Packages/documentation/thermal_vias.svg b/scripts/Packages/documentation/thermal_vias.svg index cbf5b01aa..a5c7d49c2 100644 --- a/scripts/Packages/documentation/thermal_vias.svg +++ b/scripts/Packages/documentation/thermal_vias.svg @@ -117,8 +117,8 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="1.4" - inkscape:cx="410.07997" - inkscape:cy="208.91428" + inkscape:cx="497.80342" + inkscape:cy="258.20489" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" @@ -151,9 +151,9 @@ + sodipodi:nodetypes="ccccc" /> + sodipodi:nodetypes="ccccc" /> @@ -796,9 +796,9 @@ style="fill:none;stroke:#800080;stroke-width:0.13229167;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none" id="path5818" /> @@ -828,9 +828,9 @@ id="g6417" transform="translate(-2.180303,-67.279762)"> + sodipodi:nodetypes="ccccc" /> @@ -1384,9 +1384,9 @@ transform="translate(72.502369,-66.077054)" id="g6934"> + sodipodi:nodetypes="ccc" /> Date: Sun, 5 Jan 2020 15:53:28 +0100 Subject: [PATCH 15/19] Remove definition for footprint that required a very specialized manufacturer specific package --- .../size_definitions/qfn/qfn-1x.yaml | 58 ------------------- 1 file changed, 58 deletions(-) diff --git a/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/qfn/qfn-1x.yaml b/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/qfn/qfn-1x.yaml index 7f054650c..04fc58aa5 100644 --- a/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/qfn/qfn-1x.yaml +++ b/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/qfn/qfn-1x.yaml @@ -59,64 +59,6 @@ QFN-12-1EP_3x3mm_P0.5mm_EP1.65x1.65mm: #suffix: '_Pad{pad_x:.2f}x{pad_y:.2f}mm_HandSolder' #include_suffix_in_3dpath: 'False' -QFN-14-1EP_0.64x0.64mm_P0.4mm_EP1.6x1.6mm: - device_type: 'QFN' - #manufacturer: 'man' - #part_number: 'mpn' - size_source: 'http://www.skyworksinc.com/uploads/documents/SKY13575_639LF_203270D.pdf' - ipc_class: 'qfn' # 'qfn_pull_back' - #ipc_density: 'least' #overwrite global value for this device. - # custom_name_format: - body_size_x: - nominal: 1.6 - body_size_y: - nominal: 1.6 - body_height: - minimum: 0.41 - nominal: 0.45 - maximum: 0.5 - - lead_width: - nominal: 0.18 - tolerance: 0.05 - lead_len: - nominal: 0.2 - tolerance: 0.05 - - EP_size_x: - minimum: 0.59 - nominal: 0.74 - maximum: 0.84 - EP_size_y: - minimum: 0.59 - nominal: 0.74 - maximum: 0.84 - # EP_paste_coverage: 0.65 - EP_num_paste_pads: [2, 2] - # heel_reduction: 0.05 #for relatively large EP pads (increase clearance) - - thermal_vias: - count: [2, 2] - drill: 0.2 - # min_annular_ring: 0.15 - paste_via_clearance: 0.1 - EP_num_paste_pads: [2, 2] - #paste_between_vias: 1 - #paste_rings_outside: 1 - EP_paste_coverage: 0.75 - #grid: [1, 1] - # bottom_pad_size: - paste_avoid_via: False - - pitch: 0.4 - num_pins_x: 3 - num_pins_y: 4 - #chamfer_edge_pins: 0.25 - #pin_count_grid: - #pad_length_addition: 0.5 - #suffix: '_Pad{pad_x:.2f}x{pad_y:.2f}mm_HandSolder' - #include_suffix_in_3dpath: 'False' - QFN-16-1EP_3x3mm_P0.5mm_EP1.45x1.45mm: device_type: 'QFN' #manufacturer: 'man' From 17ce20f8dc3a99a7609814ec4bbfaa211237dd37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=20P=C3=B6schl?= Date: Sun, 5 Jan 2020 16:31:04 +0100 Subject: [PATCH 16/19] fix documentation links --- .../size_definitions/qfn/qfn_texas.yaml | 26 +++++++++---------- .../size_definitions/qfn/vqfn.yaml | 4 +-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/qfn/qfn_texas.yaml b/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/qfn/qfn_texas.yaml index 8433ed722..c930a8219 100644 --- a/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/qfn/qfn_texas.yaml +++ b/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/qfn/qfn_texas.yaml @@ -109,7 +109,7 @@ Texas_S-PWQFN-N16_EP: device_type: 'QFN' #manufacturer: 'man' #part_number: 'mpn' - size_source: 'http://www.ti.com/lit/ds/symlink/drv8801.pdf#page=31' + size_source: 'http://www.ti.com/lit/ds/symlink/drv8801.pdf#page=31 MO-220 variation VGGC' ipc_class: 'qfn' # 'qfn_pull_back' #ipc_density: 'least' #overwrite global value for this device. custom_name_format: 'Texas_S-PWQFN-N{pincount}_EP{ep_size_x:g}x{ep_size_y:g}mm{vias:s}' @@ -381,7 +381,7 @@ Texas_S-PVQFN-N24_EP: device_type: 'QFN' #manufacturer: 'man' #part_number: 'mpn' - size_source: 'http://www.ti.com/lit/ds/symlink/msp430fr5720.pdf#page=111' + size_source: 'http://www.ti.com/lit/ds/symlink/msp430fr5720.pdf#page=108' ipc_class: 'qfn' # 'qfn_pull_back' #ipc_density: 'least' #overwrite global value for this device. custom_name_format: 'Texas_S-PVQFN-N{pincount}_EP{ep_size_x:g}x{ep_size_y:g}mm{vias:s}' @@ -492,7 +492,7 @@ Texas_S-PWQFN-N32_EP: device_type: 'QFN' #manufacturer: 'man' #part_number: 'mpn' - size_source: 'https://www.ti.com/lit/ds/symlink/bq25703a.pdf#page=91' + size_source: 'https://www.ti.com/lit/ds/symlink/bq25703a.pdf#page=90' ipc_class: 'qfn' # 'qfn_pull_back' #ipc_density: 'least' #overwrite global value for this device. custom_name_format: 'Texas_S-PWQFN-N{pincount}_EP{ep_size_x:g}x{ep_size_y:g}mm{vias:s}' @@ -548,7 +548,7 @@ Texas_S-PVQFN-N32_EP: device_type: 'QFN' #manufacturer: 'man' #part_number: 'mpn' - size_source: 'http://www.ti.com/lit/ds/symlink/msp430f1122.pdf#page=46' + size_source: 'http://www.ti.com/lit/ds/symlink/msp430f1122.pdf#page=54' ipc_class: 'qfn' # 'qfn_pull_back' #ipc_density: 'least' #overwrite global value for this device. custom_name_format: 'Texas_S-PVQFN-N{pincount}_EP{ep_size_x:g}x{ep_size_y:g}mm{vias:s}' @@ -603,7 +603,7 @@ Texas_S-PVQFN-N36_EP: device_type: 'QFN' #manufacturer: 'man' #part_number: 'mpn' - size_source: 'http://www.ti.com/lit/ds/slvsba5d/slvsba5d.pdf#page=31' + size_source: 'http://www.ti.com/lit/ds/slvsba5d/slvsba5d.pdf#page=35' ipc_class: 'qfn' # 'qfn_pull_back' #ipc_density: 'least' #overwrite global value for this device. custom_name_format: 'Texas_S-PVQFN-N{pincount}_EP{ep_size_x:g}x{ep_size_y:g}mm{vias:s}' @@ -659,7 +659,7 @@ Texas_S-PVQFN-N40_EP2.9x2.9mm: device_type: 'QFN' #manufacturer: 'man' #part_number: 'mpn' - size_source: 'http://www.ti.com/lit/ds/symlink/msp430fr5731.pdf#page=114' + size_source: 'http://www.ti.com/lit/ds/symlink/msp430fr5731.pdf#page=111 JEDEC MO-220 variation VJJD-2' ipc_class: 'qfn' # 'qfn_pull_back' #ipc_density: 'least' #overwrite global value for this device. custom_name_format: 'Texas_S-PVQFN-N{pincount}_EP{ep_size_x:g}x{ep_size_y:g}mm{vias:s}' @@ -715,7 +715,7 @@ Texas_S-PVQFN-N40_EP3.52x2.62mm: device_type: 'QFN' #manufacturer: 'man' #part_number: 'mpn' - size_source: 'http://www.ti.com/lit/ds/symlink/drv8308.pdf#page=56' + size_source: 'http://www.ti.com/lit/ds/symlink/drv8308.pdf#page=56 JEDEC MO-220 variation VJJD-2' ipc_class: 'qfn' # 'qfn_pull_back' #ipc_density: 'least' #overwrite global value for this device. custom_name_format: 'Texas_S-PVQFN-N{pincount}_EP{ep_size_x:g}x{ep_size_y:g}mm{vias:s}' @@ -771,7 +771,7 @@ Texas_S-PVQFN-N40_EP4.15x4.15mm: device_type: 'QFN' #manufacturer: 'man' #part_number: 'mpn' - size_source: 'http://www.ti.com/lit/ds/symlink/msp430g2755.pdf#page=70' + size_source: 'http://www.ti.com/lit/ds/symlink/msp430g2755.pdf#page=70 JEDEC MO-220 variation VJJD-2' ipc_class: 'qfn' # 'qfn_pull_back' #ipc_density: 'least' #overwrite global value for this device. custom_name_format: 'Texas_S-PVQFN-N{pincount}_EP{ep_size_x:g}x{ep_size_y:g}mm{vias:s}' @@ -827,7 +827,7 @@ Texas_S-PVQFN-N40_EP4.6x4.6mm: device_type: 'QFN' #manufacturer: 'man' #part_number: 'mpn' - size_source: 'http://www.ti.com/lit/ds/symlink/dac7750.pdf#page=55' + size_source: 'http://www.ti.com/lit/ds/symlink/dac7750.pdf#page=54' ipc_class: 'qfn' # 'qfn_pull_back' #ipc_density: 'least' #overwrite global value for this device. custom_name_format: 'Texas_S-PVQFN-N{pincount}_EP{ep_size_x:g}x{ep_size_y:g}mm{vias:s}' @@ -883,7 +883,7 @@ Texas_S-PVQFN-N48_EP: #RGZ0048A device_type: 'QFN' #manufacturer: 'man' #part_number: 'mpn' - size_source: 'http://www.ti.com/lit/ds/symlink/msp430f5232.pdf#page=112' + size_source: 'http://www.ti.com/lit/ds/symlink/msp430f5232.pdf#page=111' ipc_class: 'qfn' # 'qfn_pull_back' #ipc_density: 'least' #overwrite global value for this device. custom_name_format: 'Texas_S-PVQFN-N{pincount}_EP{ep_size_x:g}x{ep_size_y:g}mm{vias:s}' @@ -938,7 +938,7 @@ Texas_S-PVQFN-N64_EP: #RGC0064B device_type: 'QFN' #manufacturer: 'man' #part_number: 'mpn' - size_source: 'http://www.ti.com/lit/ds/symlink/msp430f5217.pdf#page=117' + size_source: 'http://www.ti.com/lit/ds/symlink/msp430f5217.pdf#page=120' ipc_class: 'qfn' # 'qfn_pull_back' #ipc_density: 'least' #overwrite global value for this device. custom_name_format: 'Texas_S-PVQFN-N{pincount}_EP{ep_size_x:g}x{ep_size_y:g}mm{vias:s}' @@ -994,7 +994,7 @@ Texas_VQFN_RGE0024H: device_type: 'QFN' manufacturer: 'Texas' #part_number: 'mpn' - size_source: 'http://www.ti.com/lit/ds/symlink/tlc5971.pdf#page=43' + size_source: 'http://www.ti.com/lit/ds/symlink/tlc5971.pdf#page=39' ipc_class: 'qfn' # 'qfn_pull_back' #ipc_density: 'least' #overwrite global value for this device. custom_name_format: 'Texas_RGE00{pincount}H_EP{ep_size_x:g}x{ep_size_y:g}mm{vias:s}' @@ -1048,7 +1048,7 @@ Texas_VQFN_RGE0024C: device_type: 'QFN' manufacturer: 'Texas' #part_number: 'mpn' - size_source: 'http://www.ti.com/lit/ds/symlink/pca9548a.pdf#page=34' + size_source: 'http://www.ti.com/lit/ds/symlink/pca9548a.pdf#page=37' ipc_class: 'qfn' # 'qfn_pull_back' #ipc_density: 'least' #overwrite global value for this device. custom_name_format: 'Texas_RGE00{pincount}C_EP{ep_size_x:g}x{ep_size_y:g}mm{vias:s}' diff --git a/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/qfn/vqfn.yaml b/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/qfn/vqfn.yaml index 8678bee45..9a5e1e8ab 100644 --- a/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/qfn/vqfn.yaml +++ b/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/qfn/vqfn.yaml @@ -57,7 +57,7 @@ VQFN-16-1EP_3x3mm_P0.5mm_EP1.45x1.45mm: device_type: 'VQFN' #manufacturer: 'man' #part_number: 'mpn' - size_source: 'http://www.ti.com/lit/ds/sbos354a/sbos354a.pdf' + size_source: 'http://www.ti.com/lit/ds/sbos354a/sbos354a.pdf, JEDEC MO-220 variant VEED-6' ipc_class: 'qfn' # 'qfn_pull_back' #ipc_density: 'least' #overwrite global value for this device. # custom_name_format: @@ -446,7 +446,7 @@ VQFN-28-1EP_4x5mm_P0.5mm_EP2.55x3.55mm: device_type: 'VQFN' #manufacturer: 'man' #part_number: 'mpn' - size_source: 'http://www.ti.com/lit/ds/symlink/lm5175.pdf#page=37' + size_source: 'http://www.ti.com/lit/ds/symlink/lm5175.pdf#page=40' ipc_class: 'qfn' # 'qfn_pull_back' #ipc_density: 'least' #overwrite global value for this device. # custom_name_format: From 7b76c118964cd1c88377d68d5ffa35cf8c4d771c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=20P=C3=B6schl?= Date: Mon, 13 Jan 2020 11:26:37 +0100 Subject: [PATCH 17/19] add page to documentation link --- .../size_definitions/qfn/qfn-64_9x9.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/qfn/qfn-64_9x9.yaml b/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/qfn/qfn-64_9x9.yaml index 6db43b375..dbf9557e5 100644 --- a/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/qfn/qfn-64_9x9.yaml +++ b/scripts/Packages/Package_NoLead__DFN_QFN_LGA_SON/size_definitions/qfn/qfn-64_9x9.yaml @@ -13,7 +13,7 @@ QFN-64-1EP_9x9mm_P0.5mm_EP3.8x3.8xmm: EP_size_x: 3.55 .. 3.80 .. 4.05 EP_size_y: 3.55 .. 3.80 .. 4.05 - + # EP_paste_coverage: 0.65 EP_num_paste_pads: [3, 3] #heel_reduction: 0.1 #for relatively large EP pads (increase clearance) @@ -146,7 +146,7 @@ QFN-64-1EP_9x9mm_P0.5mm_EP6x6mm: device_type: 'QFN' #manufacturer: 'man' #part_number: 'mpn' - size_source: 'http://www.ti.com/lit/ds/symlink/tusb8041.pdf' + size_source: 'http://www.ti.com/lit/ds/symlink/tusb8041.pdf#page=42' ipc_class: 'qfn' # 'qfn_pull_back' #ipc_density: 'least' #overwrite global value for this device. # custom_name_format: From 7ab2304924b436d4c70337a97ad1276929be66af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=20P=C3=B6schl?= Date: Tue, 14 Jan 2020 22:15:40 +0100 Subject: [PATCH 18/19] Backwardscompatibility: use default round radius 0 for chamfered and ep --- KicadModTree/nodes/base/Pad.py | 12 ++++++++--- .../nodes/specialized/ChamferedPad.py | 20 ++++++++++++------- .../nodes/specialized/ChamferedPadGrid.py | 9 +++++++++ KicadModTree/nodes/specialized/ExposedPad.py | 2 +- 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/KicadModTree/nodes/base/Pad.py b/KicadModTree/nodes/base/Pad.py index 453012eb2..1c3b61e1f 100644 --- a/KicadModTree/nodes/base/Pad.py +++ b/KicadModTree/nodes/base/Pad.py @@ -31,8 +31,8 @@ class RoundRadiusHandler(object): See below :Keyword Arguments: - * *radius_ratio* (``float``) -- - The radius ratio of the rounded rectangle. + * *radius_ratio* (``float [0 <= r <= 0.5]``) -- + The radius ratio of the rounded rectangle. (default set by default_radius_ratio) * *maximum_radius* (``float``) -- The maximum radius for the rounded rectangle. If the radius produced by the radius_ratio parameter for the pad would @@ -40,10 +40,16 @@ class RoundRadiusHandler(object): (This is useful for IPC-7351C compliance as it suggests 25% ratio with limit 0.25mm) * *round_radius_exact* (``float``) -- Set an exact round radius for a pad. + * *default_radius_ratio* (``float [0 <= r <= 0.5]``) -- + This parameter allows to set the default radius ratio + (backwards compatibility option for chamfered pads) """ def __init__(self, **kwargs): + default_radius_ratio = getOptionalNumberTypeParam( + kwargs, 'default_radius_ratio', default_value=0.25, + low_limit=0, high_limit=0.5) self.radius_ratio = getOptionalNumberTypeParam( - kwargs, 'radius_ratio', default_value=0.25, + kwargs, 'radius_ratio', default_value=default_radius_ratio, low_limit=0, high_limit=0.5) self.maximum_radius = getOptionalNumberTypeParam(kwargs, 'maximum_radius') diff --git a/KicadModTree/nodes/specialized/ChamferedPad.py b/KicadModTree/nodes/specialized/ChamferedPad.py index 153f32ed8..2460b0368 100644 --- a/KicadModTree/nodes/specialized/ChamferedPad.py +++ b/KicadModTree/nodes/specialized/ChamferedPad.py @@ -213,6 +213,7 @@ class ChamferedPad(Node): * *radius_ratio* (``float``) -- The radius ratio of the rounded rectangle. + (default 0 for backwards compatibility) * *maximum_radius* (``float``) -- The maximum radius for the rounded rectangle. If the radius produced by the radius_ratio parameter for the pad would @@ -232,11 +233,6 @@ def __init__(self, **kwargs): self._initMirror(**kwargs) self._initPadSettings(**kwargs) - if('round_radius_handler' in kwargs): - self.round_radius_handler = kwargs['round_radius_handler'] - else: - self.round_radius_handler = RoundRadiusHandler(**kwargs) - self.pad = self._generatePad() def _initSize(self, **kwargs): @@ -268,10 +264,17 @@ def _initPadSettings(self, **kwargs): self.chamfer_size = toVectorUseCopyIfNumber( kwargs.get('chamfer_size'), low_limit=0, must_be_larger=False) + if('round_radius_handler' in kwargs): + self.round_radius_handler = kwargs['round_radius_handler'] + else: + # default radius ration 0 for backwards compatibility + self.round_radius_handler = RoundRadiusHandler(default_radius_ratio=0, **kwargs) + self.padargs = copy(kwargs) self.padargs.pop('size', None) self.padargs.pop('shape', None) self.padargs.pop('at', None) + self.padargs.pop('round_radius_handler', None) def _generatePad(self): if self.chamfer_size[0] >= self.size[0] or self.chamfer_size[1] >= self.size[1]: @@ -293,7 +296,10 @@ def _generatePad(self): polygon_width = 0 if self.round_radius_handler.roundingRequested(): if self.chamfer_size[0] != self.chamfer_size[1]: - raise NotImplementedError('rounded chamfered pads are only supported for 45 degree chamfers') + raise NotImplementedError( + 'Rounded chamfered pads are only supported for 45 degree chamfers.' + ' Chamfer {}'.format(self.chamfer_size) + ) # We prefer the use of rounded rectangle over chamfered pads. r_chamfer = self.chamfer_size[0] + sqrt(2)*self.chamfer_size[0]/2 if radius >= r_chamfer: @@ -333,7 +339,7 @@ def _generatePad(self): else: return Pad( at=self.at, shape=Pad.SHAPE_ROUNDRECT, size=self.size, - **self.padargs + round_radius_handler=self.round_radius_handler, **self.padargs ) def chamferAvoidCircle(self, center, diameter, clearance=0): diff --git a/KicadModTree/nodes/specialized/ChamferedPadGrid.py b/KicadModTree/nodes/specialized/ChamferedPadGrid.py index ddee3c557..25ca2eafa 100644 --- a/KicadModTree/nodes/specialized/ChamferedPadGrid.py +++ b/KicadModTree/nodes/specialized/ChamferedPadGrid.py @@ -181,6 +181,7 @@ class ChamferedPadGrid(Node): * *radius_ratio* (``float``) -- The radius ratio of the rounded rectangle. + (default 0 for backwards compatibility) * *maximum_radius* (``float``) -- The maximum radius for the rounded rectangle. If the radius produced by the radius_ratio parameter for the pad would @@ -236,11 +237,18 @@ def _initPadSettings(self, **kwargs): self.chamfer_size = toVectorUseCopyIfNumber( kwargs.get('chamfer_size'), low_limit=0, must_be_larger=False) + if('round_radius_handler' in kwargs): + self.round_radius_handler = kwargs['round_radius_handler'] + else: + # default radius ration 0 for backwards compatibility + self.round_radius_handler = RoundRadiusHandler(default_radius_ratio=0, **kwargs) + self.padargs = copy(kwargs) self.padargs.pop('size', None) self.padargs.pop('number', None) self.padargs.pop('at', None) self.padargs.pop('chamfer_size', None) + self.padargs.pop('round_radius_handler', None) def chamferAvoidCircle(self, center, diameter, clearance=0): r""" set the chamfer such that the pad avoids a cricle located at near corner. @@ -338,6 +346,7 @@ def _generatePads(self): at=[x, y], number=self.number, size=self.size, chamfer_size=self.chamfer_size, corner_selection=corner, + round_radius_handler=self.round_radius_handler, **self.padargs )) return pads diff --git a/KicadModTree/nodes/specialized/ExposedPad.py b/KicadModTree/nodes/specialized/ExposedPad.py index a7f94b8c1..d58f5f538 100644 --- a/KicadModTree/nodes/specialized/ExposedPad.py +++ b/KicadModTree/nodes/specialized/ExposedPad.py @@ -137,7 +137,7 @@ def __init__(self, **kwargs): self.size_round_base = kwargs.get('size_round_base', 0.01) self.grid_round_base = kwargs.get('grid_round_base', 0.01) - self.round_radius_handler = RoundRadiusHandler(**kwargs) + self.round_radius_handler = RoundRadiusHandler(default_radius_ratio=0, **kwargs) self.kicad4_compatible = kwargs.get('kicad4_compatible', False) self.paste_round_radius_handler = RoundRadiusHandler( From 06d7d27ee351d2c54f544d60bd0964402f484951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=20P=C3=B6schl?= Date: Tue, 14 Jan 2020 22:20:47 +0100 Subject: [PATCH 19/19] coding style --- KicadModTree/util/paramUtil.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/KicadModTree/util/paramUtil.py b/KicadModTree/util/paramUtil.py index 164771538..a7e7e5745 100644 --- a/KicadModTree/util/paramUtil.py +++ b/KicadModTree/util/paramUtil.py @@ -165,7 +165,9 @@ def toVectorUseCopyIfNumber(value, length=2, low_limit=None, must_be_larger=True return result -def getOptionalNumberTypeParam(kwargs, param_name, default_value=None, + +def getOptionalNumberTypeParam( + kwargs, param_name, default_value=None, low_limit=None, high_limit=None, allow_equal_limit=True): r""" Get a named parameter from packed dict and guarantee it is a number (float or int)