added lcd snake to projects

master
Ondřej Hruška 10 years ago
parent bbaf8bad62
commit 03658ad213
  1. 5
      devel/cstruct.h
  2. 182
      devel/lcdsnake/main.c
  3. 19
      devel/lib/lcd.h
  4. 2
      devel/lib/ws_rgb.h
  5. 0
      devel/moo2/Makefile
  6. 0
      devel/moo2/lcd_default.asm
  7. 0
      devel/moo2/lib
  8. 426
      devel/moo2/main.c
  9. 166
      projects/lcdsnake/Makefile
  10. 10
      projects/lcdsnake/README.md
  11. 351
      projects/lcdsnake/lcd_default.asm
  12. 8
      projects/lcdsnake/lib/README.md
  13. 39
      projects/lcdsnake/lib/adc.h
  14. 42
      projects/lcdsnake/lib/arduino_pins.h
  15. 89
      projects/lcdsnake/lib/calc.h
  16. 83
      projects/lcdsnake/lib/colors.h
  17. 103
      projects/lcdsnake/lib/debounce.h
  18. 391
      projects/lcdsnake/lib/lcd.h
  19. 22
      projects/lcdsnake/lib/loops.h
  20. 6
      projects/lcdsnake/lib/meta.h
  21. 21
      projects/lcdsnake/lib/nsdelay.h
  22. 107
      projects/lcdsnake/lib/pins.h
  23. 98
      projects/lcdsnake/lib/ws_rgb.h
  24. 31
      projects/lcdsnake/lib/yeolde.h
  25. 426
      projects/lcdsnake/main.c

@ -1,5 +0,0 @@
#pragma once
#define loop(varname, count) for(uint8_t varname = 0; varname < (count); varname++)
#define repeat(count) for(uint8_t _repeat_i = 0; _repeat_i < (count); _repeat_i++)
#define until(cond) while(!(cond))

@ -1,182 +0,0 @@
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "lib/meta.h"
#include "lib/arduino_pins.h"
#include "lib/calc.h"
#define LCD_PIN_RS D10
#define LCD_PIN_RW D11
#define LCD_PIN_E D12
#define LCD_PIN_D4 D13
#define LCD_PIN_D5 D14
#define LCD_PIN_D6 D15
#define LCD_PIN_D7 D16
// D17 = A3 = source of entropy for random.
#include "lib/hd44780.h"
// Buttons (to ground)
#define BTN_LEFT D2
#define BTN_RIGHT D3
#define BTN_UP D4
#define BTN_DOWN D5
#define BTN_SELECT D6
#define BTN_RESTART D7
// Debouncer channels for buttons
// (Must be added in this order to debouncer)
#define D_LEFT 0
#define D_RIGHT 1
#define D_UP 2
#define D_DOWN 3
#define D_SELECT 4
#define D_RESTART 5
#define DEBO_CHANNELS 6
#define DEBO_TICKS 1 // in 0.01s
#include "lib/debounce.h"
// Board size (!!! rows = 2x number of display lines, max 2*4 = 8 !!!)
#define ROWS 4
#define COLS 20
// proto
void render();
void update();
void SECTION(".init8") init()
{
// Randomize RNG
adc_init();
srand(adc_read_word(3));
// Init LCD
lcd_init();
// gamepad buttons
as_input_pu(BTN_LEFT);
as_input_pu(BTN_RIGHT);
as_input_pu(BTN_UP);
as_input_pu(BTN_DOWN);
as_input_pu(BTN_SELECT);
as_input_pu(BTN_RESTART);
// add buttons to debouncer
debo_add_rev(BTN_LEFT);
debo_add_rev(BTN_RIGHT);
debo_add_rev(BTN_UP);
debo_add_rev(BTN_DOWN);
debo_add_rev(BTN_SELECT);
debo_add_rev(BTN_RESTART);
// setup timer
TCCR0A = _BV(WGM01); // CTC
TCCR0B = _BV(CS02) | _BV(CS00); // prescaler 1024
OCR0A = 156; // interrupt every 10 ms
sbi(TIMSK0, OCIE0A);
sei();
}
/** timer 0 interrupt vector */
ISR(TIMER0_COMPA_vect)
{
debo_tick(); // poll debouncer
update(); // update game state
render();
}
// sub-glyphs
#define _HEAD_ 31, 21, 21, 31
#define _BODY_ 31, 31, 31, 31
#define _FOOD_ 10, 21, 17, 14
#define _NONE_ 0, 0, 0, 0
// complete glyphs for loading into memory
// Only one food & one head glyph have to be loaded at a time.
// Body - Body
const uint8_t BB[] PROGMEM = {_BODY_, _BODY_};
// Body - None
const uint8_t BX[] PROGMEM = {_BODY_, _NONE_};
const uint8_t XB[] PROGMEM = {_NONE_, _BODY_};
// Head - None
const uint8_t HX[] PROGMEM = {_HEAD_, _NONE_};
const uint8_t XH[] PROGMEM = {_NONE_, _HEAD_};
// Body - Head
const uint8_t BH[] PROGMEM = {_BODY_, _HEAD_};
const uint8_t HB[] PROGMEM = {_HEAD_, _BODY_};
// Head - Food
const uint8_t HF[] PROGMEM = {_HEAD_, _FOOD_};
const uint8_t FH[] PROGMEM = {_FOOD_, _HEAD_};
// Food - None
const uint8_t FX[] PROGMEM = {_FOOD_, _NONE_};
const uint8_t XF[] PROGMEM = {_NONE_, _FOOD_};
// Body - Food
const uint8_t BF[] PROGMEM = {_BODY_, _FOOD_};
const uint8_t FB[] PROGMEM = {_FOOD_, _BODY_};
// board block (snake, food...)
typedef enum {
EMPTY = 0x00,
HEAD = 0x01,
FOOD = 0x02,
BODY_LEFT = 0x80;
BODY_RIGHT = 0x81,
BODY_UP = 0x82,
BODY_DOWN = 0x83,
} block_t;
typedef struct {
uint8_t x;
uint8_t y;
} coord_t;
#define is_body(blk) ((blk) & 0x80)
// y, x indexing
block_t board[ROWS][COLS];
coord_t head_pos;
coord_t food_pos;
void main()
{
lcd_define_glyph_pgm(0, BX);
lcd_define_glyph_pgm(1, BB);
lcd_define_glyph_pgm(2, XB);
lcd_define_glyph_pgm(3, XH);
lcd_define_glyph_pgm(4, XF);
// Test
lcd_char_xy(5, 0, 0);
lcd_char_xy(6, 0, 0);
lcd_char_xy(7, 0, 1);
lcd_char_xy(7, 1, 0);
lcd_char_xy(8, 1, 0);
lcd_char_xy(9, 1, 1);
lcd_char_xy(10, 1, 2);
lcd_char_xy(11, 1, 3);
lcd_char_xy(14, 1, 4);
while(1);
}

