component
STM32F103C8T6 - Arm Cortex-M3 MCU (Blue Pill)
Public Made by Adomby adom
STMicroelectronics mainstream Arm Cortex-M3 MCU. 72 MHz, 64 KB Flash, 20 KB SRAM, USB, CAN, LQFP-48. The workhorse Blue Pill microcontroller.
← Commit history
sections: pinout and tools get their own blocks (adom/wiki#148); the readme is reference material again
4 files changed
+611−23
page.json+9−3@@ -4,7 +4,7 @@ "slug": "stm32f103c8t6", "title": "STM32F103C8T6 - Arm Cortex-M3 MCU (Blue Pill)", "brief": "STMicroelectronics mainstream Arm Cortex-M3 MCU. 72 MHz, 64 KB Flash, 20 KB SRAM, USB, CAN, LQFP-48. The workhorse Blue Pill microcontroller.",- "version": "0.2.8",+ "version": "0.3.0", "tags": [ "component", "microcontroller",@@ -160,7 +160,7 @@ "stm32f103c8t6-board.glb", "thumbnails/**" ],- "changelog": "0.2.8: Fusion/EAGLE library applet, built on Linux where adom-lbr runs.",+ "changelog": "0.3.0: pinout and tools become their own sections (adom/wiki#148); the readme is reference material only.", "applet_connect": [ "https://verify-3i7rt4do98l3.adom.cloud", "https://component-ocean-jk89eeqplwfl.adom.cloud",@@ -169,7 +169,13 @@ "sections": [ "hero", "viewers",+ "pinout", "readme",+ "tools", "info"- ]+ ],+ "section_sources": {+ "pinout": "sections/pinout.html",+ "tools": "sections/tools.html"+ } }
readme.html−20@@ -130,26 +130,6 @@ tr[data-pin].hl td,tr[data-pin].hl td.data{color:var(--text)} <div class=callout>The <b>3D, symbol, and footprint viewers at the top of the page</b> are a single cross-linked set (hover a pin, pad, or 3D lead and they light up together, with layer toggles and datasheet tooltips). The <b>datasheet pin map</b> below is cross-linked to them too — hover a row to light the matching pin in the viewers above. Downloadable CAD and the datasheet PDF are on the Files tab (<code>.kicad_sym</code>, <code>.kicad_mod</code>, <code>.step</code>, <code>.glb</code>).</div> </div> -<h2 id=pinout>Datasheet pin map <span class=s>hover a row to light the matching pin in the viewers above — grouped by function, with the datasheet description for every pin</span></h2>-<div class=pinout>- <div class=pintbl><div class=pingroup><h3>POWER <span style="color:var(--text-3);font-weight:400">(9)</span></h3><table><thead><tr><th style=width:44px>Pin</th><th style=width:170px>Name</th><th style=width:70px>Type</th><th>Function (datasheet)</th></tr></thead><tbody><tr data-pin="1"><td class=data>1</td><td class=data>VBAT</td><td><span class="tbadge t-power">Power</span></td><td class=note style="color:var(--text-2)">Backup battery supply for RTC and backup registers. Typical operating range 1.8 to 3.6 V. Supplies the RTC and backup domain when VDD is off.</td></tr><tr data-pin="8"><td class=data>8</td><td class=data>VSSA</td><td><span class="tbadge t-power">Power</span></td><td class=note style="color:var(--text-2)">Analog ground reference for ADC, reset block, PLL, and analog peripherals. Connect to digital ground at a single point.</td></tr><tr data-pin="9"><td class=data>9</td><td class=data>VDDA</td><td><span class="tbadge t-power">Power</span></td><td class=note style="color:var(--text-2)">Analog supply for ADC, reset block, PLL, and analog I/O. 2.0 to 3.6 V. Decouple with 1 µF + 10 nF close to the pin.</td></tr><tr data-pin="23"><td class=data>23</td><td class=data>VSS_1</td><td><span class="tbadge t-power">Power</span></td><td class=note style="color:var(--text-2)">Digital ground, group 1. Connect to the ground plane; keep short return path to VDD_1 decoupling.</td></tr><tr data-pin="24"><td class=data>24</td><td class=data>VDD_1</td><td><span class="tbadge t-power">Power</span></td><td class=note style="color:var(--text-2)">Digital supply, group 1. 2.0 to 3.6 V. Decouple with 100 nF close to the pin.</td></tr><tr data-pin="35"><td class=data>35</td><td class=data>VSS_2</td><td><span class="tbadge t-power">Power</span></td><td class=note style="color:var(--text-2)">Digital ground, group 2. Connect to the ground plane; pair with VDD_2 decoupling.</td></tr><tr data-pin="36"><td class=data>36</td><td class=data>VDD_2</td><td><span class="tbadge t-power">Power</span></td><td class=note style="color:var(--text-2)">Digital supply, group 2. 2.0 to 3.6 V. Decouple with 100 nF close to the pin.</td></tr><tr data-pin="47"><td class=data>47</td><td class=data>VSS_3</td><td><span class="tbadge t-power">Power</span></td><td class=note style="color:var(--text-2)">Digital ground, group 3. Connect to the ground plane; pair with VDD_3 decoupling.</td></tr><tr data-pin="48"><td class=data>48</td><td class=data>VDD_3</td><td><span class="tbadge t-power">Power</span></td><td class=note style="color:var(--text-2)">Digital supply, group 3. 2.0 to 3.6 V. Decouple with 100 nF close to the pin.</td></tr></tbody></table></div><div class=pingroup><h3>GPIO <span style="color:var(--text-3);font-weight:400">(26)</span></h3><table><thead><tr><th style=width:44px>Pin</th><th style=width:170px>Name</th><th style=width:70px>Type</th><th>Function (datasheet)</th></tr></thead><tbody><tr data-pin="2"><td class=data>2</td><td class=data>PC13-TAMPER-RTC</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port C bit 13, muxed with TAMPER input and RTC output. Supplied through the backup power switch (limited to 3 mA sink); output speed limited to 2 MHz with max 30 pF load; must not source current.</td></tr><tr data-pin="10"><td class=data>10</td><td class=data>PA0-WKUP</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port A bit 0, 5 V-tolerant when configured as digital input. Alternate functions: standby WKUP, USART2_CTS (remap), ADC12_IN0, TIM2_CH1_ETR.</td></tr><tr data-pin="11"><td class=data>11</td><td class=data>PA1</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port A bit 1. Alternate functions: USART2_RTS, ADC12_IN1, TIM2_CH2.</td></tr><tr data-pin="12"><td class=data>12</td><td class=data>PA2</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port A bit 2. Alternate functions: USART2_TX, ADC12_IN2, TIM2_CH3.</td></tr><tr data-pin="13"><td class=data>13</td><td class=data>PA3</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port A bit 3. Alternate functions: USART2_RX, ADC12_IN3, TIM2_CH4.</td></tr><tr data-pin="14"><td class=data>14</td><td class=data>PA4</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port A bit 4. Alternate functions: SPI1_NSS, USART2_CK, ADC12_IN4.</td></tr><tr data-pin="15"><td class=data>15</td><td class=data>PA5</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port A bit 5. Alternate functions: SPI1_SCK, ADC12_IN5.</td></tr><tr data-pin="16"><td class=data>16</td><td class=data>PA6</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port A bit 6. Alternate functions: SPI1_MISO, ADC12_IN6, TIM3_CH1; remap: TIM1_BKIN.</td></tr><tr data-pin="17"><td class=data>17</td><td class=data>PA7</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port A bit 7. Alternate functions: SPI1_MOSI, ADC12_IN7, TIM3_CH2; remap: TIM1_CH1N.</td></tr><tr data-pin="18"><td class=data>18</td><td class=data>PB0</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port B bit 0. Alternate functions: ADC12_IN8, TIM3_CH3; remap: TIM1_CH2N.</td></tr><tr data-pin="19"><td class=data>19</td><td class=data>PB1</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port B bit 1. Alternate functions: ADC12_IN9, TIM3_CH4; remap: TIM1_CH3N.</td></tr><tr data-pin="20"><td class=data>20</td><td class=data>PB2</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port B bit 2, 5 V-tolerant. Doubles as BOOT1 boot mode selection input sampled at reset.</td></tr><tr data-pin="21"><td class=data>21</td><td class=data>PB10</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port B bit 10, 5 V-tolerant. Alternate functions: I2C2_SCL, USART3_TX; remap: TIM2_CH3.</td></tr><tr data-pin="22"><td class=data>22</td><td class=data>PB11</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port B bit 11, 5 V-tolerant. Alternate functions: I2C2_SDA, USART3_RX; remap: TIM2_CH4.</td></tr><tr data-pin="25"><td class=data>25</td><td class=data>PB12</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port B bit 12, 5 V-tolerant. Alternate functions: SPI2_NSS, I2C2_SMBAl, USART3_CK, TIM1_BKIN.</td></tr><tr data-pin="26"><td class=data>26</td><td class=data>PB13</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port B bit 13, 5 V-tolerant. Alternate functions: SPI2_SCK, USART3_CTS, TIM1_CH1N.</td></tr><tr data-pin="27"><td class=data>27</td><td class=data>PB14</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port B bit 14, 5 V-tolerant. Alternate functions: SPI2_MISO, USART3_RTS, TIM1_CH2N.</td></tr><tr data-pin="28"><td class=data>28</td><td class=data>PB15</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port B bit 15, 5 V-tolerant. Alternate functions: SPI2_MOSI, TIM1_CH3N.</td></tr><tr data-pin="29"><td class=data>29</td><td class=data>PA8</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port A bit 8, 5 V-tolerant. Alternate functions: USART1_CK, TIM1_CH1, MCO (master clock output).</td></tr><tr data-pin="30"><td class=data>30</td><td class=data>PA9</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port A bit 9, 5 V-tolerant. Alternate functions: USART1_TX, TIM1_CH2.</td></tr><tr data-pin="31"><td class=data>31</td><td class=data>PA10</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port A bit 10, 5 V-tolerant. Alternate functions: USART1_RX, TIM1_CH3.</td></tr><tr data-pin="41"><td class=data>41</td><td class=data>PB5</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port B bit 5 (NOT 5 V-tolerant on this pin). Alternate functions: I2C1_SMBAl; remap: TIM3_CH2, SPI1_MOSI.</td></tr><tr data-pin="42"><td class=data>42</td><td class=data>PB6</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port B bit 6, 5 V-tolerant. Alternate functions: I2C1_SCL, TIM4_CH1; remap: USART1_TX.</td></tr><tr data-pin="43"><td class=data>43</td><td class=data>PB7</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port B bit 7, 5 V-tolerant. Alternate functions: I2C1_SDA, TIM4_CH2; remap: USART1_RX.</td></tr><tr data-pin="45"><td class=data>45</td><td class=data>PB8</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port B bit 8, 5 V-tolerant. Alternate functions: TIM4_CH3; remap: I2C1_SCL, CANRX.</td></tr><tr data-pin="46"><td class=data>46</td><td class=data>PB9</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port B bit 9, 5 V-tolerant. Alternate functions: TIM4_CH4; remap: I2C1_SDA, CANTX.</td></tr></tbody></table></div><div class=pingroup><h3>TIMING <span style="color:var(--text-3);font-weight:400">(4)</span></h3><table><thead><tr><th style=width:44px>Pin</th><th style=width:170px>Name</th><th style=width:70px>Type</th><th>Function (datasheet)</th></tr></thead><tbody><tr data-pin="3"><td class=data>3</td><td class=data>PC14-OSC32_IN</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port C bit 14, muxed with 32 kHz LSE oscillator input (OSC32_IN). Backup-domain pin routed through the power switch; same 2 MHz / 30 pF drive limitation as PC13.</td></tr><tr data-pin="4"><td class=data>4</td><td class=data>PC15-OSC32_OUT</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port C bit 15, muxed with 32 kHz LSE oscillator output (OSC32_OUT). Backup-domain pin routed through the power switch; same 2 MHz / 30 pF drive limitation as PC13/PC14.</td></tr><tr data-pin="5"><td class=data>5</td><td class=data>PD0-OSC_IN</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">HSE crystal oscillator input (OSC_IN) after reset; remappable to GPIO PD0 by software. In GPIO mode limited to 50 MHz output. Connect to 4-16 MHz crystal or external clock.</td></tr><tr data-pin="6"><td class=data>6</td><td class=data>PD1-OSC_OUT</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">HSE crystal oscillator output (OSC_OUT) after reset; remappable to GPIO PD1 by software. In GPIO mode limited to 50 MHz output.</td></tr></tbody></table></div><div class=pingroup><h3>CONTROL <span style="color:var(--text-3);font-weight:400">(2)</span></h3><table><thead><tr><th style=width:44px>Pin</th><th style=width:170px>Name</th><th style=width:70px>Type</th><th>Function (datasheet)</th></tr></thead><tbody><tr data-pin="7"><td class=data>7</td><td class=data>NRST</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">Active-low bidirectional device reset. Pulled low by internal or external reset sources; external pull-up recommended (typ 40 kΩ internal weak pull-up).</td></tr><tr data-pin="44"><td class=data>44</td><td class=data>BOOT0</td><td></td><td class=note style="color:var(--text-2)">Boot mode selection input sampled at reset (with BOOT1/PB2): selects boot from Flash, System memory (bootloader), or embedded SRAM. Must be tied to a defined logic level.</td></tr></tbody></table></div><div class=pingroup><h3>USB <span style="color:var(--text-3);font-weight:400">(2)</span></h3><table><thead><tr><th style=width:44px>Pin</th><th style=width:170px>Name</th><th style=width:70px>Type</th><th>Function (datasheet)</th></tr></thead><tbody><tr data-pin="32"><td class=data>32</td><td class=data>PA11</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port A bit 11, 5 V-tolerant. Alternate functions: USART1_CTS, CANRX, USBDM (USB D-), TIM1_CH4.</td></tr><tr data-pin="33"><td class=data>33</td><td class=data>PA12</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port A bit 12, 5 V-tolerant. Alternate functions: USART1_RTS, CANTX, USBDP (USB D+), TIM1_ETR.</td></tr></tbody></table></div><div class=pingroup><h3>DEBUG <span style="color:var(--text-3);font-weight:400">(5)</span></h3><table><thead><tr><th style=width:44px>Pin</th><th style=width:170px>Name</th><th style=width:70px>Type</th><th>Function (datasheet)</th></tr></thead><tbody><tr data-pin="34"><td class=data>34</td><td class=data>PA13</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port A bit 13, 5 V-tolerant. Default after reset: JTMS/SWDIO (serial-wire debug data/JTAG mode select). Can be released to GPIO via SWJ-DP disable.</td></tr><tr data-pin="37"><td class=data>37</td><td class=data>PA14</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port A bit 14, 5 V-tolerant. Default after reset: JTCK/SWCLK (serial-wire debug clock/JTAG clock).</td></tr><tr data-pin="38"><td class=data>38</td><td class=data>PA15</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port A bit 15, 5 V-tolerant. Default after reset: JTDI (JTAG data in). Remappable to TIM2_CH1_ETR/PA15/SPI1_NSS.</td></tr><tr data-pin="39"><td class=data>39</td><td class=data>PB3</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port B bit 3, 5 V-tolerant. Default after reset: JTDO / TRACESWO. Remappable to TIM2_CH2, PB3, SPI1_SCK.</td></tr><tr data-pin="40"><td class=data>40</td><td class=data>PB4</td><td><span class="tbadge t-io">I/O</span></td><td class=note style="color:var(--text-2)">GPIO port B bit 4, 5 V-tolerant. Default after reset: NJTRST (JTAG reset, active low). Remappable to TIM3_CH1, PB4, SPI1_MISO.</td></tr></tbody></table></div></div>-</div>--<h2 id=pin1>Pin 1, chip against footprint <span class=s>the datasheet already says which pad is pin 1; what matters is whether pin 1 on the chip lands on pin 1 of the footprint, because when they disagree the part goes down rotated</span></h2>-<script src="https://verify-3i7rt4do98l3.adom.cloud/applet.js"></script>-<adom-verify- page="STM32F103C8T6" fact="pin-1-registration" wiki-page="adom/stm32f103c8t6"- chip-image="https://wiki.adom.inc/blob/component/stm32f103c8t6/pin1/chip-top.png"- footprint-svg="https://wiki.adom.inc/blob/component/stm32f103c8t6/pin1/footprint.svg"></adom-verify>--<h2 id=fusion-lbr>Fusion / EAGLE library <span class=s>web control cannot build one: a bridge runs on Windows or macOS, adom-lbr is Linux-only, and a browser runs no binaries. So this button asks a Linux service that has it</span></h2>-<script src="https://lbr-5o0yi1odd4w1.adom.cloud/applet.js"></script>-<adom-fusion-lbr page="adom/stm32f103c8t6"></adom-fusion-lbr>--<h2 id=sourcing>Price and stock <span class=s>the one thing on this page that cannot be baked: it changes without the page changing, so the button asks Component Ocean live</span></h2>-<script src="https://component-ocean-jk89eeqplwfl.adom.cloud/applet.js"></script>-<adom-sourcing mpn="STM32F103C8T6" wiki-page="adom/stm32f103c8t6"></adom-sourcing>- <h2>Datasheet key pages <span class=s>the pages worth looking at first · pick a page, click it to go full screen</span></h2> <div class=dsviewer> <div class=dsrail><button data-i="0" class="on" title="Features (datasheet p.1)"><img src="https://wiki.adom.inc/blob/component/stm32f103c8t6/keypages/features.png" loading=lazy alt="Features"><span class=rl>Features</span></button><button data-i="1" class="" title="Block diagram (datasheet p.11)"><img src="https://wiki.adom.inc/blob/component/stm32f103c8t6/keypages/block-diagram.png" loading=lazy alt="Block diagram"><span class=rl>Block diagram</span></button><button data-i="2" class="" title="Pinout (datasheet p.29)"><img src="https://wiki.adom.inc/blob/component/stm32f103c8t6/keypages/pinout.png" loading=lazy alt="Pinout"><span class=rl>Pinout</span></button><button data-i="3" class="" title="Pin table (datasheet p.28)"><img src="https://wiki.adom.inc/blob/component/stm32f103c8t6/keypages/pin-table.png" loading=lazy alt="Pin table"><span class=rl>Pin table</span></button><button data-i="4" class="" title="Package (datasheet p.101)"><img src="https://wiki.adom.inc/blob/component/stm32f103c8t6/keypages/package.png" loading=lazy alt="Package"><span class=rl>Package</span></button></div>
sections/pinout.htmladded+567@@ -0,0 +1,567 @@+<!doctype html><html lang=en><head><meta charset=utf-8>+<meta name=viewport content="width=device-width,initial-scale=1"><title>STM32F103C8T6 pinout</title>+<style>+:root{color-scheme:dark}+body{margin:0;padding:4px 0 10px;background:transparent;+ font-family:'Satoshi',system-ui,-apple-system,'Segoe UI',sans-serif;color:#e6edf3}+</style></head><body>+<adom-pinout><script type="application/json">{+ "mpn": "STM32F103C8T6",+ "package": "LQFP-48",+ "pins": [+ {+ "number": 1,+ "name": "VBAT",+ "type": "power"+ },+ {+ "number": 2,+ "name": "PC13-TAMPER-RTC",+ "type": "io"+ },+ {+ "number": 3,+ "name": "PC14-OSC32_IN",+ "type": "io"+ },+ {+ "number": 4,+ "name": "PC15-OSC32_OUT",+ "type": "io"+ },+ {+ "number": 5,+ "name": "PD0-OSC_IN",+ "type": "io"+ },+ {+ "number": 6,+ "name": "PD1-OSC_OUT",+ "type": "io"+ },+ {+ "number": 7,+ "name": "NRST",+ "type": "io"+ },+ {+ "number": 8,+ "name": "VSSA",+ "type": "power"+ },+ {+ "number": 9,+ "name": "VDDA",+ "type": "power"+ },+ {+ "number": 10,+ "name": "PA0-WKUP",+ "type": "io"+ },+ {+ "number": 11,+ "name": "PA1",+ "type": "io"+ },+ {+ "number": 12,+ "name": "PA2",+ "type": "io"+ },+ {+ "number": 13,+ "name": "PA3",+ "type": "io"+ },+ {+ "number": 14,+ "name": "PA4",+ "type": "io"+ },+ {+ "number": 15,+ "name": "PA5",+ "type": "io"+ },+ {+ "number": 16,+ "name": "PA6",+ "type": "io"+ },+ {+ "number": 17,+ "name": "PA7",+ "type": "io"+ },+ {+ "number": 18,+ "name": "PB0",+ "type": "io"+ },+ {+ "number": 19,+ "name": "PB1",+ "type": "io"+ },+ {+ "number": 20,+ "name": "PB2",+ "type": "io"+ },+ {+ "number": 21,+ "name": "PB10",+ "type": "io"+ },+ {+ "number": 22,+ "name": "PB11",+ "type": "io"+ },+ {+ "number": 23,+ "name": "VSS_1",+ "type": "power"+ },+ {+ "number": 24,+ "name": "VDD_1",+ "type": "power"+ },+ {+ "number": 25,+ "name": "PB12",+ "type": "io"+ },+ {+ "number": 26,+ "name": "PB13",+ "type": "io"+ },+ {+ "number": 27,+ "name": "PB14",+ "type": "io"+ },+ {+ "number": 28,+ "name": "PB15",+ "type": "io"+ },+ {+ "number": 29,+ "name": "PA8",+ "type": "io"+ },+ {+ "number": 30,+ "name": "PA9",+ "type": "io"+ },+ {+ "number": 31,+ "name": "PA10",+ "type": "io"+ },+ {+ "number": 32,+ "name": "PA11",+ "type": "io"+ },+ {+ "number": 33,+ "name": "PA12",+ "type": "io"+ },+ {+ "number": 34,+ "name": "PA13",+ "type": "io"+ },+ {+ "number": 35,+ "name": "VSS_2",+ "type": "power"+ },+ {+ "number": 36,+ "name": "VDD_2",+ "type": "power"+ },+ {+ "number": 37,+ "name": "PA14",+ "type": "io"+ },+ {+ "number": 38,+ "name": "PA15",+ "type": "io"+ },+ {+ "number": 39,+ "name": "PB3",+ "type": "io"+ },+ {+ "number": 40,+ "name": "PB4",+ "type": "io"+ },+ {+ "number": 41,+ "name": "PB5",+ "type": "io"+ },+ {+ "number": 42,+ "name": "PB6",+ "type": "io"+ },+ {+ "number": 43,+ "name": "PB7",+ "type": "io"+ },+ {+ "number": 44,+ "name": "BOOT0",+ "type": "io"+ },+ {+ "number": 45,+ "name": "PB8",+ "type": "io"+ },+ {+ "number": 46,+ "name": "PB9",+ "type": "io"+ },+ {+ "number": 47,+ "name": "VSS_3",+ "type": "power"+ },+ {+ "number": 48,+ "name": "VDD_3",+ "type": "power"+ }+ ]+}</script></adom-pinout>+<script>/*!+ * <adom-pinout> — a reusable, dependency-free pinout widget.+ *+ * Drop-in:+ * <script src="pinout-widget.js"></script>+ * <adom-pinout src="part.json"></adom-pinout>+ * <adom-pinout><script type="application/json">{ ...pin data... }</script></adom-pinout>+ * // or: document.querySelector('adom-pinout').data = { ... }+ *+ * Data contract (everything except pins[].name is optional):+ * {+ * "mpn": "STM32F103C8T6", // label shown in the chip body+ * "package": "LQFP-48", // label + hints the inferred layout (quad vs dual)+ * "pins": [+ * { "number": 1, "name": "VBAT", // number+name are the essentials+ * "type": "power", // power|io|clock|ctrl|other (auto-inferred from name if omitted)+ * "xy": [-2.75, 4.25] } // mm pad centre (footprint frame, +y up) — best placement.+ * // omit xy → placed by inferred quad/dual layout.+ * ]+ * }+ *+ * Attributes: src (fetch JSON), layout ("auto"|"quad"|"dual"), heading (override title).+ * Self-contained (Shadow DOM), themes to the host via prefers-color-scheme.+ */+(() => {+ const TYPE_COLORS = {+ power: '#ff5c5c', io: '#00d3c8', clock: '#4c9dff', ctrl: '#b57cff', control: '#b57cff', other: '#8b97ac',+ };+ const TYPE_LABEL = { power: 'power', io: 'I/O', clock: 'clock', ctrl: 'control', control: 'control', other: 'other' };++ function inferType(name) {+ const n = String(name || '').toUpperCase();+ if (n === 'NC' || n === '') return 'other';+ if (/^(VDD|VSS|VCC|GND|VBAT|VDDA|VSSA|AVDD|AVSS|VREF|VEE|VBUS|V\+|V-|AGND|DGND)/.test(n)) return 'power';+ if (/(OSC|XTAL|CLKIN|CLKOUT|\bCLK\b|SCLK|MCLK)/.test(n)) return 'clock';+ if (/^(NRST|~?RESET|RST|BOOT|EN\b|~?CE|~?CS|MODE|TEST|WAKE|SHDN|~?SHDN|PDN)/.test(n)) return 'ctrl';+ return 'io';+ }++ // Exposed / thermal pad (QFN/DFN/SON/PowerPAD) — belongs in the chip centre, not on an edge.+ const isCenterPad = p => {+ const n = String(p.name || '').trim().toUpperCase();+ return /^(EP|EPAD|PAD|TAB|THERMAL|E[-_]?PAD|EXP|EXPPAD)$/.test(n) || /EXPOSED\s*PAD|THERMAL\s*PAD/.test(n);+ };++ // Return each pin as { ...pin, x, y, side } in a S×S box. Center pads get side:'center'.+ function layout(pins, pkg, forced) {+ const S = 400, pad = 60, mid = S / 2;+ const centers = pins.filter(isCenterPad);+ const edge = pins.filter(p => !isCenterPad(p));+ let laid;+ const hasXY = edge.length > 0 && edge.every(p => Array.isArray(p.xy) && p.xy.length === 2);+ if (hasXY && forced !== 'quad' && forced !== 'dual') {+ const xs = edge.map(p => p.xy[0]), ys = edge.map(p => p.xy[1]);+ const minx = Math.min(...xs), maxx = Math.max(...xs), miny = Math.min(...ys), maxy = Math.max(...ys);+ const span = Math.max(maxx - minx, maxy - miny) || 1, sc = (S - 2 * pad) / span;+ const cx = (maxx + minx) / 2, cy = (maxy + miny) / 2, off = (v, c) => mid + (v - c) * sc;+ laid = edge.map(p => {+ const x = off(p.xy[0], cx), y = S - off(p.xy[1], cy); // flip y (mm +y up → screen down)+ const side = p.xy[1] === maxy ? 'top' : p.xy[1] === miny ? 'bottom' : p.xy[0] === maxx ? 'right' : 'left';+ return { ...p, x, y, side };+ });+ } else {+ const N = edge.length, inner = pad, outer = S - pad, at = (a, b, t) => a + (b - a) * t;+ const quad = forced === 'quad' || (forced !== 'dual' && /QF|BGA|LGA|LCC|QFN|QFP/i.test(pkg || ''));+ if (quad) {+ const per = Math.ceil(N / 4);+ laid = edge.map((p, i) => {+ const s = Math.floor(i / per), t = ((i % per) + 0.5) / per;+ if (s === 0) return { ...p, x: inner, y: at(inner, outer, t), side: 'left' };+ if (s === 1) return { ...p, x: at(inner, outer, t), y: outer, side: 'bottom' };+ if (s === 2) return { ...p, x: outer, y: at(outer, inner, t), side: 'right' };+ return { ...p, x: at(outer, inner, t), y: inner, side: 'top' };+ });+ } else {+ const half = Math.ceil(N / 2); // DIP-style: left top→bottom, right bottom→top+ laid = edge.map((p, i) => i < half+ ? { ...p, x: inner, y: at(inner, outer, (i + 0.5) / half), side: 'left' }+ : { ...p, x: outer, y: at(outer, inner, (i - half + 0.5) / (N - half)), side: 'right' });+ }+ }+ centers.forEach(p => laid.push({ ...p, x: mid, y: mid, side: 'center' }));+ return laid;+ }++ const esc = s => String(s).replace(/[&<>"]/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c]));++ class AdomPinout extends HTMLElement {+ set data(d) { this._data = d; if (this.shadowRoot) this._render(); }+ get data() { return this._data; }++ async connectedCallback() {+ if (this._init) return; this._init = true;+ this.attachShadow({ mode: 'open' });+ this._busInit();+ if (!this._data) {+ const inline = this.querySelector('script[type="application/json"]');+ if (inline) { try { this._data = JSON.parse(inline.textContent); } catch (e) { /* ignore */ } }+ const src = this.getAttribute('src');+ if (!this._data && src) { try { this._data = await (await fetch(src)).json(); } catch (e) { this._err = 'could not load ' + src; } }+ }+ this._render();+ }++ _render() {+ const d = this._data;+ const root = this.shadowRoot;+ if (this._err || !d || !Array.isArray(d.pins)) {+ root.innerHTML = `<style>${this._css()}</style><div class="card"><div class="empty">${esc(this._err || 'no pin data')}</div></div>`;+ return;+ }+ const pins = d.pins.map(p => ({ ...p, type: (p.type || inferType(p.name)).toLowerCase() }));+ const laid = layout(pins, d.package, this.getAttribute('layout'));+ const typesPresent = [...new Set(pins.map(p => p.type))];+ const heading = this.getAttribute('heading') || d.mpn || '';+ root.innerHTML = `<style>${this._css()}</style>+ <div class="card">+ <div class="hd">${heading ? `<span class="mpn">${esc(heading)}</span>` : ''}${d.package ? `<span class="pkg">${esc(d.package)}</span>` : ''}<span class="count">${pins.length} pins</span><span class="tools"><button class="tbtn" data-act="copy" title="Copy the pin map as JSON">copy</button><button class="tbtn" data-act="dl" title="Download the pin map as JSON">JSON ↓</button></span></div>+ <div class="body">+ <div class="diagram">+ <div class="readout" data-readout>hover a pin →</div>+ ${this._svg(laid, d)}+ <div class="legend">${typesPresent.map(t => `<span><i style="background:${TYPE_COLORS[t] || TYPE_COLORS.other}"></i>${TYPE_LABEL[t] || t}</span>`).join('')}</div>+ </div>+ <div class="tablewrap"><table>+ <thead><tr><th>#</th><th>Name</th><th>Type</th></tr></thead>+ <tbody>${pins.slice().sort((a, b) => a.number - b.number).map(p => `+ <tr data-pin="${esc(p.number)}"><td class="num">${esc(p.number)}</td><td class="nm">${esc(p.name)}</td>+ <td><span class="tp" style="color:${TYPE_COLORS[p.type] || TYPE_COLORS.other}">${TYPE_LABEL[p.type] || p.type}</span></td></tr>`).join('')}</tbody>+ </table></div>+ </div>+ </div>`;+ this._wire(pins);+ }++ _svg(laid, d) {+ const S = 400;+ const edge = laid.filter(p => p.side !== 'center');+ const cen = laid.filter(p => p.side === 'center');+ const xs = edge.map(p => p.x), ys = edge.map(p => p.y);+ const pminX = Math.min(...xs), pmaxX = Math.max(...xs), pminY = Math.min(...ys), pmaxY = Math.max(...ys);+ const sides = new Set(edge.map(p => p.side)), hasTB = sides.has('top') || sides.has('bottom');+ const bL = pminX + 16, bR = pmaxX - 16, bT = hasTB ? pminY + 16 : pminY - 12, bB = hasTB ? pmaxY - 16 : pmaxY + 12;+ const cx = (bL + bR) / 2, cyB = (bT + bB) / 2;+ let s = `<svg viewBox="0 0 ${S} ${S}" class="chip">`;+ s += `<rect x="${bL}" y="${bT}" width="${bR - bL}" height="${bB - bT}" rx="10" class="pkgbody"/>`;+ s += `<circle cx="${bL + 15}" cy="${bT + 15}" r="4" class="pin1"/>`;+ const lY = cen.length ? bT + 32 : cyB - 3;+ s += `<text x="${cx}" y="${lY}" text-anchor="middle" class="lbl">${esc(d.mpn || '')}</text>`;+ if (d.package) s += `<text x="${cx}" y="${lY + 13}" text-anchor="middle" class="lbl2">${esc(d.package)}</text>`;+ // center pad(s) — exposed/thermal pad, drawn in the body centre+ cen.forEach((p, i) => {+ const col = TYPE_COLORS[p.type] || TYPE_COLORS.other;+ const ex = cx + (i - (cen.length - 1) / 2) * 40, ey = cyB + (cen.length ? 22 : 0);+ s += `<g class="pin" data-pin="${esc(p.number)}">`+ + `<rect class="ep" x="${ex - 19}" y="${ey - 13}" width="38" height="26" rx="4" fill="${col}" fill-opacity="0.15" stroke="${col}"/>`+ + `<text class="ept" x="${ex}" y="${ey + 3}" text-anchor="middle">${esc(p.name)}·${esc(p.number)}</text></g>`;+ });+ for (const p of edge) {+ const col = TYPE_COLORS[p.type] || TYPE_COLORS.other;+ let ex = p.x, ey = p.y, lx = p.x, ly = p.y, anc = 'middle';+ if (p.side === 'left') { ex = bL; lx = p.x - 11; anc = 'end'; ly = p.y + 3; }+ else if (p.side === 'right') { ex = bR; lx = p.x + 11; anc = 'start'; ly = p.y + 3; }+ else if (p.side === 'top') { ey = bT; ly = p.y - 9; }+ else { ey = bB; ly = p.y + 14; }+ s += `<g class="pin" data-pin="${esc(p.number)}">`+ + `<line class="stub" x1="${p.x}" y1="${p.y}" x2="${ex}" y2="${ey}" stroke="${col}"/>`+ + `<circle class="dot" cx="${p.x}" cy="${p.y}" r="3.1" fill="${col}"/>`+ + `<text class="pn" x="${lx}" y="${ly}" text-anchor="${anc}">${esc(p.number)}</text></g>`;+ }+ return s + `</svg>`;+ }++ // Set the highlighted pin. `emit` fires a `pinhover` event (internal hover);+ // external callers use highlight() which does NOT emit (avoids feedback loops).+ _set(n, emit) {+ const root = this.shadowRoot; if (!root) return;+ const s = (n == null ? '' : String(n));+ root.querySelectorAll('tr[data-pin]').forEach(r => r.classList.toggle('hot', r.dataset.pin === s));+ root.querySelectorAll('svg .pin[data-pin]').forEach(g => g.classList.toggle('hot', g.dataset.pin === s));+ const p = (this._pinsRendered || []).find(x => String(x.number) === s);+ const ro = root.querySelector('[data-readout]');+ if (ro) ro.innerHTML = p+ ? `<b>PIN ${esc(p.number)}</b> · ${esc(p.name)}<span class="rt" style="color:${TYPE_COLORS[p.type] || TYPE_COLORS.other}">${TYPE_LABEL[p.type] || p.type}</span>`+ : 'hover a pin →';+ if (emit) { this.dispatchEvent(new CustomEvent('pinhover', { detail: p || null, bubbles: true })); this._emitBus(s); }+ }++ /** Public: drive the highlight from outside (e.g. a linked footprint/symbol widget). */+ highlight(n) { this._set(n, false); }++ /** Public: the canonical pin map as a plain object — the structured handle for code/AI.+ * Types are resolved (inferred filled in) so consumers get a complete map. No fabricated fields. */+ toJSON() {+ const d = this._data || {};+ const pins = (Array.isArray(d.pins) ? d.pins : [])+ .map(p => ({ number: p.number, name: p.name, type: (p.type || inferType(p.name)).toLowerCase(), ...(p.xy ? { xy: p.xy } : {}) }))+ .sort((a, b) => (Number(a.number) - Number(b.number)) || String(a.number).localeCompare(String(b.number)));+ const out = {};+ if (d.mpn) out.mpn = d.mpn;+ if (d.package) out.package = d.package;+ out.pins = pins;+ return out;+ }++ _exportJson() { return JSON.stringify(this.toJSON(), null, 2); }+ _exportName() { const d = this._data || {}; return ((d.mpn || 'pinout') + (d.package ? '-' + d.package : '')).replace(/[^\w.-]+/g, '_') + '.pins.json'; }+ _download() {+ const blob = new Blob([this._exportJson()], { type: 'application/json' });+ const url = URL.createObjectURL(blob), a = document.createElement('a');+ a.href = url; a.download = this._exportName(); document.body.appendChild(a); a.click();+ a.remove(); setTimeout(() => URL.revokeObjectURL(url), 1000);+ }+ async _copy(btn) {+ const txt = this._exportJson(), done = () => { if (!btn) return; const o = btn.textContent; btn.textContent = 'copied'; btn.classList.add('ok'); setTimeout(() => { btn.textContent = o; btn.classList.remove('ok'); }, 1100); };+ try { await navigator.clipboard.writeText(txt); done(); return; } catch (e) { /* sandboxed / denied — fall back */ }+ try { const ta = document.createElement('textarea'); ta.value = txt; ta.style.position = 'fixed'; ta.style.opacity = '0'; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); ta.remove(); done(); }+ catch (e2) { this._download(); } // last resort: hand them the file+ }++ // adom-lbr shared cross-highlight bus: BroadcastChannel('lbr-hover') + parent relay.+ _busInit() {+ this._self = 'w' + Math.random().toString(36).slice(2);+ this._bus = ('BroadcastChannel' in window) ? new BroadcastChannel('lbr-hover') : null;+ const on = e => { const d = e.data || {}; if (d.type === 'lbr-hover' && d.src !== this._self) this._set(d.pin == null ? '' : d.pin, false); };+ if (this._bus) this._bus.onmessage = on;+ this._winmsg = on; window.addEventListener('message', on);+ }+ disconnectedCallback() { try { this._bus && this._bus.close(); } catch (_) { /*noop*/ } if (this._winmsg) window.removeEventListener('message', this._winmsg); }+ _emitBus(n) {+ const m = { type: 'lbr-hover', pin: (n === '' || n == null) ? null : String(n), mpn: (this._data && this._data.mpn) || '', src: this._self };+ try { this._bus && this._bus.postMessage(m); } catch (_) { /*noop*/ }+ try { if (window.parent && window.parent !== window) window.parent.postMessage(m, '*'); } catch (_) { /*noop*/ }+ }++ _wire(pins) {+ this._pinsRendered = pins;+ const root = this.shadowRoot;+ const dl = root.querySelector('.tbtn[data-act="dl"]'); if (dl) dl.onclick = () => this._download();+ const cp = root.querySelector('.tbtn[data-act="copy"]'); if (cp) cp.onclick = () => this._copy(cp);+ root.querySelectorAll('tr[data-pin]').forEach(r => { r.onmouseenter = () => this._set(r.dataset.pin, true); r.onmouseleave = () => this._set('', true); });+ root.querySelectorAll('svg .pin[data-pin]').forEach(g => { g.onmouseenter = () => this._set(g.dataset.pin, true); g.onmouseleave = () => this._set('', true); });+ // Match the pin table's height to the chip diagram column so it fills the+ // space and scrolls, instead of a fixed short box next to a taller chip.+ const dia = root.querySelector('.diagram'), tw = root.querySelector('.tablewrap');+ if (dia && tw) {+ const fit = () => { const h = dia.getBoundingClientRect().height; if (h > 40) tw.style.maxHeight = Math.round(h) + 'px'; };+ requestAnimationFrame(fit); setTimeout(fit, 60);+ try { if (this._ro) this._ro.disconnect(); this._ro = new ResizeObserver(fit); this._ro.observe(dia); } catch (e) { /* */ }+ }+ }++ _css() {+ return `+ :host{display:block;+ --bg:#0d1420;--panel:#121a26;--line:#26324a;--line2:#33425f;--text:#dbe2ec;--dim:#8b97ac;--dim2:#66738c;--accent:#00e6dc;+ --hot:rgba(0,230,220,.13)}+ @media (prefers-color-scheme:light){:host{--bg:#f6f8fc;--panel:#fff;--line:#dbe2ee;--line2:#c3cede;--text:#1a2230;--dim:#5a6678;--dim2:#8592a6;--accent:#0d8f88;--hot:rgba(13,143,136,.10)}}+ :host([theme="dark"]){--bg:#0d1420;--panel:#121a26;--line:#26324a;--line2:#33425f;--text:#dbe2ec;--dim:#8b97ac;--dim2:#66738c;--accent:#00e6dc;--hot:rgba(0,230,220,.13)}+ :host([theme="light"]){--bg:#f6f8fc;--panel:#fff;--line:#dbe2ee;--line2:#c3cede;--text:#1a2230;--dim:#5a6678;--dim2:#8592a6;--accent:#0d8f88;--hot:rgba(13,143,136,.10)}+ *{box-sizing:border-box}+ .card{background:var(--bg);border:1px solid var(--line);border-radius:14px;padding:16px;color:var(--text);+ font:14px/1.5 -apple-system,'Segoe UI',Roboto,Arial,sans-serif;container-type:inline-size}+ .hd{display:flex;align-items:baseline;gap:10px;margin-bottom:12px}+ .hd .mpn{font:700 17px ui-monospace,'SF Mono',Menlo,monospace;letter-spacing:-.2px}+ .hd .pkg{font:600 11px ui-monospace,monospace;color:var(--accent);background:color-mix(in srgb,var(--accent) 12%,transparent);border:1px solid color-mix(in srgb,var(--accent) 30%,transparent);padding:2px 8px;border-radius:6px}+ .hd .count{margin-left:auto;font:12px ui-monospace,monospace;color:var(--dim2)}+ .hd .tools{display:flex;gap:6px;align-self:center}+ .tbtn{font:600 10.5px ui-monospace,monospace;letter-spacing:.03em;color:var(--dim);background:var(--panel);+ border:1px solid var(--line);border-radius:6px;padding:3px 8px;cursor:pointer;transition:color .12s,border-color .12s,background .12s;white-space:nowrap}+ .tbtn:hover{color:var(--text);border-color:var(--line2)}+ .tbtn.ok{color:var(--accent);border-color:color-mix(in srgb,var(--accent) 45%,transparent)}+ .body{display:grid;grid-template-columns:minmax(300px,1fr) minmax(220px,1fr);gap:20px;align-items:start}+ /* stack diagram over table when the WIDGET (not the viewport) is narrow —+ e.g. side-by-side in a cross-link row — so the table never clips */+ @container (max-width:580px){.body{grid-template-columns:1fr}}+ .readout{height:24px;display:flex;align-items:center;gap:9px;font:600 13px ui-monospace,monospace;color:var(--dim);padding:0 4px}+ .readout b{color:var(--accent)} .readout .rt{margin-left:auto;font-size:11px;text-transform:uppercase;letter-spacing:.08em}+ .diagram{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:8px}+ svg.chip{width:100%;height:auto;display:block;max-height:440px}+ .pkgbody{fill:color-mix(in srgb,var(--text) 6%,var(--panel));stroke:var(--line2);stroke-width:1}+ .pin1{fill:var(--accent)}+ .lbl{fill:var(--dim);font:600 11px ui-monospace,monospace} .lbl2{fill:var(--dim2);font:9px ui-monospace,monospace}+ .pin .stub{stroke-width:2} .pin .pn{fill:var(--dim2);font:8px ui-monospace,monospace}+ .pin:hover .stub,.pin.hot .stub{stroke-width:3.6} .pin:hover .pn,.pin.hot .pn{fill:var(--text);font-weight:700}+ .pin .ep{stroke-width:1.2} .pin .ept{fill:var(--dim);font:8px ui-monospace,monospace}+ .pin:hover .ep,.pin.hot .ep{fill-opacity:.34;stroke-width:2} .pin:hover .ept,.pin.hot .ept{fill:var(--text);font-weight:700}+ .pin{cursor:default}+ .legend{display:flex;flex-wrap:wrap;gap:13px;margin:8px 2px 2px;font-size:11.5px;color:var(--dim)}+ .legend i{display:inline-block;width:9px;height:9px;border-radius:2px;margin-right:5px;vertical-align:middle}+ .tablewrap{max-height:440px;overflow:auto;border:1px solid var(--line);border-radius:10px;scrollbar-width:thin;scrollbar-color:var(--line2) transparent}+ .tablewrap::-webkit-scrollbar{width:9px;height:9px}+ .tablewrap::-webkit-scrollbar-track{background:transparent}+ .tablewrap::-webkit-scrollbar-thumb{background:var(--line2);border-radius:9px;border:2px solid transparent;background-clip:padding-box}+ .tablewrap::-webkit-scrollbar-thumb:hover{background:var(--dim2);border:2px solid transparent;background-clip:padding-box}+ .tablewrap::-webkit-scrollbar-button{display:none;width:0;height:0}+ .tablewrap::-webkit-scrollbar-corner{background:transparent}+ table{width:100%;border-collapse:collapse;font-size:12.5px}+ thead th{position:sticky;top:0;background:var(--panel);text-align:left;color:var(--dim2);font:600 10px ui-monospace,monospace;letter-spacing:.08em;text-transform:uppercase;padding:7px 9px;border-bottom:1px solid var(--line)}+ td{padding:4px 9px;border-bottom:1px solid color-mix(in srgb,var(--line) 55%,transparent);font-family:ui-monospace,monospace}+ td.num{color:var(--dim2)} td.nm{color:var(--text)}+ tr[data-pin]{cursor:default} tr.hot td{background:var(--hot)}+ .tp{font-size:10px;text-transform:uppercase;letter-spacing:.05em}+ .empty{color:var(--dim2);padding:30px;text-align:center;font-family:ui-monospace,monospace}`;+ }+ }+ if (!customElements.get('adom-pinout')) customElements.define('adom-pinout', AdomPinout);+})();+</script>+<script>+function fit(){parent.postMessage({type:'readme-height',height:document.documentElement.scrollHeight},'*')}+addEventListener('load',fit);new ResizeObserver(fit).observe(document.documentElement);+</script>+</body></html>
sections/tools.htmladded+35@@ -0,0 +1,35 @@+<!doctype html><html lang=en><head><meta charset=utf-8>+<meta name=viewport content="width=device-width,initial-scale=1"><title>STM32F103C8T6 tools</title>+<style>+:root{--bg:#0d1117;--surface:#161b22;--elevated:#1c2128;--border:#30363d;--text:#e6edf3;+ --text-2:#8b949e;--text-3:#484f58;--accent:#00b8b0;--accent-bright:#00e6dc;--green:#3fb950;+ --red:#f85149;--blue:#58a6ff;color-scheme:dark}+body{margin:0;padding:2px 0 10px;background:transparent;color:var(--text);+ font-family:'Satoshi',system-ui,-apple-system,'Segoe UI',sans-serif;font-size:14px;line-height:1.55}+h3{margin:14px 0 2px;font-size:13px;font-weight:600}+h3:first-child{margin-top:2px}+p.s{margin:0 0 6px;font-size:12px;color:var(--text-3);line-height:1.5}+</style></head><body>++<h3>Pin 1, chip against footprint</h3>+<p class=s>The datasheet already says which pad is pin 1. What matters is whether pin 1 on the chip lands on pin 1 of the footprint, because when they disagree the part goes down rotated.</p>+<script src="https://verify-3i7rt4do98l3.adom.cloud/applet.js"></script>+<adom-verify page="STM32F103C8T6" fact="pin-1-registration" wiki-page="adom/stm32f103c8t6"+ chip-image="https://wiki.adom.inc/blob/component/stm32f103c8t6/pin1/chip-top.png"+ footprint-svg="https://wiki.adom.inc/blob/component/stm32f103c8t6/pin1/footprint.svg"></adom-verify>++<h3>Fusion / EAGLE library</h3>+<p class=s>Web control cannot build one: a bridge runs on Windows or macOS, adom-lbr is Linux-only, and a browser runs no binaries. This asks a Linux service that has it.</p>+<script src="https://lbr-5o0yi1odd4w1.adom.cloud/applet.js"></script>+<adom-fusion-lbr page="adom/stm32f103c8t6"></adom-fusion-lbr>++<h3>Price and stock</h3>+<p class=s>The one thing here that cannot be baked, because it changes without the page changing.</p>+<script src="https://component-ocean-jk89eeqplwfl.adom.cloud/applet.js"></script>+<adom-sourcing mpn="STM32F103C8T6" wiki-page="adom/stm32f103c8t6"></adom-sourcing>++<script>+function fit(){parent.postMessage({type:'readme-height',height:document.documentElement.scrollHeight},'*')}+addEventListener('load',fit);new ResizeObserver(fit).observe(document.documentElement);+</script>+</body></html>