Skip to content
Draft
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
27 changes: 23 additions & 4 deletions src/fn/qfn.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
import type { AnySoupElement } from "circuit-json"
import { base_quad_def, quad, quad_def, quadTransform } from "./quad"
import { base_quad_def, quad, quadTransform } from "./quad"
import type { z } from "zod"

export const qfn_def = base_quad_def.extend({}).transform(quadTransform)

export const qfn = (
parameters: z.input<typeof qfn_def>,
raw_params: z.input<typeof qfn_def>,
): { circuitJson: AnySoupElement[]; parameters: any } => {
parameters.legsoutside = false
return quad(parameters)
raw_params.legsoutside = false

// Apply QFN-specific pad sizing if not explicitly provided
// Based on KiCad IPC standards for QFN packages
if (!raw_params.pw && !raw_params.pl) {
const pitchValue =
typeof raw_params.p === "string" ? parseFloat(raw_params.p) : raw_params.p

if (pitchValue) {
// KiCad IPC standard for QFN:
// - Pad width = 0.5 × pitch
// - Pad length = 0.8mm
// - Pads extend ~0.35mm beyond package edge for proper soldering
raw_params.pw = pitchValue * 0.5
raw_params.pl = 0.8
// Offset pads 0.35mm further from center so they extend beyond package edge
raw_params.padoffset = -0.35
}
}

return quad(raw_params)
}
7 changes: 5 additions & 2 deletions src/fn/quad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const base_quad_def = z.object({
pl: length.optional(),
thermalpad: z.union([z.literal(true), dim2d]).optional(),
legsoutside: z.boolean().default(false),
padoffset: length.optional(), // Additional offset for pad position (positive = further from center)
})

export const quadTransform = <T extends z.infer<typeof base_quad_def>>(
Expand Down Expand Up @@ -80,8 +81,9 @@ export const getQuadCoords = (params: {
p: number // pitch between pins
pl: number // length of the pin
legsoutside?: boolean
padoffset?: number // Additional offset for pad position
}) => {
const { pin_count, pn, w, h, p, pl, legsoutside } = params
const { pin_count, pn, w, h, p, pl, legsoutside, padoffset = 0 } = params
const sidePinCount = pin_count / 4
const side = SIDES_CCW[Math.floor((pn - 1) / sidePinCount)]
const pos = (pn - 1) % sidePinCount
Expand All @@ -92,7 +94,7 @@ export const getQuadCoords = (params: {
const ibh = p * (sidePinCount - 1)

/** pad center distance from edge (negative is inside, positive is outside) */
const pcdfe = legsoutside ? pl / 2 : -pl / 2
const pcdfe = (legsoutside ? pl / 2 : -pl / 2) - padoffset

switch (side) {
case "left":
Expand Down Expand Up @@ -129,6 +131,7 @@ export const quad = (
p: parameters.p ?? 0.5,
pl: parameters.pl,
legsoutside: parameters.legsoutside,
padoffset: parameters.padoffset,
})

let pw = parameters.pw
Expand Down
2 changes: 1 addition & 1 deletion tests/__snapshots__/qfn16_w4_h4_p0.65mm.snap.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading