GPIO Mapping Session
The Carryout G2’s firmware has a gpio submenu. We didn’t expect it to be particularly interesting — maybe a few pins for LEDs, maybe a relay output. Then we ran gpio regs and got back 92 pins worth of state data across five I/O ports.
That was the start of a long session cross-referencing live GPIO readings with the K60 datasheet pin mux table, boot log output, and the A3981 motor driver datasheet. By the end, we had a functional map of how the MCU talks to every major peripheral on the board.
The dump
Section titled “The dump”The GPIO submenu has four commands: dir <pin> (direction), r <pin> (read), w <pin> <val> (write), and regs (dump everything). The regs command is the one that matters:
TRK> gpioGPIO> regsPort A: A0=0 A1=0 A2=0 A3=0 A4=0 A5=1 A6=0 A7=0 A8=0 A9=0 A10=0 A11=0 A12=1 A13=0 A14=0 A15=0 A16=1 A17=1 A18=0 A19=0 A24=0 A25=0 A26=0 A27=0 A28=0 A29=0Port B: B0=1 B1=1 B2=1 B3=1 B4=0 B5=0 B6=0 B7=0 B8=0 B9=0 B10=0 B11=1 B16=0 B17=0 B18=0 B19=0 B20=0 B21=0 B22=0 B23=0Port C: ...Five ports (A through E), with each pin showing its current logic level. The first thing we noticed: A20-A23 and B12-B15 are absent. Not zero — absent. The MK60DN512VLQ10 in the 144-LQFP package doesn’t bond those pins out. The firmware knows which pins exist on its own package variant.
Then there’s E29:
Port E: ...E28=1 Unknown bit E29The firmware doesn’t know what to do with this one. It’s in the register space, but there’s no pin mux entry for it. The K60 reference manual shows E29 doesn’t exist on the 144-LQFP package — but the firmware still tries to read it and prints a confused message instead of silently skipping. A minor firmware bug, preserved in the GPIO register dump handler since 2013.
Walking the pins
Section titled “Walking the pins”The regs dump gives you state but not direction. For that, you need gpio dir <pin> on each pin individually. We walked all 92:
GPIO> dir E0E0: OUTPUTGPIO> dir E1E1: OUTPUTGPIO> dir E2E2: OUTPUTGPIO> dir E3E3: INPUTGPIO> dir E4E4: INPUTGPIO> dir E5E5: OUTPUTTedious, but necessary. The direction register tells you which pins the MCU is driving (OUTPUT) versus which it’s listening to (INPUT). Combined with the K60 datasheet’s pin mux table and the boot log, you can identify what each pin does.
SPI1 — the motor drivers
Section titled “SPI1 — the motor drivers”The boot log gave us the first clue:
SPI1 init @ 4 MHzMotor init: System=12Inch, master=40000 steps, slave=24960 steps, ratio=1.602564SPI1 at 4 MHz, mode 0x03 (CPOL=1, CPHA=1). The K60 datasheet shows SPI1 on port E:
| K60 Pin | GPIO | Alt Function | Live Dir | Live State | Assignment |
|---|---|---|---|---|---|
| PTE0 | E0 | SPI1_PCS1 | OUT | 1 | A3981 #2 chip select (EL motor) |
| PTE1 | E1 | SPI1_SOUT | OUT | 1 | MOSI — MCU to A3981 |
| PTE2 | E2 | SPI1_SCK | OUT | 1 | SPI clock |
| PTE3 | E3 | SPI1_SIN | IN | 0 | MISO — A3981 to MCU |
| PTE4 | E4 | SPI1_PCS0 | IN | 1 | A3981 #1 chip select (AZ motor) |
| PTE5 | E5 | SPI1_PCS2 | OUT | 1 | Possibly A3981 RESET or enable |
E4 shows INPUT in the GPIO direction register, but it’s muxed to SPI1_PCS0 — the SPI controller manages chip select assertion directly, so the GPIO direction is meaningless here. The live state of 1 (high) on the chip select lines means both A3981s are deselected (active-low CS), which is the expected idle state.
The A3981 is an Allegro stepper motor driver. Two of them on SPI1 — one for azimuth (PCS0, 40000 steps/rev), one for elevation (PCS1, 24960 steps/rev). They support 1/16 microstepping in AUTO mode, which matches what the firmware’s a3981 ss command reports.
We could confirm this from the a3981 submenu:
A3981> cmAZ Current Control Mode: AUTOEL Current Control Mode: AUTOA3981> smAZ Step Mode: AUTOEL Step Mode: AUTOA3981> diagAZ DIAG: OK EL DIAG: OKBoth drivers healthy, both in AUTO mode. The DIAG pins on the A3981 are active-low open-drain — the “OK” reading means the GPIO pins reading the fault output are pulled high. The exact GPIO pins for DIAG are TBD (we haven’t isolated them from the regs dump yet), but they’re likely somewhere in the cluster of unidentified input pins.
SPI2 — the DVB tuner
Section titled “SPI2 — the DVB tuner”SPI2 init @ 6.857 MHzBCM4515 ID 0x4515 Rev B0, FW v113.37SPI2 at 6.857 MHz, same mode 0x03. On port D:
| K60 Pin | GPIO | Alt Function | Live Dir | Live State | Assignment |
|---|---|---|---|---|---|
| PTD11 | D11 | SPI2_PCS0 | OUT | 1 | BCM4515 chip select |
| PTD12 | D12 | SPI2_SCK | IN | 1 | SPI clock |
| PTD13 | D13 | SPI2_SOUT | IN | 1 | MOSI — MCU to BCM4515 |
| PTD14 | D14 | SPI2_SIN | — | 0 | MISO — BCM4515 to MCU |
| PTD15 | D15 | SPI2_PCS1 | — | 0 | Secondary chip select (unused?) |
D12 and D13 show INPUT in the GPIO register despite being SPI clock and MOSI — again, the peripheral mux overrides GPIO direction. D15 is a secondary chip select that’s held low; likely unused (the BCM4515 only needs one CS).
The BCM4515 is a Broadcom DVB-S2 demodulator. It handles satellite signal reception — carrier tracking, forward error correction, NID (Network ID) detection. The firmware talks to it over SPI at nearly 7 MHz, which is fast enough for real-time signal monitoring (RSSI, AGC, SNR readings).
UART4 — the console we’re talking through
Section titled “UART4 — the console we’re talking through”GPIO> dir E24E24: OUTPUTGPIO> dir E25E25: INPUTGPIO> dir E26E26: INPUTPort E pins 24-28:
| K60 Pin | GPIO | Alt Function | Live Dir | Live State | Notes |
|---|---|---|---|---|---|
| PTE24 | E24 | UART4_TX | OUT | 1 | Console TX (to computer RX pair) |
| PTE25 | E25 | UART4_RX | IN | 1 | Console RX (from computer TX pair) |
| PTE26 | E26 | UART4_CTS | IN | 1 | Hardware flow control (idle high) |
| PTE27 | E27 | GPIO? | IN | 1 | Unknown — RTS, or just pulled up |
| PTE28 | E28 | GPIO? | IN | 1 | Unknown |
This is the RS-422 console port — the one we’re using to send all these queries. UART4_TX on E24 drives one differential pair of the RS-422 transceiver (which connects to pin 4/5 on the RJ-12, our RX pair). UART4_RX on E25 receives from the other pair (pin 2/3, our TX pair). CTS on E26 is idle high, meaning the firmware is ready to receive.
The K60 has five UART peripherals (UART0-4). UART4 is the last one, and it’s the debug console. The firmware probably uses UART0 or UART1 for the DVB tuner’s serial interface (some BCM4515 configurations use SPI + UART), but we haven’t confirmed that yet.
The mystery pins
Section titled “The mystery pins”After mapping the three major peripherals (SPI1, SPI2, UART4), we still had a bunch of pins in known states that we couldn’t attribute to specific functions:
| GPIO | Dir | State | Best guess |
|---|---|---|---|
| D10 | OUT | 1 | BCM4515 reset or power enable — it’s adjacent to the SPI2 cluster |
| B0-B3 | — | 1 | Contiguous high block — possibly SPI0 or I2C0, both available on port B |
| B11 | — | 1 | Isolated high pin — status LED or peripheral enable |
| C10-C13 | — | 1 | Four contiguous pins, all high — could be a bus interface or DIP switch reads |
| C18 | — | 1 | Single pin — LNB voltage control relay? The lnbdc odu command switches 13V/18V |
The DIP switch reads are particularly interesting. The dipswitch submenu returns val:ffffff01 — a 32-bit register read. The 0xffffff01 pattern (bits 1-24 all high, bit 0 high) suggests GPIO pins with internal pullups and all switches in the OFF/up position. Port C has enough pins in the right state to be candidates, but without being able to flip individual switches during a GPIO read, we can’t confirm the mapping.
What the map tells us
Section titled “What the map tells us”The functional pin map isn’t just an academic exercise. It tells us what’s possible:
Motor control is SPI-based, not bit-banged. The A3981 drivers are on a proper SPI bus with dedicated chip selects. The MCU can read back driver status (fault flags, step mode, current mode) in real time. This is why the a3981 diag command works — it’s doing an SPI read of the DIAG register, not just checking a GPIO fault pin.
The DVB tuner has a high-bandwidth connection. SPI2 at 6.857 MHz means the MCU can stream signal data from the BCM4515 fast enough for real-time RSSI and AGC monitoring. The dvb agc streaming command works because the bus can sustain the data rate.
The UART has hardware flow control available. CTS is wired (E26). If we ever need to send large data blocks to the firmware (firmware updates, configuration dumps), we have flow control to prevent buffer overruns. Currently unused by our code since command/response at 115200 never overruns.
There are unaccounted pins. Port B0-B3, C10-C13, and several others are in definite states but unmapped. These could be additional peripherals (I2C EEPROM? second UART? temperature sensor?) or just board-level power control. A board-level reverse engineering session (tracing PCB traces under a microscope) would resolve these, but we’d need to open the dish enclosure for that.
The GPIO map is a snapshot — it captures the board’s state at idle, after boot, with the tracker disabled. Different states during motor movement or DVB tuning would show different patterns (chip select toggling, SPI activity, motor driver current modes changing from LOW to HIGH torque). But as a static reference for “what’s connected to what,” it’s the most detailed view we have without physical board access.