PSoC 6 Blackjack
A complete game of Blackjack running on a PSoC 6 microcontroller with an LCD, joystick, push buttons, SPI EEPROM, and an I2C IO expander, built with Ryan O'Sullivan for ECE 353 at UW–Madison in spring 2025. The interesting part isn't the card game; it's how the firmware is organized. Rather than a single main loop polling everything, the application is a set of FreeRTOS tasks that only talk to each other through RTOS primitives.
One task per game state. Start, Shuffle, Bet, Dealer Show, Player Hit, Dealer Hit, and Hand Complete are each a task blocked on ulTaskNotifyTake(). A state transition is a single xTaskNotifyGive() to the next state's handle: no dispatcher, no state enum, and exactly one FSM task runnable at any time.
Gatekeeper tasks own the hardware. The LCD, SPI EEPROM, I2C IO expander, and UART console each have one task that is the only code allowed to touch that bus. Game logic sends commands through queues, which removes bus-reentrancy races entirely. EEPROM reads carry the caller's own reply-queue handle in the request message, so multiple states can share the EEPROM task without clobbering each other's answer.
Input fans in through an event group. Debounced button and joystick tasks run at a higher priority than the FSM, so input is never missed while a state is drawing. They set bits in a shared event group, and each state waits on just the subset of inputs it cares about. The IO-expander button arrives in interrupt context via xEventGroupSetBitsFromISR.
A binary semaphore guards the shared game struct: deck, both hands, funds, and bet. The hardware TRNG drives a Fisher–Yates shuffle so every power-up deals a different game. Logging is non-blocking: task_print() formats into a heap buffer and queues it to the console task, so any task can log safely.
Driver work underneath: ADC joystick, GPIO push buttons with software debounce, SPI EEPROM for a persistent high score, a TCA9534 I2C IO expander driving an LED win-streak display and an interrupt-driven button, parallel-bus LCD rendering of cards and stats, and a UART console with colour-coded state logging.
What we built versus what was provided: the course supplied header skeletons, the LCD and font bitmaps, and the board support package. Ryan and I wrote the seven FSM state tasks, the gatekeeper task bodies (screen command handling, EEPROM read/write with reply routing, debouncing, joystick edge detection, IO-expander ISR hookup), the game model (deck, hands, scoring, screen layout), and the driver implementations for the joystick, buttons, SPI, I2C, IO expander, console, and remote UART.
The repository is private because it's a course project. Happy to walk through it or share access — email me.