This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <avr/interrupt.h> | |
#include <avr/pgmspace.h> | |
#include <stdbool.h> | |
static void init(void); | |
#define A_BUTTON 128 | |
#define B_BUTTON 64 | |
#define RIGHT_BUTTON 32 | |
#define DOWN_BUTTON 16 | |
#define UP_BUTTON 8 | |
#define LEFT_BUTTON 4 | |
#define NOTE_A5 1136 | |
#define NOTE_GS5 1203 | |
#define NOTE_G5 1275 | |
#define NOTE_F5 1431 | |
#define NOTE_E5 1516 | |
#define NOTE_D5 1702 | |
#define NOTE_C5 1911 | |
#define NOTE_B4 2024 | |
#define NOTE_A4 2272 | |
#define NOTE_GS4 2407 | |
typedef struct | |
{ | |
int8_t second; | |
int8_t minute; | |
int8_t hour; | |
} time; | |
time t; | |
bool clockMode; | |
uint16_t counter; | |
int8_t d2, d3, d4, position, player, obstacles; | |
uint8_t SEG_1B, SEG_1C; | |
uint8_t SEG_2A, SEG_2B, SEG_2C, SEG_2D, SEG_2E, SEG_2F, SEG_2G; | |
uint8_t SEG_3A, SEG_3B, SEG_3C, SEG_3D, SEG_3E, SEG_3F, SEG_3G; | |
uint8_t noteIndex, currentButtonState, previousButtonState; | |
const uint16_t notes[192] PROGMEM = {NOTE_E5, NOTE_E5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_A4, NOTE_A4, NOTE_A4, NOTE_C5, NOTE_E5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_B4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_D5, NOTE_E5, NOTE_E5, NOTE_C5, NOTE_C5, NOTE_A4, NOTE_A4, NOTE_A4, NOTE_A4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_D5, NOTE_D5, NOTE_F5, NOTE_A5, NOTE_A5, NOTE_G5, NOTE_F5, NOTE_E5, NOTE_E5, NOTE_E5, NOTE_C5, NOTE_E5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_B4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_D5, NOTE_E5, NOTE_E5, NOTE_C5, NOTE_C5, NOTE_A4, NOTE_A4, NOTE_A4, NOTE_A4, NOTE_B4, NOTE_C5, NOTE_E5, NOTE_E5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_A4, NOTE_A4, NOTE_A4, NOTE_C5, NOTE_E5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_B4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_D5, NOTE_E5, NOTE_E5, NOTE_C5, NOTE_C5, NOTE_A4, NOTE_A4, NOTE_A4, NOTE_A4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_D5, NOTE_D5, NOTE_F5, NOTE_A5, NOTE_A5, NOTE_G5, NOTE_F5, NOTE_E5, NOTE_E5, NOTE_E5, NOTE_C5, NOTE_E5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_B4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_D5, NOTE_E5, NOTE_E5, NOTE_C5, NOTE_C5, NOTE_A4, NOTE_A4, NOTE_A4, NOTE_A4, NOTE_B4, NOTE_C5, NOTE_E5, NOTE_E5, NOTE_E5, NOTE_E5, NOTE_C5, NOTE_C5, NOTE_C5, NOTE_C5, NOTE_D5, NOTE_D5, NOTE_D5, NOTE_D5, NOTE_B4, NOTE_B4, NOTE_B4, NOTE_B4, NOTE_C5, NOTE_C5, NOTE_C5, NOTE_C5, NOTE_A4, NOTE_A4, NOTE_A4, NOTE_A4, NOTE_GS4, NOTE_GS4, NOTE_GS4, NOTE_GS4, NOTE_B4, NOTE_B4, NOTE_B4, NOTE_B4, NOTE_E5, NOTE_E5, NOTE_E5, NOTE_E5, NOTE_C5, NOTE_C5, NOTE_C5, NOTE_C5, NOTE_D5, NOTE_D5, NOTE_D5, NOTE_D5, NOTE_B4, NOTE_B4, NOTE_B4, NOTE_B4, NOTE_C5, NOTE_C5, NOTE_E5, NOTE_E5, NOTE_A5, NOTE_A5, NOTE_A5, NOTE_A5, NOTE_GS5, NOTE_GS5, NOTE_GS5, NOTE_GS5, NOTE_GS5, NOTE_GS5, NOTE_A4, NOTE_C5}; | |
uint8_t buttonsState(void) | |
{ | |
return ((~PINE) & 0b11111100); | |
} | |
void pollButtons(void) | |
{ | |
previousButtonState = currentButtonState; | |
currentButtonState = buttonsState(); | |
} | |
bool pressed(uint8_t buttons) | |
{ | |
return (buttonsState() & buttons) == buttons; | |
} | |
bool justPressed(uint8_t button) | |
{ | |
return (!(previousButtonState & button) && (currentButtonState & button)); | |
} | |
bool justReleased(uint8_t button) | |
{ | |
return ((previousButtonState & button) && !(currentButtonState & button)); | |
} | |
int main(void) | |
{ | |
ASSR = 0b00001000; // Asynchronous Timer/Counter2 | |
ADCSRA = 0b00000000; // Disable ADC | |
PRR = 0b00000011; // Power Reduction Register (Shutdown USART/ADC) | |
LCDFRR = 0b01000000; // Prescaler clkLCD/512 | |
LCDCCR = 0b00010000; // LCD Maximum Drive Time | |
LCDCRB = 0b10000111; // LCD Clock Select, LCD Mux Select, LCD Port Mask | |
LCDCRA = 0b10000110; // LCD Enable, LCD Buffer Disable, LCD Contrast Control Disable | |
DDRA = 0b11110001; // COM0, SEG0-SEG3 | |
DDRB = 0b01000110; // SPI, Buzzer | |
DDRC = 0b11111111; // SEG12-SEG5 | |
DDRD = 0b11111111; // SEG22-SEG15 | |
DDRE = 0b00000000; // Button Inputs | |
DDRF = 0b10001101; // XOR 1A,3A,4A,2A | |
DDRG = 0b00011111; // SEG14,SEG13,SEG4,SEG24,SEG23 | |
// enable pull-ups on inputs and unused pins | |
PORTA = 0b00001110; | |
PORTB = 0b10110001; | |
PORTE = 0b11111111; | |
PORTF = 0b01110010; | |
// set up Timer 1 | |
TCCR1A = 0; // set normal mode (which disconnects the pin) | |
TCCR1B = 0b00001001; // CTC mode, No prescaling | |
t.hour = 12; | |
clockMode = true; | |
/* Initialize registers and configure RTC. */ | |
init(); | |
while (1) | |
{ | |
pollButtons(); | |
if (justReleased(B_BUTTON)) | |
{ | |
if (clockMode) // leaving clock mode | |
{ | |
clockMode = false; | |
SEG_1C = 0; | |
SEG_1B = 0; | |
counter = 0; | |
position = 4; | |
obstacles = 0; | |
d2 = -1; | |
d3 = -1; | |
d4 = -1; | |
} | |
else if (!clockMode) // leaving game mode | |
{ | |
clockMode = true; | |
PORTF = 0b01110010; | |
TCCR1A = 0; // set normal mode (which disconnects the pin) | |
} | |
} | |
if (clockMode) | |
{ | |
if (justReleased(UP_BUTTON) || justReleased(DOWN_BUTTON)) | |
{ | |
t.second = 0; | |
TIMSK2 = 1; // Enable Overflow Interrupt | |
TCCR1A = 0; // set normal mode (which disconnects the pin) | |
} | |
if (justPressed(UP_BUTTON) || justPressed(DOWN_BUTTON)) | |
{ | |
TIMSK2 = 0; // Disable Overflow Interrupt | |
PORTF = 0b01110010; // Disable Colon | |
OCR1A = pgm_read_word(¬es[noteIndex]); | |
TCCR1A = 0b00010000; // set toggle on compare mode (which connects the pin) | |
counter = 0; | |
} | |
if (pressed(UP_BUTTON) && ++counter == 200) | |
{ | |
counter = 0; | |
if(++noteIndex == 192) noteIndex = 0; | |
OCR1A = pgm_read_word(¬es[noteIndex]); | |
if (++t.minute == 60) | |
{ | |
t.minute = 0; | |
if (++t.hour == 13) | |
{ | |
t.hour = 1; | |
} | |
} | |
} | |
else if (pressed(DOWN_BUTTON) && ++counter == 200) | |
{ | |
counter = 0; | |
if(++noteIndex == 192) noteIndex = 0; | |
OCR1A = pgm_read_word(¬es[noteIndex]); | |
if (--t.minute == -1) | |
{ | |
t.minute = 59; | |
if (--t.hour == 0) | |
{ | |
t.hour = 12; | |
} | |
} | |
} | |
d3 = 0; | |
d4 = t.minute; | |
while (d4 > 9) | |
{ | |
d4 = d4 - 10; | |
d3 = d3 + 1; | |
} | |
if (t.hour > 9) | |
{ | |
d2 = t.hour - 10; | |
SEG_1C = 2; | |
SEG_1B = 128; | |
} | |
else | |
{ | |
d2 = t.hour; | |
SEG_1C = 0; | |
SEG_1B = 0; | |
} | |
} | |
else // game mode | |
{ | |
if (pressed(UP_BUTTON)) | |
{ | |
PORTF = 0b00001000; // player top (SEG_1A) | |
player = 10; | |
} | |
else if (pressed(DOWN_BUTTON)) | |
{ | |
PORTF = 0b10000000; // player bottom (SEG_1D) | |
player = 11; | |
} | |
else | |
{ | |
PORTF = 0b00000001; // player middle (SEG_1G) | |
} | |
if (counter == 1500) | |
{ | |
OCR1A = 2272; // 440Hz | |
TCCR1A = 0b00010000; // set toggle on compare mode (which connects the pin) | |
} | |
if (++counter == 2000) | |
{ | |
switch(position) | |
{ | |
case 1: | |
if (player == d2) // GAME OVER! | |
{ | |
for (uint16_t z = 0; z < 65535; z++) | |
{ | |
switch(z) | |
{ | |
case 0: // SEG A | |
OCR1A = 1136; // 880Hz, see https://pages.mtu.edu/~suits/notefreqs.html and https://eleccelerator.com/avr-timer-calculator/ | |
LCDDR2 = 0b00010001; | |
LCDDR1 = 0b00010000; | |
LCDDR0 = 0; | |
PORTF = 0b00001000; | |
break; | |
case 4096: | |
OCR1A = 1203; // 830.61Hz | |
break; | |
case 8192: // SEG F | |
OCR1A = 1275; // 783.99Hz | |
LCDDR3 = 0b00000001; | |
LCDDR2 = 0b00100010; | |
LCDDR1 = 0b00100000; | |
PORTF = 0; | |
break; | |
case 12288: | |
OCR1A = 1351; // 739.99Hz | |
break; | |
case 16384: // SEG G | |
OCR1A = 1431; // 698.46Hz | |
LCDDR3 = 0; | |
LCDDR2 = 0b01000100; | |
LCDDR1 = 0b01000000; | |
PORTF = 0b00000001; | |
break; | |
case 20480: | |
OCR1A = 1516; // 659.25Hz | |
break; | |
case 24576: // SEG C | |
OCR1A = 1607; // 622.25Hz | |
LCDDR2 = 0; | |
LCDDR1 = 0b00000100; | |
LCDDR0 = 0b10010010; | |
PORTF = 0; | |
break; | |
case 28672: | |
OCR1A = 1702; // 587.33Hz | |
break; | |
case 32768: // SEG D | |
OCR1A = 1803; // 554.37Hz | |
LCDDR1 = 0b00000010; | |
LCDDR0 = 0b01001000; | |
PORTF = 0b10000000; | |
break; | |
case 36864: | |
OCR1A = 1911; // 523.25Hz | |
break; | |
case 40960: // SEG E | |
OCR1A = 2024; // 493.88Hz | |
LCDDR1 = 0b00000001; | |
LCDDR0 = 0b00100101; | |
PORTF = 0; | |
break; | |
case 45056: | |
OCR1A = 2145; // 466.16Hz | |
break; | |
case 49152: // SEG G | |
OCR1A = 2272; // 440Hz | |
LCDDR2 = 0b01000100; | |
LCDDR1 = 0b01000000; | |
LCDDR0 = 0; | |
PORTF = 0b00000001; | |
break; | |
case 53248: | |
OCR1A = 2407; // 415.3Hz | |
break; | |
case 57344: // SEG B | |
OCR1A = 2551; // 392Hz | |
LCDDR2 = 0b10001000; | |
LCDDR1 = 0b10001000; | |
PORTF = 0b01110010; | |
break; | |
} | |
} | |
clockMode = true; | |
} | |
else | |
{ | |
for (uint16_t z = 0; z < 20000; z++) | |
{ | |
switch(z) | |
{ | |
case 0: | |
OCR1A = 1136; // 880Hz | |
break; | |
case 10000: | |
OCR1A = 568; // 1760Hz | |
break; | |
} | |
} | |
} | |
d2 = -1; | |
position = 4; | |
break; | |
case 2: | |
d2 = d3; | |
d3 = -1; | |
player = d2; | |
position--; | |
break; | |
case 3: | |
d3 = d4; | |
d4 = -1; | |
position--; | |
break; | |
case 4: | |
if (TCNT2 & 1) d4 = 10; | |
else d4 = 11; | |
position--; | |
break; | |
} | |
TCCR1A = 0; // set normal mode (which disconnects the pin) | |
counter = ++obstacles * 10; | |
if (counter > 1450) counter = 1450; | |
} | |
} | |
// DIGIT2 | |
if (d2 == 0 || d2 == 2 || d2 == 3 || d2 == 5 || d2 == 6 || d2 == 7 || d2 == 8 || d2 == 9 || d2 == 10) SEG_2A = 16; | |
else SEG_2A = 0; | |
if (d2 == 0 || d2 == 1 || d2 == 2 || d2 == 3 || d2 == 4 || d2 == 7 || d2 == 8 || d2 == 9 || d2 == 10) SEG_2B = 8; | |
else SEG_2B = 0; | |
if (d2 == 0 || d2 == 1 || d2 == 3 || d2 == 4 || d2 == 5 || d2 == 6 || d2 == 7 || d2 == 8 || d2 == 9 || d2 == 11) SEG_2C = 16; | |
else SEG_2C = 0; | |
if (d2 == 0 || d2 == 2 || d2 == 3 || d2 == 5 || d2 == 6 || d2 == 8 || d2 == 9 || d2 == 11) SEG_2D = 8; | |
else SEG_2D = 0; | |
if (d2 == 0 || d2 == 2 || d2 == 6 || d2 == 8 || d2 == 11) SEG_2E = 4; | |
else SEG_2E = 0; | |
if (d2 == 0 || d2 == 4 || d2 == 5 || d2 == 6 || d2 == 7 || d2 == 8 || d2 == 9 || d2 == 10) SEG_2F = 32; | |
else SEG_2F = 0; | |
if (d2 == 2 || d2 == 3 || d2 == 4 || d2 == 5 || d2 == 6 || d2 == 8 || d2 == 9 || d2 == 10 || d2 == 11) SEG_2G = 64; | |
else SEG_2G = 0; | |
// DIGIT3 | |
if (d3 == 0 || d3 == 2 || d3 == 3 || d3 == 5 || d3 == 10) SEG_3A = 1; | |
else SEG_3A = 0; | |
if (d3 == 0 || d3 == 1 || d3 == 2 || d3 == 3 || d3 == 4 || d3 == 10) SEG_3B = 128; | |
else SEG_3B = 0; | |
if (d3 == 0 || d3 == 1 || d3 == 3 || d3 == 4 || d3 == 5 || d3 == 11) SEG_3C = 128; | |
else SEG_3C = 0; | |
if (d3 == 0 || d3 == 2 || d3 == 3 || d3 == 5 || d3 == 11) SEG_3D = 64; | |
else SEG_3D = 0; | |
if (d3 == 0 || d3 == 2 || d3 == 11) SEG_3E = 32; | |
else SEG_3E = 0; | |
if (d3 == 0 || d3 == 4 || d3 == 5 || d3 == 10) SEG_3F = 2; | |
else SEG_3F = 0; | |
if (d3 == 2 || d3 == 3 || d3 == 4 || d3 == 5 || d3 == 10 || d3 == 11) SEG_3G = 4; | |
else SEG_3G = 0; | |
LCDDR0 = SEG_3C + SEG_3D + SEG_3E + SEG_2C + SEG_2D + SEG_2E + SEG_1C; | |
LCDDR2 = SEG_1B + SEG_2G + SEG_2F + SEG_2A + SEG_2B + SEG_3G + SEG_3F + SEG_3A; | |
// DIGIT4 | |
switch(d4) | |
{ | |
case 0: | |
LCDDR1 = 0b00111111 + SEG_3B; | |
break; | |
case 1: | |
LCDDR1 = 0b00001100 + SEG_3B; | |
break; | |
case 2: | |
LCDDR1 = 0b01011011 + SEG_3B; | |
break; | |
case 3: | |
LCDDR1 = 0b01011110 + SEG_3B; | |
break; | |
case 4: | |
LCDDR1 = 0b01101100 + SEG_3B; | |
break; | |
case 5: | |
LCDDR1 = 0b01110110 + SEG_3B; | |
break; | |
case 6: | |
LCDDR1 = 0b01110111 + SEG_3B; | |
break; | |
case 7: | |
LCDDR1 = 0b00111100 + SEG_3B; | |
break; | |
case 8: | |
LCDDR1 = 0b01111111 + SEG_3B; | |
break; | |
case 9: | |
LCDDR1 = 0b01111110 + SEG_3B; | |
break; | |
case 10: // top obstacle | |
LCDDR1 = 0b01111000; | |
break; | |
case 11: // bottom obstacle | |
LCDDR1 = 0b01000111; | |
break; | |
default: | |
LCDDR1 = 0b00000000 + SEG_3B; | |
} | |
} | |
} | |
// AVR134: Real Time Clock (RTC) Using the Asynchronous Timer | |
static void init(void) | |
{ | |
/* Wait for external clock crystal to stabilize */ | |
for (uint16_t j = 0; j < 0xFFFF; j++) | |
{ | |
/* Do a nop instruction to keep the empty | |
* loop from being optimized away */ | |
asm volatile("nop"); | |
} | |
/* Make sure all TC2 interrupts are disabled */ | |
TIMSK2 = 0; | |
/* Reset timer */ | |
TCNT2 = 0; | |
/* Prescale the timer to be clock source/64 to make */ | |
/* TC2 overflow precisely twice every second. */ | |
TCCR2A = (1 << CS22); | |
/* Wait until TC2 is updated */ | |
while (ASSR & ((1 << TCN2UB) | (1 << OCR2UB) | (1 << TCR2UB))) {} | |
/* Enable Timer/Counter2 Overflow Interrupt */ | |
TIMSK2 = 1; | |
/* Set the Global Interrupt Enable Bit */ | |
sei(); | |
} | |
/* keep track of time */ | |
ISR(TIMER2_OVF_vect) | |
{ | |
if (clockMode) PINF = 0b00000100; // toggle colon | |
if (++t.second == 120) | |
{ | |
t.second = 0; | |
if (++t.minute == 60) | |
{ | |
t.minute = 0; | |
if (++t.hour == 13) | |
{ | |
t.hour = 1; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment