ShiboSoftwareDev/mspm0g3507-gameboy-handheld

An AO3400A N-channel MOSFET for power switching and an AP2112K-3.3TRG1 LDO regulator providing a regulated 3.3 V output, both in compact SMD packages.

Version
1.2.1
License
unset
Stars
0

scripts/generate-assembly-files.mjs

import { readFileSync, writeFileSync } from "node:fs"
import { convertCircuitJsonToPickAndPlaceCsv } from "circuit-json-to-pnp-csv"

const circuitPath = process.argv[2] ?? "dist/index/circuit.json"
const circuit = JSON.parse(readFileSync(circuitPath, "utf8"))
const sourceComponents = circuit.filter(
  (element) => element.type === "source_component" && element.ftype !== "simple_test_point",
)

// C965805 is a symmetric two-pad LED package, so the generic orientation
// analyzer cannot derive a corner-style pin-1 frame. The imported EasyEDA
// footprint places cathode/pin 1 on the left and slightly below pin 2.
const powerLed = sourceComponents.find((component) => component.name === "D_POWER")
const powerLedPcb = circuit.find(
  (element) => element.type === "pcb_component" && element.source_component_id === powerLed?.source_component_id,
)
const powerLedPads = circuit.filter(
  (element) => element.type === "pcb_smtpad" && element.pcb_component_id === powerLedPcb?.pcb_component_id,
)
const powerLedPin1 = powerLedPads.find((pad) => pad.port_hints?.includes("pin1"))
const powerLedPin2 = powerLedPads.find((pad) => pad.port_hints?.includes("pin2"))
if (!powerLedPcb || !powerLedPin1 || !powerLedPin2 || !(powerLedPin1.x < powerLedPin2.x)) {
  throw new Error("Could not verify D_POWER cathode/pin-1 orientation")
}
powerLedPcb.pin1_location = "leftside_bottom"
powerLedPcb.supplier_pin1_location_map = {
  ...powerLedPcb.supplier_pin1_location_map,
  jlcpcb: "leftside_bottom",
}
writeFileSync(circuitPath, `${JSON.stringify(circuit, null, 2)}\n`)

const comments = {
  J_USB_C: "USB-C receptacle",
  R_USB_DM_JOIN: "USB D- reversible-contact crossover",
  R_VBUS_A_LINK: "USB-C VBUS orientation link",
  R_VBUS_B_LINK: "USB-C VBUS orientation link",
  U_USB_ESD: "USB ESD protection",
  U_USB_UART: "USB-to-UART bootloader bridge",
  R_USB_CC1: "USB-C CC1 pull-down",
  R_USB_CC2: "USB-C CC2 pull-down",
  C_USB_UART: "USB-UART decoupling",
  R_UART_TX: "UART TX protection resistor",
  R_UART_RX: "UART RX protection resistor",
  J_BATTERY: "Protected 1S LiPo connector with 10k NTC",
  U_BATTERY_CHARGER: "USB power-path LiPo charger",
  C_CHARGER_INPUT: "Charger input capacitor",
  C_CHARGER_BATTERY: "Charger battery capacitor",
  C_CHARGER_SYSTEM: "Charger system-output capacitor",
  R_CHARGE_CURRENT: "LiPo charge-current programming resistor",
  R_INPUT_CURRENT_LIMIT: "USB input-current-limit programming resistor",
  R_CHARGE_TIMER: "Charge safety-timer programming resistor",
  SW_POWER: "Main slide power switch",
  U_3V3_BUCK_BOOST: "Fixed 3.3V buck-boost regulator",
  R_POWER_ENABLE_LINK: "Regulator enable link",
  L_3V3_BUCK_BOOST: "3.3V buck-boost inductor",
  C_3V3_INPUT: "3.3V regulator input bulk capacitor",
  C_3V3_INPUT_BYPASS: "3.3V regulator input bypass capacitor",
  C_3V3_OUTPUT_A: "3.3V regulator output capacitor",
  C_3V3_OUTPUT_B: "3.3V regulator output capacitor",
  U_BACKLIGHT_BOOST: "Fixed 5V display-backlight boost regulator",
  L_BACKLIGHT_BOOST: "Display-backlight boost inductor",
  C_BACKLIGHT_BOOST_INPUT: "Display-backlight boost input capacitor",
  C_BACKLIGHT_5V_OUTPUT: "Display-backlight 5V output capacitor",
  R_GND_USB_LINK: "USB ground-domain star link",
  R_GND_POWER_LINK: "Power ground-domain star link",
  R_GND_MCU_LINK: "MCU ground-domain star link",
  R_GND_CONTROLS_LINK: "Controls ground-domain star link",
  R_GND_DISPLAY_LINK: "Display ground-domain star link",
  R_GND_IO_LINK: "User-I/O ground-domain star link",
  U_MCU: "80MHz Arm Cortex-M0+ MCU",
  C_MCU_BULK: "MCU supply bulk capacitor",
  C_MCU_BYPASS: "MCU supply bypass capacitor",
  C_MCU_VCORE: "MCU VCORE capacitor",
  R_RESET_PULLUP: "Reset pull-up",
  C_RESET: "Reset filter capacitor",
  SW_RESET: "Reset tactile switch",
  R_BOOT_PULLDOWN: "ROM BSL invoke pull-down",
  SW_BOOT: "ROM BSL invoke tactile switch",
  SW_UP: "D-pad up tactile switch",
  SW_DOWN: "D-pad down tactile switch",
  SW_LEFT: "D-pad left tactile switch",
  SW_RIGHT: "D-pad right tactile switch",
  SW_A: "A action tactile switch",
  SW_B: "B action tactile switch",
  SW_START: "Start tactile switch",
  SW_SELECT: "Select tactile switch",
  J_DISPLAY_FPC: "50-pin 0.5mm top-contact display FPC connector",
  R_DISPLAY_5V_LINK: "Display backlight 5V link",
  R_BACKLIGHT_K1: "Display backlight LEDK1 current limiter",
  R_BACKLIGHT_K2: "Display backlight LEDK2 current limiter",
  R_BACKLIGHT_K3: "Display backlight LEDK3 current limiter",
  R_BACKLIGHT_K4: "Display backlight LEDK4 current limiter",
  U_BACKLIGHT_SWITCH: "Display backlight PWM switch",
  R_BACKLIGHT_GATE_PULLDOWN: "Display backlight gate pull-down",
  R_POWER_LED: "Power LED resistor",
  D_POWER: "Green power LED",
  R_RGB_RED: "RGB debug LED red resistor",
  R_RGB_GREEN: "RGB debug LED green resistor",
  R_RGB_BLUE: "RGB debug LED blue resistor",
  D_USER_RGB: "Common-cathode RGB user/debug LED",
  J_AUDIO_MODULE: "JST-SH 4-pin external amplified-audio module connector",
}

