Close
0%
0%

μTris: ATtiny13A Tetris

Falling blocks game on the ATtiny 13A

Similar projects worth following
An implementation of the falling blocks game using an ATTiny13A, shift registers, and buttons.

So far this is a quick implementation of the falling blocks game. It is complete and works, albeit with trominos instead of the standard tetrominos. The current compilation statistics are (with avr-gcc 6.2.0):

AVR Memory Usage
----------------
Device: attiny13a

Program:    1016 bytes (99.2% Full)
(.text + .data + .bootloader)

Data:         31 bytes (48.4% Full)
(.data + .bss + .noinit)

Things it has:

  • Full falling blocks game logic, with row removal and game speedup as removed rows increase.
  • Full control capability (down, rotate, left, right), using a single I/O pin (via the ADC).
  • Single-buffered video (it had double buffered, but I needed the RAM from the second buffer and the flash from the buffer swapping code). The framerate is high enough so that it isn't noticeable.
  • Complete tromino bitmap.
  • Completely implemented in C, relying only on the GCC -Os optimizer flag.

Things it doesn't yet have:

  • Space to put the tetromino bitmap in (140 bytes with the current inefficient bitmap format).
  • Randomized piece selection (currently it just alternates between pieces). I plan to squeeze a small LFSR in there at some point soon. Sadly the ATTiny13A's LSL operation only shifts once per instruction.
  • A schematic in Kicad format. It's just a picture from my notebook right now. Possibly the worst schematic I've ever published online.
  • A second 74HC595. I only have one and so the other I had to hack together from some Fry's Electronics rebranded, overpriced 7400 ICs.

  • 1 × ATTiny13A Microprocessors, Microcontrollers, DSPs / ARM, RISC-Based Microcontrollers
  • 2 × 74HC595 Electronic Components / Misc. Electronic Components
  • 1 × Dot matrix display, anode row, cathode column
  • 8 × 150Ω resistor Resistors (Fixed) / Wirewound
  • 1 × 10KΩ resistor Resistors (Fixed) / Wirewound

View all 10 components

  • First implementation

    Kevin Cuzner12/23/2016 at 03:55 0 comments

    The first initial working implementation is complete. The code currently has three sections: The display logic, the uTris game loop, and the input logic. I'll try to describe some of the solutions to various obstacles I encountered.

    The display logic is fairly straightforward bitbanging out to shift registers. Since the microcontroller has only 5 or so usable pins (ok, 6, but who wants to give up ~RESET?) the shift registers were an absolute must for running an 8x8 display. The arrangement consists of two 8-bit shift registers in series, the first for the rows and the last for the columns. I had a bit of trouble sourcing a second 74HC595 from my parts bin, so I had to go to Fry's electronics down the street and pick up some equivalent chips. I couldn't believe that they didn't have any 74HC595s in their massive selection of overpriced, rebranded 7400 series ICs. I ended up combining a 74HC164 (8-bit shift register with no latches) and 74HC374 (octal D flip-flop) to form something roughly resembling a 74HC595 so I could chain it to the one I already had. I use three GPIOs total for this arrangement: One for the shift register clock, one for the storage register clock, and the third for the serial data output. Display data is stored internally as an 8-byte array. For each row, I output one byte containing the inverted column data for that row and one byte containing the active-high row mask. The column data is inverted because the second shift register (which drives the columns) is attached to the cathodes of my anode-row dot matrix display. After the two bytes are clocked out, I strobe the storage register clock and move on to the next row. I don't need to worry about ghosting between rows because both the rows and columns have the same storage register clock and are strobed at the same time.

    I have no set framerate and the microcontroller will shift out the row data as fast as it can. In between rows I run the main game logic tick function. Since the function takes roughly the same amount of time for each run, this contributes to making all of the rows on the display have the same brightness, more or less.

    The game logic is fairly standard for falling block games. I think the only real interesting thing about it is that it's completely agnostic of the actual piece shapes and sizes. The piece data is stored in a bitmap format that is rather wasteful, but takes comparatively few instructions to load. Changing the game to tetrominos from trominos is just a matter of swapping out the bitmap (and finding the additional 120 bytes or so). I'm still working on optimizing the balance between the bitmap size and the code size (so far they seem to have an inverse relationship, as is expected). Other than the bitmap, the only other challenge implementing this part was the balance between function calls and available RAM. When the stack overruns the display buffer, it makes gameplay a little difficult. I used some bit-level packing of various game variables and removed display double buffering capability to increase the amount of RAM available for the stack to compensate for this problem.

    The input logic is something I haven't tried before. I use a single ADC pin to measure the voltage of a resistor divider formed between a fixed upper resistor and a lower resistor changed out by button presses. I've chosen the resistor values for the upper and lower resistors so that the input voltage range of the ADC is divided into five quadrants, one for each state: no button pressed, down pressed, rotate pressed, left pressed, and right pressed. A capacitor is present on the pin for debouncing and to reduce noise. The program captures the lowest voltage value it sees until the voltage returns to the "no button pressed" level. This compensates for the fact that the voltage level needs to travel down and back up again during a button press at some finite speed. With some tuning, I found this system to work very reliably, though I imagine it would need to be...

    Read more »

View project log

Enjoy this project?

Share

Discussions

Does this project spark your interest?

Become a member to follow this project and never miss any updates