@ -6,7 +6,6 @@
#include <avr/pgmspace.h> #include <avr/pgmspace.h>
#include <util/delay.h> #include <util/delay.h>
#include "arduino_pins.h"
#include "calc.h" #include "calc.h"
#include "pins.h" #include "pins.h"
#include "nsdelay.h" #include "nsdelay.h"
@ -103,6 +102,10 @@ void lcd_xy(const uint8_t x, const uint8_t y);
void lcd_set_addr_cgram(const uint8_t acg); void lcd_set_addr_cgram(const uint8_t acg);
/** Set address in DDRAM */ /** Set address in DDRAM */
void lcd_set_addr(const uint8_t add); void lcd_set_addr(const uint8_t add);
/** Go home */
void lcd_home();
/** Clear the screen */
void lcd_clear();
/** Set cursor */ /** Set cursor */
#define CURSOR_NONE 0b00 #define CURSOR_NONE 0b00
@ -340,6 +343,20 @@ void lcd_enable()
} }
/** Go home */
void lcd_home()
{
lcd_write_command(LCD_HOME);
}
/** Clear the screen */
void lcd_clear()
{
lcd_write_command(LCD_CLEAR);
}
/** Define a glyph */ /** Define a glyph */
void lcd_define_glyph(const uint8_t index, const uint8_t* array) void lcd_define_glyph(const uint8_t index, const uint8_t* array)
{ {

@ -1,7 +1,7 @@
#pragma once #pragma once
/** /**
Utils for driving a WS2812 (WS2812B) RGB LED strips. Utils for driving a WS28xx (tested on WS2812B) RGB LED strips.
It's implemented as macros to avoid overhead when passing values, and to It's implemented as macros to avoid overhead when passing values, and to
enable driving multiple strips at once. enable driving multiple strips at once.

@ -0,0 +1,426 @@
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "lib/meta.h"
#include "lib/arduino_pins.h"
#include "lib/calc.h"
#include "lib/adc.h"
#define LCD_PIN_RS D10
#define LCD_PIN_RW D11
#define LCD_PIN_E D12
#define LCD_PIN_D4 D13
#define LCD_PIN_D5 D14
#define LCD_PIN_D6 D15
#define LCD_PIN_D7 D16
// D17 = A3 = source of entropy for random.
#include "lib/lcd.h"
// Buttons (to ground)
#define BTN_LEFT D2
#define BTN_RIGHT D3
#define BTN_UP D4
#define BTN_DOWN D5
#define BTN_SELECT D6
#define BTN_RESTART D7
// Debouncer channels for buttons
// (Must be added in this order to debouncer)
#define D_LEFT 0
#define D_RIGHT 1
#define D_UP 2
#define D_DOWN 3
#define D_SELECT 4
#define D_RESTART 5
#define DEBO_CHANNELS 6
#define DEBO_TICKS 1 // in 0.01s
#include "lib/debounce.h"
// Board size (!!! rows = 2x number of display lines, max 2*4 = 8 !!!)
#define ROWS 4
#define COLS 20
// Delay between snake steps, in 10 ms
#define STEP_DELAY 24
// proto
void update();
void init_cgram();
void init_gameboard();
void SECTION(".init8") init()
{
// Randomize RNG
adc_init();
srand(adc_read_word(3));
// Init LCD
lcd_init();
init_cgram(); // load default glyphs
// Init game board.
init_gameboard();
// gamepad buttons
as_input_pu(BTN_LEFT);
as_input_pu(BTN_RIGHT);
as_input_pu(BTN_UP);
as_input_pu(BTN_DOWN);
as_input_pu(BTN_SELECT);
as_input_pu(BTN_RESTART);
// add buttons to debouncer
debo_add_rev(BTN_LEFT);
debo_add_rev(BTN_RIGHT);
debo_add_rev(BTN_UP);
debo_add_rev(BTN_DOWN);
debo_add_rev(BTN_SELECT);
debo_add_rev(BTN_RESTART);
// setup timer
TCCR0A = _BV(WGM01); // CTC
TCCR0B = _BV(CS02) | _BV(CS00); // prescaler 1024
OCR0A = 156; // interrupt every 10 ms
sbi(TIMSK0, OCIE0A);
sei();
}
/** timer 0 interrupt vector */
ISR(TIMER0_COMPA_vect)
{
debo_tick(); // poll debouncer
update(); // update and display
}
// sub-glyphs
#define _HEAD_ 15, 21, 21, 30
#define _BODY_ 15, 31, 31, 30
#define _FOOD_ 10, 21, 17, 14
//14, 17, 17, 14
#define _NONE_ 0, 0, 0, 0
// complete glyphs for loading into memory
// Only one food & one head glyph have to be loaded at a time.
// Body - Body
const uint8_t SYMBOL_BB[] PROGMEM = {_BODY_, _BODY_};
// Body - None
const uint8_t SYMBOL_BX[] PROGMEM = {_BODY_, _NONE_};
const uint8_t SYMBOL_XB[] PROGMEM = {_NONE_, _BODY_};
// Head - None
const uint8_t SYMBOL_HX[] PROGMEM = {_HEAD_, _NONE_};
const uint8_t SYMBOL_XH[] PROGMEM = {_NONE_, _HEAD_};
// Body - Head
const uint8_t SYMBOL_BH[] PROGMEM = {_BODY_, _HEAD_};
const uint8_t SYMBOL_HB[] PROGMEM = {_HEAD_, _BODY_};
// Head - Food
const uint8_t SYMBOL_HF[] PROGMEM = {_HEAD_, _FOOD_};
const uint8_t SYMBOL_FH[] PROGMEM = {_FOOD_, _HEAD_};
// Food - None
const uint8_t SYMBOL_FX[] PROGMEM = {_FOOD_, _NONE_};
const uint8_t SYMBOL_XF[] PROGMEM = {_NONE_, _FOOD_};
// Body - Food
const uint8_t SYMBOL_BF[] PROGMEM = {_BODY_, _FOOD_};
const uint8_t SYMBOL_FB[] PROGMEM = {_FOOD_, _BODY_};
// board block (snake, food...)
typedef enum {
bEMPTY = 0x00,
bHEAD = 0x01,
bFOOD = 0x02,
bBODY_LEFT = 0x80,
bBODY_RIGHT = 0x81,
bBODY_UP = 0x82,
bBODY_DOWN = 0x83,
} block_t;
// Snake direction
typedef enum {
dLEFT = 0x00,
dRIGHT = 0x01,
dUP = 0x02,
dDOWN = 0x03,
} dir_t;
// Coordinate on board
typedef struct {
int8_t x;
int8_t y;
} coord_t;
#define is_body(blk) (((blk) & 0x80) != 0)
#define mk_body_dir(dir) (0x80 + (dir))
// compare two coords
#define coord_eq(a, b) (((a).x == (b).x) && ((a).y == (b).y))
bool crashed;
uint8_t snake_len;
// y, x indexing
block_t board[ROWS][COLS];
coord_t head_pos;
coord_t tail_pos;
dir_t head_dir;
const uint8_t CODE_BB = 0;
const uint8_t CODE_BX = 1;
const uint8_t CODE_XB = 2;
const uint8_t CODE_H = 3; // glyph with head, dynamic
const uint8_t CODE_F = 4; // glyph with food, dynamic
const uint8_t CODE_XX = 32; // space
// Set a block in board
#define set_block_xy(x, y, block) do { board[y][x] = (block); } while(0)
#define get_block_xy(x, y) board[y][x]
#define get_block(pos) get_block_xy((pos).x, (pos).y)
#define set_block(pos, block) set_block_xy((pos).x, (pos).y, (block))
void init_cgram()
{
// those will be always the same
lcd_define_glyph_pgm(CODE_BB, SYMBOL_BB);
lcd_define_glyph_pgm(CODE_BX, SYMBOL_BX);
lcd_define_glyph_pgm(CODE_XB, SYMBOL_XB);
lcd_define_glyph_pgm(5, SYMBOL_XF);
}
void place_food()
{
while(1) {
const uint8_t xx = rand() % COLS;
const uint8_t yy = rand() % ROWS;
if (get_block_xy(xx, yy) == bEMPTY) {
set_block_xy(xx, yy, bFOOD);
break;
}
}
}
void init_gameboard()
{
//erase the board
for (uint8_t x = 0; x < COLS; x++) {
for (uint8_t y = 0; y < ROWS; y++) {
set_block_xy(x, y, bEMPTY);
}
}
lcd_clear();
tail_pos = (coord_t) {.x = 0, .y = 0};
set_block_xy(0, 0, bBODY_RIGHT);
set_block_xy(1, 0, bBODY_RIGHT);
set_block_xy(2, 0, bBODY_RIGHT);
set_block_xy(3, 0, bHEAD);
head_pos = (coord_t) {.x = 3, .y = 0};
snake_len = 4; // includes head
head_dir = dRIGHT;
crashed = false;
place_food();
}
uint8_t presc = 0;
bool restart_held;
void update()
{
if (debo_get_pin(D_RESTART)) {
if (!restart_held) {
// restart
init_gameboard();
presc = 0;
restart_held = true;
}
} else {
restart_held = false;
}
if(!crashed) {
// resolve movement direction
if (debo_get_pin(D_LEFT))
head_dir = dLEFT;
else if (debo_get_pin(D_RIGHT))
head_dir = dRIGHT;
else if (debo_get_pin(D_UP))
head_dir = dUP;
else if (debo_get_pin(D_DOWN))
head_dir = dDOWN;
// time's up for a move
if (presc++ == STEP_DELAY) {
presc = 0;
// move snake
const coord_t oldpos = head_pos;
switch (head_dir) {
case dLEFT: head_pos.x--; break;
case dRIGHT: head_pos.x++; break;
case dUP: head_pos.y--; break;
case dDOWN: head_pos.y++; break;
}
bool do_move = false;
bool do_grow = false;
if (head_pos.x < 0 || head_pos.x >= COLS || head_pos.y < 0 || head_pos.y >= ROWS) {
// ouch, a wall!
crashed = true;
} else {
// check if tile occupied or not
if (coord_eq(head_pos, tail_pos)) {
// head moved in previous tail, that's OK.
do_move = true;
} else {
// moved to other tile than tail
switch (get_block(head_pos)) {
case bFOOD:
do_grow = true; // fall through
case bEMPTY:
do_move = true;
break;
default: // includes all BODY_xxx
crashed = true; // snake crashed into some block
}
}
}
if (do_move) {
// Move tail
if (do_grow) {
// let tail as is
snake_len++; // grow the counter
} else {
// tail dir
dir_t td = get_block(tail_pos) & 0xF;
// clean tail
set_block(tail_pos, bEMPTY);
// move tail based on old direction of tail block
switch (td) {
case dLEFT: tail_pos.x--; break;
case dRIGHT: tail_pos.x++; break;
case dUP: tail_pos.y--; break;
case dDOWN: tail_pos.y++; break;
}
}
// Move head
set_block(head_pos, bHEAD); // place head in new pos
set_block(oldpos, mk_body_dir(head_dir)); // directional body in old head pos
if (do_grow) {
// food eaten, place new
place_food();
}
}
}
} // end !crashed
// Render the board
for (uint8_t r = 0; r < ROWS / 2; r++) {
lcd_xy(0, r);
for (uint8_t c = 0; c < COLS; c++) {
const block_t t1 = get_block_xy(c, r * 2);
const block_t t2 = get_block_xy(c, (r * 2) + 1);
uint8_t code = '!'; // ! marks fail
if ((t1 == bEMPTY) && (t2 == bEMPTY)) {
code = CODE_XX;
if (crashed) code = '*';
} else if (is_body(t1) && is_body(t2))
code = CODE_BB;
else if (is_body(t1) && (t2 == bEMPTY))
code = CODE_BX;
else if (t1 == bEMPTY && is_body(t2))
code = CODE_XB;
else if ((t1 == bFOOD) || (t2 == bFOOD)) {
// one is food
code = CODE_F;
if (t1 == bFOOD) {
if (t2 == bEMPTY)
lcd_define_glyph_pgm(code, SYMBOL_FX);
else if (t2 == bHEAD)
lcd_define_glyph_pgm(code, SYMBOL_FH);
else if (is_body(t2))
lcd_define_glyph_pgm(code, SYMBOL_FB);
} else { // t2 is food
if (t1 == bEMPTY)
lcd_define_glyph_pgm(code, SYMBOL_XF);
else if (t1 == bHEAD)
lcd_define_glyph_pgm(code, SYMBOL_HF);
else if (is_body(t1))
lcd_define_glyph_pgm(code, SYMBOL_BF);
}
lcd_xy(c,r);
} else if ((t1 == bHEAD )|| (t2 == bHEAD)) {
// one is head
code = CODE_H;
if (t1 == bHEAD) {
if (t2 == bEMPTY)
lcd_define_glyph_pgm(code, SYMBOL_HX);
else if (is_body(t2))
lcd_define_glyph_pgm(code, SYMBOL_HB);
} else { // t2 is head
if (t1 == bEMPTY)
lcd_define_glyph_pgm(code, SYMBOL_XH);
else if (is_body(t1))
lcd_define_glyph_pgm(code, SYMBOL_BH);
}
lcd_xy(c,r);
}
lcd_char(code);
}
}
}
void main() { while(1); } // timer does everything

@ -0,0 +1,166 @@
MCU = atmega328p
F_CPU = 16000000
LFUSE = 0xFF
HFUSE = 0xDE
EFUSE = 0x05
MAIN = main.c
## If you've split your program into multiple files,
## include the additional .c source (in same directory) here
## (and include the .h files in your foo.c)
LOCAL_SOURCE =
## Here you can link to one more directory (and multiple .c files)
# EXTRA_SOURCE_DIR = ../AVR-Programming-Library/
EXTRA_SOURCE_DIR =
EXTRA_SOURCE_FILES =
##########------------------------------------------------------##########
########## Programmer Defaults ##########
########## Set up once, then forget about it ##########
########## (Can override. See bottom of file.) ##########
##########------------------------------------------------------##########
#19200
PROGRAMMER_TYPE = arduino
PROGRAMMER_ARGS = -b 57600 -P /dev/ttyUSB0
##########------------------------------------------------------##########
########## Makefile Magic! ##########
########## Summary: ##########
########## We want a .hex file ##########
########## Compile source files into .elf ##########
########## Convert .elf file into .hex ##########
########## You shouldn't need to edit below. ##########
##########------------------------------------------------------##########
## Defined programs / locations
CC = avr-gcc
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
AVRSIZE = avr-size
AVRDUDE = sudo avrdude
## Compilation options, type man avr-gcc if you're curious.
CFLAGS = -std=gnu99 -mmcu=$(MCU) -DF_CPU=$(F_CPU)UL -I. -I$(EXTRA_SOURCE_DIR)
CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
CFLAGS += -Wall -Wno-main -Wno-strict-prototypes -Wno-comment
CFLAGS += -g2 -Wextra -Wfatal-errors
CFLAGS += -ffunction-sections -fdata-sections -Wl,--gc-sections -Wl,--relax
CFLAGS_BUILD = $(CFLAGS) -Os
# CFLAGS += -lm
## CFLAGS += -Wl,-u,vfprintf -lprintf_flt -lm ## for floating-point printf
## CFLAGS += -Wl,-u,vfprintf -lprintf_min ## for smaller printf
## Lump target and extra source files together
TARGET = $(strip $(basename $(MAIN)))
SRC1 = $(TARGET).c
SRC = $(SRC1)
EXTRA_SOURCE = $(addprefix $(EXTRA_SOURCE_DIR), $(EXTRA_SOURCE_FILES))
SRC += $(EXTRA_SOURCE)
SRC += $(LOCAL_SOURCE)
## List of all header files
HEADERS = $(SRC:.c=.h)
## For every .c file, compile an .o object file
OBJ = $(SRC:.c=.o)
## Generic Makefile targets. (Only .hex file is necessary)
all: $(TARGET).hex size
pre: $(TARGET).pre
%.hex: %.elf
$(OBJCOPY) -R .eeprom -O ihex $< $@
%.elf: $(SRC)
$(CC) $(CFLAGS_BUILD) $(SRC) --output $@
%.pre: $(SRC1)
$(CC) $(CFLAGS) -E $(SRC1) --output $@
%.eeprom: %.elf
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@
debug:
@echo
@echo "Source files:" $(SRC)
@echo "MCU, F_CPU, BAUD:" $(MCU), $(F_CPU), $(BAUD)
@echo
# Optionally create listing file from .elf
# This creates approximate assembly-language equivalent of your code.
# Useful for debugging time-sensitive bits,
# or making sure the compiler does what you want.
disassemble: $(TARGET).lst
dis: disassemble
lst: disassemble
eeprom: $(TARGET).eeprom
%.lst: %.elf
$(OBJDUMP) -S $< > $@
# Optionally show how big the resulting program is
size: $(TARGET).elf
$(AVRSIZE) -C --mcu=$(MCU) $(TARGET).elf
clean:
rm -f $(TARGET).elf $(TARGET).hex $(TARGET).obj \
$(TARGET).o $(TARGET).d $(TARGET).eep $(TARGET).lst \
$(TARGET).lss $(TARGET).sym $(TARGET).map $(TARGET)~ \
$(TARGET).eeprom
squeaky_clean:
rm -f *.elf *.hex *.obj *.o *.d *.eep *.lst *.lss *.sym *.map *~
##########------------------------------------------------------##########
########## Programmer-specific details ##########
########## Flashing code to AVR using avrdude ##########
##########------------------------------------------------------##########
flash: $(TARGET).hex
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U flash:w:$<
flash_eeprom: $(TARGET).eeprom
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U eeprom:w:$<
terminal:
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -nt
flash_arduino: PROGRAMMER_TYPE = arduino
flash_arduino: PROGRAMMER_ARGS =
flash_arduino: flash
flash_dragon_isp: PROGRAMMER_TYPE = dragon_isp
flash_dragon_isp: PROGRAMMER_ARGS =
flash_dragon_isp: flash
##########------------------------------------------------------##########
########## Fuse settings and suitable defaults ##########
##########------------------------------------------------------##########
## Generic
FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m
fuses:
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) \
$(PROGRAMMER_ARGS) $(FUSE_STRING)
show_fuses:
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -nv
## Called with no extra definitions, sets to defaults
set_default_fuses: FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m
set_default_fuses: fuses

@ -0,0 +1,10 @@
Snake for HD44780
=================
This is a Snake game (known from old Nokia phones) played on a character display with HD44780.
Program tested on Arduino Pro Mini (flashed with avrdude using the Makefile).
**Connections** are `#define`d in `main.c`, board size and snake speed can also be adjusted. No external parts except the display and buttons required.
Best works with a gamepad, but any buttons will work.

@ -0,0 +1,351 @@
; Zapojeni (Připojen DMC-50399 v 4-bitovem modu):
; +------u------+
; Vcc -> reset --+ /RST Vcc +-- napajeni +5V
; --+ PD0 PB7 +-- RS (0=instr W, BF+addr R; 1=data W/R)
; --+ PD1 PB6 +-- R/W (1=read,0=write)
; --+ PA1 PB5 +-- E (clock, active falling edge)
; --+ PA0 PB4 +--
; --+ PD2 PB3 +-- DATA 7
; --+ PD3 PB2 +-- DATA 6
; --+ PD4 PB1 +-- DATA 5
; --+ PD5 PB0 +-- DATA 4
; GND --+ GND PD6 +--
; +-------------+
;DMC-50399:
; 1 - GND
; 2 - +5V
; 3 - 0V (lcd driver)
; 4 - RS
; 5 - R/W
; 6 - E
; 7 - DATA 0
; 8 - DATA 1
;...
;14 - DATA 7
.device attiny2313
;běží na 4MHz, ckdiv8=1 (vypnuto)
;250x16=1ms=1000us
;LFUSE: 0xE2 -U lfuse:w:0xE2:m
;HFUSE: 0xDF -U hfuse:w:0xDF:m
;K O N S T A N T Y + P R E Z D I V K Y P O R T U A P I N U
.equ LCDPORT = PORTB
.equ LCDPIN = PINB
.equ LCDDDR = DDRB
.equ RS = 7
.equ RW = 6
.equ E = 5
.equ LCD_CLEAR = 0b00000001
.equ LCD_HOME = 0b00000010
.equ LCD_MODE_INC_NOSHIFT = 0b00000110
.equ LCD_MODE_INC_SHIFT = 0b00000111
.equ LCD_MODE_DEC_NOSHIFT = 0b00000100
.equ LCD_MODE_DEC_SHIFT = 0b00000101
.equ LCD_DISPLAY_DISABLED = 0b00001000
.equ LCD_DISPLAY_NOCURSOR = 0b00001100
.equ LCD_DISPLAY_CURSOR = 0b00001110
.equ LCD_DISPLAY_ALTER = 0b00001101
.equ LCD_DISPLAY_CURSOR_ALTER = 0b00001111
.equ LCD_CURSOR_LEFT = 0b00010000
.equ LCD_CURSOR_RIGHT = 0b00010100
.equ LCD_SHIFT_LEFT = 0b00011000
.equ LCD_SHIFT_RIGHT = 0b00011100
;5x7 font, 1-line
.equ LCD_MODE_4BIT_1LINE = 0b00100000
;.equ LCD_MODE_8BIT_1LINE = 0b00110000
;5x7 font, 2-line
.equ LCD_MODE_4BIT_2LINE = 0b00101000
;.equ LCD_MODE_8BIT_2LINE = 0b00111000
.equ ROW1_ADDR = 0x00
.equ ROW2_ADDR = 0x40
.equ ROW3_ADDR = 0x14
.equ ROW4_ADDR = 0x54
;aliases
.def ZH = r31
.def ZL = r30
.def YH = r29
.def YL = r28
.def XH = r27
.def XL = r26
; Z A C A T E K P R O G R A M U
;vektory preruseni
.org 0x0000 ;RESET
rjmp RESET ;skok na start po resetu
.org 0x0013
;nastaveni po resetu
.DB "HD44780 INTERFACE" ;(nazev programu)
RESET:
ldi r16,low(RAMEND) ;nastavi stack pointer
out SPL,r16
cli ;zakazat vsechna preruseni
; Nastaveni portu
;PORTB = LCDPORT
ldi r16,0b11111111 ;smer portu B
out LCDDDR,r16
ldi r16,0b00000000 ;vypnout B
out LCDPORT,r16
sei ;Global Interrupt Enable
; == display init ==
rcall LCD_INIT
ldi r17,LCD_MODE_INC_NOSHIFT
rcall TX_INSTR
ldi r17,LCD_DISPLAY_NOCURSOR
rcall TX_INSTR
; == load user-defined characters to CGRAM == (default, array label named MYCHARS, end-mark=0xFE)
ldi r17,0
rcall CGRAM_SET_ADDR
ldi ZH,high(MYCHARS*2)
ldi ZL,low(MYCHARS*2)
CGRAM_loop:
lpm r17,Z+
cpi r17,0xFE
breq CGRAM_loop_end
rcall TX_DATA
rjmp CGRAM_loop
CGRAM_loop_end:
; == pgm body ==
;load text to DDRAM
ldi r17,ROW1_ADDR
rcall DDRAM_SET_ADDR
ldi ZH,high(MYTEXT1*2)
ldi ZL,low(MYTEXT1*2)
DDRAM_loop:
lpm r17,Z+
cpi r17,0xFE
breq DDRAM_loop_end
rcall TX_DATA
rjmp DDRAM_loop
DDRAM_loop_end:
;load text to DDRAM
ldi r17,ROW2_ADDR
rcall DDRAM_SET_ADDR
ldi ZH,high(MYTEXT2*2)
ldi ZL,low(MYTEXT2*2)
DDRAM2_loop:
lpm r17,Z+
cpi r17,0xFE
breq DDRAM2_loop_end
rcall TX_DATA
rjmp DDRAM2_loop
DDRAM2_loop_end:
;direct write to X,Y - example
ldi r16,3 ;Y, zacina 0 a roste smerem dolu
ldi r17,5 ;X, zacina nulou a roste smerem doprava
rcall LCD_CURSOR_XY
ldi r17,"%"
rcall TX_DATA
ldi r17,"%"
rcall TX_DATA
ldi r17,"%"
rcall TX_DATA
;infinite loop
loop: rjmp loop
MYTEXT1:
.DB 0,0,0," POKUSNY TEXT ",0,0,0,0xFE
MYTEXT2:
.DB "Opravdu pekny text!",0xFE
; == USER-DEFINED CHARS ==
MYCHARS:
; 5x8, first 3 bits are not used
;end of mychars
.DB 0xe,0x1f,0x15,0x1f,0x1f,0x1f,0x15 ;smajlik
;konec
.DB 0xFE
;r16=Y
;r17=X
LCD_CURSOR_XY:
cpi r16,0
brne test1
fail: ldi r16,ROW1_ADDR
rjmp addrdone
test1:
cpi r16,1
brne test2
ldi r16,ROW2_ADDR
rjmp addrdone
test2:
cpi r16,2
brne test3
ldi r16,ROW3_ADDR
rjmp addrdone
test3:
cpi r16,3
brne fail
ldi r16,ROW4_ADDR
addrdone:
add r17,r16
rcall DDRAM_SET_ADDR
ret
;r16=počet ms (cca)
delay:
push r17 ;2
push r18 ;2
d1:
ldi r17,250 ;1
d2:
ldi r18,14 ;1
d3:
dec r18 ;1
nop
brne d3 ;2 (1
dec r17 ; +1)
brne d2 ;2 (1
dec r16 ; +1)
brne d1 ;2 (1)
pop r18 ;2
pop r17 ;2
ret
LCD_INIT:
ldi r16,16
rcall delay
ldi r16,0b00000010 ;4bit
out PORTB,r16
rcall LCD_CLK
ldi r16,5
rcall delay
ldi r17,LCD_MODE_4BIT_2LINE ;set 4-bit mode
rcall TX_INSTR
ret
;r17
TX_INSTR:
swap r17 ;send high nibble
mov r16,r17
andi r16,0b00001111
out LCDPORT,r16
rcall LCD_CLK
swap r17 ;send low nibble
mov r16,r17
andi r16,0b00001111
out LCDPORT,r16
rcall LCD_CLK
ret
;r17
TX_DATA:
swap r17 ;send high nibble
mov r16,r17
andi r16,0b00001111
sbr r16,(1<<RS)
out LCDPORT,r16
rcall LCD_CLK
swap r17 ;send low nibble
mov r16,r17
andi r16,0b00001111
sbr r16,(1<<RS)
out LCDPORT,r16
rcall LCD_CLK
ret
;r17 disabled to reduce code size
;RX_DATA:
; ;input
; ldi r16,0b11110000 ;LCDPORT dirrection (RS RW E n.c.) output, (D7 D6 D5 D4) input
; out LCDDDR,r16
; ldi r16,0b00001111 ;pullups to data pins enabled
; out LCDPORT,r16
;
; clr r17
; ldi r16,(1<<RW)|(1<<RS)
; out LCDPORT,r16 ;set LCD to read mode, for data
;
; rcall LCD_CLK ;receive high nibble
; in r16,LCDPIN
; andi r16,0b00001111
; or r17,r16
; swap r17 ;store high nibble
;
; rcall LCD_CLK ;receive low nibble
; in r16,LCDPIN
; andi r16,0b00001111
; or r17,r16 ;store low nibble
;
; ;output
; ldi r16,0b11111111 ;LCDPORT as output (RS RW E n.c. D7 D6 D5 D4)
; out LCDDDR,r16
; ldi r16,0b00000000 ;LCDPORT off
; out LCDPORT,r16
;
; ;r17=received value (D7 D6 D5 D4 D3 D2 D1 D0)
; ret
LCD_CLK:
sbi LCDPORT,E ;EXECUTE on
nop
nop
nop
cbi LCDPORT,E ;EXECUTE off
ldi r16,150 ;pause: 100 for 4MHZ
clkw:
dec r16
brne clkw
ret
;r17
;7 bitu (1.radek zacina 00,druhej 40)
DDRAM_SET_ADDR:
clr r16
sbr r16,0b10000000
or r17,r16
rcall TX_INSTR
ret
;r17
;6 bitu (5,4,3 = znak, 2,1,0 = radek - shora)
CGRAM_SET_ADDR:
clr r16
sbr r16,0b01000000
or r17,r16
rcall TX_INSTR
ret

@ -0,0 +1,8 @@
AVR utils library
=================
This is my ever-evolving library (not only) for AVR programming.
When I'm done with a project, I copy the current library to the project, so it doesn't break when I do further improvements.
Each library file contains a large comment block explaining it's function.

@ -0,0 +1,39 @@
#pragma once
#include <avr/io.h>
#include <stdbool.h>
#include "calc.h"
/** Initialize the ADC */
void adc_init()
{
ADCSRA |= _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128 prescaler -> 125 kHz
ADMUX |= _BV(REFS0); // Voltage reference
sbi(ADCSRA, ADEN); // Enable ADC
}
/** Sample analog pin with 8-bit precision */
uint8_t adc_read_byte(uint8_t channel)
{
write_low_nibble(ADMUX, channel); // Select channel to sample
sbi(ADMUX, ADLAR); // Align result to left
sbi(ADCSRA, ADSC); // Start conversion
while(bit_is_high(ADCSRA, ADSC)); // Wait for it...
return ADCH; // The upper 8 bits of ADC result
}
/** Sample analog pin with 10-bit precision */
uint16_t adc_read_word(uint8_t channel)
{
write_low_nibble(ADMUX, channel); // Select channel to sample
cbi(ADMUX, ADLAR); // Align result to right
sbi(ADCSRA, ADSC); // Start conversion
while(get_bit(ADCSRA, ADSC)); // Wait for it...
return ADCW; // The whole ADC word (10 bits)
}

@ -0,0 +1,42 @@
#pragma once
/**
Pin definitions for Arduino (Pro Mini with ATmega328P)
*/
#include "pins.h"
#define D0 D,0
#define D1 D,1
#define D2 D,2
#define D3 D,3
#define D4 D,4
#define D5 D,5
#define D6 D,6
#define D7 D,7
#define D8 B,0
#define D9 B,1
#define D10 B,2
// MOSI MISO SCK - not good for input
#define D11 B,3
#define D12 B,4
#define D13 B,5
#define D14 C,0
#define D15 C,1
#define D16 C,2
#define D17 C,3
#define D18 C,4
#define D19 C,5
#define D20 C,6
#define D21 C,7
#define A0 C,0
#define A1 C,1
#define A2 C,2
#define A3 C,3
#define A4 C,4
#define A5 C,5
#define A6 C,6
#define A7 C,7

@ -0,0 +1,89 @@
#pragma once
/**
Bit and byte manipulation utilities
*/
// --- Increment in range ---
// when overflown, wraps within range. Lower bound < upper bound.
// ..., upper bound excluded
#define inc_wrap(var, min, max) do { if ((var) >= (max - 1)) { (var) = (min); } else { (var)++; } } while(0)
// ..., upper bound included
#define inc_wrapi(var, min, max) inc_wrap((var), (min), (max) + 1)
// --- Decrement in range ---
// when underflown, wraps within range. Lower bound < upper bound.
// ..., upper bound excluded
#define dec_wrap(var, min, max) do { if ((var) <= (min)) { (var) = (max) - 1; } else { (var)--; } } while(0)
// ..., upper bound included
#define dec_wrapi(var, min, max) dec_wrap((var), (min), (max) + 1)
// --- Bit manipulation --
// Set bit
#define sbi(reg, bit) do { (reg) |= (1 << (uint8_t)(bit)); } while(0)
// Clear bit
#define cbi(reg, bit) do { (reg) &= ~(1 << (uint8_t)(bit)); } while(0)
// Get n-th bit
#define read_bit(reg, bit) (((reg) >> (uint8_t)(bit)) & 0x1)
#define get_bit(reg, bit) read_bit(reg, bit)
// Test n-th bit (Can't use bit_is_set, as it's redefined in sfr_def.h)
#define bit_is_high(reg, bit) read_bit(reg, bit)
#define bit_is_low(reg, bit) (!read_bit(reg, bit))
// Write value to n-th bit
#define write_bit(reg, bit, value) do { (reg) = ((reg) & ~(1 << (uint8_t)(bit))) | (((uint8_t)(value) & 0x1) << (uint8_t)(bit)); } while(0)
#define set_bit(reg, bit, value) write_bit(reg, bit, value)
// Invert n-th bit
#define toggle_bit(reg, bit) do { (reg) ^= (1 << (uint8_t)(bit)); } while(0)
// --- Bit manipulation with pointer to variable ---
// Set n-th bit in pointee
#define sbi_p(reg_p, bit) do { (*(reg_p)) |= (1 << (uint8_t)(bit)); } while(0)
// Clear n-th bit in pointee
#define cbi_p(reg_p, bit) do { (*(reg_p)) &= ~(1 << (uint8_t)(bit)); } while(0)
// Get n-th bit in pointee
#define read_bit_p(reg_p, bit) ((*(reg_p) >> (uint8_t)(bit)) & 0x1)
#define get_bit_p(reg_p, bit) read_bit_p(reg_p, bit)
// Test n-th bit in pointee (Can't use bit_is_set, as it's redefined in sfr_def.h)
#define bit_is_high_p(reg_p, bit) read_bit_p(reg_p, bit)
#define bit_is_low_p(reg_p, bit) (!read_bit_p(reg_p, bit))
// Write value to a bit in pointee
#define write_bit_p(reg_p, bit, value) do { *(reg_p) = (*(reg_p) & ~(1 << ((uint8_t)(bit) & 0x1))) | (((uint8_t)(value) & 0x1) << (uint8_t)(bit)); } while(0)
#define set_bit_p(reg_p, bit, value) write_bit_p(reg_p, bit, value)
#define toggle_bit_p(reg_p, bit) do { *(reg_p) ^= (1 << (uint8_t)(bit)); } while(0)
// --- Nibble manipulation ---
// Replace nibble in a byte
#define write_low_nibble(reg, value) do { (reg) = ((reg) & 0xF0) | ((uint8_t)(value) & 0xF); } while(0)
#define write_high_nibble(reg, value) do { (reg) = ((reg) & 0x0F) | (((uint8_t)(value) & 0xF) << 4); } while(0)
#define write_low_nibble_p(reg_p, value) do { *(reg_p) = (*(reg_p) & 0xF0) | ((uint8_t)(value) & 0xF); } while(0)
#define write_high_nibble_p(reg_p, value) do { *(reg_p) = (*(reg_p) & 0x0F) | (((uint8_t)(value) & 0xF) << 4); } while(0)
// --- Range tests ---
// Test if X is within low..high, regardless of bounds order
#define in_range(x, low, high) ((((low) < (high)) && ((x) >= (low) && (x) < (high))) || (((low) > (high)) && ((x) >= (high) && (x) < (low))))
// ..., include greater bound
#define in_rangei(x, low, high) ((((low) <= (high)) && ((x) >= (low) && (x) <= (high))) || (((low) > (high)) && ((x) >= (high) && (x) <= (low))))
// Test if X in low..high, wrap around ends if needed.
#define in_range_wrap(x, low, high) ((((low) < (high)) && ((x) >= (low) && (x) < (high))) || (((low) > (high)) && ((x) >= (low) || (x) < (high))))
// ..., include upper bound
#define in_range_wrapi(x, low, high) ((((low) <= (high)) && ((x) >= (low) && (x) <= (high))) || (((low) > (high)) && ((x) >= (low) || (x) <= (high))))

@ -0,0 +1,83 @@
#pragma once
/*
Some useful utilities for RGB color manipulation
The XXXc macros don't use cast, so they can be used in array initializers.
xrgb ... 3-byte true-color RGB (8 bits per component)
rgbXX ... XX-bit color value, with equal nr of bits per component
XX_r (_g, _b) ... extract component from the color, and convert it to 0..255
*/
typedef struct {
uint8_t r;
uint8_t g;
uint8_t b;
} xrgb_t;
typedef uint32_t rgb24_t;
typedef uint16_t rgb16_t;
typedef uint16_t rgb12_t;
typedef uint8_t rgb6_t;
#define xrgb(rr, gg, bb) ((xrgb_t)xrgbc(rr, gg, bb))
// xrgb for constant array declarations
#define xrgbc(rr, gg, bb) { .r = ((uint8_t)(rr)), .g = ((uint8_t)(gg)), .b = ((uint8_t)(bb)) }
#define xrgb_r(c) ((uint8_t)(c.r))
#define xrgb_g(c) ((uint8_t)(c.g))
#define xrgb_b(c) ((uint8_t)(c.b))
#define xrgb_rgb24(c) ((((rgb24_t)c.r) << 16) | (((rgb24_t)c.g) << 8) | (((rgb24_t)c.b)))
#define xrgb_rgb15(c) (((((rgb15_t)c.r) & 0xF8) << 7) | ((((rgb15_t)c.g) & 0xF8) << 2) | ((((rgb15_t)c.b) & 0xF8) >> 3))
#define xrgb_rgb12(c) (((((rgb12_t)c.r) & 0xF0) << 4) | ((((rgb12_t)c.g) & 0xF0)) | ((((rgb12_t)c.b) & 0xF0) >> 4))
#define xrgb_rgb6(c) (((((rgb6_t)c.r) & 0xC0) >> 2) | ((((rgb6_t)c.g) & 0xC0) >> 4) | ((((rgb6_t)c.b) & 0xC0) >> 6))
#define rgb24c(r,g,b) (((((rgb24_t)r) & 0xFF) << 16) | ((((rgb24_t)g) & 0xFF) << 8) | (((rgb24_t)b) & 0xFF))
#define rgb24(r,g,b) ((rgb24_t) rgb24(r,g,b))
#define rgb24_r(c) ((((rgb24_t) (c)) >> 16) & 0xFF)
#define rgb24_g(c) ((((rgb24_t) (c)) >> 8) & 0xFF)
#define rgb24_b(c) ((((rgb24_t) (c)) >> 0) & 0xFF)
#define rgb24_xrgb(c) xrgb(rgb24_r(c), rgb24_g(c), rgb24_b(c))
#define rgb24_xrgbc(c) xrgbc(rgb24_r(c), rgb24_g(c), rgb24_b(c))
#define rgb15(r,g,b) ((rgb16_t) rgb15c(r,g,b))
#define rgb15c(r,g,b) (((r & 0x1F) << 10) | ((g & 0x1F) << 5) | (b & 0x1F))
#define rgb15_r(c) ((((rgb15_t) (c)) & 0x7C00) >> 7)
#define rgb15_g(c) ((((rgb15_t) (c)) & 0x3E0) >> 2)
#define rgb15_b(c) ((((rgb15_t) (c)) & 0x1F) << 3)
#define rgb15_xrgb(c) xrgb(rgb15_r(c), rgb15_g(c), rgb15_b(c))
#define rgb15_rgb24(c) rgb24(rgb15_r(c), rgb15_g(c), rgb15_b(c))
#define rgb15_rgb24c(c) rgb24c(rgb15_r(c), rgb15_g(c), rgb15_b(c))
#define rgb12(r,g,b) ((rgb12_t) rgb12c(r,g,b))
#define rgb12c(r,g,b) (((r & 0xF) << 8) | ((g & 0xF) << 4) | (b & 0xF))
#define rgb12_r(c) ((((rgb12_t) (c)) & 0xF00) >> 4)
#define rgb12_g(c) (((rgb12_t) (c)) & 0xF0)
#define rgb12_b(c) (((r(rgb12_t) (c)gb) & 0x0F) << 4)
#define rgb12_xrgb(c) xrgb(rgb12_r(c), rgb12_g(c), rgb12_b(c))
#define rgb12_xrgbc(c) xrgbc(rgb12_r(c), rgb12_g(c), rgb12_b(c))
#define rgb12_rgb24(c) rgb24(rgb12_r(c), rgb12_g(c), rgb12_b(c))
#define rgb12_rgb24c(c) rgb24c(rgb12_r(c), rgb12_g(c), rgb12_b(c))
#define rgb6(r,g,b) ((rgb6_t) rgb6c(r,g,b))
#define rgb6c(r,g,b) (((r & 3) << 4) | ((g & 3) << 2) | (b & 3))
#define rgb6_r(c) ((((rgb6_t) (c)) & 0x30) << 2)
#define rgb6_g(c) ((((rgb6_t) (c)) & 0xC) << 4)
#define rgb6_b(c) ((((rgb6_t) (c)) & 0x3) << 6)
#define rgb6_xrgb(c) xrgb(rgb6_r(c), rgb6_g(c), rgb6_b(c))
#define rgb6_xrgbc(c) xrgbc(rgb6_r(c), rgb6_g(c), rgb6_b(c))
#define rgb6_rgb24(c) rgb24(rgb6_r(c), rgb6_g(c), rgb6_b(c))
#define rgb6_rgb24c(c) rgb24c(rgb6_r(c), rgb6_g(c), rgb6_b(c))
#define add_xrgb(x, y) ((xrgb_t) { (((y).r > (255 - (x).r)) ? 255 : ((x).r + (y).r)), (((y).g > (255 - (x).g)) ? 255 : ((x).g + (y).g)), (((y).b > 255 - (x).b) ? 255 : ((x).b + (y).b)) })

@ -0,0 +1,103 @@
#pragma once
/**
An implementation of button debouncer.
First, the system must be initialized - even before including:
#define DEBO_CHANNELS 2
#define DEBO_TICKS 5
#inclue "lib/debounce.h"
A pin is registered like this:
#define BTN1 B,0
#define BTN2 B,1
debo_add(BTN0); // The function returns number assigned to the pin (0, 1, ...)
debo_add_rev(BTN1); // active low
debo_register(&PINB, PB2, 0); // direct access - register, pin & invert
Then periodically call the tick function (perhaps in a timer interrupt):
debo_tick();
To check if input is active, use
debo_get_pin(0); // state of input registered as #0
debo_get_pin(1); // state of input registered as #1
*/
#include <avr/io.h>
#include <stdbool.h>
#include "calc.h"
#include "pins.h"
// Number of pins to debounce
#ifndef DEBO_CHANNELS
# error "DEBO_CHANNELS not defined!"
#endif
#ifndef DEBO_TICKS
# warning "DEBO_TICKS not defined, defaulting to 5!"
# define DEBO_TICKS 5
#endif
/* Internal deboucer entry */
typedef struct {
PORT_P reg; // pointer to IO register
uint8_t bit; // bits 6 and 7 of this hold "state" & "invert" flag
uint8_t count; // number of ticks this was in the new state
} debo_slot_t;
/** Debounce data array */
debo_slot_t debo_slots[DEBO_CHANNELS];
uint8_t debo_next_slot = 0;
/** Define a debounced pin (must be IO!) */
#define debo_add_rev(io) debo_register(&io2pin(io_pack(io)), io2n(io_pack(io)), 1)
#define debo_add(io) debo_register(&io2pin(io_pack(io)), io2n(io_pack(io)), 0)
uint8_t debo_register(PORT_P reg, uint8_t bit, bool invert)
{
debo_slots[debo_next_slot] = (debo_slot_t){
.reg = reg,
.bit = bit | ((invert & 1) << 7) | (get_bit_p(reg, bit) << 6), // bit 7 = invert, bit 6 = state
.count = 0,
};
return debo_next_slot++;
}
/** Check debounced pins, should be called periodically. */
void debo_tick()
{
for (uint8_t i = 0; i < debo_next_slot; i++) {
// current pin value (right 3 bits, xored with inverse bit)
bool value = get_bit_p(debo_slots[i].reg, debo_slots[i].bit & 0x7);
if (value != get_bit(debo_slots[i].bit, 6)) {
// different pin state than last recorded state
if (debo_slots[i].count < DEBO_TICKS) {
debo_slots[i].count++;
} else {
// overflown -> latch value
set_bit(debo_slots[i].bit, 6, value); // set state bit
debo_slots[i].count = 0;
}
} else {
debo_slots[i].count = 0; // reset the counter
}
}
}
/** Get a value of debounced pin */
#define debo_get_pin(i) (get_bit(debo_slots[i].bit, 6) ^ get_bit(debo_slots[i].bit, 7))

@ -0,0 +1,391 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include "calc.h"
#include "pins.h"
#include "nsdelay.h"
/*
HD44780 LCD display driver - 4-bit mode
Required macros - pin settings (eg. B,3 or D,0)
LCD_PIN_RS
LCD_PIN_RW
LCD_PIN_E
LCD_PIN_D7
LCD_PIN_D6
LCD_PIN_D5
LCD_PIN_D4
Define those before including the header file.
*/
// Commands for user
// Clear screen (reset)
#define LCD_CLEAR 0b00000001
// Move cursor to (0,0), unshift...
#define LCD_HOME 0b00000010
// Set mode: Increment + NoShift
#define LCD_MODE_INC 0b00000110
// Set mode: Increment + Shift
#define LCD_MODE_INC_SHIFT 0b00000111
// Set mode: Decrement + NoShift
#define LCD_MODE_DEC 0b00000100
// Set mode: Decrement + Shift
#define LCD_MODE_DEC_SHIFT 0b00000101
// Disable display (data remains untouched)
#define LCD_DISABLE 0b00001000
// Disable cursor
#define LCD_CURSOR_NONE 0b00001100
// Set cursor to still underscore
#define LCD_CURSOR_BAR 0b00001110
// Set cursor to blinking block
#define LCD_CURSOR_BLINK 0b00001101
// Set cursor to both of the above at once
#define LCD_CURSOR_BOTH (LCD_CURSOR_BAR | LCD_CURSOR_BLINK)
// Move cursor
#define LCD_MOVE_LEFT 0b00010000
#define LCD_MOVE_RIGHT 0b00010100
// Shift display
#define LCD_SHIFT_LEFT 0b00011000
#define LCD_SHIFT_RIGHT 0b00011100
// Set iface to 5x7 font, 1-line
#define LCD_IFACE_4BIT_1LINE 0b00100000
#define LCD_IFACE_8BIT_1LINE 0b00110000
// Set iface to 5x7 font, 2-line
#define LCD_IFACE_4BIT_2LINE 0b00101000
#define LCD_IFACE_8BIT_2LINE 0b00111000
// Start address of rows
const uint8_t LCD_ROW_ADDR[] = {0x00, 0x40, 0x14, 0x54};
// prototypes
// --- PUBLIC API ---
/** Init the display */
void lcd_init();
/** Write a command */
void lcd_write_command(const uint8_t bb);
/** Write data byte */
void lcd_write_data(const uint8_t bb);
/** Read busy flag & address */
uint8_t lcd_read_bf_addr();
/** Read byte from ram */
uint8_t lcd_read_ram();
/** Show string */
void lcd_str(char* str);
/** Show string at X, Y */
#define lcd_str_xy(x, y, str_p) do { lcd_xy((x), (y)); lcd_str((str_p)); } while(0)
/** Show char */
void lcd_char(const char c);
/** Show char at X, Y */
#define lcd_char_xy(x, y, c) do { lcd_xy((x), (y)); lcd_char((c)); } while(0)
/** Move cursor to X, Y */
void lcd_xy(const uint8_t x, const uint8_t y);
/** Set address in CGRAM */
void lcd_set_addr_cgram(const uint8_t acg);
/** Set address in DDRAM */
void lcd_set_addr(const uint8_t add);
/** Go home */
void lcd_home();
/** Clear the screen */
void lcd_clear();
/** Set cursor */
#define CURSOR_NONE 0b00
#define CURSOR_BAR 0b10
#define CURSOR_BLINK 0b01
#define CURSOR_BOTH 0b11
void lcd_cursor(uint8_t type);
/** Disable / enable, preserving cursor */
void lcd_disable();
void lcd_enable();
/** Define a custom glyph */
void lcd_define_glyph(const uint8_t index, const uint8_t* array);
// Internals
void _lcd_mode_r();
void _lcd_mode_w();
void _lcd_clk();
void _lcd_wait_bf();
void _lcd_write_byte(uint8_t bb);
uint8_t _lcd_read_byte();
// Write utilities
#define _lcd_write_low(bb) _lcd_write_nibble((bb) & 0x0F)
#define _lcd_write_high(bb) _lcd_write_nibble(((bb) & 0xF0) >> 4)
#define _lcd_write_nibble(nib) do { \
write_pin(LCD_PIN_D7, get_bit((nib), 3)); \
write_pin(LCD_PIN_D6, get_bit((nib), 2)); \
write_pin(LCD_PIN_D5, get_bit((nib), 1)); \
write_pin(LCD_PIN_D4, get_bit((nib), 0)); \
} while(0)
// 0 W, 1 R
bool _lcd_mode;
/** Initialize the display */
void lcd_init()
{
// configure pins as output
as_output(LCD_PIN_E);
as_output(LCD_PIN_RW);
as_output(LCD_PIN_RS);
_lcd_mode = 1; // force data pins to output
_lcd_mode_w();
// Magic sequence to enter 4-bit mode
_delay_ms(16);
_lcd_write_nibble(0b0011);
_lcd_clk();
_delay_ms(5);
_lcd_clk();
_delay_ms(5);
_lcd_clk();
_delay_ms(5);
_lcd_write_nibble(0b0010);
_lcd_clk();
_delay_us(100);
// Configure the display
lcd_write_command(LCD_IFACE_4BIT_2LINE);
lcd_write_command(LCD_DISABLE);
lcd_write_command(LCD_CLEAR);
lcd_write_command(LCD_MODE_INC);
lcd_enable();
}
/** Send a pulse on the ENABLE line */
void _lcd_clk()
{
pin_up(LCD_PIN_E);
delay_ns(420);
pin_down(LCD_PIN_E);
}
/** Enter READ mode */
void _lcd_mode_r()
{
if (_lcd_mode == 1) return; // already in R mode
pin_up(LCD_PIN_RW);
as_input_pu(LCD_PIN_D7);
as_input_pu(LCD_PIN_D6);
as_input_pu(LCD_PIN_D5);
as_input_pu(LCD_PIN_D4);
_lcd_mode = 1;
}
/** Enter WRITE mode */
void _lcd_mode_w()
{
if (_lcd_mode == 0) return; // already in W mode
pin_down(LCD_PIN_RW);
as_output(LCD_PIN_D7);
as_output(LCD_PIN_D6);
as_output(LCD_PIN_D5);
as_output(LCD_PIN_D4);
_lcd_mode = 0;
}
/** Read a byte */
uint8_t _lcd_read_byte()
{
_lcd_mode_r();
uint8_t res = 0;
_lcd_clk();
res = (read_pin(LCD_PIN_D7) << 7) | (read_pin(LCD_PIN_D6) << 6) | (read_pin(LCD_PIN_D5) << 5) | (read_pin(LCD_PIN_D4) << 4);
_lcd_clk();
res |= (read_pin(LCD_PIN_D7) << 3) | (read_pin(LCD_PIN_D6) << 2) | (read_pin(LCD_PIN_D5) << 1) | (read_pin(LCD_PIN_D4) << 0);
return res;
}
/** Write an instruction byte */
void lcd_write_command(uint8_t bb)
{
_lcd_wait_bf();
pin_down(LCD_PIN_RS); // select instruction register
_lcd_write_byte(bb); // send instruction byte
}
/** Write a data byte */
void lcd_write_data(uint8_t bb)
{
_lcd_wait_bf();
pin_up(LCD_PIN_RS); // select data register
_lcd_write_byte(bb); // send data byte
}
/** Read BF & Address */
uint8_t lcd_read_bf_addr()
{
pin_down(LCD_PIN_RS);
return _lcd_read_byte();
}
/** Read CGRAM or DDRAM */
uint8_t lcd_read_ram()
{
pin_up(LCD_PIN_RS);
return _lcd_read_byte();
}
/** Write a byte using the 8-bit interface */
void _lcd_write_byte(uint8_t bb)
{
_lcd_mode_w(); // enter W mode
_lcd_write_high(bb);
_lcd_clk();
_lcd_write_low(bb);
_lcd_clk();
}
/** Wait until the device is ready */
void _lcd_wait_bf()
{
while(lcd_read_bf_addr() & _BV(7));
}
/** Send a string to LCD */
void lcd_str(char* str_p)
{
while (*str_p)
lcd_char(*str_p++);
}
/** Sedn a char to LCD */
void lcd_char(const char c)
{
lcd_write_data(c);
}
/** Set cursor position */
void lcd_xy(const uint8_t x, const uint8_t y)
{
lcd_set_addr(LCD_ROW_ADDR[y] + (x));
}
uint8_t _lcd_old_cursor = CURSOR_NONE;
bool _lcd_enabled = false;
/** Set LCD cursor. If not enabled, only remember it. */
void lcd_cursor(uint8_t type)
{
_lcd_old_cursor = (type & CURSOR_BOTH);
if (_lcd_enabled) lcd_write_command(LCD_CURSOR_NONE | _lcd_old_cursor);
}
/** Display display (preserving cursor) */
void lcd_disable()
{
lcd_write_command(LCD_DISABLE);
_lcd_enabled = false;
}
/** Enable display (restoring cursor) */
void lcd_enable()
{
_lcd_enabled = true;
lcd_cursor(_lcd_old_cursor);
}
/** Go home */
void lcd_home()
{
lcd_write_command(LCD_HOME);
}
/** Clear the screen */
void lcd_clear()
{
lcd_write_command(LCD_CLEAR);
}
/** Define a glyph */
void lcd_define_glyph(const uint8_t index, const uint8_t* array)
{
lcd_set_addr_cgram(index * 8);
for (uint8_t i = 0; i < 8; ++i) {
lcd_write_data(array[i]);
}
}
/** Define a glyph */
void lcd_define_glyph_pgm(const uint8_t index, const uint8_t* array)
{
lcd_set_addr_cgram(index * 8);
for (uint8_t i = 0; i < 8; ++i) {
lcd_write_data(pgm_read_byte(&array[i]));
}
}
/** Set address in CGRAM */
void lcd_set_addr_cgram(const uint8_t acg)
{
lcd_write_command(0b01000000 | ((acg) & 0b00111111));
}
/** Set address in DDRAM */
void lcd_set_addr(const uint8_t add)
{
lcd_write_command(0b10000000 | ((add) & 0b01111111));
}