const footprintsByMpn = {
  "TYPE-C-31-M-12": "USB-C-SMD-16P+4TH",
  "1206W4F0000T5E": "1206",
  "0402WGF0000TCE": "0402",
  "USBLC6-2SC6": "SOT-23-6",
  CH340C: "SOP-16-150mil",
  "0402WGF5101TCE": "0402",
  CL05B104KO5NNNC: "0402",
  "0402WGF1001TCE": "0402",
  "0402WGF1000TCE": "0402",
  "0402WGF3300TCE": "0402",
  "0402WGF3301TCE": "0402",
  "B3B-PH-SM4-TB(LF)(SN)": "JST-PH-3P-SMD",
  BQ24075RGTR: "VQFN-16-3x3mm",
  MSK12C02: "SMD-SLIDE-SWITCH",
  TPS63031DSKR: "WSON-10-2.5x2.5mm",
  FNR3015S1R5MT: "SMD-INDUCTOR-3x3mm",
  TPS61240DRVR: "WSON-6-2x2mm",
  CL10A475KO8NNNC: "0603",
  CL10A106KP8NNNC: "0603",
  MSPM0G3507SPTR: "LQFP-48-7x7mm",
  CL10B474KA8NNNC: "0603",
  "0402WGF4702TCE": "0402",
  CL05B103KB5NNNC: "0402",
  "TS342A2P-WZ": "SMD-TACT-2P",
  "4*4*1.7 贴片": "SMD-TACT-4P-5.2x5.2mm",
  "0402WGF1002TCE": "0402",
  "0402WGF4701TCE": "0402",
  "XL-1608SYGC-06": "0603-LED",
  "SZYY1615RGB-A 4pin": "SMD-4P-1.6x1.5mm-RGB-LED",
  "0.5K-AS-50PWBG": "FPC-0.5mm-50P-TOP-CONTACT",
  AO3400A: "SOT-23",
  "SM04B-SRSS-TB(LF)(SN)": "JST-SH-4P-SMD",
  "ZX-HY2.0-4PZZ": "GROVE-2.0MM-4P-THT",
  "PH2.54-1X20P-H25": "THT-1x20-P2.54mm-OD1.6-ID1.05",
}

const componentValue = (component) => {
  if (component.display_resistance) return component.display_resistance
  if (component.display_capacitance) return component.display_capacitance
  if (component.ftype === "simple_push_button") return "SPST-NO"
  if (component.name === "D_POWER") return "green"
  if (component.name === "D_USER_RGB") return "RGB common cathode"
  if (component.ftype === "simple_pin_header") return "1x20 2.54mm"
  return component.manufacturer_part_number
}

const csvField = (value) => {
  const text = String(value ?? "")
  return /[",\n]/.test(text) ? `"${text.replaceAll('"', '""')}"` : text
}

const header = [
  "Designator",
  "Comment",
  "Value",
  "Footprint",
  "JLCPCB Part #",
  "Manufacturer Part Number",
]
const rows = sourceComponents.map((component) => {
  const jlcPart = component.supplier_part_numbers?.jlcpcb?.[0]
  const mpn = component.manufacturer_part_number
  if (!comments[component.name]) throw new Error(`Missing BOM comment for ${component.name}`)
  if (!jlcPart) throw new Error(`Missing JLCPCB part number for ${component.name}`)
  if (!mpn) throw new Error(`Missing manufacturer part number for ${component.name}`)
  if (!footprintsByMpn[mpn]) throw new Error(`Missing BOM footprint for ${component.name} (${mpn})`)
  return [
    component.name,
    comments[component.name],
    componentValue(component),
    footprintsByMpn[mpn],
    jlcPart,
    mpn,
  ]
})

const bom = [header, ...rows].map((row) => row.map(csvField).join(",")).join("\n") + "\n"
const cpl = convertCircuitJsonToPickAndPlaceCsv(circuit, {
  supplier: "jlcpcb",
  requireSupplierRotation: true,
}).replaceAll("\r\n", "\n").trimEnd() + "\n"

writeFileSync("outputs/MSPM0G3507_GAMEBOY_JLCPCB_BOM.csv", bom)
writeFileSync("outputs/MSPM0G3507_GAMEBOY_JLCPCB_CPL.csv", cpl)
writeFileSync("manufacturing/bom.csv", bom)
writeFileSync("manufacturing/pick_and_place.csv", cpl)

console.log(`Generated ${rows.length} BOM placements and ${cpl.trim().split(/\r?\n/).length - 1} CPL placements`)