PCB progress

This commit is contained in:
Bán Dénes 2020-07-12 23:23:30 +02:00
parent a6ed52b4bd
commit 05a33d00ec
7 changed files with 367 additions and 12 deletions

View file

@ -572,7 +572,12 @@ Everything should be ready for a handwire, but if you'd like the design to be mo
To help you get started, the necessary footprints and an edge cut can be automatically positioned so that all you need to do manually is the routing.
Footprints can be specified at the key-level (under the `points` section, like we discussed above), or here with manually given anchors.
The only difference between the two footprint types is that an omitted `ref` in the anchor means the current key for key-level declarations, while here it defaults to `[0, 0]`.
The differences between the two footprint types are:
- an omitted `ref` in the anchor means the current key for key-level declarations, while here it defaults to `[0, 0]`
- a parameter starting with an exclamation point is an indirect reference to an eponymous key-level attribute -- so, for example, `from = !col_wire` would mean that the key's `col_wire` attribute is read there.
Another alternative to `anchor` here (here being under the `pcb` declaration) is to use the `between` key and place the footprint at the average of multiple anchors -- mostly useful for anchoring to the center, by averaging a key and its mirror.
Additionally, the edge cut of the PCB can be specified using a previously defined outline name under the `edge` key.
```yaml
@ -581,6 +586,9 @@ pcb:
footprints:
- type: <footprint type>
anchor: <anchor declaration>
between:
- <anchor declaration>
- ...
params: <type-specific footprint params>
- ...
```

View file

