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

Samtec Q Strip/Q Pairs, Basic Blade & Beam, Razor Beam, and mPOWER connector generators #397

Open
wants to merge 67 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
7034840
Add YAML file template for a future QStrip_Vertical.py script
Jul 11, 2019
ea323ad
Create QStrip_Vertical.py to generate Samtec Q Strip footprints
Jul 18, 2019
e7fe7ec
Minor modification to make KicadFileHandler._serialize_LinePoints()
Jul 18, 2019
bf2d404
Finish F.Fab layer drawing code in QStrip_Vertical.py
Jul 18, 2019
40bd953
Add fab and silkscreen drawing code to QStrip_Vertical.py;
Jul 19, 2019
fb31e29
Add oval hole capability to QStrip_Vertical.py
Jul 19, 2019
bf250a0
Finish adding QSH and QTH specifications to QStrip_Vertical.yaml
Jul 19, 2019
e920965
Add 3D model path code to QStrip_Vertical.py
Jul 19, 2019
be34746
Fix a minor issue with differential banks in QStrip_Vertical.py;
Jul 20, 2019
5e56924
Remove 4- and 5-bank QTS specifications from QStrip_Vertical.yaml,
Jul 20, 2019
092c80d
Add QSS specifications to QStrip_Vertical.yaml
Jul 20, 2019
fcb88f5
Add QTE specifications to QStrip_Vertical.yaml
Jul 20, 2019
aa845e6
Add QSE specifications to QStrip_Vertical.yaml; fix a differential
Jul 20, 2019
1a378fe
Minor edits/clean-up
Jul 21, 2019
09d3077
Make height option generic for Q Strip footprints (-01 changed to -xx)
Jul 21, 2019
bd45310
Clean up in QStrip_Vertical.py: simplify ground pad code and replace
Jul 21, 2019
dd87a80
Minor clean-up
Jul 21, 2019
4f51e18
Rename QStrip_Vertical* to QStrip_QPairs_Vertical* and update metadata
Jul 21, 2019
c8d90f1
QStrip_QPairs_Vertical.py: rewrite ground pad code to make it generic
Jul 22, 2019
95d5d9a
Clean up QStrip_QPairs_Vertical.py
Jul 22, 2019
35b9416
Add heading comments to QStrip_QPairs_Vertical*
Jul 22, 2019
82eba24
Move markerArrow to helpers.py
Jul 22, 2019
1f6fe3b
Add copyright notice to QStrip_QPairs_Vertical.py
Jul 23, 2019
91fa1c6
Fix a typo
Jul 23, 2019
dff42b1
Move license from docstring to comment block
Jul 23, 2019
2ca0199
Rename parameters in QStrip_QPairs_Vertical.*
Jul 29, 2019
f0e43f6
Merge remote-tracking branch 'upstream/master' into CalebReister
May 13, 2020
a3bc5e6
Create initial version of RazorBeam_Vertical.py and YAML file
Jul 5, 2020
590fd60
Add LSEM series to RazorBeam_Vertical
Jul 5, 2020
fb0beb8
Adjust RazorBeam pin 1 indicator
Jul 5, 2020
4ac386a
Fix LSEM part names
Jul 5, 2020
c4bcac3
QStrip_QPairs_Vertical: rename plane pads to "P{bank}"
Jul 6, 2020
c81552f
Change QStrip_QPairs_Vertical.yaml property names
Jul 6, 2020
5f4e7bb
Minor update to YAML schema
Jul 7, 2020
494053c
Create initial version of QStrip_QPairs_Horizontal script
Jul 12, 2020
b442e13
Rewrite QStrip_QPairs_Vertical code to draw sockets from right to
Jul 29, 2020
5893c21
Fix pin numbering error in QStrip_QPairs_Vertical script
Jul 29, 2020
4b63a72
+ Finish terminal mode in QStrip_QPairs_Horizontal
Jul 30, 2020
5d5625d
+ Clean up/restructure QStrip_QPairs scripts in preparation for
Aug 4, 2020
5cea3cd
QStrip_Vertical:
Aug 8, 2020
3fb44b2
BasicBladeAndBeam_Vertical:
Aug 8, 2020
11c481a
BasicBladeAndBeam_Vertical:
Aug 9, 2020
718b304
BasicBladeAndBeam_Vertical:
Aug 9, 2020
c03d287
Add dict_tools.py
Aug 22, 2020
d07cc96
QStrip_Vertical.yaml:
Aug 22, 2020
7fef5e8
QStrip_Vertical.py:
Aug 23, 2020
dd48cda
Merge branch 'master' into CalebReister
Aug 23, 2020
8c82a0f
Add QTS family to QStrip_Vertical.json
Aug 23, 2020
bfcd2dc
+ Finish populating QStrip_Vertical.json
Aug 24, 2020
bcab8b9
Create BasicBladeAndBeam_Vertical.json
Aug 24, 2020
58b8422
Add BSH series to BasicBladeAndBeam_Vertical.json
Aug 26, 2020
99b9854
+ Add BTS/BSS and BTE/BSE families to BasicBladeAndBeam_Vertical.json
Aug 29, 2020
a7e5475
dict_tools.py: rework dictMerge() so it copies the first argument
Sep 5, 2020
0c594da
Merge branch 'CalebReister-dict_tools' into CalebReister
Sep 5, 2020
fd6bbcd
Minor corrections in QStrip_Vertical.json
Sep 8, 2020
8b08975
Add support for "+" prefix in dict_merge.py, edits to Samtec scripts
Sep 8, 2020
1943b4d
Merge branch 'CalebReister' of github.com:calebreister/kicad-footprin…
Sep 19, 2020
26325c3
Add better silkscreen drawing around shield pins in RazorBeam_Vertica…
Sep 19, 2020
430ffb9
Add y-offset for LSS connector fab/silkscreen
Sep 20, 2020
19bf684
Correct descriptions in QStrip_Vertical and RazorBeam_Vertical
Sep 20, 2020
19a4add
Create Samtec mPOWER_Vertical connector script
Sep 21, 2020
cd7e3f5
Add pin names to welding tab holes in mPOWER_Vertical.json
Sep 21, 2020
5d7dd1d
Minor edits to QStrip_Vertical.py
Sep 23, 2020
d03cf16
Rename Samtec connector footprints based on KLC guidelines
Sep 28, 2020
2b7bd67
Remove outdated QStrip_Horizontal script and YAML file
Oct 2, 2020
2806964
Expand Wago terminal connectors according to the actual product range
MartinStej Oct 10, 2020
6223fa5
Merge branch 'master' into CalebReister
Oct 13, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions KicadModTree/KicadFileHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,8 @@ def _serialize_Circle(self, node):
return sexpr

