0hmX/robocraze-ov7670-camera-module-clone
This code defines and generates a 4-layer PCB hardware layout featuring an OV7670 VGA camera sensor module with fixed component placement, power regulation using ME6211 voltage regulators, a 2x9 header interface, and associated signal routing and mechanical footprints, designed for manufacturing and assembly.
- Version
- 1.0.7
- License
- MIT
- Stars
- 0
boards/RobocrazeOV7670CameraModuleBoard.tsx
import { Fragment } from "react"
import {
AutoroutingPipelineSolver,
type SimpleRouteJson as CapacitySimpleRouteJson,
} from "@tscircuit/capacity-autorouter"
import type {
AutorouterCompleteEvent,
AutorouterErrorEvent,
AutorouterProgressEvent,
GenericLocalAutorouter,
SimpleRouteJson,
SimplifiedPcbTrace,
} from "tscircuit"
import { OV7670ModuleHeader } from "../components/OV7670ModuleHeader"
import { OV7670Sensor } from "../components/OV7670Sensor"
import { ME6211C18M5G_N } from "../imports/ME6211C18M5G_N"
import { ME6211C28M5G_N } from "../imports/ME6211C28M5G_N"
const INPUT_POWER = "input_and_2v8_power"
const CORE_POWER = "core_1v8_power"
const SENSOR = "ov7670_sensor"
const ANALOG_RAILS = "sensor_analog_and_reference"
const IO_RAIL = "sensor_io_power"
const INTERFACE = "camera_interface"
const DNP = "dnp"
const parts = {
c100n: { jlcpcb: ["C14663"], mpn: "CC0603KRX7R9BB104" },
c1u: { jlcpcb: ["C15849"], mpn: "CL10A105KB8NNNC" },
c4u7: { jlcpcb: ["C19666"], mpn: "CL10A475KO8NNNC" },
c10u: { jlcpcb: ["C19702"], mpn: "CL10A106KP8NNNC" },
r10: { jlcpcb: ["C22859"], mpn: "0603WAF100JT5E" },
r0: { jlcpcb: ["C21189"], mpn: "0603WAF0000T5E" },
r1k: { jlcpcb: ["C21190"], mpn: "0603WAF1001T5E" },
r10k: { jlcpcb: ["C2906982"], mpn: "FRC0603F1002TS" },
ledRed: { jlcpcb: ["C2286"], mpn: "KT-0603R" },
}
const headerPairs = [
["3V3", "GND"],
["SIOC", "SIOD"],
["VSYNC", "HREF"],
["PCLK", "XCLK"],
["D7", "D6"],
["D5", "D4"],
["D3", "D2"],
["D1", "D0"],
["RESET#", "PWDN"],
] as const
const directSignals = [
"SIO_C",
"VSYNC",
"HREF",
"PCLK",
"XCLK",
"D7",
"D6",
"D5",
"D4",
"D3",
"D2",
"D1",
"D0",
"PWDN",
] as const
const schematicSignalLabels: Record<(typeof directSignals)[number], string> = {
SIO_C: "SCCB_SCL",
VSYNC: "VSYNC",
HREF: "HREF",
PCLK: "PCLK",
XCLK: "XCLK",
D7: "D7",
D6: "D6",
D5: "D5",
D4: "D4",
D3: "D3",
D2: "D2",
D1: "D1",
D0: "D0",
PWDN: "PWDN",
}
/**
* Reserve the demonstrated 0.10 mm CSP escapes before whole-board routing.
* XCLK and VREF1 hand off to the router after a through-via. HREF uses a fixed
* 0.15 mm bottom route after its 0.10 mm ball-field escape because the automatic
* HREF route otherwise misses the neighboring DOVDD clearance by 0.006 mm.
*/
const prepareReservedCspRoutes = (simpleRouteJson: SimpleRouteJson) => {
const input = structuredClone(
simpleRouteJson,
) as unknown as CapacitySimpleRouteJson
const xclkConnection = input.connections.find((connection) =>
connection.pointsToConnect.some(
(point) => Math.hypot(point.x - 3, point.y + 0.93) < 0.2,
),
)
const sensorPoint = xclkConnection?.pointsToConnect.find(
(point) => Math.hypot(point.x - 3, point.y + 0.93) < 0.2,
)
if (!xclkConnection || !sensorPoint) {
return {
input,
preexistingTraces: [] as SimplifiedPcbTrace[],
}
}
const breakoutPointId = "XCLK_CSP_BREAKOUT_BOTTOM"
const originalPortId = sensorPoint.pcb_port_id
xclkConnection.pointsToConnect = xclkConnection.pointsToConnect.map((point) =>
point === sensorPoint
? {
x: 6,
y: -2.5,
layer: "bottom",
pointId: breakoutPointId,
}
: point,
)
const xclkDogbone = {
type: "pcb_trace",
pcb_trace_id: "XCLK_CSP_DOGBONE",
connection_name: xclkConnection.name,
connectsTo: [originalPortId, breakoutPointId].filter(
(value): value is string => Boolean(value),
),
route: [
{
route_type: "wire",
x: 3,
y: -0.93,
width: 0.1,
layer: "top",
start_pcb_port_id: originalPortId,
},
{ route_type: "wire", x: 3, y: -0.31, width: 0.1, layer: "top" },
{ route_type: "wire", x: 6, y: -0.31, width: 0.1, layer: "top" },
{ route_type: "wire", x: 6, y: -2.5, width: 0.1, layer: "top" },
{
route_type: "via",
x: 6,
y: -2.5,
from_layer: "top",
to_layer: "bottom",
via_diameter: 0.45,
via_hole_diameter: 0.2,
},
{ route_type: "wire", x: 6, y: -2.5, width: 0.1, layer: "bottom" },
],
} as unknown as SimplifiedPcbTrace
const vref1Connection = input.connections.find((connection) =>
connection.pointsToConnect.some(
(point) => Math.hypot(point.x - 2.38, point.y - 0.31) < 0.2,
),
)
const vref1SensorPoint = vref1Connection?.pointsToConnect.find(
(point) => Math.hypot(point.x - 2.38, point.y - 0.31) < 0.2,
)
const reservedTraces = [xclkDogbone]
if (vref1Connection && vref1SensorPoint) {
const vref1BreakoutPointId = "VREF1_CSP_BREAKOUT_BOTTOM"
const vref1PortId = vref1SensorPoint.pcb_port_id
vref1Connection.pointsToConnect = vref1Connection.pointsToConnect.map(
(point) =>
point === vref1SensorPoint
? {
x: 5.2,
y: 0.5,
layer: "bottom",
pointId: vref1BreakoutPointId,
}
: point,
)
reservedTraces.push({
type: "pcb_trace",
pcb_trace_id: "VREF1_CSP_DOGBONE",
connection_name: vref1Connection.name,
connectsTo: [vref1PortId, vref1BreakoutPointId].filter(
(value): value is string => Boolean(value),
),
route: [
{
route_type: "wire",
x: 2.38,
y: 0.31,
width: 0.1,
layer: "top",
start_pcb_port_id: vref1PortId,
},
{ route_type: "wire", x: 2.69, y: 0, width: 0.1, layer: "top" },
{ route_type: "wire", x: 5.2, y: 0, width: 0.1, layer: "top" },
{ route_type: "wire", x: 5.2, y: 0.5, width: 0.1, layer: "top" },
{
route_type: "via",
x: 5.2,
y: 0.5,
from_layer: "top",
to_layer: "bottom",
via_diameter: 0.45,
via_hole_diameter: 0.2,
},
{ route_type: "wire", x: 5.2, y: 0.5, width: 0.1, layer: "bottom" },
],
} as unknown as SimplifiedPcbTrace)
}
const hrefConnection = input.connections.find((connection) =>
connection.pointsToConnect.some(
(point) => Math.hypot(point.x - 2.38, point.y + 0.31) < 0.2,
),
)
const hrefSensorPoint = hrefConnection?.pointsToConnect.find(
(point) => Math.hypot(point.x - 2.38, point.y + 0.31) < 0.2,
)
const hrefHeaderPoint = hrefConnection?.pointsToConnect.find(
(point) => point !== hrefSensorPoint,
)
if (hrefConnection && hrefSensorPoint && hrefHeaderPoint) {
const hrefPortId = hrefSensorPoint.pcb_port_id
const hrefHeaderPortId = hrefHeaderPoint.pcb_port_id
input.connections = input.connections.filter(
(connection) => connection !== hrefConnection,
)
reservedTraces.push({
type: "pcb_trace",
pcb_trace_id: "HREF_CSP_ESCAPE_AND_HEADER_ROUTE",
connection_name: hrefConnection.name,
connectsTo: [hrefPortId, hrefHeaderPortId].filter(
(value): value is string => Boolean(value),
),
route: [
{
route_type: "wire",
x: 2.38,
y: -0.31,
width: 0.1,
layer: "top",
start_pcb_port_id: hrefPortId,
},
{ route_type: "wire", x: 2.07, y: -0.62, width: 0.1, layer: "top" },
{ route_type: "wire", x: 0.5, y: -0.62, width: 0.1, layer: "top" },
{
route_type: "via",
x: 0.5,
y: -0.62,
from_layer: "top",
to_layer: "bottom",
via_diameter: 0.45,
via_hole_diameter: 0.2,
},
{ route_type: "wire", x: 0.5, y: -0.62, width: 0.15, layer: "bottom" },
{ route_type: "wire", x: 0.5, y: -8.5, width: 0.15, layer: "bottom" },
{ route_type: "wire", x: -9.5, y: -8.5, width: 0.15, layer: "bottom" },
{
route_type: "wire",
x: -9.5,
y: hrefHeaderPoint.y,
width: 0.15,
layer: "bottom",
},
{
route_type: "wire",
x: hrefHeaderPoint.x,
y: hrefHeaderPoint.y,
width: 0.15,
layer: "bottom",
end_pcb_port_id: hrefHeaderPortId,
},
],
} as unknown as SimplifiedPcbTrace)
}
input.traces = [
...(input.traces ?? []),
...(reservedTraces as unknown as NonNullable<
CapacitySimpleRouteJson["traces"]
>),
]
return { input, preexistingTraces: reservedTraces }
}
class ThroughViaOnlyAutorouter implements GenericLocalAutorouter {
input: SimpleRouteJson
isRouting = false
private solver: AutoroutingPipelineSolver
private preexistingTraces: SimplifiedPcbTrace[]
private timeoutId?: ReturnType<typeof setTimeout>
private eventHandlers = {
complete: [] as Array<(event: AutorouterCompleteEvent) => void>,
error: [] as Array<(event: AutorouterErrorEvent) => void>,
progress: [] as Array<(event: AutorouterProgressEvent) => void>,
}
constructor(simpleRouteJson: SimpleRouteJson) {
const prepared = prepareReservedCspRoutes(simpleRouteJson)
this.preexistingTraces = prepared.preexistingTraces
this.input = {
...prepared.input,
// Preserve inner1/inner2 as uninterrupted power/ground plane layers.
// The automatic router may use top and bottom and ordinary through-vias.
layerCount: 2,
allowViaInPad: false,
// Leave manufacturing margin around CSP lands. The core checker requires
// 0.10 mm; asking the router for 0.15 mm prevents borderline rounded
// geometry from being emitted below that limit.
minViaEdgeToPadEdgeClearance: 0.15,
} as unknown as SimpleRouteJson
this.solver = new AutoroutingPipelineSolver(this.input as unknown as CapacitySimpleRouteJson)
}
start() {
if (this.isRouting) return
this.isRouting = true
this.runCycle()
}
stop() {
this.isRouting = false
if (this.timeoutId !== undefined) clearTimeout(this.timeoutId)
}
on(event: "complete", callback: (event: AutorouterCompleteEvent) => void): void
on(event: "error", callback: (event: AutorouterErrorEvent) => void): void
on(event: "progress", callback: (event: AutorouterProgressEvent) => void): void
on(
event: "complete" | "error" | "progress",
callback:
| ((event: AutorouterCompleteEvent) => void)
| ((event: AutorouterErrorEvent) => void)
| ((event: AutorouterProgressEvent) => void),
) {
;(this.eventHandlers[event] as Array<typeof callback>).push(callback)
}
solveSync(): SimplifiedPcbTrace[] {
this.solver.solve()
if (this.solver.failed) {
throw new Error(this.solver.error ?? "Through-via-only autorouting failed")
}
return [
...this.preexistingTraces,
...((this.solver.getOutputSimpleRouteJson().traces ?? []) as unknown as SimplifiedPcbTrace[]),
]
}
getOutputSimpleRouteJson() {
const output = this.solver.getOutputSimpleRouteJson()
return {
...output,
traces: [
...this.preexistingTraces,
...((output.traces ?? []) as unknown as SimplifiedPcbTrace[]),
],
} as unknown as SimpleRouteJson
}
private runCycle = () => {
if (!this.isRouting) return
try {
const startedAt = Date.now()
const startingIterations = this.solver.iterations
while (
Date.now() - startedAt < 200 &&
!this.solver.solved &&
!this.solver.failed
) {
this.solver.step()
}
if (this.solver.failed) {
this.isRouting = false
const error = new Error(
this.solver.error ?? "Through-via-only autorouting failed",
)
for (const handler of this.eventHandlers.error) {
handler({ type: "error", error })
}
return
}
if (this.solver.solved) {
this.isRouting = false
const traces = [
...this.preexistingTraces,
...((this.solver.getOutputSimpleRouteJson().traces ??
[]) as unknown as SimplifiedPcbTrace[]),
]
for (const handler of this.eventHandlers.complete) {
handler({ type: "complete", traces })
}
return
}
const elapsedMs = Math.max(1, Date.now() - startedAt)
const iterationsPerSecond =
((this.solver.iterations - startingIterations) / elapsedMs) * 1000
for (const handler of this.eventHandlers.progress) {
handler({
type: "progress",
steps: this.solver.iterations,
progress: this.solver.progress,
phase: this.solver.getCurrentPhase(),
iterationsPerSecond,
})
}
this.timeoutId = setTimeout(this.runCycle, 0)
} catch (caught) {
this.isRouting = false
const error = caught instanceof Error ? caught : new Error(String(caught))
for (const handler of this.eventHandlers.error) {
handler({ type: "error", error })
}
}
}
}
const createThroughViaOnlyAutorouter = async (simpleRouteJson: SimpleRouteJson) =>
new ThroughViaOnlyAutorouter(simpleRouteJson)
function NamedTrace({
from,
to,
label,
routingPhaseIndex,
maxLength,
width,
}: {
from: string
to: string
label?: string
routingPhaseIndex?: number
maxLength?: string
width?: string
}) {
const clean = (value: string) => value.replace(/[^a-zA-Z0-9]+/g, "_")
const connectedNet = [from, to].find((value) => value.startsWith("net."))
const schematicLabel = label ?? connectedNet?.slice(4)
// Perimeter placement intentionally makes several reference and decoupling
// connections longer than the JSX default. Physical quality is validated
// after autorouting instead of using a pre-route straight-line veto.
const effectiveMaxLength = maxLength ?? "100mm"
return (
<trace
name={`${clean(from)}_TO_${clean(to)}`}
from={from}
to={to}
schDisplayLabel={schematicLabel}
routingPhaseIndex={routingPhaseIndex}
maxLength={effectiveMaxLength}
width={width}
/>
)
}
/**
* Standalone FIFO-less OV7670 module matching the B0FDRCHSFW interface.
* Component placement is fixed; PCB traces are generated by the autorouter.
*/
export function RobocrazeOV7670CameraModuleBoard() {
return (
<board
name="ROBOCRAZE_OV7670_CAMERA_MODULE"
title="Electrobot OV7670 VGA Camera Module Interface Clone"
width="35.16mm"
height="34.29mm"
layers={4}
thickness="1.6mm"
borderRadius="0.5mm"
solderMaskColor="blue"
silkscreenColor="white"
doubleSidedAssembly
autorouter={{ local: true, algorithmFn: createThroughViaOnlyAutorouter }}
schTraceAutoLabelEnabled
defaultTraceWidth="0.15mm"
nominalTraceWidth="0.15mm"
minTraceWidth="0.1mm"
minTraceToPadEdgeClearance="0.1mm"
minViaEdgeToPadEdgeClearance="0.1mm"
minViaHoleEdgeToViaHoleEdgeClearance="0.2mm"
minViaHoleDiameter="0.2mm"
minViaPadDiameter="0.45mm"
minBoardEdgeClearance="0.3mm"
>
<schematicsheet
name="main"
displayName="OV7670 standalone camera module"
sheetIndex={0}
>
<schematicsection
name={INPUT_POWER}
displayName="POWER INPUT + 2.8 V ANALOG / I/O SUPPLY"
/>
<schematicsection
name={CORE_POWER}
displayName="1.8 V SENSOR CORE SUPPLY"
/>
<schematicsection name={SENSOR} displayName="OV7670 IMAGE SENSOR" />
<schematicsection
name={ANALOG_RAILS}
displayName="AVDD + ANALOG REFERENCE FILTERING"
/>
<schematicsection
name={IO_RAIL}
displayName="DOVDD 2.8 V SENSOR I/O FILTERING"
/>
<schematicsection
name={INTERFACE}
displayName="CAMERA HEADER / DVP / SCCB CONTROL"
/>
<schematicsection
name={DNP}
displayName="OPTIONAL POWER INDICATOR — DO NOT POPULATE"
/>
</schematicsheet>
<group name="MAIN_SHEET_CONTENT" schSheetName="main">
{[
"V3V3",
"V2V8",
"AVDD",
"DOVDD",
"AGND",
"GND",
"RESET_N",
"VREF1",
"VREF2",
"DNP_LED_A",
].map((name) => (
<Fragment key={name}>
<net name={name} />
</Fragment>
))}
<copperpour
name="V2V8_POUR_INNER1"
layer="inner1"
connectsTo="net.V2V8"
clearance="0.3mm"
padMargin="0.3mm"
traceMargin="0.3mm"
boardEdgeMargin="0.3mm"
cutoutMargin="0.3mm"
useThermalReliefs
coveredWithSolderMask
/>
<copperpour
name="GND_POUR_INNER2"
layer="inner2"
connectsTo="net.GND"
clearance="0.3mm"
padMargin="0.3mm"
traceMargin="0.3mm"
boardEdgeMargin="0.3mm"
cutoutMargin="0.3mm"
useThermalReliefs
coveredWithSolderMask
/>
<hole name="H1" pcbX={-14.765} pcbY={14.235} diameter="2.5mm" />
<hole name="H2" pcbX={14.765} pcbY={14.235} diameter="2.5mm" />
<hole name="H3" pcbX={-14.765} pcbY={-14.235} diameter="2.5mm" />
<hole name="H4" pcbX={14.765} pcbY={-14.235} diameter="2.5mm" />
<OV7670ModuleHeader
name="J1"
displayName="2x9 CAMERA HEADER"
layer="bottom"
pcbX={-12.5}
pcbY={0}
pcbRotation={-90}
schSectionName={INTERFACE}
schX={11}
schY={2}
schWidth={2.15}
schHeight={2}
schPinArrangement={{
leftSide: {
pins: ["3V3", "SIO_C", "VSYNC", "PCLK", "D7", "D5", "D3", "D1", "RESET"],
direction: "top-to-bottom",
},
rightSide: {
pins: ["GND", "SIO_D", "HREF", "XCLK", "D6", "D4", "D2", "D0", "PWDN"],
direction: "top-to-bottom",
},
}}
/>
<ME6211C28M5G_N
name="U1"
displayName="2.8 V LDO"
layer="bottom"
pcbX={-4}
pcbY={12}
pcbRotation={90}
schSectionName={INPUT_POWER}
schX={-9}
schY={5.5}
schWidth={1.6}
schHeight={0.6}
schPinArrangement={{
leftSide: { pins: ["VIN", "CE"], direction: "top-to-bottom" },
rightSide: { pins: ["VOUT", "VSS"], direction: "top-to-bottom" },
}}
/>
<ME6211C18M5G_N
name="U2"
displayName="1.8 V LDO"
layer="bottom"
pcbX={6.5}
pcbY={-12}
pcbRotation={90}
schSectionName={CORE_POWER}
schX={-9}
schY={-4.5}
schWidth={1.6}
schHeight={0.6}
schPinArrangement={{
leftSide: { pins: ["VIN", "CE"], direction: "top-to-bottom" },
rightSide: { pins: ["VOUT", "VSS"], direction: "top-to-bottom" },
}}
/>
<capacitor
name="C1"
displayName="3V3 input bulk"
capacitance="4.7uF"
footprint="0603"
manufacturerPartNumber={parts.c4u7.mpn}
supplierPartNumbers={{ jlcpcb: parts.c4u7.jlcpcb }}
layer="bottom"
pcbX={-7.5}
pcbY={10.7}
schSectionName={INPUT_POWER}
schX={-12}
schY={2.5}
schOrientation="vertical"
/>
<capacitor
name="C2"
displayName="2V8 output bulk"
capacitance="10uF"
footprint="0603"
manufacturerPartNumber={parts.c10u.mpn}
supplierPartNumbers={{ jlcpcb: parts.c10u.jlcpcb }}
layer="bottom"
pcbX={-0.5}
pcbY={13.3}
pcbRotation={180}
schSectionName={INPUT_POWER}
schX={-8}
schY={2.5}
schOrientation="vertical"
/>
<capacitor
name="C3"
displayName="2V8 output bypass"
capacitance="1uF"
footprint="0603"
manufacturerPartNumber={parts.c1u.mpn}
supplierPartNumbers={{ jlcpcb: parts.c1u.jlcpcb }}
layer="bottom"
pcbX={2.8}
pcbY={13.3}
pcbRotation={180}
schSectionName={INPUT_POWER}
schX={-5.5}
schY={2.5}
schOrientation="vertical"
/>
<capacitor
name="C5"
displayName="1V8 regulator input"
capacitance="10uF"
footprint="0603"
manufacturerPartNumber={parts.c10u.mpn}
supplierPartNumbers={{ jlcpcb: parts.c10u.jlcpcb }}
layer="bottom"
pcbX={10}
pcbY={-13.3}
pcbRotation={180}
schSectionName={CORE_POWER}
schX={-12.5}
schY={-7}
schOrientation="vertical"
/>
<capacitor
name="C6"
displayName="1V8 output bypass"
capacitance="1uF"
footprint="0603"
manufacturerPartNumber={parts.c1u.mpn}
supplierPartNumbers={{ jlcpcb: parts.c1u.jlcpcb }}
layer="bottom"
pcbX={3}
pcbY={-10.7}
schSectionName={CORE_POWER}
schX={-9.3}
schY={-7}
schOrientation="vertical"
/>
<OV7670Sensor
name="U3"
displayName="OV7670 image sensor (CSP2)"
pcbX={3}
pcbY={0}
schSectionName={SENSOR}
schX={1}
schY={1.5}
schWidth={2.245}
schHeight={2.6}
schPinArrangement={{
leftSide: {
pins: ["AVDD", "DOVDD", "DVDD", "AGND", "DOGND", "VREF1", "VREF2", "SIO_C", "SIO_D", "XCLK", "RESET_N", "PWDN"],
direction: "top-to-bottom",
},
rightSide: {
pins: ["PCLK", "VSYNC", "HREF", "D7", "D6", "D5", "D4", "D3", "D2", "D1", "D0", "STROBE"],
direction: "top-to-bottom",
},
}}
noConnect={["STROBE"]}
/>
<resistor
name="R1"
displayName="AVDD 10Ω filter"
resistance="10ohm"
footprint="0603"
manufacturerPartNumber={parts.r10.mpn}
supplierPartNumbers={{ jlcpcb: parts.r10.jlcpcb }}
layer="bottom"
pcbX={9.4}
pcbY={10.7}
pcbRotation={180}
schSectionName={ANALOG_RAILS}
schX={-3}
schY={6.2}
/>
<resistor
name="R2"
displayName="DOVDD 10Ω filter"
resistance="10ohm"
footprint="0603"
manufacturerPartNumber={parts.r10.mpn}
supplierPartNumbers={{ jlcpcb: parts.r10.jlcpcb }}
layer="bottom"
pcbX={-6.9}
pcbY={-10.7}
pcbRotation={180}
schSectionName={IO_RAIL}
schX={-3.5}
schY={-4}
/>
<resistor
name="R3"
displayName="AGND–GND star link"
resistance="0ohm"
footprint="0603"
manufacturerPartNumber={parts.r0.mpn}
supplierPartNumbers={{ jlcpcb: parts.r0.jlcpcb }}
layer="bottom"
pcbX={14.3}
pcbY={0}
pcbRotation={90}
schSectionName={ANALOG_RAILS}
schX={8}
schY={4.2}
/>
<capacitor name="C8" displayName="2V8 rail bulk" capacitance="10uF" footprint="0603" manufacturerPartNumber={parts.c10u.mpn} supplierPartNumbers={{ jlcpcb: parts.c10u.jlcpcb }} layer="bottom" pcbX={6.1} pcbY={10.7} schSectionName={ANALOG_RAILS} schX={-6} schY={6.2} schOrientation="vertical" />
<capacitor name="C9" displayName="DOVDD high-frequency bypass" capacitance="100nF" footprint="0603" manufacturerPartNumber={parts.c100n.mpn} supplierPartNumbers={{ jlcpcb: parts.c100n.jlcpcb }} layer="bottom" pcbX={-3.6} pcbY={-10.7} pcbRotation={180} schSectionName={IO_RAIL} schX={0} schY={-4} schOrientation="vertical" />
<capacitor name="C10" displayName="DVDD bulk" capacitance="4.7uF" footprint="0603" manufacturerPartNumber={parts.c4u7.mpn} supplierPartNumbers={{ jlcpcb: parts.c4u7.jlcpcb }} layer="bottom" pcbX={3} pcbY={-13.3} schSectionName={CORE_POWER} schX={-6.6} schY={-7} schOrientation="vertical" />
<capacitor name="C11" displayName="DVDD rail bypass" capacitance="100nF" footprint="0603" manufacturerPartNumber={parts.c100n.mpn} supplierPartNumbers={{ jlcpcb: parts.c100n.jlcpcb }} layer="bottom" pcbX={-0.3} pcbY={-13.3} schSectionName={CORE_POWER} schX={-3.9} schY={-7} schOrientation="vertical" />
<capacitor name="C12" displayName="AVDD high-frequency bypass" capacitance="100nF" footprint="0603" manufacturerPartNumber={parts.c100n.mpn} supplierPartNumbers={{ jlcpcb: parts.c100n.jlcpcb }} layer="bottom" pcbX={2} pcbY={10.7} schSectionName={ANALOG_RAILS} schX={0} schY={6.2} schOrientation="vertical" />
<capacitor name="C13" displayName="VREF1 bypass" capacitance="100nF" footprint="0603" manufacturerPartNumber={parts.c100n.mpn} supplierPartNumbers={{ jlcpcb: parts.c100n.jlcpcb }} layer="bottom" pcbX={-7.8} pcbY={1.5} pcbRotation={90} schSectionName={ANALOG_RAILS} schX={2} schY={4.2} schOrientation="vertical" />
<capacitor name="C14" displayName="VREF2 bypass" capacitance="100nF" footprint="0603" manufacturerPartNumber={parts.c100n.mpn} supplierPartNumbers={{ jlcpcb: parts.c100n.jlcpcb }} layer="bottom" pcbX={-7.8} pcbY={-1.5} pcbRotation={270} schSectionName={ANALOG_RAILS} schX={5} schY={4.2} schOrientation="vertical" />
<capacitor name="C15" displayName="DOVDD local bypass" capacitance="100nF" footprint="0603" manufacturerPartNumber={parts.c100n.mpn} supplierPartNumbers={{ jlcpcb: parts.c100n.jlcpcb }} layer="bottom" pcbX={10} pcbY={-10.7} pcbRotation={270} schSectionName={IO_RAIL} schX={6} schY={-4} schOrientation="vertical" />
<capacitor name="C16" displayName="DVDD sensor bypass" capacitance="100nF" footprint="0603" manufacturerPartNumber={parts.c100n.mpn} supplierPartNumbers={{ jlcpcb: parts.c100n.jlcpcb }} layer="bottom" pcbX={-0.3} pcbY={-10.7} schSectionName={CORE_POWER} schX={-1.2} schY={-7} schOrientation="vertical" />
<capacitor name="C17" displayName="AVDD bulk" capacitance="4.7uF" footprint="0603" manufacturerPartNumber={parts.c4u7.mpn} supplierPartNumbers={{ jlcpcb: parts.c4u7.jlcpcb }} layer="bottom" pcbX={12.7} pcbY={10.7} schSectionName={ANALOG_RAILS} schX={3} schY={6.2} schOrientation="vertical" />
<capacitor name="C18" displayName="DOVDD bulk" capacitance="4.7uF" footprint="0603" manufacturerPartNumber={parts.c4u7.mpn} supplierPartNumbers={{ jlcpcb: parts.c4u7.jlcpcb }} layer="bottom" pcbX={-6.9} pcbY={-13.3} schSectionName={IO_RAIL} schX={3} schY={-4} schOrientation="vertical" />
<resistor
name="R4"
displayName="SCCB SDA series protection"
resistance="1kohm"
footprint="0603"
manufacturerPartNumber={parts.r1k.mpn}
supplierPartNumbers={{ jlcpcb: parts.r1k.jlcpcb }}
layer="bottom"
pcbX={-10.2}
pcbY={-13.3}
schSectionName={INTERFACE}
schX={7}
schY={-2.5}
/>
<resistor
name="R5"
displayName="RESET# pull-up"
resistance="10kohm"
footprint="0603"
manufacturerPartNumber={parts.r10k.mpn}
supplierPartNumbers={{ jlcpcb: parts.r10k.jlcpcb }}
layer="bottom"
pcbX={14.3}
pcbY={-6.6}
pcbRotation={270}
schSectionName={INTERFACE}
schX={8.5}
schY={-4}
/>
<resistor
name="R0"
displayName="Power LED resistor (DNP)"
resistance="1kohm"
footprint="0603"
manufacturerPartNumber={parts.r1k.mpn}
supplierPartNumbers={{ jlcpcb: parts.r1k.jlcpcb }}
doNotPlace
pcbX={-9.6}
pcbY={14}
schSectionName={DNP}
schX={8}
schY={-7}
/>
<led
name="D0_IND"
displayName="Optional power LED (DNP)"
color="red"
footprint="0603"
manufacturerPartNumber={parts.ledRed.mpn}
supplierPartNumbers={{ jlcpcb: parts.ledRed.jlcpcb }}
doNotPlace
pcbX={-6.5}
pcbY={14}
schSectionName={DNP}
schX={12.5}
schY={-7}
/>
<NamedTrace from="J1.3V3" to="net.V3V3" width="0.3mm" />
<NamedTrace from="J1.GND" to="net.GND" width="0.3mm" />
{directSignals.map((signal) => (
<Fragment key={`header-${signal}`}>
<NamedTrace
from={`J1.${signal}`}
to={`U3.${signal}`}
label={schematicSignalLabels[signal]}
/>
</Fragment>
))}
<NamedTrace from="J1.SIO_D" to="R4.pin1" label="SCCB_SDA_HOST" />
<NamedTrace from="R4.pin2" to="U3.SIO_D" label="SCCB_SDA" />
<NamedTrace from="J1.RESET" to="U3.RESET_N" label="RESET_N" />
<NamedTrace from="U3.RESET_N" to="R5.pin1" label="RESET_N" />
<NamedTrace from="R5.pin2" to="C15.pin1" label="DOVDD_2V8" width="0.2mm" />
<NamedTrace from="U1.VIN" to="net.V3V3" width="0.3mm" />
<NamedTrace from="U1.CE" to="net.V3V3" width="0.3mm" />
<NamedTrace from="U1.VSS" to="net.GND" width="0.3mm" />
<NamedTrace from="U1.VOUT" to="C2.pin1" label="V2V8" width="0.25mm" />
<NamedTrace from="U1.VOUT" to="net.V2V8" width="0.25mm" />
<NamedTrace from="C1.pin1" to="net.V3V3" width="0.3mm" />
<NamedTrace from="C1.pin2" to="net.GND" width="0.3mm" />
<NamedTrace from="C2.pin2" to="net.GND" width="0.25mm" />
<NamedTrace from="C3.pin1" to="net.V2V8" width="0.25mm" />
<NamedTrace from="C3.pin2" to="net.GND" width="0.25mm" />
<NamedTrace from="U2.VIN" to="C5.pin1" label="V2V8" width="0.25mm" />
<NamedTrace from="U2.VIN" to="net.V2V8" width="0.25mm" />
<NamedTrace from="U2.CE" to="net.V2V8" width="0.25mm" />
<NamedTrace from="U2.VSS" to="net.GND" width="0.25mm" />
<NamedTrace from="U2.VOUT" to="C6.pin1" label="DVDD_1V8" width="0.25mm" />
<NamedTrace from="C5.pin2" to="net.GND" width="0.25mm" />
<NamedTrace from="C6.pin2" to="net.GND" width="0.25mm" />
<NamedTrace from="R1.pin1" to="C8.pin1" label="V2V8" width="0.25mm" />
<NamedTrace from="R1.pin1" to="net.V2V8" width="0.25mm" />
<NamedTrace from="R1.pin2" to="C12.pin1" label="AVDD" width="0.2mm" />
<NamedTrace from="R1.pin2" to="C17.pin1" label="AVDD" width="0.2mm" />
<NamedTrace from="C12.pin1" to="U3.AVDD" label="AVDD" width="0.2mm" />
<NamedTrace from="R2.pin1" to="net.V2V8" width="0.25mm" />
<NamedTrace from="R2.pin2" to="C9.pin1" label="DOVDD_2V8" width="0.2mm" />
<NamedTrace from="R2.pin2" to="C18.pin1" label="DOVDD_2V8" width="0.2mm" />
<NamedTrace from="C9.pin1" to="U3.DOVDD" label="DOVDD_2V8" width="0.2mm" />
<NamedTrace from="C6.pin1" to="C10.pin1" label="DVDD_1V8" width="0.25mm" />
<NamedTrace from="C10.pin1" to="C11.pin1" label="DVDD_1V8" width="0.2mm" />
<NamedTrace from="C11.pin1" to="C16.pin1" label="DVDD_1V8" width="0.2mm" />
<NamedTrace from="C16.pin1" to="U3.DVDD" label="DVDD_1V8" width="0.2mm" />
<NamedTrace from="U3.AGND" to="C13.pin2" label="AGND" width="0.2mm" />
<NamedTrace from="C13.pin2" to="C14.pin2" label="AGND" width="0.2mm" />
<NamedTrace from="C14.pin2" to="R3.pin1" label="AGND" width="0.2mm" />
<NamedTrace from="C13.pin2" to="C12.pin2" label="AGND" width="0.2mm" />
<NamedTrace from="C12.pin2" to="C17.pin2" label="AGND" width="0.2mm" />
<NamedTrace from="R3.pin2" to="net.GND" width="0.25mm" />
<NamedTrace from="U3.DOGND" to="net.GND" width="0.2mm" />
<NamedTrace from="U3.VREF1" to="C13.pin1" label="VREF1" />
<NamedTrace from="U3.VREF2" to="C14.pin1" label="VREF2" />
<NamedTrace from="C8.pin2" to="net.GND" width="0.25mm" />
<NamedTrace from="C9.pin1" to="C15.pin1" label="DOVDD_2V8" width="0.2mm" />
<NamedTrace from="C9.pin2" to="net.GND" width="0.2mm" />
<NamedTrace from="C10.pin2" to="net.GND" width="0.2mm" />
<NamedTrace from="C11.pin2" to="net.GND" width="0.2mm" />
<NamedTrace from="C15.pin2" to="net.GND" width="0.2mm" />
<NamedTrace from="C16.pin2" to="net.GND" width="0.2mm" />
<NamedTrace from="C18.pin2" to="net.GND" width="0.2mm" />
<NamedTrace from="R0.pin1" to="net.V3V3" />
<NamedTrace from="R0.pin2" to="net.DNP_LED_A" />
<NamedTrace from="D0_IND.anode" to="net.DNP_LED_A" />
<NamedTrace from="D0_IND.cathode" to="net.GND" />
{headerPairs.map(([left, right], pair) => {
const pcbY = 10.16 - pair * 2.54
return (
<Fragment key={`${left}-${right}`}>
<silkscreentext text={left} pcbX={-16.15} pcbY={pcbY} fontSize="0.42mm" anchorAlignment="center" />
<silkscreentext text={right} pcbX={-8.85} pcbY={pcbY} fontSize="0.42mm" anchorAlignment="center" />
</Fragment>
)
})}
<silkscreentext text="OV7670" pcbX={4} pcbY={15.4} fontSize="1mm" anchorAlignment="center" />
<silkscreentext text="3V3 PWR / 2V8 IO" pcbX={4} pcbY={-15.2} fontSize="1mm" anchorAlignment="center" />
<silkscreentext text="PIN 1" pcbX={-16.1} pcbY={11.8} fontSize="0.65mm" anchorAlignment="center" />
</group>
</board>
)
}