The game is centered around an XMega32E5 controller. This controller is nice because it has a 12 bit DAC and an EDMA engine, which are really easy to use as a digital audio playback system (see also my GPS Talking Clock project). The controller doesn't have nearly enough flash space for audio samples, so to hold them, there is an SPI flash chip. The flash chip has a FAT disk image in it with the audio sample files. There are also 4 buttons and 4 3-color LEDs. The LEDs are in a matrix, where the common anodes are selected by one set of 4 bits and the color cathodes are selected by another 3 bits. There are MOSFET drivers for each to protect the controller. The anodes have pull-downs on them to cut down on ghosting.

The SPI flash chip wiring is fairly perfunctory. It's the one SPI peripheral on the board, so it just gets all 4 of the SPI wires sent directly to it, along with 3.3v power and a bypass cap. Parallel to the chip there needs to be an interface to allow in-situ programming. I've chosen to use the AVR ISP pinout for this purpose, but with !RESET replaced with !CS (which for ISP programming of an AVR really serves the same purpose). Since the controller uses PDI, it's just a lot easier to put two separate headers on the board.

Power for the system is supplied by a pair of AA batteries and a boost converter. The boost converter is the TPS613221A. There is a Schottky diode and snubber circuit in parallel to the chip that is recommended for high current applications (in this case, the audio amplifier can sometimes draw a few hundred mA briefly). The system has soft power control, which starts with a P MOSFET between the battery and the boost converter. The MOSFET gate has two mechanisms to pull it down. First, one output of the controller is an output pin that holds the power on until the software decides to power down. Because the controller has protection diodes to Vcc on its I/O pins, we must isolate them from the !PWR line (which has a pull-up powered from the batteries, and so would potentially be a leakage path). To serve that purpose, there's an N MOSFET that will pull !PWR down when the software outputs a high level on the control pin. But since the MOSFET gate is isolated, there's no chance of power leaking through it. As a bootstrap, any of the game buttons being pushed momentarily draws the pin low, where it will then quickly be held low by the controller (since the boost converter has a bunch of output capacitance we don't have to be too fussy about debouncing the buttons. The caps should be able to easily power the controller long enough for it to pull the !PWR pin on). There are two requirements for this to work. One is that in normal operation, the buttons must be separate to the controller, but any of them should be able to power the system on. Secondly, again because of the controller protection diodes, we must insure that there's no potential leakage path from the P MOSFET gate line. To do this, each button has a common cathode diode array. One anode of each goes to the !PWR line, the other to its I/O pin of the controller. The diodes in front of the controller pins block any potential leakage, and the other diodes isolate each switch from each other despite the common ability to pull down on !PWR when pushed. In order for this system to be reliable, the brown-out detector of the controller needs to be set up. Otherwise, as the power dies it can cause spurious RESETs, and the first thing the firmware does is turn the power on. With a proper brown-out detector, as the voltage falls, the controller is held in RESET with hysteresis to prevent that from happening. The BOD threshold is 2.8 volts because the safe area for the XMega32E5 at 32 MHz bottoms out at 2.7v.

The DAC output is fed into an LM4871 push-pull audio amplifier. It's more or less straight from the datasheet. There is an extra cap in parallel with the feedback resistor to give a little bit of low-pass filtering. This acts as a sort of primitive anti-aliasing filter for the digital audio.

The firmware sets up timer C4 to run at 8 kHz. This timer will both drive the audio system and give us a tick counter ISR we can use for both to raster the LEDs and other software timing (using an unsigned long counting at 8 kHz would take a bit more than 6 days to overflow, but it's unlikely a pair of AA batteries would be able to power the system continuously for that long). The timer overflow triggers an event on the chip's event router. The DAC is set up to use that event to trigger a conversion. The DAC data registers being empty triggers the EDMA engine to perform a transfer burst to load the next sample. The EDMA engine is configured for double-buffering with two 512 byte buffers in RAM. While audio is playing, the firmware will call a polling routine that will check for either buffer being empty and refill it from the currently playing file. The refill method reads in a half-buffer sized chunk and performs µ-law expansion on it, doubling the size of the read data.

To check on the health of the battery, we use the internal analog comparator to compare the battery pack terminal voltage to a threshold value periodically. This works because the power for the controller comes from a boost converter, so the battery terminal voltage is going to be lower. The AC can compare the input to a voltage scaler output that has a 6 bit range up to Vcc and can be configured for hysteresis to capture even brief dips below the threshold. The threshold of system reliability is just a bit lower than 2.2 volts, so using a threshold value of 42 yields a threshold voltage nominally of just over 2.21 volts, which is ideal. We periodically check the state and if the battery is deemed to be low, we briefly show a "low battery" visual effect and power down.

Because it's impossible to light more than one color of LED at a time (and it's undesirable from a power perspective), the 4 LEDs are rastered by the 8 kHz ISR. The attract mode of the game has a "breathing" light effect, which is done by selecting one of 8 brightness levels. The rastering system accomplishes this by giving each LED 8 cycle periods and turning them on for more or fewer of them.

The buttons require software debouncing, and we want to be able to support chording, so the debounce system requires all of the button states to stay unchanged for the duration of the debounce cycle or else the debounce restarts. The debounce time is 50 ms, which is plenty of time for a human to get two buttons to press "simultaneously." The button routine will report a button event once and then wait for the buttons to change (and be debounced) before reporting again.

The random number generator requires a seed, and if the seed's initial value is always the same, then the game will repeat the same patterns, so to avoid this the seed is stored in EEPROM across power-down/power-up.