mohan-bee/mini-mp3-player

Portable, battery-powered MP3 player hardware with USB charging, microSD storage, an MCU, VS10xx audio decoder, headphone output, status controls, and regulated power rails.

Version
1.2.0
License
unset
Stars
0

index.circuit.tsx

PCBPCB preview for index.circuit.tsx
SchematicSchematic preview for index.circuit.tsx
import { savedAutorouting } from './routing/saved-autorouting'
import { PlayerAssembly } from './mechanical/PlayerAssembly'
import { CH32V203C8T6 } from './imports/CH32V203C8T6'
import { VS1003B_L } from './imports/VS1003B_L'
import { BQ24074RGTR } from './imports/BQ24074RGTR'
import { ME6211C33M5G_N } from './imports/ME6211C33M5G_N'
import { ME6211A25M3G_N } from './imports/ME6211A25M3G_N'
import { ME6219C27M5G } from './imports/ME6219C27M5G'
import { TF_01A } from './imports/TF_01A'
import { PJ_320D } from './imports/PJ_320D'
import { TS_1088_AR02016 } from './imports/TS_1088_AR02016'
import { MSK12C02 } from './imports/MSK12C02'
import { X32258MOB4SI } from './imports/X32258MOB4SI'
import { Fragment } from 'react'
import { KT_0603R } from './imports/KT_0603R'
import { KT_0805G } from './imports/KT_0805G'
import { AO3401A } from './imports/AO3401A'
import { A_3225_12_288_20_10_10_A } from './imports/A_3225_12_288_20_10_10_A'
import { resistorParts, capacitorParts } from './parts'
import { pinNets, resistors, caps } from './design'
import { B3B_PH_SM4_TB_LF__SN_ } from './imports/B3B_PH_SM4_TB_LF__SN_'
import { RVT0J221M0605_220UF_6_3V } from './imports/RVT0J221M0605_220UF_6_3V'

const N = (s:string) => `net.${s}`
const conn = (pins:Record<string,string>) => Object.fromEntries(Object.entries(pins).map(([p,n])=>[/^\d+$/.test(p)?`pin${p}`:p,N(n)]))
const buttons = [['SW1','PLAY',-12],['SW2','PREV',0],['SW3','NEXT',12],['SW4','MODE',24]] as const
const chips = [
 [BQ24074RGTR,'U1',-20,-8.6,'Power'],[ME6211C33M5G_N,'U2',-12,-13,'Power'],
 [ME6211A25M3G_N,'U3',-3,-12,'Power'],[ME6219C27M5G,'U4',8,-12,'Power'],
 [CH32V203C8T6,'U5',-13,6,'MCU'],[VS1003B_L,'U6',12,12,'Decoder'],
] as const

const sheetBySection: Record<string, string> = {
 Power: "power", MCU: "controller", Debug: "controller",
 Decoder: "audio", Audio: "audio", Storage: "interface", Controls: "interface",
}

function SheetNotes({ x, y, lines }: { x: number; y: number; lines: string[] }) {
 return <>{lines.map((text, i) => <Fragment key={text}><schematictext text={text} schX={x} schY={y-i*1.4} fontSize={0.7} anchor="left" /></Fragment>)}</>
}