@ -0,0 +1,22 @@
#pragma once
/**
Custom loops
*/
// Repeat code n times (uint8_t counter)
#define repeat(count) repeat_aux(count, _repeat_##__COUNTER__)
#define repeat_aux(count, cntvar) for (uint8_t cntvar = 0; cntvar < (count); cntvar++)
// Repeat code n times (uint16_t counter)
#define repeatx(count) repeatx_aux(count, _repeatx_##__COUNTER__)
#define repeatx_aux(count, cntvar) for (uint16_t cntvar = 0; cntvar < (count); cntvar++)
// Repeat with custom counter name (uint8_t)
#define loop(var, count) repeat_aux(count, var)
// ..., uint16_t
#define loopx(var, count) repeatx_aux(count, var)
// Do until condition is met
#define until(what) while(!(what))

@ -0,0 +1,6 @@
#pragma once
/** Weird constructs for the compiler */
// general macros
#define SECTION(pos) __attribute__((naked, used, section(pos)))

@ -0,0 +1,21 @@
#pragma once
/**
Functions for precise delays (nanoseconds / cycles)
*/
#include <avr/io.h>
#include <util/delay_basic.h>
#include <stdint.h>
/* Convert nanoseconds to cycle count */
#define ns2cycles(ns) ( (ns) / (1000000000L / (signed long) F_CPU) )
/** Wait c cycles */
#define delay_c(c) (((c) > 0) ? __builtin_avr_delay_cycles(c) : __builtin_avr_delay_cycles(0))
/** Wait n nanoseconds, plus c cycles */
#define delay_ns_c(ns, c) delay_c(ns2cycles(ns) + (c))
/** Wait n nanoseconds */
#define delay_ns(ns) delay_c(ns2cycles(ns))

