ATtiny85 Stacker Game
A simple stacking game running on a low-power Atmel chip.
A quick demonstration of the game.
#Overview
This is a standalone game device. The game is a simple stacking game, requiring the user to press the button at the specific time to align each layer. The button is implemented by sensing skin conductivity.
#Motivation
I wanted to try my hand at a low-power embedded project. In the past, I had done projects which ran off of battery power, but I wanted to try to make something that could be left with the batteries in for years and still function. This required working with the sleep mode on my microcontroller of choice, which was something I hadn’t used before.
#Technical Description
The microcontroller is an ATtiny85, and it’s connected to an I2C LED matrix with an HT16K33 chip.
The techniques for putting the microcontroller in sleep mode are based on this tutorial from Adafruit.
The code uses functions from <avr/sleep.h> and <avr/power.h>, documented on the avr-libc site: sleep.h, power.h. (Documentation for the set_sleep_mode macro is notably absent, but the source is here.) The Wandering Engineer also published a more detailed description of the registers used (PCMSK and GIMSK). The MCUCR (MCU Control Register) is documented here.
Here’s the meat of the implementation:
void setup() {
// ...
// Disable unused peripherals (Timer1 and ADC)
power_timer1_disable();
power_adc_disable();
// Enable interrupt on PB4 pin change
// (Set the Pin Change Interrupt 4 bit in the Pin Change Mask register)
PCMSK |= _BV(PCINT4);
// ...
}
void awaitButton() {
// Send command 0x20 to the I2C display
// (system setup / turn off system oscillator / standby mode)
TinyWireM.beginTransmission(0x70);
TinyWireM.write(0x20);
TinyWireM.endTransmission();
// Enable Pin Change Interrupts
// (Set the Pin Change Interrupt Enable bit in the General Interrupt Mask register)
GIMSK = _BV(PCIE);
// Disable all modules
power_all_disable();
// Enter power down mode
// Set the sleep mode 1 bit (SM1) in the MCU control register (MCUCR)
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
// Enable sleep mode
// Set the sleep enable bit (SE) in the MCU control register (MCUCR)
sleep_enable();
// Enable interrupts globally
// (Set the global interrupt flag, I, in the status register, SREG)
sei();
// Enter sleep mode
sleep_mode();
// Clear the General Interrupt Mask register
GIMSK = 0;
// Enable Timer 0 and the USI (Serial) peripherals
power_timer0_enable();
power_usi_enable();
// Re-initialize the display
TinyWireM.begin();
initDisplay(0);
}
#Results
I’ve left it with the batteries in for more than a year by now, and I haven’t noticed a drop in the display brightness. I’d consider it a success, although the rationale behind some of the hardware decisions has definitely been lost to time… Why didn’t I just use an actual button? Regardless, this project certainly helped me get closer to understanding AVRs at a low level.