export default function MiniMp3Player({ exploded = false }: { exploded?: boolean } = {}) {
 return <board width="82mm" height="62mm" layers={2} thickness="1.6mm"
   schAutoLayoutEnabled autorouterVersion="beta_pipeline9" autorouterEffortLevel="2x"
   autorouter={{local:true,allowViaInPad:false,traceClearance:"0.25mm",algorithmFn:savedAutorouting}} minTraceWidth="0.18mm"
   minViaHoleDiameter="0.3mm" minViaPadDiameter="0.45mm"
   minViaEdgeToPadEdgeClearance="0.15mm" minViaHoleEdgeToViaHoleEdgeClearance="0.25mm">
  <schematicsheet name="power" displayName="01 · USB-C, Battery & Power" sheetIndex={1} sheetWidth={750} sheetHeight={500}>
    <schematicsection name="Power" displayName="Power" />
    <SheetNotes x={-44} y={-28} lines={[
      'POWER: USB-C input, protected battery, charger and regulated supplies.',
      'J1: 5 V charging only. Separate 5.1k CC resistors identify a USB-C sink.',
      'U1 BQ24074: power path; about 300 mA charge current, USB500 input limit.',
      'J2: pin 1 BAT+, pin 2 cell-mounted 10k NTC, pin 3 GND. Protected 1S pack only.',
      'SW7 controls Q1 system power. Charging remains available with the switch OFF.',
      'Q2 disconnects the battery ADC divider while OFF, preventing MCU back-power.',
      'U2 = 3.3 V logic/card; U3 = 2.5 V decoder core; U4 = 2.7 V decoder analog.',
      'D1 red = charging. Local capacitors filter rails and supply transient current.',
    ]} />
  </schematicsheet>
  <schematicsheet name="controller" displayName="02 · MCU & Programming" sheetIndex={2} sheetWidth={800} sheetHeight={950}>
    <schematicsection name="MCU" displayName="MCU" />
    <schematicsection name="Debug" displayName="Debug" />
    <SheetNotes x={-16} y={-76} lines={[
      'U5 CH32V203: reads FAT files, streams compressed MP3 bytes, handles buttons.',
      'Y1: 8 MHz MCU reference. Firmware uses a 72 MHz PLL clock; MCU does not decode audio.',
      'Supply capacitors serve every VDD group; reset RC and boot straps select user flash.',
      'WCH-Link: TP1 GND, TP2 3.3 V reference, TP3 PA13/SWDIO, TP4 PA14/SWCLK.',
      'TP5 = reset; TP6/TP7 = 3.3 V UART TX/RX, 115200 baud. Do not backfeed TP2.',
      'PA0 measures half the battery voltage. Calibrate ADC; low-battery stop is not power-off.',
      'D2 green: play/pause, volume mode and numbered firmware error indication.',
      'TP16 PGOOD is open drain: add an external pull-up to 3.3 V when measuring logic.',
    ]} />
  </schematicsheet>
  <schematicsheet name="audio" displayName="03 · MP3 Decoder & Headphones" sheetIndex={3} sheetWidth={850} sheetHeight={800}>
    <schematicsection name="Decoder" displayName="Decoder" />
    <schematicsection name="Audio" displayName="Audio" />
    <SheetNotes x={-10} y={-59} lines={[
      'U6 VS1003: hardware MP3 decoder and stereo headphone DAC. Y2 = 12.288 MHz.',
      'SPI2 carries SCI commands via XCS and compressed audio via XDCS.',
      'DREQ tells the MCU when another burst of up to 32 audio bytes can be accepted.',
      'Core = 2.5 V; I/O = 3.3 V; analog = 2.7 V. Never feed analog pins with 3.3 V.',
      'C43/C44: 220 uF DC-blocking capacitors; positive terminals face decoder outputs.',
      'J4: standard ground-referenced headphones. GBUF must never be shorted to GND.',
      'Output RC networks stabilize the headphone drivers. No speaker amplifier is fitted.',
      'Volume is set digitally through SCI_VOL; test with dummy loads before headphones.',
    ]} />
  </schematicsheet>
  <schematicsheet name="interface" displayName="04 · microSD & Buttons" sheetIndex={4} sheetWidth={800} sheetHeight={400}>
    <schematicsection name="Storage" displayName="Storage" />
    <schematicsection name="Controls" displayName="Controls" />
    <SheetNotes x={-25} y={-59} lines={[
      'J3 microSD: SPI1 storage at 3.3 V; FAT16/FAT32, root MP3 files with short 8.3 names.',
      'Card CS pull-up keeps it deselected during reset. Local bulk capacitance buffers reads.',
      'SW1 PLAY/PAUSE; SW2 PREV; SW3 NEXT; SW4 VOL toggles volume mode.',
      'In volume mode PREV lowers volume and NEXT raises it; VOL returns to track mode.',
      'R40-R43 pull inputs high; pressing a switch connects its signal to ground.',
      'Firmware debounces presses for 25 ms. Switch OFF before removing the microSD.',
    ]} />
  </schematicsheet>
  {['USB5V','BAT','SYS_RAW','SYS','V3V3','V2V5','V2V7'].map(name=><Fragment key={name}><net name={name} isPowerNet nominalTraceWidth="0.3mm" /></Fragment>)}
  <net name="GND" isGroundNet />
  <copperpour connectsTo="net.GND" layer="bottom" clearance="0.4mm" boardEdgeMargin="0.5mm" />
  <copperpour connectsTo="net.GND" layer="top" clearance="0.4mm" boardEdgeMargin="0.5mm" />
  {chips.map(([Part,name,pcbX,pcbY,schSectionName])=><Part pcbRotation={name === "U6" ? 180 : 0} key={name} schMarginX={0.8} schMarginY={0.8} schSheetName={sheetBySection[schSectionName]} {...{name,pcbX,pcbY,schSectionName}} connections={conn(pinNets[name])} />)}
  <connector name="J1" standard="usb_c" supplierPartNumbers={{jlcpcb:["C2765186"]}} pcbX={-34} pcbY={-27} schMarginX={0.8} schMarginY={0.8} schSectionName="Power" schSheetName="power"
    connections={conn({VBUS1:'USB5V',VBUS2:'USB5V',CC1:'CC1',CC2:'CC2',GND1:'GND',GND2:'GND',SHELL1:'GND',SHELL2:'GND'})} />
  {/* Protected 1S cell, with a 10k NTC thermally attached to the cell. */}
  <B3B_PH_SM4_TB_LF__SN_ name="J2" pcbX={-32} pcbY={-4} schMarginX={0.8} schMarginY={0.8} schSectionName="Power" schSheetName="power" connections={conn({1:'BAT',2:'NTC',3:'GND'})} />
  {/* Charger remains connected when the system is switched off. */}
  <MSK12C02 name="SW7" pcbX={-23} pcbY={-27} schMarginX={0.8} schMarginY={0.8} schSectionName="Power" schSheetName="power" connections={conn({1:'GND',2:'PWR_GATE',4:'GND'})} />
  <AO3401A name="Q1" pcbX={-16} pcbY={-14} schMarginX={0.8} schMarginY={0.8} schSectionName="Power" schSheetName="power" connections={conn({1:'PWR_GATE',2:'SYS_RAW',3:'SYS'})} />
  <AO3401A name="Q2" pcbX={-28} pcbY={-11} schMarginX={0.8} schMarginY={0.8} schSectionName="Power" schSheetName="power" connections={conn({1:'PWR_GATE',2:'BAT',3:'BAT_SENSE'})} />
  <resistor supplierPartNumbers={{jlcpcb:["C25803"]}} name="R36" resistance="100k" footprint="0603" pcbX={-16} pcbY={-17} schMarginX={0.8} schMarginY={0.8} schSectionName="Power" schSheetName="power" connections={conn({1:'PWR_GATE',2:'SYS_RAW'})} />
  <X32258MOB4SI name="Y1" loadCapacitance="12pF" pcbX={-14} pcbY={-1.8} pcbRotation={270} schMarginX={0.8} schMarginY={0.8} schSectionName="MCU" schSheetName="controller" connections={conn({1:'MCU_XI',3:'MCU_XO',2:'GND',4:'GND'})} />
  <A_3225_12_288_20_10_10_A name="Y2" pcbX={3.8} pcbY={12.5} schMarginX={0.8} schMarginY={0.8} schSectionName="Decoder" schSheetName="audio" connections={conn({1:'VS_XI',3:'VS_XO',2:'GND',4:'GND'})} />
  <TF_01A name="J3" pcbX={-31} pcbY={18} pcbRotation={270} schMarginX={0.8} schMarginY={0.8} schSectionName="Storage" schSheetName="interface" connections={conn(pinNets.J3)} />
  <PJ_320D name="J4" pcbX={31} pcbY={-14} pcbRotation={180} schMarginX={0.8} schMarginY={0.8} schSectionName="Audio" schSheetName="audio" connections={conn(pinNets.J4)} />
  <RVT0J221M0605_220UF_6_3V name="C43" pcbX={17} pcbY={-11} pcbRotation={90} schMarginX={0.8} schMarginY={0.8} schSectionName="Audio" schSheetName="audio" connections={conn({1:'LEFT',2:'HP_L'})} />
  <RVT0J221M0605_220UF_6_3V name="C44" pcbX={17} pcbY={-21} pcbRotation={90} schMarginX={0.8} schMarginY={0.8} schSectionName="Audio" schSheetName="audio" connections={conn({1:'RIGHT',2:'HP_R'})} />
  {resistors.map(([name,resistance,a,b,pcbX,pcbY,schSectionName])=><resistor supplierPartNumbers={{jlcpcb:[resistorParts[resistance]]}} pcbRotation={["R11","R16","R26","R30","R34","R37"].includes(name)?180:0} key={name} schMarginX={0.8} schMarginY={0.8} schSheetName={sheetBySection[schSectionName]} {...{name,resistance,pcbX,pcbY,schSectionName}} footprint="0603" connections={conn({1:a,2:b})} />)}
  {caps.map(([name,capacitance,a,b,pcbX,pcbY,schSectionName])=><capacitor supplierPartNumbers={{jlcpcb:[capacitorParts[capacitance]]}} pcbRotation={["C7","C18","C25","C31"].includes(name)?180:0} key={name} schMarginX={0.8} schMarginY={0.8} schSheetName={sheetBySection[schSectionName]} {...{name,capacitance,pcbX,pcbY,schSectionName}} footprint={capacitance==='10uF'?'0805':'0603'} connections={conn({1:a,2:b})} />)}
  {buttons.map(([name,signal,x],i)=><Fragment key={name}>
    <TS_1088_AR02016 name={name} pcbX={x} pcbY={-28} schMarginX={0.8} schMarginY={0.8} schSectionName="Controls" schSheetName="interface" connections={conn({1:signal,2:'GND'})} />
    <resistor supplierPartNumbers={{jlcpcb:["C25804"]}} name={`R${40+i}`} resistance="10k" footprint="0603" pcbX={x+4.75} pcbY={-28} schMarginX={0.8} schMarginY={0.8} schSectionName="Controls" schSheetName="interface" connections={conn({1:signal,2:'V3V3'})} />
    <silkscreentext text={signal === 'MODE' ? 'VOL' : signal} pcbX={x} pcbY={-30} fontSize="0.7mm" />
  </Fragment>)}
  <KT_0603R name="D1" pcbX={-34} pcbY={-11} schMarginX={0.8} schMarginY={0.8} schSectionName="Power" schSheetName="power" connections={conn({anode:'CHG_LED_A',cathode:'CHG'})} />
  <KT_0805G name="D2" pcbX={-3} pcbY={23} schMarginX={0.8} schMarginY={0.8} schSectionName="MCU" schSheetName="controller" connections={conn({anode:'STATUS_A',cathode:'STATUS'})} />
  {['GND','V3V3','SWDIO','SWCLK','NRST','UART_TX','UART_RX','BOOT0'].map((n,i)=><Fragment key={n}>
    <testpoint name={`TP${i+1}`} pcbX={-16+i*2.5} pcbY={-7} doNotPlace padDiameter="1.2mm" footprintVariant="pad" schMarginX={0.8} schMarginY={0.8} schSectionName="Debug" schSheetName="controller" connections={{pin1:N(n)}} />
    <silkscreentext text={n.replace('UART_','').replace('SWDIO','DIO').replace('SWCLK','CLK')} pcbX={-16+i*2.5} pcbY={-8.4} fontSize="0.5mm" />
  </Fragment>)}
  {['USB5V','BAT','SYS','V2V5','V2V7','VS_DREQ','VS_RESET','PGOOD'].map((n,i)=><Fragment key={n}>
    <testpoint name={`TP${i+9}`} pcbX={-20+i*4} pcbY={-20} doNotPlace padDiameter="1.2mm" footprintVariant="pad" schMarginX={0.8} schMarginY={0.8} schSectionName="Debug" schSheetName="controller" connections={{pin1:N(n)}} />
    <silkscreentext text={n.replace('VS_','')} pcbX={-20+i*4} pcbY={-21.4} fontSize="0.55mm" />
  </Fragment>)}
  {/* Bottom copper exclusions force vias clear of these top-side pad edges. */}
  <keepout shape="rect" layer="bottom" pcbX={-13} pcbY={10.25} width="6.3mm" height="2.15mm" />
  <keepout shape="rect" layer="bottom" pcbX={12} pcbY={16.25} width="6.3mm" height="2.15mm" />
  <keepout shape="rect" layer="bottom" pcbX={-17.25} pcbY={3.75} width="1.85mm" height="0.5mm" />
  <keepout shape="rect" layer="bottom" pcbX={-13.15} pcbY={-0.7} width="1.4mm" height="1.8mm" />
  <keepout shape="rect" layer="bottom" pcbX={16.25} pcbY={12} width="1.75mm" height="6.1mm" />
  <keepout shape="rect" layer="bottom" pcbX={-8.75} pcbY={6} width="1.85mm" height="6.1mm" />
  <keepout shape="rect" layer="bottom" pcbX={-6.825} pcbY={9} width="1mm" height="1.2mm" />
  {/* Oscillator loops are hand-routed; normal nets use the autorouter. */}
  <trace from=".U5 > .pin5" to=".Y1 > .pin1" pcbPath={[".U5 > .pin5",{x:-0.75,y:-5.4},{x:-1.85,y:-6.5},".Y1 > .pin1"]} />
  <trace from=".U5 > .pin6" to=".Y1 > .pin3" pcbPath={[".U5 > .pin6",{x:-0.25,y:-5.3},{x:1.1,y:-5.3},{x:1.1,y:-8.9},".Y1 > .pin3"]} />
  <trace from=".Y1 > .pin1" to=".C18 > .pin1" pcbPath={[".Y1 > .pin1",{x:-1.3,y:-2.3},".C18 > .pin1"]} />
  <trace from=".Y1 > .pin3" to=".C19 > .pin1" pcbPath={[".Y1 > .pin3",".C19 > .pin1"]} />
  <trace from=".U6 > .pin18" to=".Y2 > .pin1" pcbPath={[".U6 > .pin18",{x:5.4,y:-0.25},{x:5.8,y:0.15},{x:5.8,y:1.4},{x:9.3,y:1.4},".Y2 > .pin1"]} />
  <trace from=".U6 > .pin17" to=".Y2 > .pin3" pcbPath={[".U6 > .pin17",{x:5.4,y:-0.75},{x:6.025,y:-1.375},".Y2 > .pin3"]} />
  <trace from=".Y2 > .pin1" to=".C31 > .pin1" pcbPath={[".Y2 > .pin1",".C31 > .pin1"]} />
  <trace from=".Y2 > .pin3" to=".C32 > .pin1" pcbPath={[".Y2 > .pin3",".C32 > .pin1"]} />
  <via name="VG1" pcbX={-22.5} pcbY={-6} holeDiameter="0.3mm" outerDiameter="0.6mm" fromLayer="top" toLayer="bottom" connectsTo="net.GND" />
  <via name="VG2" pcbX={-18} pcbY={-6.5} holeDiameter="0.3mm" outerDiameter="0.6mm" fromLayer="top" toLayer="bottom" connectsTo="net.GND" />
  <hole name="H1" pcbX={-8} pcbY={27} diameter="2.2mm" />
  <hole name="H2" pcbX={16} pcbY={27} diameter="2.2mm" />
  <hole name="H3" pcbX={37} pcbY={-26} diameter="2.2mm" />
  <fiducial name="FID1" pcbX={-38} pcbY={28} padDiameter="1mm" soldermaskPullback="0.5mm" />
  <fiducial name="FID2" pcbX={37} pcbY={-22} padDiameter="1mm" soldermaskPullback="0.5mm" />
  <silkscreentext text="MINI MP3 / REV B" pcbX={28} pcbY={27} fontSize="1mm" />
  <PlayerAssembly exploded={exploded} />
 </board>
}