Source code for jitxexamples.components.mcus.nordic_NRF52840_QIAA_R

"""
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