pixalynx/usb3-hub-10g

A USB 3.2 Gen 2 six-port hub board with USB-C PD power input, regulated 5 V/3.3 V/1.15 V rails, one upstream and four protected downstream USB-A/USB-C ports, SuperSpeed muxes, ESD protection, and controlled port power.

Version
0.0.1
License
unset
Stars
0

lib/footprints.tsx

import { Children, Fragment, type ReactElement } from "react"

/** Land-pattern generators. Every dimension is in millimetres and is annotated
 * with the manufacturer document it was read from (see docs/sources.md and research/).
 * Pin numbering follows the manufacturer's pin table; pin 1 is top-left in the
 * unrotated footprint and numbering proceeds counter-clockwise for QFN/LGA
 * packages (left side downwards, bottom left-to-right, right upwards, top
 * right-to-left), which is the JEDEC convention used by Nordic, TI and ST. */

export const pad = (pin: number | string, x: number, y: number, w: number, h: number) => (
  <Fragment key={`p${pin}-${x}-${y}`}>
    <smtpad portHints={[typeof pin === "number" ? `pin${pin}` : pin]} pcbX={x} pcbY={y} width={w} height={h} shape="rect" />
  </Fragment>
)
export const circlePad = (pin: number | string, x: number, y: number, d: number) => (
  <Fragment key={`c${pin}-${x}-${y}`}>
    <smtpad portHints={[typeof pin === "number" ? `pin${pin}` : pin]} pcbX={x} pcbY={y} radius={d / 2} shape="circle" />
  </Fragment>
)
export const via = (pin: number | string, x: number, y: number, drill: number, padD: number) => (
  <Fragment key={`v${pin}-${x}-${y}`}>
    <platedhole portHints={[typeof pin === "number" ? `pin${pin}` : pin]} pcbX={x} pcbY={y} holeDiameter={drill} outerDiameter={padD} shape="circle" />
  </Fragment>
)
export const outline = (w: number, h: number, stroke = 0.12) => (
  <silkscreenpath route={[{ x: -w / 2, y: -h / 2 }, { x: w / 2, y: -h / 2 }, { x: w / 2, y: h / 2 }, { x: -w / 2, y: h / 2 }, { x: -w / 2, y: -h / 2 }]} strokeWidth={stroke} />
)
export const courtyard = (w: number, h: number) => (
  <courtyardoutline outline={[{ x: -w / 2, y: h / 2 }, { x: w / 2, y: h / 2 }, { x: w / 2, y: -h / 2 }, { x: -w / 2, y: -h / 2 }, { x: -w / 2, y: h / 2 }]} />
)
const pin1Mark = (x: number, y: number) => <silkscreencircle pcbX={x} pcbY={y} radius={0.15} strokeWidth={0.1} />

/** Generic QFN/DFN land pattern.
 * @param nx pins per horizontal side (top/bottom); @param ny pins per vertical side (left/right)
 * @param pitch pin pitch; @param span centre-to-centre distance of opposite land rows
 * @param landL land length (radial); @param landW land width (tangential)
 * @param ep exposed-pad copper size (0 = none); @param epVias grid of ground vias in the EP
 */
export function Qfn(p: { nx: number; ny: number; pitch: number; spanX: number; spanY?: number; landL: number; landW: number; ep?: number | [number, number]; epVias?: { n: number; pitch: number; drill: number; pad: number }; body: [number, number]; epPin?: number }) {
  const { nx, ny, pitch, spanX, landL, landW } = p
  const spanY = p.spanY ?? spanX
  const total = 2 * nx + 2 * ny
  const epPin = p.epPin ?? total + 1
  const pads: ReactElement[] = []
  let n = 1
  // left side, top to bottom
  for (let i = 0; i < ny; i++) pads.push(pad(n++, -spanX / 2, ((ny - 1) / 2 - i) * pitch, landL, landW))
  // bottom, left to right
  for (let i = 0; i < nx; i++) pads.push(pad(n++, (i - (nx - 1) / 2) * pitch, -spanY / 2, landW, landL))
  // right, bottom to top
  for (let i = 0; i < ny; i++) pads.push(pad(n++, spanX / 2, (i - (ny - 1) / 2) * pitch, landL, landW))
  // top, right to left
  for (let i = 0; i < nx; i++) pads.push(pad(n++, ((nx - 1) / 2 - i) * pitch, spanY / 2, landW, landL))
  const [epW, epH] = p.ep ? (Array.isArray(p.ep) ? p.ep : [p.ep, p.ep]) : [0, 0]
  const vias: ReactElement[] = []
  if (p.epVias && epW) {
    const { n: g, pitch: vp, drill, pad: vpad } = p.epVias
    for (let i = 0; i < g; i++) for (let j = 0; j < g; j++)
      vias.push(via(epPin, (i - (g - 1) / 2) * vp, (j - (g - 1) / 2) * vp, drill, vpad))
  }
  return (
    <footprint>
      {Children.toArray(pads)}
      {epW > 0 && pad(epPin, 0, 0, epW, epH)}
      {Children.toArray(vias)}
      {outline(p.body[0], p.body[1])}
      {pin1Mark(-p.body[0] / 2 - 0.45, p.body[1] / 2 - 0.1)}
      {courtyard(spanX + landL + 0.3, spanY + landL + 0.3)}
    </footprint>
  )
}

/** 0201 chip capacitor: the footprinter's 0201 lands (0.46 x 0.40 mm at +/-0.33 mm) with a courtyard drawn at the
 * pad extents + 0.05 mm, so 0201 decoupling can sit at a QFN pin tip as in Nordic's nRF7002 reference layout. */
export const Cap0201 = () => (
  <footprint>
    {pad(1, -0.33, 0, 0.46, 0.4)}
    {pad(2, 0.33, 0, 0.46, 0.4)}
    {courtyard(1.22, 0.5)}
  </footprint>
)

/** 0201 "least" land for decouplers that sit in a 0.40 mm QFN lead lane between two SuperSpeed pairs: 0.30 x 0.30 mm
 * lands at +/-0.30 mm (IPC-7351 least; sync-stream-media-hub used the same land for the USB7206C). A 0.27 mm pair
 * conductor on the neighbouring 0.4 mm-pitch lead then clears the land by 0.115 mm without jogging. */
export const Cap0201Lane = () => (
  <footprint>
    {pad(1, -0.3, 0, 0.3, 0.3)}
    {pad(2, 0.3, 0, 0.3, 0.3)}
    {courtyard(1.2, 0.6)}
  </footprint>
)