Skip to the content
PCBKitNew board
2 layers114.3 × 88.9 mmJc224364, the printed circuit board in 3D

Using knowledge from ECE 319H at UT Austin (Intro to Embedded Systems) to build a custom video game console and to program Block Blast from scratch.

Size
114.3 × 88.9 mm
Copper layers
2
Thickness
1.6 mm
Parts
54
Nets
73
Pads
184
Tracks, vias
348, 26
Pours
1
Routed
Yes
KiCad
KiCad 9.0 (file 20241229)
Created
17 Mar 2026
Updated
22 Apr 2026

Overview

Jc224364 is an open-source printed circuit board by se1yu, from the se1yu/BlockBlast repository on GitHub. It is a 2-layer board of 114.3 × 88.9 mm with 54 parts on 73 nets, routed with 348 tracks and 26 vias.

It is built around the TSOP31438 and ULN2003BD. Its parts include 18 resistors, 5 ICs, 5 connectors, 4 capacitors and 4 diodes. It belongs with the retro computing and games boards of this library.

Main components

2

README

README.md

BlockBlast!

Using knowledge from ECE 319H at UT Austin (Intro to Embedded Systems) to build a custom video game console and to program Block Blast from scratch.

Block Blast is a puzzle game I implemented for a MSPM0G3507 microcontroller, displayed on a 128×160 ST7735 Liquid Crystal Display (LCD) screen. The player is presented with an 8×8 grid and a tray of three randomly selected blocks. Using a joystick and four external buttons, the player picks up blocks from the tray, moves them around the grid, and drops them into position. When an entire row or column of the grid becomes filled, it clears and the player earns points. The game ends when any remaining block in the tray has no valid position anywhere on the board. The game supports English and Spanish, tracks a persistent high score across rounds, and features four distinct sound effects triggered by gameplay events.

Images

img_1748 img_1751 img_1343 img_1342 img_1750

System Architecture

There are two ISRs running concurrently: Armed with TimerG12_IntArm(80000000/30, 2), which fires an interrupt every 30ms. It does three things each tick:

  • Calls JoyStick_In(&JoyX, &JoyY) — which internally triggers ADC_InDual (analog-to-digital-convertor) on ADC1 channels 4 and 6 (PB17 and PB19), sampling both joystick axes simultaneously via a dual-channel 12-bit ADC conversion. According to the Nyquist Theorem, an input should be sampled at a frequency at least twice the maximum input frequency. 30Hz (30 times a second) sampling is well above the maximum frequency a human wrist can move a joystick!
  • Calls Switch_In() — reads the four external buttons wired to PA24–PA27, returning a 4-bit value where each bit represents one directional button that is currently being pressed (right/down/left/up).
  • Stores results in the global variables JoyX, JoyY, and sw_status for the main program to access these external hardware updates.

Regarding Audio, we have the SysTick_Handler — Audio DAC at 11 kHz: Armed with SysTick_IntArm(80000000/11000, 0), this fires every ~90µs and drives the 5-bit binary-weighted DAC on PB0–PB4. It maintains three global variables: pointer to sound array(values to be played to output sound), arrayCount (total # of bytes per sound array), and arrayIndex (current position). Each tick it outputs pointer[arrayIndex] to DAC5_Out() and increments the index. When the array is exhausted it disables the interrupt. Sound is started by calling Sound_Start(pt, count) which resets the index and re-enables the interrupt. This ISR runs at higher priority (0) than the game timer (2) so audio never stutters during gameplay.

ADC & Joystick

JoyStick_Init() calls ADC_InitDual(ADC1, 4, 6, ADCVREF_VDDA), configuring ADC1 to sequentially sample two channels in one software trigger: channel 4 (PB17, horizontal) and channel 6 (PB19, vertical). ADC_InDual triggers the conversion and "busy-waits" for both results, returning values 0–4095 for each axis. The neutral position is approximately 2048. In Game_Tick, the bounding numbers of 1000 and 3000 define deflection zones — below 1000 means pushed one way, above 3000 means the other. The joystick counts as two slide potentiometers sampled periodically by the ADC at 30 Hz inside the timer ISR.

Switches/Buttons

Switch_Init() configures PA24–PA27 as digital inputs with PINCM = 0x40081 (input enable + pull-up). Switch_In() reads GPIOA->DIN31_0, masks bits 24–27, and right-shifts by 24 to produce a clean 4-bit value. These are four external buttons wired:

  • Bit 0 = right (select / confirm)
  • Bit 1 = down (place block)
  • Bit 2 = left (back / cancel, requires ~1 second hold on game screen to go backwards)
  • Bit 3 = up (pick up block / cancel pickup)

Sound

Four gameplay sounds are defined in sounds.h as const uint8_t arrays of 5-bit DAC samples recorded at 11 kHz:

  • Block_Pickup() — plays when the player picks up a block from the tray
  • Block_Place() — plays on successful block placement
  • Block_Reject() — plays when a drop is attempted in an invalid position
  • Block_Clear() — plays when one or more rows or columns are cleared Each calls Sound_Start(array, count) which loads the pointer/counter globals and enables the SysTick interrupt. The DAC is a 5-bit R-2R ladder on PB0–PB4, giving 32 voltage levels from 0 to 3.3V.

Sprites & Graphics

All sprites I drew in Microsoft Paint are stored as const uint16_t arrays in images.h in RGB565 format (16 bit). ST7735_DrawBitmap(x, y, ptr, w, h) renders them with y as the bottom pixel edge. The active sprites in gameplay are:

  • Home (128×160) — the menu/title screen background
  • grid (128×160) — the full game screen including the 8×8 grid and tray area background
  • single (12×12) — 1×1 cell
  • miniwide (24×12) — 2×1 cells
  • minitall (12×24) — 1×2 cells
  • minisquare (24×24) — 2×2 cells
  • wide (36×12) — 3×1 cells
  • tall (12×36) — 1×3 cells
  • square (36×36) — 3×3 cells Moving sprite: The held block moves across the grid in 12px increments (one cell) responding to joystick deflection. On each move, ST7735_DrawBitmap(grid) redraws the background, all filled cells are repainted as solid 10×10px colored squares inside each 12×12 cell, the tray is redrawn, and finally the held block is drawn at its new position.

Grid & Game Logic

The grid state is a uint8_t grid_matrix[8][8] where 0 = empty and 1 = filled. Block types are described by a BlockType_t struct containing the sprite pointer, pixel dimensions, and cell footprint. The active tray is traySlotType[3] — indices into allBlockTypes[7] — randomized each respawn using a linear congruential generator from SysTick->VAL at game start for true randomness. Random32() implements a linear congruential generator: M = 1664525*M + 1013904223. RandomType() uses (Random32() >> 24) % 7 — taking bits 31–24 for a cycle length of 2²⁴ rather than the short-cycling low bits. The seed M = SysTick->VAL is set once at Game_Init(), capturing the hardware counter at the moment the player starts the game, ensuring a different block sequence every round.

The canPlace(slot, col, row) checks bounds and scans the footprint against the matrix. stampBlock writes 1s into the matrix. checkAndClearLines() after every placement scans all 8 rows and 8 columns, marks full ones, then zeroes any cell whose row or column was full — handling intersections correctly without double-counting. isGameOver() iterates all available tray blocks and calls canBlockFitAnywhere() which scans every valid grid position; if any available block has zero valid placements, the game ends.

LEDs

Three LEDs on PA15–PA17 indicate which tray slot is currently selected. LED_On(data) left-shifts data by 15 to align with the GPIO pins. Only the LED corresponding to traySelection lights up, and it goes dark when that slot's block is picked up or placed.

Language Support

All user-facing strings exist in both English and Spanish. A lang variable (0 = English, 1 = Spanish) indexes into parallel string arrays for menu labels, tutorial text, and prompts. The language cycles with the Language menu option and persists through the game session.

Parts

54
QtyValueFootprintReferences
8LOGOLOGOG***, G***, G***, G***, G***, G***, G***, G***
51.5kECE319K:R_Axial_DIN0204_L3.6mm_D1.6mm_P7.62mm_HorizontalR4, R5, R6, R7, R8
510kECE319K:R_Axial_DIN0204_L3.6mm_D1.6mm_P7.62mm_HorizontalR12, R13, R14, R15, R17
4220ECE319K:R_Axial_DIN0204_L3.6mm_D1.6mm_P7.62mm_HorizontalR1, R2, R3, R18
4LEDECE319K:LED_D5.0mmD1, D2, D3, D4
4~ECE319K:MountingHole_4_40H1, H2, H3, H4
312kECE319K:R_Axial_DIN0204_L3.6mm_D1.6mm_P7.62mm_HorizontalR9, R10, R11
20.1uFECE319K:C_Axial_200milC3, C4
11uFECE319K:CP_Radial_Tantal200milC1
120kECE319K:R_Axial_DIN0204_L3.6mm_D1.6mm_P7.62mm_HorizontalR16
13.3VECE319K:Testpoint_1x02_P2.54mmTP1
14.7uFECE319K:CP_Radial_Tantal200milC2
1DACOUTECE319K:Testpoint_1x02_P2.54mmJ7
1DOWNECE319K:SW_PUSH_6mmSW3
1GNDECE319K:Testpoint_1x02_P2.54mmJ6
1HiLetGo_ST7735RECE319K:hiletgo_st7735rU4
1JoystickECE319K:SparkfunJoystickJ1
1LEFTECE319K:SW_PUSH_6mmSW2
1LP-MSPM0G3507ECE319K:ti_LP_MSPM0G3507U2
1MCP34119PECE319K:DIP-8_W7.62mm_LongPadsU3
1RGHTECE319K:SW_PUSH_6mmSW4
1SpeakerECE319K:PinHeader_1x02_P2.54mm_VerticalJ2
1TExaSECE319K:PinHeader_1x03_P2.54mm_VerticalJP1
1TSOP31438ECE319K:TSOP31438U6
1UART1ECE319K:PinHeader_1x03_P2.54mm_VerticalJ3
1ULN2003BECE319K:DIP-16_W7.62mmU5
1UPECE319K:SW_PUSH_6mmSW1

Questions

How big is the Jc224364 PCB?

It measures 114.3 × 88.9 mm and has 2 copper layers, 1.6 mm thick.

What parts does the Jc224364 use?

54 parts, built around the TSOP31438 and ULN2003BD. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.

Where can I download the Jc224364 KiCad files?

From the se1yu/BlockBlast repository on GitHub, at the commit this page was made from: the board is hardware/jc224364.kicad_pcb and its schematic hardware/jc224364.kicad_sch. PCBKit opens the board in the browser, with nothing to install.

Can I use the Jc224364 design in my own project?

Its repository states no licence, so its author keeps all rights: ask se1yu before reusing the design.

Similar boards

  • 4 layers179.3 × 72.5 mmStarter, the printed circuit board in 3D

    Starter

    alicedimauro/Tilt

    59 parts1
  • 2 layers114.3 × 88.9 mmCrickHit PCB, the printed circuit board in 3D

    CrickHit PCB

    devanshjoshi08/CrickHit

    Real-time cricket batting game on the TI MSPM0G3507 (ARM Cortex-M0+) with triple-ISR architecture, custom KiCad PCB, 5-bit DAC audio, and 3D-printed enclosure. Built for ECE 319H at UT Austin.

    54 parts1
  • 2 layers132.5 × 83 mmAM96353, the printed circuit board in 3D

    AM96353

    ananth-abbaraju/319H_PCB

    44 parts
  • 2 layers114.3 × 88.9 mmKC47852, the printed circuit board in 3D

    KC47852

    KaitlynWChang/ECE-319H-PCB

    53 parts
  • 2 layers114.3 × 88.9 mmMVM833, the printed circuit board in 3D

    MVM833

    SiddharthaG09/319KFinalProject

    For our final project, we recreated Space Invaders in C, using a custom-designed PCB and an MSPM0G3507 microcontroller.

    46 partsMIT
  • 2 layers140.8 × 66.4 mmM0+rixPCB, the printed circuit board in 3D

    M0+rixPCB

    SuryaUT/TheM0trix_Segfaulted

    ECE319H Game

    50 parts
  • 2 layers118 × 100 mmDuelTank, the printed circuit board in 3D

    DuelTank

    SathvikY3533/DuelTanks

    Duel Tanks is a real-time two-player artillery game built completely from scratch on the MSPM0G3507 microcontroller.

    72 parts
  • 2 layers203 × 26.6 mmBlade13, the printed circuit board in 3D

    Blade13

    opulo-inc/lumenpnp

    The LumenPnP is an open source pick and place machine.

    54 parts3.7k