Skip to content
This repository has been archived by the owner on Oct 7, 2020. It is now read-only.

Commit

Permalink
Support hidden and deleted pins (#604)
Browse files Browse the repository at this point in the history
It turns out this was actually already supported, but with a different name and we have agreed on new terminology (#586 (comment))
  • Loading branch information
evanshultz authored Sep 10, 2020
1 parent 4a50ccb commit 738ff16
Show file tree
Hide file tree
Showing 7 changed files with 238 additions and 26 deletions.
32 changes: 24 additions & 8 deletions KicadModTree/nodes/specialized/PadArray.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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]):
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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):
Expand All @@ -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'],
Expand All @@ -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)

Expand All @@ -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'],
Expand All @@ -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'],
Expand All @@ -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(
Expand All @@ -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']:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
Original file line number Diff line number Diff line change
@@ -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]
6 changes: 6 additions & 0 deletions scripts/Packages/package_config_KLCv3.yaml
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
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}'

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"
22 changes: 18 additions & 4 deletions scripts/tools/pad_number_generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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):
Expand Down Expand Up @@ -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)

Expand Down
Loading

0 comments on commit 738ff16

Please sign in to comment.