

Jc224364
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
2TSOP31438
SIP-3-2.54mm2.5V~5.5V 24m 350uA 38kHz 45° SIP-3-2.54mm Infrared Remote Receiver (IRM) ROHSVishay IntertechnologyInfrared Remote Receiver (IRM)ULN2003BD
SOIC-16-40℃~+105℃ 1.4V 10mA 30V 500mA 500nA 50V 5pF Seven channels SOIC-16 Darlington Transistor Arrays ROHSTexas InstrumentsDarlington Transistor Arrays
README
README.mdBlockBlast!
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
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| Qty | Value | Footprint | References |
|---|---|---|---|
| 8 | LOGO | LOGO | G***, G***, G***, G***, G***, G***, G***, G*** |
| 5 | 1.5k | ECE319K:R_Axial_DIN0204_L3.6mm_D1.6mm_P7.62mm_Horizontal | R4, R5, R6, R7, R8 |
| 5 | 10k | ECE319K:R_Axial_DIN0204_L3.6mm_D1.6mm_P7.62mm_Horizontal | R12, R13, R14, R15, R17 |
| 4 | 220 | ECE319K:R_Axial_DIN0204_L3.6mm_D1.6mm_P7.62mm_Horizontal | R1, R2, R3, R18 |
| 4 | LED | ECE319K:LED_D5.0mm | D1, D2, D3, D4 |
| 4 | ~ | ECE319K:MountingHole_4_40 | H1, H2, H3, H4 |
| 3 | 12k | ECE319K:R_Axial_DIN0204_L3.6mm_D1.6mm_P7.62mm_Horizontal | R9, R10, R11 |
| 2 | 0.1uF | ECE319K:C_Axial_200mil | C3, C4 |
| 1 | 1uF | ECE319K:CP_Radial_Tantal200mil | C1 |
| 1 | 20k | ECE319K:R_Axial_DIN0204_L3.6mm_D1.6mm_P7.62mm_Horizontal | R16 |
| 1 | 3.3V | ECE319K:Testpoint_1x02_P2.54mm | TP1 |
| 1 | 4.7uF | ECE319K:CP_Radial_Tantal200mil | C2 |
| 1 | DACOUT | ECE319K:Testpoint_1x02_P2.54mm | J7 |
| 1 | DOWN | ECE319K:SW_PUSH_6mm | SW3 |
| 1 | GND | ECE319K:Testpoint_1x02_P2.54mm | J6 |
| 1 | HiLetGo_ST7735R | ECE319K:hiletgo_st7735r | U4 |
| 1 | Joystick | ECE319K:SparkfunJoystick | J1 |
| 1 | LEFT | ECE319K:SW_PUSH_6mm | SW2 |
| 1 | LP-MSPM0G3507 | ECE319K:ti_LP_MSPM0G3507 | U2 |
| 1 | MCP34119P | ECE319K:DIP-8_W7.62mm_LongPads | U3 |
| 1 | RGHT | ECE319K:SW_PUSH_6mm | SW4 |
| 1 | Speaker | ECE319K:PinHeader_1x02_P2.54mm_Vertical | J2 |
| 1 | TExaS | ECE319K:PinHeader_1x03_P2.54mm_Vertical | JP1 |
| 1 | TSOP31438 | ECE319K:TSOP31438 | U6 |
| 1 | UART1 | ECE319K:PinHeader_1x03_P2.54mm_Vertical | J3 |
| 1 | ULN2003B | ECE319K:DIP-16_W7.62mm | U5 |
| 1 | UP | ECE319K:SW_PUSH_6mm | SW1 |
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 mm

- 2 layers114.3 × 88.9 mm

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 mm

- 2 layers114.3 × 88.9 mm

- 2 layers114.3 × 88.9 mm

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 mm

- 2 layers118 × 100 mm

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 mm