@ -0,0 +1,107 @@
#pragma once
/**
This file provides macros for pin manipulation.
You can define your application pins like so:
// Led at PORTB, pin 1
#define LED B,1
// Switch at PORTD, pin 7
#define SW1 D,7
Now you can use macros from this file to wirh with the pins, eg:
as_output(LED);
as_input(SW1);
pullup_on(SW1);
toggle_pin(LED);
while (pin_is_low(SW1));
- The macros io2XXX() can be used to get literal name of register associated with the pin.
- io2n() provides pin number.
- The XXX_aux() macros are internal and should not be used elsewhere.
- The io_pack() macro is used to pass pin (io) to other macro without expanding it.
*/
#include <avr/io.h>
#include "calc.h"
// Get particular register associated with the name X (eg. D -> PORTD)
#define reg_ddr(X) DDR ## X
#define reg_port(X) PORT ## X
#define reg_pin(X) PIN ## X
#define io2ddr_aux(reg, bit) reg_ddr(reg)
#define io2ddr(io) io2ddr_aux(io)
#define io2port_aux(reg, bit) reg_port(reg)
#define io2port(io) io2port_aux(io)
#define io2pin_aux(reg, bit) reg_pin(reg)
#define io2pin(io) io2pin_aux(io)
#define io2n_aux(reg, bit) bit
#define io2n(io) io2n_aux(io)
#define io_pack(port, bit) port, bit
// pointer to port
typedef volatile uint8_t* PORT_P;
// number of bit in port
typedef uint8_t BIT_N;
// === pin manipulation ===
#define set_pin_aux(port, bit) sbi(reg_port(port), (bit))
#define clear_pin_aux(port, bit) cbi(reg_port(port), (bit))
#define read_pin_aux(port, bit) get_bit(reg_pin(port), (bit))
#define write_pin_aux(port, bit, value) set_bit(reg_port(port), (bit), (value))
#define toggle_pin_aux(port, bit) sbi(reg_pin(port), (bit))
#define pin_up(io) set_pin_aux(io)
#define pin_high(io) set_pin_aux(io)
#define pin_down(io) clear_pin_aux(io)
#define pin_low(io) clear_pin_aux(io)
#define get_pin(io) read_pin_aux(io)
#define read_pin(io) read_pin_aux(io)
#define pin_is_low(io) !read_pin_aux(io)
#define pin_is_high(io) read_pin_aux(io)
#define set_pin(io, value) write_pin_aux(io, (value))
#define write_pin(io, value) write_pin_aux(io, (value))
#define toggle_pin(io) toggle_pin_aux(io)
// setting pin direction
#define as_input_aux(port, bit) cbi(reg_ddr(port), (bit))
#define as_output_aux(port, bit) sbi(reg_ddr(port), (bit))
#define set_dir_aux(port, bit, dir) write_bit(reg_ddr(port), (bit), (dir))
#define as_input(io) as_input_aux(io)
#define as_input_pu(io) do { as_input_aux(io); pullup_enable_aux(io); } while(0)
#define as_output(io) as_output_aux(io)
#define set_dir(io, dir) set_dir_aux(io, (dir))
// setting pullup
#define pullup_enable_aux(port, bit) sbi(reg_port(port), (bit))
#define pullup_disable_aux(port, bit) cbi(reg_port(port), (bit))
#define set_pullup_aux(port, bit, on) write_bit(reg_port(port), (bit), (on))
#define pullup_enable(io) pullup_enable_aux(io)
#define pullup_on(io) pullup_enable_aux(io)
#define pullup_disable(io) pullup_disable_aux(io)
#define pullup_off(io) pullup_disable_aux(io)
#define set_pullup(io, on) set_pullup_aux(io, on)

