diff --git a/KicadModTree/nodes/specialized/PadArray.py b/KicadModTree/nodes/specialized/PadArray.py index 3a0998956..378b3aa1d 100644 --- a/KicadModTree/nodes/specialized/PadArray.py +++ b/KicadModTree/nodes/specialized/PadArray.py @@ -78,8 +78,11 @@ class PadArray(Node): shape for marking pad 1 for through hole components. (deafult: ``Pad.SHAPE_ROUNDRECT``) * *tht_pad1_id* (``int, string``) -- pad number used for "pin 1" (default: 1) - * *exclude_pin_list* (``int, Vector1D``) -- - which pin number should be skipped" + * *hidden_pins* (``int, Vector1D``) -- + pin number(s) to be skipped; a footprint with hidden pins has missing pads and matching pin numbers + * *deleted_pins* (``int, Vector1D``) -- + pin locations(s) to be skipped; a footprint with deleted pins has pads missing but no missing pin numbers" + :Example: @@ -105,9 +108,15 @@ def _initPincount(self, **kwargs): if type(self.pincount) is not int or self.pincount <= 0: raise ValueError('{pc} is an invalid value for pincount'.format(pc=self.pincount)) + if kwargs.get('hidden_pins') and kwargs.get('deleted_pins'): + raise KeyError('hidden pins and deleted pins cannot be used together') + self.exclude_pin_list = [] - if kwargs.get('exclude_pin_list'): - self.exclude_pin_list = kwargs.get('exclude_pin_list') + if kwargs.get('hidden_pins'): + # exclude_pin_list is for pads being removed based on pad number + # deleted pins are filtered out later by pad location (not number) + self.exclude_pin_list = kwargs.get('hidden_pins') + if type(self.exclude_pin_list) not in [list, tuple]: raise TypeError('exclude pin list must be specified like "exclude_pin_list=[0,1]"') elif any([type(i) not in [int] for i in self.exclude_pin_list]): @@ -235,10 +244,17 @@ def _createPads(self, **kwargs): for i, number in enumerate(pad_numbers): includePad = True - if type(self.initialPin) == 'int': - includePad = (self.initialPin + i) not in self.exclude_pin_list - else: - includePad = number not in self.exclude_pin_list + + # deleted pins are filtered by pad/pin position (they are 'None' in pad_numbers list) + if type(number) not in [int, str]: + includePad = False + + # hidden pins are filtered out by pad number (index of pad_numbers list) + if not kwargs.get('deleted_pins'): + if type(self.initialPin) == 'int': + includePad = (self.initialPin + i) not in self.exclude_pin_list + else: + includePad = number not in self.exclude_pin_list if includePad: current_pad_pos = Vector2D( 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 52a3ba51b..af63085ba 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 @@ -140,10 +140,21 @@ def deviceDimensions(device_size_data): def generateFootprint(self, device_params, header): dimensions = Gullwing.deviceDimensions(device_params) - if dimensions['has_EP'] and 'thermal_vias' in device_params: - self.__createFootprintVariant(device_params, header, dimensions, True) + if 'deleted_pins' in device_params: + if type(device_params['deleted_pins']) is int: + device_params['deleted_pins'] = [device_params['deleted_pins']] - self.__createFootprintVariant(device_params, header, dimensions, False) + if 'hidden_pins' in device_params: + if type(device_params['hidden_pins']) is int: + device_params['hidden_pins'] = [device_params['hidden_pins']] + + if 'deleted_pins' in device_params and 'hidden_pins' in device_params: + print("A footprint may not have deleted pins and hidden pins.") + else: + if dimensions['has_EP'] and 'thermal_vias' in device_params: + self.__createFootprintVariant(device_params, header, dimensions, True) + + self.__createFootprintVariant(device_params, header, dimensions, False) def __createFootprintVariant(self, device_params, header, dimensions, with_thermal_vias): fab_line_width = self.configuration.get('fab_line_width', 0.1) @@ -154,7 +165,17 @@ def __createFootprintVariant(self, device_params, header, dimensions, with_therm size_x = dimensions['body_size_x'].nominal size_y = dimensions['body_size_y'].nominal - pincount = device_params['num_pins_x']*2 + device_params['num_pins_y']*2 + pincount_full = device_params['num_pins_x']*2 + device_params['num_pins_y']*2 + + if 'hidden_pins' in device_params: + pincount_text = '{}-{}'.format(pincount_full - len(device_params['hidden_pins']), pincount_full) + pincount = pincount_full - len(device_params['hidden_pins']) + elif 'deleted_pins' in device_params: + pincount_text = '{}-{}'.format(pincount_full, pincount_full - len(device_params['deleted_pins'])) + pincount = pincount_full - len(device_params['deleted_pins']) + else: + pincount_text = '{}'.format(pincount_full) + pincount = pincount_full ipc_reference = 'ipc_spec_gw_large_pitch' if device_params['pitch'] >= 0.625 else 'ipc_spec_gw_small_pitch' if device_params.get('force_small_pitch_ipc_definition', False): @@ -166,12 +187,12 @@ def __createFootprintVariant(self, device_params, header, dimensions, with_therm pitch = device_params['pitch'] - name_format = self.configuration['fp_name_format_string_no_trailing_zero'] + name_format = self.configuration['fp_name_format_string_no_trailing_zero_pincount_text'] EP_size = {'x':0, 'y':0} EP_mask_size = {'x':0, 'y':0} if dimensions['has_EP']: - name_format = self.configuration['fp_name_EP_format_string_no_trailing_zero'] + name_format = self.configuration['fp_name_EP_format_string_no_trailing_zero_pincount_text'] if 'EP_size_x_overwrite' in device_params: EP_size = { 'x':device_params['EP_size_x_overwrite'], @@ -183,7 +204,7 @@ def __createFootprintVariant(self, device_params, header, dimensions, with_therm 'y':dimensions['EP_size_y'].nominal } if 'EP_mask_x' in dimensions: - name_format = self.configuration['fp_name_EP_custom_mask_format_string_no_trailing_zero'] + name_format = self.configuration['fp_name_EP_custom_mask_format_string_no_trailing_zero_pincount_text'] EP_mask_size = {'x':dimensions['EP_mask_x'].nominal, 'y':dimensions['EP_mask_y'].nominal} EP_size = Vector2D(EP_size) @@ -201,7 +222,7 @@ def __createFootprintVariant(self, device_params, header, dimensions, with_therm man=device_params.get('manufacturer',''), mpn=device_params.get('part_number',''), pkg=header['device_type'], - pincount=pincount, + pincount=pincount_text, size_y=size_y, size_x=size_x, pitch=device_params['pitch'], @@ -218,7 +239,7 @@ def __createFootprintVariant(self, device_params, header, dimensions, with_therm man=device_params.get('manufacturer',''), mpn=device_params.get('part_number',''), pkg=header['device_type'], - pincount=pincount, + pincount=pincount_text, size_y=size_y, size_x=size_x, pitch=device_params['pitch'], @@ -240,7 +261,7 @@ def __createFootprintVariant(self, device_params, header, dimensions, with_therm kicad_mod = Footprint(fp_name) - # init kicad footprint + # init kicad footprint kicad_mod.setDescription( "{manufacturer} {mpn} {package}, {pincount} Pin ({datasheet}), generated with kicad-footprint-generator {scriptname}"\ .format( @@ -260,7 +281,10 @@ 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) + if 'custom_pad_layout' in device_params: + pad_radius = add_custom_pad_layout(kicad_mod, configuration, pad_details, device_params) + else: + pad_radius = add_dual_or_quad_pad_border(kicad_mod, configuration, pad_details, device_params) EP_round_radius = 0 if dimensions['has_EP']: 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 a71fa723c..e1737a864 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, 5] + hidden_pins: [2, 5] SOIC-4_4.55x2.6mm_P1.27mm: size_source: 'https://toshiba.semicon-storage.com/info/docget.jsp?did=12884&prodName=TLP291' diff --git a/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/size_definitions/test_hidden_deleted_pins.yaml b/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/size_definitions/test_hidden_deleted_pins.yaml new file mode 100644 index 000000000..baff90ef3 --- /dev/null +++ b/scripts/Packages/Package_Gullwing__QFP_SOIC_SO/size_definitions/test_hidden_deleted_pins.yaml @@ -0,0 +1,150 @@ +# hidden and deleted pins are defined and described by IPC 7351B naming convention +# http://ohm.bu.edu/~pbohn/__Engineering_Reference/pcb_layout/pcbmatrix/IPC-7x51%20&%20PCBM%20Land%20Pattern%20Naming%20Convention.pdf +# hidden pins remove a pin location and pin number (for example, going from pad 1 to pad 3 with pin 2 missing from the package) +# deleted pins remove a pin location but not pin number (for example, pin 2 is missing from the package but the footprint has pad 2) +# deleted pins do not support custom numbering schemes https://github.com/pointhi/kicad-footprint-generator/pull/371 + +FileHeader: + library_Suffix: 'TO_SOT_SMD' + device_type: 'SOT' + +# test add_quad_pad_border() in quad_dual_pad_border.py +LQFP-48_7x7mm_P0.5mm: + size_source: '~' + body_size_x: 7 + body_size_y: 7 + overall_size_x: 9 + overall_size_y: 9 + lead_width: 0.17 .. 0.27 + lead_len: 0.45 .. 0.75 + pitch: 0.5 + num_pins_x: 12 + num_pins_y: 12 + +# test add_quad_pad_border() in quad_dual_pad_border.py with hidden pins +LQFP-48_7x7mm_P0.5mm_hidden: + size_source: '~' + body_size_x: 7 + body_size_y: 7 + overall_size_x: 9 + overall_size_y: 9 + lead_width: 0.17 .. 0.27 + lead_len: 0.45 .. 0.75 + pitch: 0.5 + num_pins_x: 12 + num_pins_y: 12 + hidden_pins: [1,15,24,30,31,44] + +# test add_quad_pad_border() in quad_dual_pad_border.py with deleted pins +LQFP-48_7x7mm_P0.5mm_deleted: + size_source: '~' + body_size_x: 7 + body_size_y: 7 + overall_size_x: 9 + overall_size_y: 9 + lead_width: 0.17 .. 0.27 + lead_len: 0.45 .. 0.75 + pitch: 0.5 + num_pins_x: 12 + num_pins_y: 12 + deleted_pins: [1,15,24,30,31,44] + +# test add_dual_pad_border_y() in quad_dual_pad_border.py +SOT-23-6: + size_source: '~' + custom_name_format: 'SOT-23-6' + body_size_x: 1.6 + body_size_y: 2.9 + overall_size_x: 2.8 + lead_width: 0.3 .. 0.5 + lead_len: 0.3 .. 0.45 .. 0.6 + pitch: 0.95 + num_pins_x: 0 + num_pins_y: 3 + +# test add_dual_pad_border_y() in quad_dual_pad_border.py with hidden pins +# manually-made footprint merged at https://github.com/KiCad/kicad-footprints/pull/2298 +SOIC-14-16_3.9x9.9mm_P1.27mm: + size_source: '~' + body_size_x: 3.8 .. 4.0 + body_size_y: 9.8 .. 10 + overall_height: + maximum: 1.75 + + overall_size_x: 6 +/-0.2 + lead_len: 0.4 .. 1.27 + lead_width: 0.31 .. 0.51 + + pitch: 1.27 + num_pins_x: 0 + num_pins_y: 8 + hidden_pins: [2, 13] + +# test add_dual_pad_border_y() in quad_dual_pad_border.py with deleted pins +SOT-23-5: + size_source: '~' + custom_name_format: 'SOT-23-5' + body_size_x: 1.6 + body_size_y: 2.9 + overall_size_x: 2.8 + lead_width: 0.3 .. 0.5 + lead_len: 0.3 .. 0.45 .. 0.6 + pitch: 0.95 + num_pins_x: 0 + num_pins_y: 3 + deleted_pins: 5 + +# test add_dual_pad_border_x() in quad_dual_pad_border.py +SOT-23-6R: + size_source: '~' + custom_name_format: 'SOT-23-6R' + body_size_x: 1.6 + body_size_y: 2.9 + overall_size_x: 2.8 + lead_width: 0.3 .. 0.5 + lead_len: 0.3 .. 0.45 .. 0.6 + pitch: 0.95 + num_pins_x: 3 + num_pins_y: 0 + +# test add_dual_pad_border_x() in quad_dual_pad_border.py with hidden pins +SOT-23-6R_hidden: + size_source: '~' + custom_name_format: 'SOT-23-6R_Hidden' + body_size_x: 1.6 + body_size_y: 2.9 + overall_size_x: 2.8 + lead_width: 0.3 .. 0.5 + lead_len: 0.3 .. 0.45 .. 0.6 + pitch: 0.95 + num_pins_x: 3 + num_pins_y: 0 + hidden_pins: [5] + +# test add_dual_pad_border_x() in quad_dual_pad_border.py with deleted pins +SOT-23-6R_deleted: + size_source: '~' + custom_name_format: 'SOT-23-6R_Deleted' + body_size_x: 1.6 + body_size_y: 2.9 + overall_size_x: 2.8 + lead_width: 0.3 .. 0.5 + lead_len: 0.3 .. 0.45 .. 0.6 + pitch: 0.95 + num_pins_x: 3 + num_pins_y: 0 + deleted_pins: [5] + +# test footprint with removed pins on both sides +SOT-23: + size_source: '~' + custom_name_format: 'SOT-23' + body_size_x: 1.6 + body_size_y: 2.9 + overall_size_x: 2.8 + lead_width: 0.3 .. 0.5 + lead_len: 0.3 .. 0.45 .. 0.6 + pitch: 0.95 + num_pins_x: 0 + num_pins_y: 3 + deleted_pins: [2, 4, 6] diff --git a/scripts/Packages/package_config_KLCv3.yaml b/scripts/Packages/package_config_KLCv3.yaml index 8c2abc1b0..aea82f2a3 100644 --- a/scripts/Packages/package_config_KLCv3.yaml +++ b/scripts/Packages/package_config_KLCv3.yaml @@ -1,9 +1,12 @@ fp_name_format_string: '{man:s}_{mpn:s}_{pkg:s}-{pincount:d}_{size_x:.1f}x{size_y:.1f}mm_P{pitch:.2f}mm{suffix:s}' +fp_name_format_string_pincount_text: '{man:s}_{mpn:s}_{pkg:s}-{pincount:s}_{size_x:.1f}x{size_y:.1f}mm_P{pitch:.2f}mm{suffix:s}' + keyword_fp_string: '{man:s} {package:s} {category:s}' lib_name_format_string: 'Package_{category:s}' fp_name_format_string_no_trailing_zero: '{man:s}_{mpn:s}_{pkg:s}-{pincount:d}_{size_x:g}x{size_y:g}mm_P{pitch:g}mm{suffix:s}{suffix2:s}' +fp_name_format_string_no_trailing_zero_pincount_text: '{man:s}_{mpn:s}_{pkg:s}-{pincount:s}_{size_x:g}x{size_y:g}mm_P{pitch:g}mm{suffix:s}{suffix2:s}' fp_name_lga_format_string_no_trailing_zero: '{man:s}_{mpn:s}_{pkg:s}-{pincount:d}_{size_x:g}x{size_y:g}mm_P{pitch:g}mm{layout:s}{suffix:s}{suffix2:s}' @@ -11,6 +14,9 @@ lga_layout_grid: '_LayoutGrid{nx:d}x{ny:d}' lga_layout_border: '_LayoutBorder{nx:d}x{ny:d}y' fp_name_EP_format_string_no_trailing_zero: '{man:s}_{mpn:s}_{pkg:s}-{pincount:d}-1EP_{size_x:g}x{size_y:g}mm_P{pitch:g}mm{suffix:s}_EP{ep_size_x:g}x{ep_size_y:g}mm{suffix2:s}{vias:s}' +fp_name_EP_format_string_no_trailing_zero_pincount_text: '{man:s}_{mpn:s}_{pkg:s}-{pincount:s}-1EP_{size_x:g}x{size_y:g}mm_P{pitch:g}mm{suffix:s}_EP{ep_size_x:g}x{ep_size_y:g}mm{suffix2:s}{vias:s}' + fp_name_EP_custom_mask_format_string_no_trailing_zero: '{man:s}_{mpn:s}_{pkg:s}-{pincount:d}-1EP_{size_x:g}x{size_y:g}mm_P{pitch:g}mm{suffix:s}_EP{ep_size_x:g}x{ep_size_y:g}mm_Mask{mask_size_x:g}x{mask_size_y:g}mm{suffix2:s}{vias:s}' +fp_name_EP_custom_mask_format_string_no_trailing_zero_pincount_text: '{man:s}_{mpn:s}_{pkg:s}-{pincount:s}-1EP_{size_x:g}x{size_y:g}mm_P{pitch:g}mm{suffix:s}_EP{ep_size_x:g}x{ep_size_y:g}mm_Mask{mask_size_x:g}x{mask_size_y:g}mm{suffix2:s}{vias:s}' thermal_via_suffix: "_ThermalVias" diff --git a/scripts/tools/pad_number_generators.py b/scripts/tools/pad_number_generators.py index 942a2d7b7..f8a8906bc 100644 --- a/scripts/tools/pad_number_generators.py +++ b/scripts/tools/pad_number_generators.py @@ -6,7 +6,8 @@ Available generators: ---------------------- -- increment: Is a simple generator that increments the pin number by one. +- increment: Generates pin numbers increasing by one and supports skipping pin numbers + using the deleted_pins kwarg (skipped pins return a bogus value of -1) - cw_dual: Generates pin numbers counting clockwise from the starting position - ccw_dual: Generates pin numbers counting counter-clockwise from the starting position @@ -23,9 +24,22 @@ def increment(pincount, init=1, **kwargs): i = init + j = init # pad number for deleted pins + + if kwargs.get("deleted_pins"): + skip_pins = kwargs["deleted_pins"] + while i <= pincount: - yield i - i += 1 + if kwargs.get("deleted_pins"): + if i in skip_pins: + yield None + else: + yield j + j += 1 + i += 1 # i acts like pin location for deleted pins + else: + yield i + i += 1 def _get_pin_cw(pincount, loc): @@ -146,7 +160,7 @@ def get_generator(device_params): pad_nums = device_params.get("pad_numbers") if not pad_nums: - return generators["increment"](pincount) + return generators["increment"](pincount, **device_params) init = pad_nums.get("init", 1) diff --git a/scripts/tools/quad_dual_pad_border.py b/scripts/tools/quad_dual_pad_border.py index c93b53c3d..65e267e14 100644 --- a/scripts/tools/quad_dual_pad_border.py +++ b/scripts/tools/quad_dual_pad_border.py @@ -12,8 +12,10 @@ def add_dual_or_quad_pad_border(kicad_mod, configuration, pad_details, device_pa if 'round_rect_max_radius' in configuration: pad_shape_details['maximum_radius'] = configuration['round_rect_max_radius'] - if 'exclude_pin_list' in device_params: - pad_shape_details['exclude_pin_list'] = device_params['exclude_pin_list'] + if 'hidden_pins' in device_params: + pad_shape_details['hidden_pins'] = device_params['hidden_pins'] + if 'deleted_pins' in device_params: + pad_shape_details['deleted_pins'] = device_params['deleted_pins'] if device_params['num_pins_x'] == 0: radius = add_dual_pad_border_y(kicad_mod, pad_details, device_params, pad_shape_details)