Hardware Prototype Planning
Table
| Layer | Function | Copper |
|---|---|---|
| Top | Signal/components | 0.070 mm / 2 oz |
| Mid-Layer 1 | Solid GND plane | 0.035 mm / 1 oz |
| Mid-Layer 2 | Power plane | 0.035 mm / 1 oz |
| Bottom | Signal | 0.070 mm / 2 oz |
custom-4-layerTable
| Layer | Role | Copper thickness | Weight |
|---|---|---|---|
| Top | Signal | 0.070 mm | 2 oz |
| Mid-Layer 1 | Ground plane | 0.035 mm | 1 oz |
| Mid-Layer 2 | Power plane | 0.035 mm | 1 oz |
| Bottom | Signal | 0.070 mm | 2 oz |
esp32s3.Table
| Module pin | Net | Direction at U5 | Peripheral / role | Interrupt | Boot / strap note |
|---|---|---|---|---|---|
| EN | ESP_EN | Input, active high | Module enable; J7 pin 9 | No | External 10 kΩ pull-up; service may pull low |
| IO0 | ESP_GPIO0_BOOT | Input during reset; GPIO later | J7 pin 10 download-boot control | GPIO interrupt capable after boot | Strap: internal weak pull-up; ground only during reset for download mode |
| IO5 | AVR_SPI_SCK | Input | ESP-side SPI slave clock from U4 | SPI peripheral | Not a strap |
| IO6 | AVR_SPI_MOSI | Input | ESP-side SPI slave MOSI from U4 | SPI peripheral | Not a strap |
| IO7 | AVR_SPI_MISO | Output only while selected | ESP-side SPI slave MISO to U4 | SPI peripheral | Must tri-state when IO8/CS is inactive |
| IO8 | AVR_SPI_SS | Input | ESP-side SPI slave chip select from U4 | GPIO/SPI | Not a strap |
| IO9 | AVR_RESET | Output, active low | U5 owns U4 reset; J7 pin 4 also exposes reset | GPIO | Drive low only intentionally |
| IO10 | ETH_SPI_SCK | Output | Shared W5500 SPI clock | SPI master | Shared by U7 and U7B |
| IO11 | ETH_SPI_MOSI | Output | Shared W5500 SPI MOSI | SPI master | Shared by U7 and U7B |
| IO12 | ETH_SPI_MISO | Input | Shared W5500 SPI MISO | SPI master | Shared by U7 and U7B |
| IO13 | ETH_CS_LAN_N | Output, active low | U7 LAN W5500 chip select | GPIO | Default high before SPI transactions |
| IO14 | ETH_CS_DISPLAY_N | Output, active low | U7B display W5500 chip select | GPIO | Default high before SPI transactions |
| IO15 | ETH_INT_LAN_N | Input, active low | U7 interrupt | GPIO interrupt capable | Not a strap |
| IO16 | ETH_INT_DISPLAY_N | Input, active low | U7B interrupt | GPIO interrupt capable | Not a strap |
| IO17 | ETH_RST_LAN_N | Output, active low | U7 reset | GPIO | Hold low during rail startup |
| IO18 | ETH_RST_DISPLAY_N | Output, active low | U7B reset | GPIO | Hold low during rail startup |
| IO19 | USB_DN_MCU | Bidirectional analog USB | Native USB D− through U6 ESD array | USB peripheral | Not general GPIO while USB is enabled |
| IO20 | USB_DP_MCU | Bidirectional analog USB | Native USB D+ through U6 ESD array | USB peripheral | Not general GPIO while USB is enabled |
| IO35 | PSE_I2C_SCL | Open-drain bidirectional | TPS23861 SCL | I2C | Dedicated PSE bus |
| IO36 | PSE_I2C_SDA | Open-drain bidirectional | TPS23861 SDAI/SDAO | I2C | Dedicated PSE bus |
| IO37 | PSE_INT_N | Input, active low | TPS23861 interrupt | GPIO interrupt capable | Use pull-up if required by final firmware/bench behavior |
| IO38 | PSE_RESET_N | Output, active low | TPS23861 reset | GPIO | R76 10 kΩ pull-down holds PSE reset until firmware releases it |
| IO39 | PSE_SHUTDOWN_N | Output, active low | TPS23861 shutdown | GPIO | Initialize low before releasing PSE reset |
| TXD0 | ESP_UART0_TX | Output | J7 pin 7 service/program UART | UART0 | ROM log/programming path |
| RXD0 | ESP_UART0_RX | Input | J7 pin 8 service/program UART | UART0 | ROM programming path |
| 3V3/GND | +3V3/GND | Power | Module supply/return | — | All module grounds connected |
Table
| Pin | Net | Direction | Peripheral / role | Interrupt capability | Ownership note |
|---|---|---|---|---|---|
| PB3 | FRAM_CS_N | Output, active low | MB85RS256B chip select | PCINT11 | U4 owns FRAM |
| PB4 | AVR_SPI_SS | Output, active low | Selects U5 SPI-slave link | PCINT12 | Keep configured output so U4 remains SPI master |
| PB5 | AVR_SPI_MOSI | Output | Shared U4-master MOSI to FRAM and U5 | PCINT13 | J7 pin 2 tap |
| PB6 | AVR_SPI_MISO | Input | Shared MISO from selected FRAM or U5 | PCINT14 | J7 pin 1 tap; only selected slave may drive |
| PB7 | AVR_SPI_SCK | Output | Shared U4-master clock to FRAM and U5 | PCINT15 | J7 pin 3 tap |
| PC0 | AVR_I2C_SCL | Open-drain bidirectional | DS3231 and ATECC608B SCL | PCINT16 | 4.7 kΩ pull-up to +5 V |
| PC1 | AVR_I2C_SDA | Open-drain bidirectional | DS3231 and ATECC608B SDA | PCINT17 | 4.7 kΩ pull-up to +5 V |
| PD0 | MCU_UART0_RX | Input | USART0 receive from selected isolated serial receiver | PCINT24 / USART RX IRQ | Timing-critical machine serial owned by U4 |
| PD1 | MCU_UART0_TX | Output | USART0 transmit to isolated serial channels | PCINT25 / USART TX IRQ | Configured for 9-bit serial where required |
| PD2 | SER_SEL_SAS_N | Output, active low | Enables SAS receive selection | INT0 / PCINT26 available but pin used as output | Not used as USART1 RX |
| PD3 | SER_SEL_COM1_N | Output, active low | Enables COM1 receive selection | INT1 / PCINT27 available but pin used as output | Also controls Q3 gate path |
| PD4 | SER_SEL_COM2_N | Output, active low | Enables COM2 receive selection | PCINT28 | Also controls Q4 gate path |
| PA0 | PULSE_CREDIT_IN | Input | Isolated credit pulse | PCINT0 | Timing-critical machine input |
| PA1 | PULSE_COIN_IN | Input | Isolated coin pulse | PCINT1 | Timing-critical machine input |
| PA2 | BYPASS_IN | Input | Isolated bypass state | PCINT2 | Machine input |
| PA3 | TAMPER_IN | Input | Isolated tamper state | PCINT3 | Machine input |
| PA4 | CREDIT_RETURN_DRV | Output | Credit-return optocoupler drive | PCINT4 | U4 owns machine output timing |
| PA5 | CREDIT_INJECT_DRV | Output | Credit-inject optocoupler drive | PCINT5 | U4 owns machine output timing |
| PA6 | ERROR_LAMP_DRV | Output | Error-lamp optocoupler drive | PCINT6 | U4 owns machine output timing |
| PA7 | SYSTEM_ENABLE_DRV | Output | System-enable optocoupler drive | PCINT7 | Default inactive at reset |
| RESET | AVR_RESET | Input, active low | Reset from U5 IO9 and J7 pin 4 | Reset vector | U5 owns remote reset; service header can also assert |
| XTAL1/XTAL2 | AVR_XTAL1/2 | Clock | 16 MHz crystal and 18 pF C0G loads | — | External clock source |
| AREF | AVR_AREF | Analog reference | 100 nF to GND | — | No external driven reference |
| VCC/AVCC/GND | +5V/GND | Power | MCU supplies and returns | — | Each supply pin locally bypassed |
Table
| Pin | Signal | Purpose |
|---|---|---|
| 1 | AVR_SPI_MISO | AVR ISP/debug data out |
| 2 | AVR_SPI_MOSI | AVR ISP/debug data in |
| 3 | AVR_SPI_SCK | AVR ISP clock |
| 4 | AVR_RESET | AVR programming/reset |
| 5 | +5V | AVR-side service reference/power |
| 6 | GND | AVR-side return |
| 7 | ESP_UART0_TX | ESP ROM/service UART TX |
| 8 | ESP_UART0_RX | ESP ROM/service UART RX |
| 9 | ESP_EN | ESP enable/reset control |
| 10 | ESP_GPIO0_BOOT | ESP download-boot strap |
| 11 | +5V | Service power exposure; do not connect to ESP 3.3 V signals |
| 12 | +3V3 | ESP logic reference |
| 13 | GND | Return |
| 14 | GND | Return |
| 15–18 | NC | Reserved; do not populate undocumented functions |
| 19–20 | GND | Return/shielding contacts |
main/main.c)C
#include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/gpio.h" #include "driver/i2c_master.h" #include "driver/spi_master.h" #include "driver/spi_slave.h" #include "esp_check.h" #include "esp_log.h" #define ETH_SCLK 10 #define ETH_MOSI 11 #define ETH_MISO 12 #define ETH_CS_LAN 13 #define ETH_CS_DISPLAY 14 #define ETH_INT_LAN 15 #define ETH_INT_DISPLAY 16 #define ETH_RST_LAN 17 #define ETH_RST_DISPLAY 18 #define AVR_SPI_SCLK 5 #define AVR_SPI_MOSI 6 #define AVR_SPI_MISO 7 #define AVR_SPI_CS 8 #define AVR_RESET 9 #define PSE_SCL 35 #define PSE_SDA 36 #define PSE_INT 37 #define PSE_RESET 38 #define PSE_SHUTDOWN 39 static const char *TAG = "intercede"; static i2c_master_bus_handle_t pse_i2c; static spi_device_handle_t w5500_lan, w5500_display; static void gpio_safe_defaults(void) { gpio_config_t out = {.pin_bit_mask = (1ULL<<ETH_CS_LAN)|(1ULL<<ETH_CS_DISPLAY)| (1ULL<<ETH_RST_LAN)|(1ULL<<ETH_RST_DISPLAY)|(1ULL<<AVR_RESET)| (1ULL<<PSE_RESET)|(1ULL<<PSE_SHUTDOWN), .mode = GPIO_MODE_OUTPUT}; ESP_ERROR_CHECK(gpio_config(&out)); gpio_set_level(ETH_CS_LAN, 1); gpio_set_level(ETH_CS_DISPLAY, 1); gpio_set_level(ETH_RST_LAN, 0); gpio_set_level(ETH_RST_DISPLAY, 0); gpio_set_level(AVR_RESET, 0); gpio_set_level(PSE_SHUTDOWN, 0); gpio_set_level(PSE_RESET, 0); gpio_config_t in = {.pin_bit_mask=(1ULL<<ETH_INT_LAN)|(1ULL<<ETH_INT_DISPLAY)|(1ULL<<PSE_INT), .mode=GPIO_MODE_INPUT, .pull_up_en=GPIO_PULLUP_ENABLE}; ESP_ERROR_CHECK(gpio_config(&in)); } static void init_pse_i2c(void) { i2c_master_bus_config_t cfg = {.i2c_port=I2C_NUM_0, .sda_io_num=PSE_SDA, .scl_io_num=PSE_SCL, .clk_source=I2C_CLK_SRC_DEFAULT, .glitch_ignore_cnt=7, .flags.enable_internal_pullup=true}; ESP_ERROR_CHECK(i2c_new_master_bus(&cfg, &pse_i2c)); } static void init_w5500_spi(void) { spi_bus_config_t bus = {.sclk_io_num=ETH_SCLK,.mosi_io_num=ETH_MOSI,.miso_io_num=ETH_MISO, .quadwp_io_num=-1,.quadhd_io_num=-1,.max_transfer_sz=2048}; ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &bus, SPI_DMA_CH_AUTO)); spi_device_interface_config_t dev = {.clock_speed_hz=20000000,.mode=0,.spics_io_num=-1,.queue_size=4}; ESP_ERROR_CHECK(spi_bus_add_device(SPI2_HOST, &dev, &w5500_lan)); ESP_ERROR_CHECK(spi_bus_add_device(SPI2_HOST, &dev, &w5500_display)); /* TODO(application): implement W5500 variable-length data mode and manually assert the selected CS only. */ } static void init_avr_spi_slave(void) { spi_bus_config_t bus = {.sclk_io_num=AVR_SPI_SCLK,.mosi_io_num=AVR_SPI_MOSI, .miso_io_num=AVR_SPI_MISO,.quadwp_io_num=-1,.quadhd_io_num=-1,.max_transfer_sz=256}; spi_slave_interface_config_t slv = {.spics_io_num=AVR_SPI_CS,.mode=0,.queue_size=3}; ESP_ERROR_CHECK(spi_slave_initialize(SPI3_HOST, &bus, &slv, SPI_DMA_CH_AUTO)); /* TODO(application): queue framed full-duplex transactions; define CRC, command IDs and READY semantics in protocol. */ } void app_main(void) { gpio_safe_defaults(); init_pse_i2c(); init_w5500_spi(); init_avr_spi_slave(); vTaskDelay(pdMS_TO_TICKS(20)); gpio_set_level(AVR_RESET, 1); gpio_set_level(ETH_RST_LAN, 1); gpio_set_level(ETH_RST_DISPLAY, 1); /* Keep PSE_RESET low until PSE_54V is bench-verified. Then release RESET, wait >=22 ms, read the TPS23861 identity/status, and only then deassert SHUTDOWN per product policy. */ ESP_LOGI(TAG, "safe interfaces initialized; PSE remains disabled"); while (1) vTaskDelay(pdMS_TO_TICKS(1000)); }
Sh
idf.py set-target esp32s3 idf.py build
src/main.c)C
#define F_CPU 16000000UL #include <avr/io.h> #include <avr/interrupt.h> #include <util/twi.h> #include <util/delay.h> #define FRAM_CS PB3 #define ESP_CS PB4 static void gpio_init(void) { DDRA = 0xF0; /* PA0-3 inputs; PA4-7 outputs */ PORTA = 0x00; /* outputs default inactive; external pull-ups serve inputs */ DDRD = _BV(PD1)|_BV(PD2)|_BV(PD3)|_BV(PD4); PORTD = _BV(PD2)|_BV(PD3)|_BV(PD4); /* all serial receivers deselected */ } static void spi_init(void) { DDRB |= _BV(FRAM_CS)|_BV(ESP_CS)|_BV(PB5)|_BV(PB7); DDRB &= ~_BV(PB6); PORTB |= _BV(FRAM_CS)|_BV(ESP_CS); SPCR = _BV(SPE)|_BV(MSTR)|_BV(SPR0); /* mode 0, F_CPU/16 */ } static uint8_t spi_xfer(uint8_t v){ SPDR=v; while(!(SPSR&_BV(SPIF))); return SPDR; } static void twi_init(void) { TWSR = 0; TWBR = 72; /* 100 kHz at 16 MHz */ TWCR = _BV(TWEN); } static void uart0_init_9bit(uint16_t ubrr) { UBRR0H = (uint8_t)(ubrr>>8); UBRR0L=(uint8_t)ubrr; UCSR0B = _BV(RXEN0)|_BV(TXEN0)|_BV(RXCIE0)|_BV(UCSZ02); UCSR0C = _BV(UCSZ01)|_BV(UCSZ00); /* 9 data bits, 1 stop, no parity */ } static void uart0_put9(uint16_t v) { while(!(UCSR0A&_BV(UDRE0))); if(v&0x100) UCSR0B|=_BV(TXB80); else UCSR0B&=~_BV(TXB80); UDR0=(uint8_t)v; } static volatile uint8_t machine_edges; ISR(PCINT0_vect) { machine_edges |= (PINA & 0x0F); } ISR(USART0_RX_vect) { uint16_t word = (UCSR0B & _BV(RXB80)) ? 0x100 : 0; word |= UDR0; (void)word; /* TODO(application): selected SAS/COM framing and message parser. */ } static void machine_irq_init(void) { PCICR |= _BV(PCIE0); PCMSK0 = 0x0F; /* PA0..PA3 */ } int main(void) { cli(); gpio_init(); spi_init(); twi_init(); uart0_init_9bit(16); /* 57600 baud */ machine_irq_init(); sei(); for (;;) { PORTB &= ~_BV(ESP_CS); uint8_t status = spi_xfer(0x00); /* TODO(application): replace with framed command/CRC protocol. */ PORTB |= _BV(ESP_CS); (void)status; _delay_ms(10); } }
Sh
avr-gcc -mmcu=atmega1284p -DF_CPU=16000000UL -Os -o main.elf src/main.c avr-objcopy -O ihex -R .eeprom main.elf main.hex
Scope and toolchains
U5 ESP32-S3 resource map
U4 ATmega1284P resource map
J7 validated service map
ESP-IDF starter (`main/main.c`)
AVR-GCC starter (`src/main.c`)
Startup ownership