@ -0,0 +1,98 @@
#pragma once
/**
Utils for driving a WS28xx (tested on WS2812B) RGB LED strips.
It's implemented as macros to avoid overhead when passing values, and to
enable driving multiple strips at once.
To avoid bloating your code, try to reduce the number of invocations -
compute color and then send it.
[IMPORTANT]
Some seemingly random influences can ruin the communication.
If you have enough memory, consider preparing the colors in array,
and sending this array using one of the "ws_send_XXX_array" macros.
*/
#include <avr/io.h>
#include "pins.h"
#include "nsdelay.h"
#include "colors.h"
/* Driver code for WS2812B */
// --- timing constraints (NS) ---
#ifndef WS_T_1H
# define WS_T_1H 700
#endif
#ifndef WS_T_1L
# define WS_T_1L 150
#endif
#ifndef WS_T_0H
# define WS_T_0H 150
#endif
#ifndef WS_T_0L
# define WS_T_0L 700
#endif
#ifndef WS_T_LATCH
# define WS_T_LATCH 7000
#endif
/** Wait long enough for the colors to show */
#define ws_show() do {delay_ns_c(WS_T_LATCH, 0); } while(0)
/** Send one byte to the RGB strip */
#define ws_send_byte(io, bb) do { \
for (volatile int8_t __ws_tmp = 7; __ws_tmp >= 0; --__ws_tmp) { \
if ((bb) & (1 << __ws_tmp)) { \
pin_high(io_pack(io)); delay_ns_c(WS_T_1H, -2); \
pin_low(io_pack(io)); delay_ns_c(WS_T_1L, -10); \
} else { \
pin_high(io_pack(io)); delay_ns_c(WS_T_0H, -2); \
pin_low(io_pack(io)); delay_ns_c(WS_T_0L, -10); \
} \
} \
} while(0)
/** Send R,G,B color to the strip */
#define ws_send_rgb(io, r, g, b) do { \
ws_send_byte(io_pack(io), g); \
ws_send_byte(io_pack(io), r); \
ws_send_byte(io_pack(io), b); \
} while(0)
/** Send a RGB struct */
#define ws_send_xrgb(io, xrgb) ws_send_rgb(io_pack(io), (xrgb).r, (xrgb).g, (xrgb).b)
/** Send color hex */
#define ws_send_rgb24(io, rgb) ws_send_rgb(io_pack(io), rgb24_r(rgb), rgb24_g(rgb), rgb24_b(rgb))
#define ws_send_rgb15(io, rgb) ws_send_rgb(io_pack(io), rgb15_r(rgb), rgb15_g(rgb), rgb15_b(rgb))
#define ws_send_rgb12(io, rgb) ws_send_rgb(io_pack(io), rgb12_r(rgb), rgb12_g(rgb), rgb12_b(rgb))
#define ws_send_rgb6(io, rgb) ws_send_rgb(io_pack(io), rgb6_r(rgb), rgb6_g(rgb), rgb6_b(rgb))
/** Send array of colors */
#define ws_send_xrgb_array(io, rgbs, length) __ws_send_array_proto(io_pack(io), (rgbs), (length), xrgb)
#define ws_send_rgb24_array(io, rgbs, length) __ws_send_array_proto(io_pack(io), (rgbs), (length), rgb24)
#define ws_send_rgb15_array(io, rgbs, length) __ws_send_array_proto(io_pack(io), (rgbs), (length), rgb15)
#define ws_send_rgb12_array(io, rgbs, length) __ws_send_array_proto(io_pack(io), (rgbs), (length), rgb12)
#define ws_send_rgb6_array(io, rgbs, length) __ws_send_array_proto(io_pack(io), (rgbs), (length), rgb6)
// prototype for sending array. it's ugly, sorry.
#define __ws_send_array_proto(io, rgbs, length, style) do { \
for (uint8_t __ws_tmp_sap_i = 0; __ws_tmp_sap_i < length; __ws_tmp_sap_i++) { \
style ## _t __ws_tmp_sap2 = (rgbs)[__ws_tmp_sap_i]; \
ws_send_ ## style(io_pack(io), __ws_tmp_sap2); \
} \
} while(0)

