Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ cap0402
res0805
soic8_p1.27mm
dip16
SPDIP-28
pinrow10
pinrow6_rows2_cols5_p2.54mm_py5.08mm_missing(3,4,8,9)
smdpinheader6
Expand All @@ -33,6 +34,13 @@ do219ad
sod323he
```

`SPDIP-28`, `spdip28`, and `fp().spdip()` generate the 28-pin skinny plastic DIP
on a 7.62 mm row grid with 2.54 mm pin pitch. These dimensions follow the
[Microchip C04-070 package drawing](https://ww1.microchip.com/downloads/en/PackagingSpec/00049AA.pdf#page=47).
The existing DIP generator supplies pin-one marking, silkscreen and courtyard.
Drill and pad diameters default to 0.8 mm and 1.6 mm; override them to suit your
fabrication requirements, for example `SPDIP-28_id1mm_od1.8mm`.

You can use these like so:

```tsx
Expand Down
1 change: 1 addition & 0 deletions src/fn/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { dip } from "./dip"
export { spdip } from "./spdip"
export { d2pak } from "./d2pak"
export { dpak } from "./dpak"
export { diode } from "./diode"
Expand Down
12 changes: 12 additions & 0 deletions src/fn/spdip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { z } from "zod"
import { dip, dip_def } from "./dip"

/** Skinny plastic DIP: 300 mil row spacing, 100 mil pitch, 28 pins by default. */
export const spdip = (raw_params: z.input<typeof dip_def>) => {
const parameters = dip_def.parse({
...raw_params,
num_pins: raw_params.num_pins ?? 28,
w: raw_params.w ?? "300mil",
})
return dip({ ...parameters, dip: true })
}
4 changes: 4 additions & 0 deletions src/footprinter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ type CommonPassiveOptionKey =
| "textbottom"

export type Footprinter = {
spdip: (
num_pins?: number,
) => FootprinterParamsBuilder<"w" | "p" | "id" | "od" | "nosquareplating">
dip: (
num_pins?: number,
) => FootprinterParamsBuilder<"w" | "p" | "id" | "od" | "wide" | "narrow">
Expand Down Expand Up @@ -633,6 +636,7 @@ export type Footprinter = {
const normalizeDefinition = (def: string): string => {
return def
.trim()
.replace(/^spdip-(\d+)(?=_|$)/i, "spdip$1")
.replace(/^do-219ad(?=_|$)/i, "do219ad")
.replace(/^sod-323he(?=_|$)/i, "sod323he")
.replace(/^pinheader(?=[\d_]|$)/i, "pinrow")
Expand Down
1 change: 1 addition & 0 deletions tests/__snapshots__/spdip28.snap.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions tests/__snapshots__/spdip28_custom_drill.snap.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
70 changes: 70 additions & 0 deletions tests/spdip.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { expect, test } from "bun:test"
import { convertCircuitJsonToPcbSvg } from "circuit-to-svg"
import { fp, getFootprintNames } from "../src/footprinter"

test("SPDIP-28 uses the 300 mil row spacing and 100 mil pitch", () => {
const holes = fp
.string("SPDIP-28")
.circuitJson()
.filter((element) => element.type === "pcb_plated_hole")
expect(holes).toHaveLength(28)
// Microchip C04-070, 28-lead skinny plastic DIP: 0.100 inch pitch.
// Through-hole rows use the 0.300 inch insertion grid, not free lead-tip span.
for (let index = 0; index < 28; index++) {
const hole = holes[index]!
const left = index < 14
const expectedY = left ? 16.51 - index * 2.54 : -16.51 + (index - 14) * 2.54
expect(hole.x).toBeCloseTo(left ? -3.81 : 3.81, 8)
expect(hole.y).toBeCloseTo(expectedY, 8)
expect(hole.port_hints).toContain(String(index + 1))
}
expect(holes[0]!.shape).toBe("circular_hole_with_rect_pad")
expect(holes.slice(1).every((hole) => hole.shape === "circle")).toBe(true)
})

test("SPDIP aliases and default builder produce the same 28-pin footprint", () => {
const expected = fp().spdip(28).circuitJson()
for (const name of ["spdip", "spdip28", "SPDIP28", "SPDIP-28", "spdip-28"]) {
expect(fp.string(name).circuitJson()).toEqual(expected)
}
expect(fp().spdip().circuitJson()).toEqual(expected)
expect(getFootprintNames()).toContain("spdip")
})

test("SPDIP preserves DIP geometry and supports explicit drill and pad sizes", () => {
expect(fp.string("SPDIP-28_id1mm_od1.8mm").circuitJson()).toEqual(
fp.string("dip28_w7.62mm_p2.54mm_id1mm_od1.8mm").circuitJson(),
)
expect(fp.string("spdip28").json()).toMatchObject({
fn: "spdip",
num_pins: 28,
w: 7.62,
p: 2.54,
id: 0.8,
od: 1.6,
})
})

test("SPDIP global modifiers retain pin-one relocation and omit silkscreen", () => {
expect(
fp
.string("SPDIP-28_pin1location(leftside,bottom)_nosilkscreen")
.circuitJson(),
).toEqual(
fp.string("dip28_pin1location(leftside,bottom)_nosilkscreen").circuitJson(),
)
})

test("spdip28", () => {
expect(
convertCircuitJsonToPcbSvg(fp.string("SPDIP-28").circuitJson()),
).toMatchSvgSnapshot(import.meta.path, "spdip28")
})

test("spdip28_custom_drill", () => {
expect(
convertCircuitJsonToPcbSvg(
fp.string("spdip28_id1mm_od1.8mm").circuitJson(),
),
).toMatchSvgSnapshot(import.meta.path, "spdip28_custom_drill")
})
Loading