PCB and tested firmware compatibility
Reviewed 2026-11-09 against current 0.0.11-dev KiCad pad nets, pinned application source, reviewed I/O transforms and frozen compiled-profile metadata. This is a source/electrical compatibility review with carrier build-input preparation; no board was powered or flashed and no finished OS image was built.
The carrier mapping matches the carrier application profile, but the currently
tested lvgl-dev.cb6170a37a5a image uses base-fork and must not be used unchanged
on either carrier PCB. This is a firmware-profile mismatch, not evidence that
the carrier should be rewired to match the separate bench display/button board.
The first-article target is the main variants/lcd-module-2.8 service revision
0.0.11-dev, retaining the always-on display topology with four layers and
43 fitted SMT components / 18 purchasing SKUs. The separate lcd-backlight-pwm 0.0.8-dev
candidate is not the order target. Manufacturing exports must be regenerated and
matched to the selected main revision; this audit does not promote a cart.
Findings and severity
| Severity / gate | Finding | Required action |
|---|---|---|
| Critical — before carrier power-up | Bench firmware drives GPIO56/57, carrier Left/Up switch nets, as LCD DC/reset outputs. Pressing those switches can short a driven high output to ground. | Use reviewed carrier pins, not the current bench image. Do not solve this by blindly swapping connector pins. |
| High — functional bring-up | All eight bench button assignments differ; UART2 is deliberately disabled and PCSC startup held in the bench profile. | Build/select a carrier-profile image; verify actual GPIO requests, internal pulls and UART resource policy. |
| High — correct LCD configuration | HOTHMI eight-pin wiring, 10 MHz limit and 320×240 geometry differ from the tested 240×240 module. | Keep carrier CS jumpers, explicit 10 MHz profile and st7789_320x240 setting; qualify colors/orientation/clock physically. |
| High — optional PWM only | Dedicated PWM exists only on the separate candidate. Current profile has bl: disabled; no application brightness/PWM path is qualified. |
Order main board for always-on behavior. If promoting PWM later, regenerate its own assets and implement/qualify the pinmux/control path. |
| Medium — first article | 1.8 V keys rely on correct-domain pulls; LCD regulator/load, assembly orientation and power behavior remain physical checks. | Verify continuity and rails before inserting modules, then test one article before quantity assembly. |
| No new carrier conflict found | SPI, voltage-domain topology, reader UART crossing and native camera connection agree with the intended carrier design. | Preserve these nets; schematic/DRC correctness does not prove assembled operation. |
Exact Luckfox header map
Physical pin numbers below are U202 module-header pad numbers, not Linux
GPIO numbers. GPIO global number = bank × 32 + line. /dev/gpiochipN must still
be confirmed on the actual image; labels alone do not prove runtime numbering.
Carrier firmware now resolves /dev/gpiochip-bank0, -bank1 and -bank4
from kernel chip labels at boot. Device indices can differ from bank numbers;
see the bank selection review.
All GPIO pins listed are 3.3 V except pins 19/20, which are 1.8 V.
| U202 pin | SoC / global GPIO | Main carrier net and intended function | carrier runtime |
|---|---|---|---|
| 1 | VBUS | +5V |
Power input |
| 2 | GND | GND |
Return |
| 3 | 3V3 OUT | +3.3V |
Host output; default backlight enable only |
| 4 | GPIO1_B2 / 42 | UART_TX → R602 → SEC1210 U601.11 RX |
UART2 TX |
| 5 | GPIO1_B3 / 43 | UART_RX ← R603 ← SEC1210 U601.14 TX |
UART2 RX |
| 6 | GPIO1_C0 / 48 | Net-(JP203-B) → JP203/JP205 → RPI_SPI_CS |
SPI0 CS0, /dev/spidev0.0 |
| 7 | GPIO1_C1 / 49 | LCD_SCLK |
SPI0 clock |
| 8 | GPIO1_C2 / 50 | LCD_MOSI |
SPI0 MOSI; display is write-only |
| 9 | GPIO1_C3 / 51 | LCD_RST |
gpiochip-bank1 line19 output |
| 10 | GPIO1_C4 / 52 | LCD_DC |
gpiochip-bank1 line20 output |
| 11 | GPIO1_C5 / 53 | Key3, SW508 → GND |
gpiochip-bank1 line21 input/pull-up |
| 12 | GPIO1_D0 / 56 | JoystickLeft, SW501 → GND |
gpiochip-bank1 line24 input/pull-up |
| 13 | GPIO1_D1 / 57 | JoystickUp, SW502 → GND |
gpiochip-bank1 line25 input/pull-up |
| 14 | GPIO1_D2 / 58 | I2C-SDA, no other main-board endpoint |
Unused; candidate changes this to LCD_PWM |
| 15 | GPIO1_D3 / 59 | I2C-SCL, no other endpoint |
Unused |
| 16 | GPIO1_C6 / 54 | JoystickPress, SW503 → GND |
gpiochip-bank1 line22 input/pull-up |
| 17 | GPIO1_C7 / 55 | JoystickDown, SW504 → GND |
gpiochip-bank1 line23 input/pull-up |
| 18 | GPIO0_A4 / 4 | JoystickRight, SW505 → GND |
gpiochip-bank0 line4 input/pull-up |
| 19 | GPIO4_C0 / 144, 1.8 V | Key1, SW506 → GND |
gpiochip-bank4 line16 input/pull-up |
| 20 | GPIO4_C1 / 145, 1.8 V | Key2, SW507 → GND |
gpiochip-bank4 line17 input/pull-up |
| 21 | GND | GND |
Return |
| 22 | 1V8 OUT | Explicitly unconnected | Do not tie to 3.3 V |
There are no external 3.3 V pull-ups on Key1/Key2: the only carrier load is a
normally open switch to ground. That topology is appropriate for a 1.8 V input.
Confirm GPIO4 pinmux/pulls and actual released/pressed readings; the pinned button
opener can retry without pull_up after GPIO errors. Do not enable conflicting
PWM10/11 pinmux functions on these button pads.
Why the working bench image is incompatible
The frozen camera bundle manifest records ioProfile: base-fork, and its
payload/kernel-camera-abi.json records /serial@ff4c0000 UART2 as disabled.
The base-fork resource policy holds S60pcscd because GPIO42/43 belong to buttons
on that bench assembly. The current image is not an implicit carrier profile.
| Function | Tested base-fork global GPIO | Required carrier global GPIO |
|---|---|---|
| LCD DC | 56 (carrier Left switch) | 52 |
| LCD reset | 57 (carrier Up switch) | 51 |
| Up | 58 | 57 |
| Down | 53 | 55 |
| Left | 59 | 56 |
| Right | 54 | 4 |
| Press | 52 | 54 |
| Key1 | 55 | 144 |
| Key2 | 43 (carrier UART RX) | 145 |
| Key3 | 42 (carrier UART TX) | 53 |
The earlier lcdpwm fork mapping is also a distinct profile: its display pins
match the carrier but five button semantics differ. Neither a fork branch name
nor the fact that both screens use ST7789 establishes compatibility.
HOTHMI LCD and default straps
Carrier J701 numbering reverses the HOTHMI vendor header numbering. Verify the actual module's marked pin1 before mating; the eight-pin modules discussed in research are not interchangeable by controller name alone.
| Carrier J701 pin | HOTHMI vendor pin | Net / function |
|---|---|---|
| 1 | 8 | RPI_BACKLIGHT / BL_E |
| 2 | 7 | LCD_RST |
| 3 | 6 | LCD_DC |
| 4 | 5 | RPI_SPI_CS |
| 5 | 4 | LCD_SCLK |
| 6 | 3 | LCD_MOSI |
| 7 | 2 | GND |
| 8 | 1 | LCD_3V3 / VCC |
Factory default: JP201/JP203/JP205 closed; JP202/JP204/JP206 open. JP201 holds BL_E high from host 3.3 V: always on, with no brightness software. JP203+JP205 connect host CS to LCD CS. Leave JP206 open; grounding CS is a different bus mode, not the intended default. On the main board, the legacy JP202/JP204 branch shares host CS rather than exposing an independent PWM output. Do not repurpose it as independent brightness control.
LCD VCC is the separate U701 regulator output LCD_3V3; it is not shorted to the
Luckfox 3.3 V output. U701 takes +5 V and has enable tied to +5 V. BL_E is a logic
enable, not GPIO-supplied backlight current. Regulator current/temperature and
startup logic levels still require measurement on the purchased module.
The retained HOTHMI drawing specifies a minimum write cycle of 66 ns; the reviewed profile sets 10 MHz, not the upstream 40 MHz default. Current carrier transforms already implement that clock, but a physical scope check is still needed. Color-inversion behavior was established on the bench module; HOTHMI RGBW, inversion, rotation and full 320×240 extents need their own check.
LVGL's renderer_bridge.py, runtime.py and luckfox.py contain a 320×240 path,
selected by settings key display_config: st7789_320x240. Default is 240×240.
That source support is not a HOTHMI physical acceptance result. The separate
PIL/Rust frame wrapper firmware/lcd-rs/python/shieldsigner_lcd_driver.py currently
rejects geometry other than 240×240. Active LVGL does not install that wrapper,
but do not promise the retained PIL rollback frontend works at 320×240 unchanged.
Required firmware integration
Carrier profile validation and staging inputs were updated during this review. No finished carrier OS image has been built or booted. The explicit selection points are:
scripts/prepare-camera-image.py --io-profile carrierselects the intended carrier map; use--variant dev --app-startup automatic --memory-profile zram16-lzo --lcd-backend rust --frontend-backend rustwhen preparing a comparable development bundle. Its compiled kernel proof must match the carrier profile; the frozen base-fork proof must not be relabeled. Follow the documented kernel probe/rebuild cycle if a matching proof is not available.configure-image-io-profile.py ... --profile carrierchanges only FOX_22 SPI speed to 10 MHz in the pinned source.configure-hothmi-display.pyindependently checks carrier DC52/reset51, kernel-managed CS and the same 10 MHz setting. A plain runtime--profileflag onapp.pydoes not select these pins.- Pass an explicitly verified carrier camera base to
mise run firmware:stage --name UNIQUE_NAME --camera PATH_TO_CARRIER_BASE. The command's default camera base is inherited from the currently selected image, which is base-fork. Do not accept that default for this PCB. - For OS building,
SHIELDSIGNER_CAMERA_BUNDLEselects the resulting combined bundle;SHIELDSIGNER_CAMERA_BACKEND=rust,SHIELDSIGNER_IMAGE_VARIANT=devandSHIELDSIGNER_READER_VARIANT=UR2select the corresponding build paths. There is no currentSHIELDSIGNER_IO_PROFILEswitch: profile travels in the bundle. - Verify packed
ioProfile, actual installedio_config.json, DTB pinmux and UART2 state, startup resources, settings geometry and 10 MHz clock before normal use. UART2 reader availability requires carrier resource policy and hardware/service qualification; this audit does not expand smartcard scope.
Separate PWM candidate
The 0.0.8-dev candidate changes only the dedicated backlight route: U202 pin14 /
GPIO1_D2 / gpiochip-bank1 line26 / global58 becomes LCD_PWM, feeding JP202. R703
5.1 kΩ pulls it low. With JP201 closed and JP202 open it remains always on.
Optional PWM uses JP201 open and JP202 closed; never close both together.
PWM requires disabling conflicting I2C3 on that pad and enabling PWM0_M1 in
the final DTB, then implementing a controller identified from the running device
tree rather than guessed pwmchip numbering. Current bl: disabled does none of
that. Low=off, high=on; PWM frequency/duty response, startup/shutdown and possible
camera flicker remain unqualified. This is not a reason to change the main order
or to claim existing software brightness support.
Camera, power and first article
The SC3336 camera attaches to the Luckfox's native camera/FPC interface; neither carrier routes those camera lanes through the LCD/button headers. No missing carrier camera connector or SPI/pin conflict was found. Existing Linux camera/ISP and Rust/ZXing behavior can be retained with the correctly profiled kernel; it still needs testing with the larger display and assembled power load.
J301 USB-C and Keystone TP901 (+5 V) / TP902 (GND) feed +5 V. J302 is removed. They have no source isolation or reverse-polarity protection. Use one regulated source at a time; account for the Luckfox module USB input too. USB power does not prove USB data/ADB connectivity through the carrier receptacle.
Before ordering, match Gerbers/BOM/CPL to the chosen main artwork revision and retain the default straps. Before powering the first article, verify connector orientation, no rail shorts, +5 V/LCD_3V3/host3V3 and correct LCD vendor pin order. Then use the carrier image and check all buttons/pulls, RGBW/full extents, supply current, regulator temperature and camera transitions. Native ERC/DRC and these net checks do not prove soldering, connector fit, actual voltage, pinmux, optics or physical timing. A first article remains necessary.
Evidence
- Carrier pinout audit, header map, module audit, host pin data
- Main native checks and PWM candidate checks
- Earlier fork mapping audit and base-fork physical observation
- Profile transform, HOTHMI clock guard, bench I/O fixture
- Current firmware acceptance
Pad maps were independently parsed from both native PCB files. Pre-artwork main
SHA-256: b4eb7a7cef3f026bfc332a19980727a165de9ea826e4eb25dcc68e2c458004b7;
PWM candidate: c5644d7a6eb18ef013d008f880bb0813b05b33892b87eaa982da4638e973521a.
The main artwork change must retain these electrical mappings; its new file hash
and regenerated checks belong to the final manufacturing/readiness record.
Current electrical review and image guard
The regenerated variant:lcd:pinouts report is bound to current PCB SHA-256
2f949d76b53bf75e4b6f56e5e68623900b1f3e9c2403770409e647953aabc93d.
It now checks exact net membership for every one of SW501–SW508, not just the
two 1.8 V keys. Each must have only its intended host input and normally-open
contact to GND. All eight match the pinned carrier application configuration.
prepare-camera-image.py now rejects carrier application packaging unless the
compiled-kernel ABI proof explicitly identifies the carrier profile and the
single enabled UART2 node at /serial@ff4c0000. Older proof files without that
evidence require a fresh kernel-only probe; they must not be relabeled. The
kernel-only bootstrap remains available to collect this evidence. Regression
checks run with mise run firmware:io:check. This guard does not inspect live
GPIO pulls, display timing or the installed image.
The compiled pinmux validator additionally checks SPI0 CLK/MOSI/CS0, leaves
GPIO1_C3 free for reset, rejects a competing framebuffer on CS0 and detects
default pinctrl consumers on all display/button GPIOs. The retained compiled
snapshot has a dormant DHT11 node on Down/GPIO1_C7 with CONFIG_DHT11=n;
enabling that driver without disabling its node fails carrier validation.
New carrier packages require compiledCarrierPinmux in their actual kernel
proof. See the current detailed review.
For the current pill, firmware:stage --display-config st7789_320x240 now
provides an explicit first-run display template when used with a verified
carrier camera base. The prepared combined bundle is documented in
the electrical review. Existing persisted display
settings take priority; use fresh first-article media or review that value.
The current explicit pill staging option also installs the
hothmi_htm_tft028a16_spi physical panel profile. It uses the manufacturer's
initialization program rather than the generic square-panel sequence; see
the current review and its manufacturer fixture.
The current combined stage is carrier-first-article-hothmi-alias-compatible.
Earlier geometry-only stages lack the panel program, and the superseded
hothmi-review-v2 stage assumes bank numbers equal Linux device indices.
Use the current stage and its bank-label startup service.
Source: docs/PCB-SOFTWARE-COMPATIBILITY.md ↗. Documentation describes reviewed evidence; it does not replace physical testing.