def _serialize_LinePoints(self, node):
start_pos = node.getRealPosition(node.start_pos)
end_pos = node.getRealPosition(node.end_pos)
start_pos = node.getRealPosition(node.start_pos, 0)[0]
end_pos = node.getRealPosition(node.end_pos, 0)[0]
return [
['start', start_pos.x, start_pos.y],
['end', end_pos.x, end_pos.y]
Expand Down
401 changes: 401 additions & 0 deletions scripts/Connector/Connector_Samtec/BasicBladeAndBeam_Vertical.json

Large diffs are not rendered by default.

491 changes: 491 additions & 0 deletions scripts/Connector/Connector_Samtec/QStrip_Vertical.json

Large diffs are not rendered by default.

374 changes: 374 additions & 0 deletions scripts/Connector/Connector_Samtec/QStrip_Vertical.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,374 @@
#!/usr/bin/python

# This file is part of kicad-footprint-generator.
#
# kicad-footprint-generator is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# kicad-footprint-generator is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details. You should have received a copy of the GNU General Public
# License along with kicad-footprint-generator. If not, see
# <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2020 by Caleb Reister <[email protected]>
#

import sys
import os
import argparse
from copy import deepcopy
import math
import yaml

# Load parent path of KicadModTree
sys.path.append(os.path.join(sys.path[0], "..", "..", ".."))
sys.path.append(os.path.join(sys.path[0], "..", "..", "tools"))

from KicadModTree import *
from footprint_text_fields import addTextFields
from helpers import *
from dict_tools import *

def generate_one_footprint(param, config, default_lib):
fp = Footprint(param['name'])

# Terminal or Socket mode
mode = param['layout']['type'].capitalize()
if mode == "Terminal":
x_inv = 1
elif mode == "Socket":
x_inv = -1
else:
raise ValueError("Connector type must be either 'Terminal' or 'Socket'")

# Bank parameters
banks = param['banks']['n']
bank_x = param['banks']['space']
bank_w = param['banks']['width']
bank_h = param['banks']['height']

############################################################################
# Copper layer(s)

# Signal pad parameters
pitch = param['pads']['signal']['pitch']
pad_w = param['pads']['signal']['width']
pad_h = param['pads']['signal']['height']
pad_y = param['pads']['signal']['y']
pad_n = param['pads']['signal']['n']

# Pin 1 position
pin1 = Vector2D(0,0)
pin1.x = -(pad_n / 4)*pitch + pitch/2 - ((banks-1) / 2)*bank_x
pin1.y = -pad_y

# Bank 1 center point
bank1_mid = x_inv * (pin1.x - pitch/2 + (pad_n / 4)*pitch)

# Place pads
n = 1 # Pin counter
pin = [] # Pin position list, organized by bank
for b in range(banks):
pin.append([])
# Place signal pads
for slot in range(pad_n):
# Compute next pad location
pos = {'x': x_inv * (pin1.x + (slot // 2)*pitch + b*bank_x),
'y': pin1.y - (slot % 2)*(2*pin1.y),
'n': n+1, 'slot': slot}

# Skip slots for differential banks
if b < param['banks'].get('diff', 0):
if ((slot+1) % 6 == 0 or # Skip every 3rd odd slot
(slot+2) % 6 == 0 or # Skip every 3rd even slot
# Only add end-of-bank pins if they are completing a pair
(slot+2 >= pad_n and
pin[b][-2]['slot'] != slot-2)):
continue

# Create pad
pin[b].append(pos) # Add position to list
pad = Pad(number = str(n),
at = pos,
size = (pad_w, pad_h),
type = Pad.TYPE_SMT,
layers = Pad.LAYERS_SMT,
shape = Pad.SHAPE_RECT)
fp.append(pad)
n += 1

# Place plane pads
mid = bank1_mid + x_inv*b*bank_x # Bank midpoint
for plane in param['pads'].get('planes', {}):
pad = Pad(number = "P" + str(b+1),
at = (plane['x'] + mid, plane['y']),
size = (plane['width'], plane['height']),
type = Pad.TYPE_SMT,
layers = Pad.LAYERS_SMT,
shape = Pad.SHAPE_RECT)
fp.append(pad)

############################################################################
# Holes
if 'holes' in param:
for hole in param['holes']:
drill = hole['drill']
shape = Pad.SHAPE_CIRCLE if type(drill) is float else Pad.SHAPE_OVAL
h = [Pad(number = "MP" if 'pad' in hole else "",
at = (m*hole['space']/2, hole['y']),
drill = drill,
size = hole['pad'] if 'pad' in hole else drill,
type = Pad.TYPE_THT if 'pad' in hole else Pad.TYPE_NPTH,
layers = Pad.LAYERS_THT if 'pad' in hole else Pad.LAYERS_NPTH,
shape = shape) for m in (-1,1)]
fp.extend(h)

############################################################################
# Fabrication layer: F.Fab
fab_line = config['fab_line_width']
fab_mark = config['fab_pin1_marker_length']
fab_w = param['layout']['width'] if 'width' not in param else param['width']
fab_h = param['layout']['height']
fab_y = fab_h / 2
fab_edge = fab_w/2
chamfer = fab_h / 4 # 1/4 connector height, cosmetic only

if mode == 'Terminal':
# End outline
fab_end = [(-fab_edge, -fab_y),
(-fab_edge, fab_y-chamfer),
(-fab_edge+chamfer, fab_y)]
fp.append(PolygoneLine(nodes = fab_end,
layer = "F.Fab",
width = fab_line))
# Right end outline (mirrors left end)
fp.append(PolygoneLine(nodes = fab_end,
layer = "F.Fab",
width = fab_line,
x_mirror = 0))
# Top and bottom lines
fp.append(Line(start = (-fab_edge, -fab_y),
end = ( fab_edge, -fab_y),
layer = "F.Fab",
width = fab_line))
fp.append(Line(start = (-fab_edge+chamfer, fab_y),
end = ( fab_edge-chamfer, fab_y),
layer = "F.Fab",
width = fab_line))
# Pin 1 marker
fp.append(markerArrow(x = pin1.x,
y = (fab_mark-fab_h) / 2,
width = fab_mark,
angle = 180,
layer = "F.Fab",
close = False,
line_width = fab_line))
elif mode == 'Socket':
# Outline rectangle
fp.append(RectLine(start = (-fab_edge, -fab_y),
end = ( fab_edge, fab_y),
layer = "F.Fab",
width = fab_line))
# Chamfer lines
fp.append(Line(start = (-fab_edge, fab_y-chamfer),
end = (-fab_edge+chamfer, fab_y),
layer = "F.Fab",
width = fab_line))
fp.append(Line(start = (fab_edge, fab_y-chamfer),
end = (fab_edge-chamfer, fab_y),
layer = "F.Fab",
width = fab_line))
# Pin 1 marker
fp.append(markerArrow(x = -pin1.x,
y = -(fab_h-fab_mark) / 2,
width = fab_mark,
angle = 180,
layer = "F.Fab",
close = False,
line_width = fab_line))

# Draw bank and ground plane outlines
for b in range(banks):
mid = bank1_mid + x_inv*b*bank_x
# Bank outline
fp.append(RectLine(start = (mid-bank_w/2, -bank_h/2),
end = (mid+bank_w/2, bank_h/2),
layer = "F.Fab",
width = fab_line))

############################################################################
# Silkscreen: F.SilkS
silk_offset = config['silk_fab_offset']
silk_pad = {'x': config['silk_pad_clearance'] + pad_w/2,
'y': config['silk_pad_clearance'] + pad_h/2}
silk_line = config['silk_line_width']
silk_y = fab_y + silk_offset
silk_edge = fab_edge + silk_offset
silk_chamfer = chamfer + silk_offset/2

if mode == 'Terminal':
# Polygon left end outline points
silk_end = [[(pin[i][i]['x']+m*silk_pad['x'], -silk_y),
(m*silk_edge, -silk_y),
(m*silk_edge, silk_y-silk_chamfer),
(m*(silk_edge-silk_chamfer), silk_y),
(pin[i][i]['x']+m*silk_pad['x'], silk_y)]
for (i,m) in ((0,-1), (-1,1))]
# Pin 1 indicator
fp.append(Line(start = (pin[0][0]['x']-silk_pad['x'], pin1.y - pad_h/2),
end = (pin[0][0]['x']-silk_pad['x'], -silk_y),
layer = "F.SilkS",
width = silk_line))
elif mode == 'Socket':
# Left end outline points
silk_end = [[(pin[i][i]['x']+m*silk_pad['x'], silk_y),
(m*silk_edge, silk_y),
(m*silk_edge, -silk_y),
(pin[i][i]['x']+m*silk_pad['x'], -silk_y)]
for (i,m) in ((0,1), (-1,-1))]
# Pin 1 indicator
fp.append(markerArrow(x = pin[0][0]['x'],
y = pin[0][0]['y'] - silk_pad['y'],
width = fab_mark / 2,
line_width = silk_line,
angle = 180,
layer = "F.SilkS"))

# Draw end outlines
fp.extend([PolygoneLine(nodes = end,
layer = "F.SilkS",
width = silk_line) for end in silk_end])

# Draw outlines between banks
for b in range(banks-1):
fp.extend([Line(start = (pin[b][-1]['x'] + x_inv*silk_pad['x'], m*silk_y),
end = (pin[b+1][0]['x'] - x_inv*silk_pad['x'], m*silk_y),
layer = "F.SilkS",
width = silk_line) for m in (-1,1)])

############################################################################
# Courtyard: F.CrtYd
court_line = config['courtyard_line_width']
court_grid = config['courtyard_grid']
court_offset = config['courtyard_offset']['connector']

court_x = roundToBase(fab_w/2 + court_offset, court_grid)
court_y = roundToBase(max(fab_y, pad_y + pad_h/2) + court_offset, court_grid)

fp.append(RectLine(start = (-court_x, -court_y),
end = ( court_x, court_y),
layer = "F.CrtYd",
width = court_line))

############################################################################
# Set Metadata

# Draw reference and value
text_y = court_y + 1.0
fp.append(Text(type = 'reference', text = 'REF**',
at = (0, -text_y),
layer = "F.SilkS"))
fp.append(Text(type = 'user', text = '%R',
at = (0, -text_y),
layer = "F.Fab"))
fp.append(Text(type = 'value', text=param['name'],
at = (0, text_y),
layer="F.Fab"))

# Set surface-mount attribute
fp.setAttribute('smd')

# Part number
partnum = param['meta'].get('pn', param['name'].split('_')[1])

# Pins or pairs/bank
if param['banks'].get('diff', 0) == banks:
# Differential mode: round up to nearest even number of pairs
pins_or_pairs = (pad_n // 3) + (pad_n // 3) % 2
else:
pins_or_pairs = pad_n

# Description
desc = param['meta']['description']
desc = desc.format(pn = partnum,
type = mode,
pitch = pitch,
banks = banks,
pins = pins_or_pairs)
fp.setDescription(desc + ", generated with kicad-footprint-generator"
+ ", " + param['meta']['datasheet'])

# Tags
tags = param['meta']['tags']
fp.setTags(tags)

# 3D model path
library = param.get('library', default_lib)
model_path = os.path.join("${KISYS3DMOD}",
library+".3dshapes",
param['name'] + ".wrl")
fp.append(Model(filename = model_path))

############################################################################
# Write kicad_mod file

os.makedirs(library+'.pretty', exist_ok=True)
filename = os.path.join(library+'.pretty', param['name']+'.kicad_mod')
KicadFileHandler(fp).writeFile(filename)

################################################################################
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--global-config', type=str, nargs='?',
default='../../tools/global_config_files/config_KLCv3.0.yaml',
help='Global KLC configuration YAML file')
parser.add_argument('--series-config', type=str, nargs='?',
default='../conn_config_KLCv3.yaml',
help='Series KLC configuration YAML file')
parser.add_argument('--library', type=str, nargs='?',
default='Connector_Samtec_QStrip',
help='Default KiCad library name (without extension)')
parser.add_argument('files', metavar='file', type=str, nargs='*',
help='YAML file(s) containing footprint parameters')
args = parser.parse_args()

with open(args.global_config, 'r') as config_stream:
try:
config = yaml.safe_load(config_stream)
except yaml.YAMLError as exc:
print(exc)
with open(args.series_config, 'r') as config_stream:
try:
config.update(yaml.safe_load(config_stream))
except yaml.YAMLError as exc:
print(exc)

if len(args.files) == 0:
parser.print_help()
sys.exit(1)

print("Default Library:", args.library)
for path in args.files:
print("Reading", path)
with open(path, 'r') as stream:
try:
footprints = yaml.safe_load(stream)
if footprints is None:
print(path, "empty, skipping...")
continue
dictInherit(footprints)
for fp_name in footprints:
fp_params = footprints.get(fp_name)
if 'name' in fp_params:
print("WARNING: setting 'name' to", fp_name)
fp_params['name'] = fp_name
print(" - ",
fp_params.get('library', args.library), ".pretty/",
fp_name, ".kicad_mod", sep="")
generate_one_footprint(fp_params, config, args.library)
except yaml.YAMLError as exc:
print(exc)
Loading