"""
NRF52840-QIAA-R
"""
from itertools import chain
from jitx import Landpattern
from jitx.anchor import Anchor
from jitx.circuit import Circuit, SchematicGroup
from jitx.common import ADC, GPIO, Power, Timer
from jitx.component import Component
from jitx.container import inline
from jitx.feature import Courtyard, Custom, Paste, Silkscreen, Soldermask
from jitx.landpattern import Pad, PadMapping
from jitx.net import Net, Port, ShortTrace, provide
from jitx.shapes.composites import rectangle
from jitx.shapes.primitive import Arc, ArcPolyline, Circle, Polygon, Polyline, Text
from jitx.units import A, F, H, Hz
from jitxlib.parts import AtLeast, Capacitor, Inductor
from jitxlib.protocols.serial import I2C, SPI, SWD, UART
from jitxlib.protocols.usb import USB2Connector
from jitxlib.symbols.box import BoxSymbol, Column, PinGroup, Row
from jitxexamples.components.crystals.seiko_epson_TSX_3225 import (
TSX_3225_32_0000MF10Z_W6,
)
# TODO: Not sure about these dimensions
[docs]
class SMDPad(Pad):
shape = rectangle(4.85, 4.85)
soldermask = Soldermask(rectangle(4.952, 4.952))
paste = Paste(rectangle(4.952, 4.952))
[docs]
class nRFBGAPad(Pad):
shape = Circle(diameter=0.275)
soldermask = Soldermask(Circle(diameter=0.375))
paste = Paste(Circle(diameter=0.3))
[docs]
class Reset(Port):
reset = Port()
[docs]
class LFOscillator(Port):
lo_in = Port()
lo_out = Port()
[docs]
class NRF52840_QIAA_R(Circuit):
"""NRF52840 reference module.
``lfo`` includes a 32.768 kHz resonator, and ``antenna`` includes an antenna
and matching circuit. ``power_config`` selects reference circuitry option 5
or 6 from the Nordic Semiconductor product specification.
"""
vin = Power()
@inline
class mcu(Component):
"""AQFN-73-EP(7x7) Advanced Bluetooth 5, Thread and Zigbee multiprotocol SoC"""
name = "XCVR_NRF52840-QIAA-R"
mpn = "NRF52840-QIAA-R"
manufacturer = "Nordic Semiconductor"
datasheet = "https://datasheet.lcsc.com/lcsc/2304140030_Nordic-Semicon-NRF52840-QIAA-R_C190794.pdf"
P0 = [Port() for _ in range(32)]
P1 = [Port() for _ in range(16)]
Dn = Port()
Dp = Port()
SWDCLK = Port()
SWDIO = Port()
XC1 = Port()
XC2 = Port()
ANT = Port()
VBUS = Port()
VDD = [Port() for _ in range(5)]
VDDH = Port()
VSS = Port()
VSS_PA = Port()
DCC = Port()
DCCH = Port()
DEC1 = Port()
DEC2 = Port()
DEC3 = Port()
DEC4 = Port()
DEC5 = Port()
DEC6 = Port()
DECUSB = Port()
@inline
class landpattern(Landpattern):
A10 = nRFBGAPad().at(-3.25, -0.75)
A12 = nRFBGAPad().at(-3.25, -0.25)
A14 = nRFBGAPad().at(-3.25, 0.25)
A16 = nRFBGAPad().at(-3.25, 0.75)
A18 = nRFBGAPad().at(-3.25, 1.25)
A20 = nRFBGAPad().at(-3.25, 1.75)
A22 = nRFBGAPad().at(-3.25, 2.25)
A23 = nRFBGAPad().at(-3.25, 2.75)
A8 = nRFBGAPad().at(-3.25, -1.25)
AA24 = nRFBGAPad().at(2.25, 3.25)
AB2 = nRFBGAPad().at(2.5, -2.75)
AC11 = nRFBGAPad().at(2.75, -0.5)
AC13 = nRFBGAPad().at(2.75, 0)
AC15 = nRFBGAPad().at(2.75, 0.5)
AC17 = nRFBGAPad().at(2.75, 1)
AC19 = nRFBGAPad().at(2.75, 1.5)
AC21 = nRFBGAPad().at(2.75, 2)
AC24 = nRFBGAPad().at(2.75, 3.25)
AC5 = nRFBGAPad().at(2.75, -2)
AC9 = nRFBGAPad().at(2.75, -1)
AD10 = nRFBGAPad().at(3.25, -0.75)
AD12 = nRFBGAPad().at(3.25, -0.25)
AD14 = nRFBGAPad().at(3.25, 0.25)
AD16 = nRFBGAPad().at(3.25, 0.75)
AD18 = nRFBGAPad().at(3.25, 1.25)
AD20 = nRFBGAPad().at(3.25, 1.75)
AD22 = nRFBGAPad().at(3.25, 2.25)
AD23 = nRFBGAPad().at(3.25, 2.75)
AD2 = nRFBGAPad().at(3.25, -2.75)
AD4 = nRFBGAPad().at(3.25, -2.25)
AD6 = nRFBGAPad().at(3.25, -1.75)
AD8 = nRFBGAPad().at(3.25, -1.25)
B11 = nRFBGAPad().at(-2.75, -0.5)
B13 = nRFBGAPad().at(-2.75, 0)
B15 = nRFBGAPad().at(-2.75, 0.5)
B17 = nRFBGAPad().at(-2.75, 1)
B19 = nRFBGAPad().at(-2.75, 1.5)
B1 = nRFBGAPad().at(-2.75, -3.25)
B24 = nRFBGAPad().at(-2.75, 3.25)
B3 = nRFBGAPad().at(-2.75, -2.5)
B5 = nRFBGAPad().at(-2.75, -2)
B7 = nRFBGAPad().at(-2.75, -1.5)
B9 = nRFBGAPad().at(-2.75, -1)
C1 = nRFBGAPad().at(-2.25, -3.25)
D23 = nRFBGAPad().at(-2, 2.75)
D2 = nRFBGAPad().at(-2, -2.75)
E24 = nRFBGAPad().at(-1.75, 3.25)
F23 = nRFBGAPad().at(-1.5, 2.75)
F2 = nRFBGAPad().at(-1.5, -2.75)
G1 = nRFBGAPad().at(-1.25, -3.25)
H23 = nRFBGAPad().at(-1, 2.75)
H2 = nRFBGAPad().at(-1, -2.75)
J1 = nRFBGAPad().at(-0.75, -3.25)
J24 = nRFBGAPad().at(-0.75, 3.25)
K2 = nRFBGAPad().at(-0.5, -2.75)
L1 = nRFBGAPad().at(-0.25, -3.25)
L24 = nRFBGAPad().at(-0.25, 3.25)
M2 = nRFBGAPad().at(0, -2.75)
N1 = nRFBGAPad().at(0.25, -3.25)
N24 = nRFBGAPad().at(0.25, 3.25)
P23 = nRFBGAPad().at(0.5, 2.75)
P2 = nRFBGAPad().at(0.5, -2.75)
R1 = nRFBGAPad().at(0.75, -3.25)
R24 = nRFBGAPad().at(0.75, 3.25)
T23 = nRFBGAPad().at(1, 2.75)
T2 = nRFBGAPad().at(1, -2.75)
U1 = nRFBGAPad().at(1.25, -3.25)
U24 = nRFBGAPad().at(1.25, 3.25)
V23 = nRFBGAPad().at(1.5, 2.75)
W1 = nRFBGAPad().at(1.75, -3.25)
W24 = nRFBGAPad().at(1.75, 3.25)
Y23 = nRFBGAPad().at(2, 2.75)
Y2 = nRFBGAPad().at(2, -2.75)
p = {74: SMDPad().at(0, 0)}
labels = [
Silkscreen(Text(">REF", 0.5, Anchor.W).at(-0.75, 5.332)),
Custom(Text(">VALUE", 0.5, Anchor.W).at(-0.75, 4.332), name="Fab"),
]
silkscreen = [
Silkscreen(shape)
for shape in (
Polyline(0.152, [(-3.5, -3.05), (-3, -3.55), (-2.991, -3.55)]),
Polyline(
0.152,
[
(3.55, -3.55),
(3.55, 3.55),
(-3.55, 3.55),
(-3.55, -3.55),
(3.55, -3.55),
],
),
ArcPolyline(0.2, [Arc((-3.35, -3.85), 0.1, 0, -360)]),
Polygon(
[
(-3.55, -2.8),
(-3.55, -3.55),
(-2.8, -3.55),
(-3.55, -2.8),
]
),
Polygon(
[
(-3.55, -3.55),
(-3.55, -3),
(-3, -3.55),
(-3.55, -3.55),
(-3.55, -3.55),
]
),
)
]
fab_drawing = [
Custom(shape, name="Fab")
for shape in (
ArcPolyline(0.06, [Arc((-3.5, -3.5), 0.03, 0, -360)]),
ArcPolyline(0.2, [Arc((-3.35, -3.85), 0.1, 0, -360)]),
)
]
courtyard = Courtyard(rectangle(7.252, 7.252))
padmapping = PadMapping(
{
P0[0]: landpattern.D2,
P0[1]: landpattern.F2,
P0[2]: landpattern.A12,
P0[3]: landpattern.B13,
P0[4]: landpattern.J1,
P0[5]: landpattern.K2,
P0[6]: landpattern.L1,
P0[7]: landpattern.M2,
P0[8]: landpattern.N1,
P0[9]: landpattern.L24,
P0[10]: landpattern.J24,
P0[11]: landpattern.T2,
P0[12]: landpattern.U1,
P0[13]: landpattern.AD8,
P0[14]: landpattern.AC9,
P0[15]: landpattern.AD10,
P0[16]: landpattern.AC11,
P0[17]: landpattern.AD12,
P0[18]: landpattern.AC13,
P0[19]: landpattern.AC15,
P0[20]: landpattern.AD16,
P0[21]: landpattern.AC17,
P0[22]: landpattern.AD18,
P0[23]: landpattern.AC19,
P0[24]: landpattern.AD20,
P0[25]: landpattern.AC21,
P0[26]: landpattern.G1,
P0[27]: landpattern.H2,
P0[28]: landpattern.B11,
P0[29]: landpattern.A10,
P0[30]: landpattern.B9,
P0[31]: landpattern.A8,
P1[0]: landpattern.AD22,
P1[1]: landpattern.Y23,
P1[2]: landpattern.W24,
P1[3]: landpattern.V23,
P1[4]: landpattern.U24,
P1[5]: landpattern.T23,
P1[6]: landpattern.R24,
P1[7]: landpattern.P23,
P1[8]: landpattern.P2,
P1[9]: landpattern.R1,
P1[10]: landpattern.A20,
P1[11]: landpattern.B19,
P1[12]: landpattern.B17,
P1[13]: landpattern.A16,
P1[14]: landpattern.B15,
P1[15]: landpattern.A14,
Dn: landpattern.AD4,
Dp: landpattern.AD6,
SWDCLK: landpattern.AA24,
SWDIO: landpattern.AC24,
XC1: landpattern.B24,
XC2: landpattern.A23,
ANT: landpattern.H23,
VBUS: landpattern.AD2,
VDD[0]: landpattern.A22,
VDD[1]: landpattern.AD14,
VDD[2]: landpattern.AD23,
VDD[3]: landpattern.B1,
VDD[4]: landpattern.W1,
VDDH: landpattern.Y2,
VSS: [landpattern.p[74], landpattern.B7],
VSS_PA: landpattern.F23,
DCC: landpattern.B3,
DCCH: landpattern.AB2,
DEC1: landpattern.C1,
DEC2: landpattern.A18,
DEC3: landpattern.D23,
DEC4: landpattern.B5,
DEC5: landpattern.N24,
DEC6: landpattern.E24,
DECUSB: landpattern.AC5,
}
)
symbols = [
BoxSymbol(rows=Row(right=PinGroup(P1))),
BoxSymbol(rows=Row(right=PinGroup(P0))),
BoxSymbol(
rows=Row(
right=PinGroup(
DCC, DCCH, DEC1, DEC2, DEC3, DEC4, DEC5, DEC6, DECUSB
),
left=PinGroup(VBUS, *VDD, VDDH),
),
columns=Column(down=PinGroup(VSS, VSS_PA)),
),
BoxSymbol(
rows=Row(
right=PinGroup(ANT), left=PinGroup(Dn, Dp, SWDCLK, SWDIO, XC1, XC2)
)
),
]
[docs]
class IOPort(Port):
p = Port()
@provide(IOPort)
def ioport(self, io: IOPort):
return [{io.p: port} for port in chain(self.mcu.P0, self.mcu.P1)]
@provide(GPIO)
def gpio(self, gpio: GPIO):
return [{gpio.gpio: self.require(self.IOPort).p} for _ in range(48)]
@provide(Timer)
def timer(self, timer: Timer):
return [{timer.timer: self.require(self.IOPort).p} for _ in range(48)]
@provide(ADC)
def adc(self, adc: ADC):
return [{adc.adc: self.mcu.P0[i]} for i in (2, 3, 4, 5, 28, 29, 30, 31)]
@provide(I2C)
def i2c(self, i2c: I2C):
return [
{
i2c.scl: self.require(self.IOPort).p,
i2c.sda: self.require(self.IOPort).p,
}
for _ in range(2)
]
@provide(SPI(cs=True))
def spi(self, spi: SPI):
return [
{
spi.copi: self.require(self.IOPort).p,
spi.cipo: self.require(self.IOPort).p,
spi.sck: self.require(self.IOPort).p,
spi.cs: self.require(self.IOPort).p,
}
for _ in range(3)
]
@provide(UART(cts=True, rts=True))
def uart(self, uart: UART):
assert uart.cts and uart.rts
return [
{
uart.tx: self.require(self.IOPort).p,
uart.rx: self.require(self.IOPort).p,
uart.cts: self.require(self.IOPort).p,
uart.rts: self.require(self.IOPort).p,
}
for _ in range(2)
]
@provide(SWD(swo=True))
def swd(self, swd: SWD):
assert swd.swo
return [
{
swd.swdio: self.mcu.SWDIO,
swd.swdclk: self.mcu.SWDCLK,
swd.swo: self.mcu.P1[0],
}
]
@provide(Reset)
def reset(self, reset: Reset):
return [
{
reset.reset: self.mcu.P0[18],
}
]
@provide(LFOscillator)
def crystal(self, crystal: LFOscillator):
return [
{
crystal.lo_in: self.mcu.P0[0],
crystal.lo_out: self.mcu.P0[1],
}
]
def __init__(
self, *, lfo: bool = True, antenna: bool = True, power_config: int = 5
) -> None:
mcu = self.mcu
self.GND = self.vin.Vn + mcu.VSS + mcu.VSS_PA
self.bypasses: list[Capacitor] = []
self.bypass_short_traces = []
match power_config:
case 5:
self.usb = USB2Connector()
self.GND += self.usb.vbus.Vn
self.VBUS = mcu.VBUS + self.usb.vbus.Vp
self.power_config = []
for pin, cap in [
(mcu.DEC1, 100e-9),
(mcu.DEC3, 100e-9),
(mcu.DEC5, 820e-12),
(mcu.DECUSB, 4.7e-6),
(mcu.VBUS, 4.7e-6),
(mcu.DEC4, 1e-6),
(mcu.VDDH, 4.7e-6),
(mcu.VDD[0], 0.1e-6),
(mcu.VDD[1], 0.1e-6),
(mcu.VDD[2], 0.1e-6),
(mcu.VDD[3], 1e-6),
(mcu.VDD[4], 1e-6),
]:
bypass = Capacitor(capacitance=(cap) * F)
self.bypasses.append(bypass)
self.bypass_short_traces.extend(
[
ShortTrace(pin, bypass.p1),
ShortTrace(bypass.p2, self.GND.port),
]
)
self.pwr_ind = Inductor(
mounting="smd",
inductance=(10e-6) * H,
max_current=AtLeast(50e-3 * A),
)
self.filt_ind = Inductor(
mounting="smd",
inductance=(15e-9) * H,
self_resonant_frequency=AtLeast(2e9 * Hz),
material_core="ceramic",
)
# DCCH -> 10 uH -> DCDC_OUT -> 15 nH -> DEC4
self.DCCH = mcu.DCCH + self.pwr_ind.p1
self.DCDC_OUT = self.pwr_ind.p2 + self.filt_ind.p1
self.DEC4 = self.filt_ind.p2 + mcu.DEC4 + mcu.DEC6
self.VDD = Net([*mcu.VDD, mcu.VDDH, self.vin.Vp])
case 6:
for pin, cap in [
(mcu.DEC1, 100e-9),
(mcu.DEC3, 100e-9),
# mcu.DEC5 820e-12, ; not required for Fxx and later
(mcu.DEC4, 1e-6),
(mcu.VDDH, 4.7e-6),
(mcu.VDD[0], 0.1e-6),
(mcu.VDD[1], 0.1e-6),
(mcu.VDD[2], 0.1e-6),
(mcu.VDD[3], 1e-6),
]:
bypass = Capacitor(capacitance=(cap) * F)
self.bypasses.append(bypass)
self.bypass_short_traces.extend(
[
ShortTrace(pin, bypass.p1),
ShortTrace(bypass.p2, self.GND.port),
]
)
mcu.DECUSB.no_connect()
mcu.DEC5.no_connect()
mcu.DCCH.no_connect()
mcu.DCC.no_connect()
dec6cap = Capacitor(capacitance=(0.1e-6) * F)
self.bypass_short_traces.extend(
[
ShortTrace(mcu.DEC6, dec6cap.p1),
ShortTrace(dec6cap.p2, self.GND.port),
]
)
dec6cap.schematic_x_out = True
dec6cap.soldered = False
# TODO: Is it in bom?
# dec6cap.in_bom = False
self.bypasses.append(dec6cap)
self.GND += mcu.VBUS
self.DEC4 = mcu.DEC4 + mcu.DEC6
mcu.Dp.no_connect()
mcu.Dn.no_connect()
self.VDD = Net([*mcu.VDD, mcu.VDDH, self.vin.Vp])
case _:
raise ValueError(f"Invalid power config: {power_config}")
dec2cap = Capacitor(capacitance=(0.1e-6) * F)
self.bypasses.append(dec2cap)
self.bypass_short_traces.extend(
[
ShortTrace(mcu.DEC2, dec2cap.p1),
ShortTrace(dec2cap.p2, self.GND.port),
]
)
self.hfosc = TSX_3225_32_0000MF10Z_W6()
self.XC1 = mcu.XC1 + self.hfosc.OSC1
self.XC2 = mcu.XC2 + self.hfosc.OSC2
self.GND += self.hfosc.GND0 + self.hfosc.GND1
self.hfosc.add_load_caps(self.GND)
self.util = SchematicGroup(mcu.symbols[2])
self.power = SchematicGroup(self.bypasses + [mcu.symbols[3]])
self.NRF52840 = SchematicGroup()
Device: type[NRF52840_QIAA_R] = NRF52840_QIAA_R