@ -36,8 +36,14 @@ const args = yargs
describe: 'Debug mode',
type: 'boolean'
})
.option('clean', {
default: false,
describe: 'Clean output dir before parsing',
type: 'boolean'
})
.argv
if (args.clean) fs.removeSync(args.o)
fs.mkdirpSync(args.o)
const config_parser = args.c.endsWith('.yaml') ? yaml.load : JSON.parse
@ -72,7 +78,9 @@ for (const [name, outline] of Object.entries(outlines)) {
console.log('Scaffolding PCB...')
const pcb = pcb_lib.parse(config.pcb, points, outlines)
fs.writeFileSync(path.join(args.o, `pcb/pcb.kicad_pcb`, pcb))
const pcb_file = path.join(args.o, `pcb/pcb.kicad_pcb`)
fs.mkdirpSync(path.dirname(pcb_file))
fs.writeFileSync(pcb_file, pcb)
// goodbye

4
src/footprints/index.js Normal file
View file

@ -0,0 +1,4 @@
module.exports = {
mx: require('./mx'),
promicro: require('./promicro')
}

33
src/footprints/mx.js Normal file
View file

@ -0,0 +1,33 @@
module.exports = {
params: ['from', 'to'],
body: `
(module MX (layer F.Cu) (tedit 5DD4F656)
${''/* parametric position */}
__AT
${''/* corner marks */}
(fp_line (start -7 -6) (end -7 -7) (layer F.SilkS) (width 0.15))
(fp_line (start -7 7) (end -6 7) (layer F.SilkS) (width 0.15))
(fp_line (start -6 -7) (end -7 -7) (layer F.SilkS) (width 0.15))
(fp_line (start -7 7) (end -7 6) (layer F.SilkS) (width 0.15))
(fp_line (start 7 6) (end 7 7) (layer F.SilkS) (width 0.15))
(fp_line (start 7 -7) (end 6 -7) (layer F.SilkS) (width 0.15))
(fp_line (start 6 7) (end 7 7) (layer F.SilkS) (width 0.15))
(fp_line (start 7 -7) (end 7 -6) (layer F.SilkS) (width 0.15))
${''/* pins */}
(pad 1 thru_hole circle (at 2.54 -5.08) (size 2.286 2.286) (drill 1.4986) (layers *.Cu *.Mask) __PARAM_FROM)
(pad 2 thru_hole circle (at -3.81 -2.54) (size 2.286 2.286) (drill 1.4986) (layers *.Cu *.Mask) __PARAM_TO)
${''/* middle shaft */}
(pad "" np_thru_hole circle (at 0 0) (size 3.9878 3.9878) (drill 3.9878) (layers *.Cu *.Mask))
${''/* stabilizers */}
(pad "" np_thru_hole circle (at 5.08 0) (size 1.7018 1.7018) (drill 1.7018) (layers *.Cu *.Mask))
(pad "" np_thru_hole circle (at -5.08 0) (size 1.7018 1.7018) (drill 1.7018) (layers *.Cu *.Mask))
)
`
}

View file

@ -0,0 +1,88 @@
module.exports = {
nets: [
'RAW', 'GND', 'RST', 'VCC',
'P21', 'P20', 'P19', 'P18',
'P15', 'P14', 'P16', 'P10',
'P1', 'P0', 'P2', 'P3', 'P4',
'P5', 'P6', 'P7', 'P8', 'P9'
],
body: `
(module ProMicro (layer F.Cu) (tedit 5B307E4C)
${''/* parametric position */}
__AT
${''/* illustration of the USB port overhang */}
(fp_line (start -19.304 -3.556) (end -14.224 -3.556) (layer Dwgs.User) (width 0.2))
(fp_line (start -19.304 3.81) (end -19.304 -3.556) (layer Dwgs.User) (width 0.2))
(fp_line (start -14.224 3.81) (end -19.304 3.81) (layer Dwgs.User) (width 0.2))
(fp_line (start -14.224 -3.556) (end -14.224 3.81) (layer Dwgs.User) (width 0.2))
${''/* component outline */}
(fp_line (start -17.78 8.89) (end 15.24 8.89) (layer F.SilkS) (width 0.381))
(fp_line (start 15.24 8.89) (end 15.24 -8.89) (layer F.SilkS) (width 0.381))
(fp_line (start 15.24 -8.89) (end -17.78 -8.89) (layer F.SilkS) (width 0.381))
(fp_line (start -17.78 -8.89) (end -17.78 8.89) (layer F.SilkS) (width 0.381))
${''/* extra border around "RAW", in case the rectangular shape is not distinctive enough */}
(fp_line (start -15.24 6.35) (end -12.7 6.35) (layer F.SilkS) (width 0.381))
(fp_line (start -15.24 6.35) (end -15.24 8.89) (layer F.SilkS) (width 0.381))
(fp_line (start -12.7 6.35) (end -12.7 8.89) (layer F.SilkS) (width 0.381))
${''/* pin names */}
(fp_text user RAW (at -13.97 5.0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user GND (at -11.43 5.0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user RST (at -8.89 5.0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user VCC (at -6.35 5.0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 21 (at -3.81 5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 20 (at -1.27 5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 19 (at 1.27 5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 18 (at 3.81 5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 15 (at 6.35 5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 14 (at 8.89 5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 16 (at 11.43 5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 10 (at 13.97 5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 1 (at -13.97 -5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 0 (at -11.43 -5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user GND (at -8.89 -5.0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user GND (at -6.35 -5.0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 2 (at -3.81 -5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 3 (at -1.27 -5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 4 (at 1.27 -5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 5 (at 3.81 -5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 6 (at 6.35 -5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 7 (at 8.89 -5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 8 (at 11.43 -5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 9 (at 13.97 -5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
${''/* and now the actual pins */}
(pad 1 thru_hole rect (at -13.97 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_RAW)
(pad 2 thru_hole circle (at -11.43 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_GND)
(pad 3 thru_hole circle (at -8.89 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_RST)
(pad 4 thru_hole circle (at -6.35 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_VCC)
(pad 5 thru_hole circle (at -3.81 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_P21)
(pad 6 thru_hole circle (at -1.27 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_P20)
(pad 7 thru_hole circle (at 1.27 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_P19)
(pad 8 thru_hole circle (at 3.81 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_P18)
(pad 9 thru_hole circle (at 6.35 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_P15)
(pad 10 thru_hole circle (at 8.89 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_P14)
(pad 11 thru_hole circle (at 11.43 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_P16)
(pad 12 thru_hole circle (at 13.97 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_P10)
(pad 13 thru_hole circle (at -13.97 -7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_P1)
(pad 14 thru_hole circle (at -11.43 -7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_P0)
(pad 15 thru_hole circle (at -8.89 -7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_GND)
(pad 16 thru_hole circle (at -6.35 -7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_GND)
(pad 17 thru_hole circle (at -3.81 -7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_P2)
(pad 18 thru_hole circle (at -1.27 -7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_P3)
(pad 19 thru_hole circle (at 1.27 -7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_P4)
(pad 20 thru_hole circle (at 3.81 -7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_P5)
(pad 21 thru_hole circle (at 6.35 -7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_P6)
(pad 22 thru_hole circle (at 8.89 -7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_P7)
(pad 23 thru_hole circle (at 11.43 -7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_P8)
(pad 24 thru_hole circle (at 13.97 -7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_P9)
)
`
}

View file

@ -2,7 +2,119 @@ const m = require('makerjs')
const u = require('./utils')
const a = require('./assert')
const makerjs2kicad = exports._makerjs2kicad = model => {
const Point = require('./point')
const kicad_prefix = `
(kicad_pcb (version 20171130) (host pcbnew 5.1.6)
(page A3)
(title_block
(title KEYBOARD_NAME_HERE)
(rev VERSION_HERE)
(company YOUR_NAME_HERE)
)
(general
(thickness 1.6)
)
(layers
(0 F.Cu signal)
(31 B.Cu signal)
(32 B.Adhes user)
(33 F.Adhes user)
(34 B.Paste user)
(35 F.Paste user)
(36 B.SilkS user)
(37 F.SilkS user)
(38 B.Mask user)
(39 F.Mask user)
(40 Dwgs.User user)
(41 Cmts.User user)
(42 Eco1.User user)
(43 Eco2.User user)
(44 Edge.Cuts user)
(45 Margin user)
(46 B.CrtYd user)
(47 F.CrtYd user)
(48 B.Fab user)
(49 F.Fab user)
)
(setup
(last_trace_width 0.25)
(trace_clearance 0.2)
(zone_clearance 0.508)
(zone_45_only no)
(trace_min 0.2)
(via_size 0.8)
(via_drill 0.4)
(via_min_size 0.4)
(via_min_drill 0.3)
(uvia_size 0.3)
(uvia_drill 0.1)
(uvias_allowed no)
(uvia_min_size 0.2)
(uvia_min_drill 0.1)
(edge_width 0.05)
(segment_width 0.2)
(pcb_text_width 0.3)
(pcb_text_size 1.5 1.5)
(mod_edge_width 0.12)
(mod_text_size 1 1)
(mod_text_width 0.15)
(pad_size 1.524 1.524)
(pad_drill 0.762)
(pad_to_mask_clearance 0.05)
(aux_axis_origin 0 0)
(visible_elements FFFFFF7F)
(pcbplotparams
(layerselection 0x010fc_ffffffff)
(usegerberextensions false)
(usegerberattributes true)
(usegerberadvancedattributes true)
(creategerberjobfile true)
(excludeedgelayer true)
(linewidth 0.100000)
(plotframeref false)
(viasonmask false)
(mode 1)
(useauxorigin false)
(hpglpennumber 1)
(hpglpenspeed 20)
(hpglpendiameter 15.000000)
(psnegative false)
(psa4output false)
(plotreference true)
(plotvalue true)
(plotinvisibletext false)
(padsonsilk false)
(subtractmaskfromsilk false)
(outputformat 1)
(mirror false)
(drillshape 1)
(scaleselection 1)
(outputdirectory ""))
)
`
const kicad_suffix = `
)
`
const kicad_netclass = `
(net_class Default "This is the default net class."
(clearance 0.2)
(trace_width 0.25)
(via_dia 0.8)
(via_drill 0.4)
(uvia_dia 0.3)
(uvia_drill 0.1)
__ADD_NET
)
`
const makerjs2kicad = exports._makerjs2kicad = (model, layer='Edge.Cuts') => {
const grs = []
const xy = val => `${val[0]} ${val[1]}`
m.model.walk(model, {
@ -10,16 +122,14 @@ const makerjs2kicad = exports._makerjs2kicad = model => {
const p = wp.pathContext
switch (p.type) {
case 'line':
grs.push(`(gr_line (start ${xy(p.origin)}) (end ${xy(p.end)}) (angle 90) (layer Edge.Cuts) (width 0.15))`)
grs.push(`(gr_line (start ${xy(p.origin)}) (end ${xy(p.end)}) (angle 90) (layer ${layer}) (width 0.15))`)
break
case 'arc':
// console.log(require('util').inspect(p, false, 200))
// throw 2
const center = p.origin
const angle_start = Math.min(p.startAngle, p.endAngle)
const angle_diff = Math.abs(p.endAngle - p.startAngle)
const angle_start = p.startAngle > p.endAngle ? p.startAngle - 360 : p.startAngle
const angle_diff = Math.abs(p.endAngle - angle_start)
const end = m.point.rotate(m.point.add(center, [p.radius, 0]), angle_start, center)
grs.push(`(gr_arc (start ${xy(center)}) (end ${xy(end)}) (angle ${angle_diff}) (layer Edge.Cuts) (width 0.15))`)
grs.push(`(gr_arc (start ${xy(center)}) (end ${xy(end)}) (angle ${angle_diff}) (layer ${layer}) (width 0.15))`)
break
case 'circle':
break
@ -31,12 +141,107 @@ const makerjs2kicad = exports._makerjs2kicad = model => {
return grs
}
const footprint_types = require('./footprints')
const footprint = exports._footprint = (config, name, points, net_indexer, default_anchor) => {
// config sanitization
a.detect_unexpected(config, name, ['type', 'anchor', 'between', 'params'])
const type = a.in(config.type, `${name}.type`, Object.keys(footprint_types))
let anchor = a.anchor(config.anchor, `${name}.anchor`, points, true, default_anchor)
const params = a.sane(config.params, `${name}.params`, 'object')
// averaging multiple anchors, if necessary
if (config.between) {
const between = a.sane(config.between, `${name}.between`, 'array')
let x = 0, y = 0, r = 0, bi = 0
const len = between.length
for (const b of between) {
ba = a.anchor(b, `${name}.between[${++bi}]`, points, true)
x += ba.x
y += ba.y
r += ba.r
}
anchor = new Point(x / len, y / len, r / len)
}
// basic setup
const fp = footprint_types[type]
let result = fp.body
// footprint positioning
const at = `(at ${anchor.x} ${anchor.y} ${anchor.r})`
result = result.replace('__AT', at)
// connecting static nets
for (const net of (fp.nets || [])) {
const index = net_indexer(net)
result = result.replace('__NET_' + net.toUpperCase(), `(net ${index} "${net}")`)
}
// connecting parametric nets
for (const param of (fp.params || [])) {
const net = params[param]
a.sane(net, `${name}.params.${param}`, 'string')
const index = net_indexer(net)
result = result.replace('__PARAM_' + net.toUpperCase(), `(net ${index} "${net}")`)
}
return result
}
exports.parse = (config, points, outlines) => {
// config sanitization
a.detect_unexpected(config, 'pcb', ['edge', 'footprints'])
const edge = outlines[config.edge]
if (!edge) throw new Error(`Field "pcb.edge" doesn't name a valid outline!`)
// Edge.Cuts conversion
const kicad_edge = makerjs2kicad(edge)
console.log(kicad_edge.join('\n'))
throw 28
// making a global net index registry
const nets = {}
const net_indexer = net => {
if (nets[net]) return nets[net]
const index = Object.keys(nets).length
return nets[net] = index
}
const footprints = []
// key-level footprints
for (const [pname, point] of Object.entries(points)) {
let f_index = 0
for (const f of (point.meta.footprints || [])) {
footprints.push(footprint(f, `${pname}.footprints[${++f_index}]`, points, net_indexer, point))
}
}
// global one-off footprints
const global_footprints = a.sane(config.footprints || [], 'pcb.footprints', 'array')
let gf_index = 0
for (const gf of global_footprints) {
footprints.push(footprint(gf, `pcb.footprints[${++gf_index}]`, points, net_indexer))
}
// finalizing nets
const nets_arr = []
const add_nets_arr = []
for (const [net, index] of Object.entries(nets)) {
nets_arr.push(`(net ${index} "${net}")`)
add_nets_arr.push(`(add_net "${net}")`)
}
const netclass = kicad_netclass.replace('__ADD_NET', add_nets_arr.join('\n'))
const nets_text = nets_arr.join('\n')
const footprint_text = footprints.join('\n')
return `
${kicad_prefix}
${nets_text}
${netclass}
${footprint_text}
${kicad_suffix}
`
}

View file

@ -54,6 +54,10 @@ points:
home:
bind: [10]
top:
key:
footprints:
- type: mx
thumbfan:
anchor:
ref: inner_bottom
@ -152,4 +156,9 @@ outline:
name: middle
operation: stack
pcb:
edge: outline
edge: outline
footprints:
- type: promicro
between:
- ref: inner_top
- ref: mirror_inner_top