@ -0,0 +1,31 @@
#pragma once
/**
Ye Olde Control Structures
*/
#include "loops.h"
#define whilst(what) while((what))
#define when(what) if((what))
#define otherwise else
#define commence {
#define then {
#define cease }
#define choose(what) switch((what))
#define option case
#define shatter break
#define replay continue
#define equals ==
#define is ==
#define be =
#define over >
#define above >
#define under <
#define below <
#define let /**/
#define raise(what) (what)++
#define number int
#warning "This is a joke. Do not use YeOlde.h in serious code!"

@ -0,0 +1,426 @@
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "lib/meta.h"
#include "lib/arduino_pins.h"
#include "lib/calc.h"
#include "lib/adc.h"
#define LCD_PIN_RS D10
#define LCD_PIN_RW D11
#define LCD_PIN_E D12
#define LCD_PIN_D4 D13
#define LCD_PIN_D5 D14
#define LCD_PIN_D6 D15
#define LCD_PIN_D7 D16
// D17 = A3 = source of entropy for random.
#include "lib/lcd.h"
// Buttons (to ground)
#define BTN_LEFT D2
#define BTN_RIGHT D3
#define BTN_UP D4
#define BTN_DOWN D5
#define BTN_SELECT D6
#define BTN_RESTART D7
// Debouncer channels for buttons
// (Must be added in this order to debouncer)
#define D_LEFT 0
#define D_RIGHT 1
#define D_UP 2
#define D_DOWN 3
#define D_SELECT 4
#define D_RESTART 5
#define DEBO_CHANNELS 6
#define DEBO_TICKS 1 // in 0.01s
#include "lib/debounce.h"
// Board size (!!! rows = 2x number of display lines, max 2*4 = 8 !!!)
#define ROWS 4
#define COLS 20
// Delay between snake steps, in 10 ms
#define STEP_DELAY 24
// proto
void update();
void init_cgram();
void init_gameboard();
void SECTION(".init8") init()
{
// Randomize RNG
adc_init();
srand(adc_read_word(3));
// Init LCD
lcd_init();
init_cgram(); // load default glyphs
// Init game board.
init_gameboard();
// gamepad buttons
as_input_pu(BTN_LEFT);
as_input_pu(BTN_RIGHT);
as_input_pu(BTN_UP);
as_input_pu(BTN_DOWN);
as_input_pu(BTN_SELECT);
as_input_pu(BTN_RESTART);
// add buttons to debouncer
debo_add_rev(BTN_LEFT);
debo_add_rev(BTN_RIGHT);
debo_add_rev(BTN_UP);
debo_add_rev(BTN_DOWN);
debo_add_rev(BTN_SELECT);
debo_add_rev(BTN_RESTART);
// setup timer
TCCR0A = _BV(WGM01); // CTC
TCCR0B = _BV(CS02) | _BV(CS00); // prescaler 1024
OCR0A = 156; // interrupt every 10 ms
sbi(TIMSK0, OCIE0A);
sei();
}
/** timer 0 interrupt vector */
ISR(TIMER0_COMPA_vect)
{
debo_tick(); // poll debouncer
update(); // update and display
}
// sub-glyphs
#define _HEAD_ 15, 21, 21, 30
#define _BODY_ 15, 31, 31, 30
#define _FOOD_ 10, 21, 17, 14
//14, 17, 17, 14
#define _NONE_ 0, 0, 0, 0
// complete glyphs for loading into memory
// Only one food & one head glyph have to be loaded at a time.
// Body - Body
const uint8_t SYMBOL_BB[] PROGMEM = {_BODY_, _BODY_};
// Body - None
const uint8_t SYMBOL_BX[] PROGMEM = {_BODY_, _NONE_};
const uint8_t SYMBOL_XB[] PROGMEM = {_NONE_, _BODY_};
// Head - None
const uint8_t SYMBOL_HX[] PROGMEM = {_HEAD_, _NONE_};
const uint8_t SYMBOL_XH[] PROGMEM = {_NONE_, _HEAD_};
// Body - Head
const uint8_t SYMBOL_BH[] PROGMEM = {_BODY_, _HEAD_};
const uint8_t SYMBOL_HB[] PROGMEM = {_HEAD_, _BODY_};
// Head - Food
const uint8_t SYMBOL_HF[] PROGMEM = {_HEAD_, _FOOD_};
const uint8_t SYMBOL_FH[] PROGMEM = {_FOOD_, _HEAD_};
// Food - None
const uint8_t SYMBOL_FX[] PROGMEM = {_FOOD_, _NONE_};
const uint8_t SYMBOL_XF[] PROGMEM = {_NONE_, _FOOD_};
// Body - Food
const uint8_t SYMBOL_BF[] PROGMEM = {_BODY_, _FOOD_};
const uint8_t SYMBOL_FB[] PROGMEM = {_FOOD_, _BODY_};
// board block (snake, food...)
typedef enum {
bEMPTY = 0x00,
bHEAD = 0x01,
bFOOD = 0x02,
bBODY_LEFT = 0x80,
bBODY_RIGHT = 0x81,
bBODY_UP = 0x82,
bBODY_DOWN = 0x83,
} block_t;
// Snake direction
typedef enum {
dLEFT = 0x00,
dRIGHT = 0x01,
dUP = 0x02,
dDOWN = 0x03,
} dir_t;
// Coordinate on board
typedef struct {
int8_t x;
int8_t y;
} coord_t;
#define is_body(blk) (((blk) & 0x80) != 0)
#define mk_body_dir(dir) (0x80 + (dir))
// compare two coords
#define coord_eq(a, b) (((a).x == (b).x) && ((a).y == (b).y))
bool crashed;
uint8_t snake_len;
// y, x indexing
block_t board[ROWS][COLS];
coord_t head_pos;
coord_t tail_pos;
dir_t head_dir;
const uint8_t CODE_BB = 0;
const uint8_t CODE_BX = 1;
const uint8_t CODE_XB = 2;
const uint8_t CODE_H = 3; // glyph with head, dynamic
const uint8_t CODE_F = 4; // glyph with food, dynamic
const uint8_t CODE_XX = 32; // space
// Set a block in board
#define set_block_xy(x, y, block) do { board[y][x] = (block); } while(0)
#define get_block_xy(x, y) board[y][x]
#define get_block(pos) get_block_xy((pos).x, (pos).y)
#define set_block(pos, block) set_block_xy((pos).x, (pos).y, (block))
void init_cgram()
{
// those will be always the same
lcd_define_glyph_pgm(CODE_BB, SYMBOL_BB);
lcd_define_glyph_pgm(CODE_BX, SYMBOL_BX);
lcd_define_glyph_pgm(CODE_XB, SYMBOL_XB);
lcd_define_glyph_pgm(5, SYMBOL_XF);
}
void place_food()
{
while(1) {
const uint8_t xx = rand() % COLS;
const uint8_t yy = rand() % ROWS;
if (get_block_xy(xx, yy) == bEMPTY) {
set_block_xy(xx, yy, bFOOD);
break;
}
}
}
void init_gameboard()
{
//erase the board
for (uint8_t x = 0; x < COLS; x++) {
for (uint8_t y = 0; y < ROWS; y++) {
set_block_xy(x, y, bEMPTY);
}
}
lcd_clear();
tail_pos = (coord_t) {.x = 0, .y = 0};
set_block_xy(0, 0, bBODY_RIGHT);
set_block_xy(1, 0, bBODY_RIGHT);
set_block_xy(2, 0, bBODY_RIGHT);
set_block_xy(3, 0, bHEAD);
head_pos = (coord_t) {.x = 3, .y = 0};
snake_len = 4; // includes head
head_dir = dRIGHT;
crashed = false;
place_food();
}
uint8_t presc = 0;
bool restart_held;
void update()
{
if (debo_get_pin(D_RESTART)) {
if (!restart_held) {
// restart
init_gameboard();
presc = 0;
restart_held = true;
}
} else {
restart_held = false;
}
if(!crashed) {
// resolve movement direction
if (debo_get_pin(D_LEFT))
head_dir = dLEFT;
else if (debo_get_pin(D_RIGHT))
head_dir = dRIGHT;
else if (debo_get_pin(D_UP))
head_dir = dUP;
else if (debo_get_pin(D_DOWN))
head_dir = dDOWN;
// time's up for a move
if (presc++ == STEP_DELAY) {
presc = 0;
// move snake
const coord_t oldpos = head_pos;
switch (head_dir) {
case dLEFT: head_pos.x--; break;
case dRIGHT: head_pos.x++; break;
case dUP: head_pos.y--; break;
case dDOWN: head_pos.y++; break;
}
bool do_move = false;
bool do_grow = false;
if (head_pos.x < 0 || head_pos.x >= COLS || head_pos.y < 0 || head_pos.y >= ROWS) {
// ouch, a wall!
crashed = true;
} else {
// check if tile occupied or not
if (coord_eq(head_pos, tail_pos)) {
// head moved in previous tail, that's OK.
do_move = true;
} else {
// moved to other tile than tail
switch (get_block(head_pos)) {
case bFOOD:
do_grow = true; // fall through
case bEMPTY:
do_move = true;
break;
default: // includes all BODY_xxx
crashed = true; // snake crashed into some block
}
}
}
if (do_move) {
// Move tail
if (do_grow) {
// let tail as is
snake_len++; // grow the counter
} else {
// tail dir
dir_t td = get_block(tail_pos) & 0xF;
// clean tail
set_block(tail_pos, bEMPTY);
// move tail based on old direction of tail block
switch (td) {
case dLEFT: tail_pos.x--; break;
case dRIGHT: tail_pos.x++; break;
case dUP: tail_pos.y--; break;
case dDOWN: tail_pos.y++; break;
}
}
// Move head
set_block(head_pos, bHEAD); // place head in new pos
set_block(oldpos, mk_body_dir(head_dir)); // directional body in old head pos
if (do_grow) {
// food eaten, place new
place_food();
}
}
}
} // end !crashed
// Render the board
for (uint8_t r = 0; r < ROWS / 2; r++) {
lcd_xy(0, r);
for (uint8_t c = 0; c < COLS; c++) {
const block_t t1 = get_block_xy(c, r * 2);
const block_t t2 = get_block_xy(c, (r * 2) + 1);
uint8_t code = '!'; // ! marks fail
if ((t1 == bEMPTY) && (t2 == bEMPTY)) {
code = CODE_XX;
if (crashed) code = '*';
} else if (is_body(t1) && is_body(t2))
code = CODE_BB;
else if (is_body(t1) && (t2 == bEMPTY))
code = CODE_BX;
else if (t1 == bEMPTY && is_body(t2))
code = CODE_XB;
else if ((t1 == bFOOD) || (t2 == bFOOD)) {
// one is food
code = CODE_F;
if (t1 == bFOOD) {
if (t2 == bEMPTY)
lcd_define_glyph_pgm(code, SYMBOL_FX);
else if (t2 == bHEAD)
lcd_define_glyph_pgm(code, SYMBOL_FH);
else if (is_body(t2))
lcd_define_glyph_pgm(code, SYMBOL_FB);
} else { // t2 is food
if (t1 == bEMPTY)
lcd_define_glyph_pgm(code, SYMBOL_XF);
else if (t1 == bHEAD)
lcd_define_glyph_pgm(code, SYMBOL_HF);
else if (is_body(t1))
lcd_define_glyph_pgm(code, SYMBOL_BF);
}
lcd_xy(c,r);
} else if ((t1 == bHEAD )|| (t2 == bHEAD)) {
// one is head
code = CODE_H;
if (t1 == bHEAD) {
if (t2 == bEMPTY)
lcd_define_glyph_pgm(code, SYMBOL_HX);
else if (is_body(t2))
lcd_define_glyph_pgm(code, SYMBOL_HB);
} else { // t2 is head
if (t1 == bEMPTY)
lcd_define_glyph_pgm(code, SYMBOL_XH);
else if (is_body(t1))
lcd_define_glyph_pgm(code, SYMBOL_BH);
}
lcd_xy(c,r);
}
lcd_char(code);
}
}
}
void main() { while(1); } // timer does everything
Loading…
Cancel
Save