/** * 6-19-2007 * Copyright 2009, Spark Fun Electronics * Nathan Seidle * nathan at sparkfun.com * * Released under the Creative Commons Attribution Share-Alike 3.0 License * http://creativecommons.org/licenses/by-sa/3.0 * * Simon Game ported for the ATmega168 * * Fixes and cleanup by Joshua Neal * * Generates random sequence, plays music, and displays button lights. * * Simon tones from Wikipedia * - A (red, upper left) - 440Hz - 2.272ms - 1.136ms pulse * - a (green, upper right, an octave higher than A) - 880Hz - 1.136ms, * 0.568ms pulse * - D (blue, lower left, a perfect fourth higher than the upper left) * 587.33Hz - 1.702ms - 0.851ms pulse * G (yellow, lower right, a perfect fourth higher than the lower left) - * 784Hz - 1.276ms - 0.638ms pulse * * The tones are close, but probably off a bit, but they sound all right. * * The old version of SparkFun simon used an ATmega8. An ATmega8 ships * with a default internal 1MHz oscillator. You will need to set the * internal fuses to operate at the correct external 16MHz oscillator. * * Original Fuses: * avrdude -p atmega8 -P lpt1 -c stk200 -U lfuse:w:0xE1:m -U hfuse:w:0xD9:m * * Command to set to fuses to use external 16MHz: * avrdude -p atmega8 -P lpt1 -c stk200 -U lfuse:w:0xEE:m -U hfuse:w:0xC9:m * * The current version of Simon uses the ATmega168. The external osciallator * was removed to reduce component count. This version of simon relies on the * internal default 1MHz osciallator. Do not set the external fuses. */ #include #include /* Uncomment one of the following, corresponding to the board you have. */ //#define BOARD_REV_6_25_08 //#define BOARD_REV_4_9_2009 //#define BOARD_REV_6_3_2009 #define BOARD_REV_PTH #ifdef BOARD_REV_PTH #define CHIP_ATMEGA168 #define LED_RED (1 << 0) #define LED_GREEN (1 << 1) #define LED_BLUE (1 << 2) #define LED_YELLOW (1 << 3) /* LED pin definitions */ #define LED_RED_PIN 2 #define LED_RED_PORT PORTB #define LED_GREEN_PIN 3 #define LED_GREEN_PORT PORTD #define LED_BLUE_PIN 5 #define LED_BLUE_PORT PORTB #define LED_YELLOW_PIN 5 #define LED_YELLOW_PORT PORTD /* Button pin definitions */ #define BUTTON_RED_PIN 1 #define BUTTON_RED_PORT PINB #define BUTTON_GREEN_PIN 2 #define BUTTON_GREEN_PORT PIND #define BUTTON_BLUE_PIN 4 #define BUTTON_BLUE_PORT PINB #define BUTTON_YELLOW_PIN 6 #define BUTTON_YELLOW_PORT PIND /* Buzzer pin definitions */ #define BUZZER1 4 #define BUZZER1_PORT PORTD #define BUZZER2 7 #define BUZZER2_PORT PORTD #endif /* BOARD_REV_PTH */ #ifdef BOARD_REV_6_25_08 #define CHIP_ATMEGA168 #define LED_RED (1 << 0) #define LED_GREEN (1 << 1) #define LED_BLUE (1 << 2) #define LED_YELLOW (1 << 3) /* LED pin definitions */ #define LED_RED_PIN 3 #define LED_RED_PORT PORTC #define LED_GREEN_PIN 2 #define LED_GREEN_PORT PORTD #define LED_BLUE_PIN 0 #define LED_BLUE_PORT PORTC #define LED_YELLOW_PIN 5 #define LED_YELLOW_PORT PORTD /* Button pin definitions */ #define BUTTON_RED_PIN 2 #define BUTTON_RED_PORT PINC #define BUTTON_GREEN_PIN 5 #define BUTTON_GREEN_PORT PINC #define BUTTON_BLUE_PIN 1 #define BUTTON_BLUE_PORT PINC #define BUTTON_YELLOW_PIN 6 #define BUTTON_YELLOW_PORT PIND /* Buzzer pin definitions */ #define BUZZER1 3 #define BUZZER1_PORT PORTD #define BUZZER2 4 #define BUZZER2_PORT PORTD #endif /* BOARD_REV_6_25_08 */ #ifdef BOARD_REV_4_9_2009 #define CHIP_ATMEGA168 /* LED pin definitions */ #define LED_BLUE_PIN 5 #define LED_BLUE_PORT PORTB #define LED_YELLOW_PIN 5 #define LED_YELLOW_PORT PORTD #define LED_RED_PIN 2 #define LED_RED_PORT PORTB #define LED_GREEN_PIN 2 #define LED_GREEN_PORT PORTD /* Button pin definitions */ #define BUTTON_RED_PIN 0 #define BUTTON_RED_PORT PINB #define BUTTON_GREEN_PIN 1 #define BUTTON_GREEN_PORT PINB #define BUTTON_BLUE_PIN 7 #define BUTTON_BLUE_PORT PIND #define BUTTON_YELLOW_PIN 6 #define BUTTON_YELLOW_PORT PIND /* Buzzer pin definitions */ #define BUZZER1 3 #define BUZZER1_PORT PORTD #define BUZZER2 4 #define BUZZER2_PORT PORTD #endif /* BOARD_REV_4_9_2009 */ #ifdef BOARD_REV_6_3_2009 #define CHIP_ATMEGA168 #define LED_RED (1 << 0) #define LED_GREEN (1 << 1) #define LED_BLUE (1 << 2) #define LED_YELLOW (1 << 3) /* LED pin definitions */ #define LED_RED_PIN 2 #define LED_RED_PORT PORTB #define LED_GREEN_PIN 2 #define LED_GREEN_PORT PORTD #define LED_BLUE_PIN 5 #define LED_BLUE_PORT PORTB #define LED_YELLOW_PIN 5 #define LED_YELLOW_PORT PORTD /* Button pin definitions */ #define BUTTON_RED_PIN 0 #define BUTTON_RED_PORT PINB #define BUTTON_GREEN_PIN 1 #define BUTTON_GREEN_PORT PINB #define BUTTON_BLUE_PIN 7 #define BUTTON_BLUE_PORT PIND #define BUTTON_YELLOW_PIN 6 #define BUTTON_YELLOW_PORT PIND /* Buzzer pin definitions */ #define BUZZER1 3 #define BUZZER1_PORT PORTD #define BUZZER2 4 #define BUZZER2_PORT PORTD #endif /* BOARD_REV_6_3_2009 */ /* Define game parameters */ #define MOVES_TO_WIN 14 #define TIME_LIMIT 3000 /* 3000ms = 3 sec */ #define sbi(port_name, pin_number) (port_name |= 1< 256) { TIFR0 = (1< 0) { delay_us(1000); } } /* Light the given set of LEDs */ static void set_leds(uint8_t leds) { if ((leds & LED_RED) != 0) { sbi(LED_RED_PORT, LED_RED_PIN); } else { cbi(LED_RED_PORT, LED_RED_PIN); } if ((leds & LED_GREEN) != 0) { sbi(LED_GREEN_PORT, LED_GREEN_PIN); } else { cbi(LED_GREEN_PORT, LED_GREEN_PIN); } if ((leds & LED_BLUE) != 0) { sbi(LED_BLUE_PORT, LED_BLUE_PIN); } else { cbi(LED_BLUE_PORT, LED_BLUE_PIN); } if ((leds & LED_YELLOW) != 0) { sbi(LED_YELLOW_PORT, LED_YELLOW_PIN); } else { cbi(LED_YELLOW_PORT, LED_YELLOW_PIN); } } #ifdef BOARD_REV_6_25_08 static void init_gpio(void) { /* 1 = output, 0 = input */ DDRB = 0b11111111; DDRC = 0b00001001; /* LEDs and Buttons */ DDRD = 0b00111110; /* LEDs, buttons, buzzer, TX/RX */ PORTC = 0b00100110; /* Enable pull-ups on buttons 0,2,3 */ PORTD = 0b01000000; /* Enable pull-up on button 1 */ } #endif /* BOARD_REV_6_25_08 */ #ifdef BOARD_REV_4_9_2009 static void init_gpio(void) { /* 1 = output, 0 = input */ DDRB = 0b11111100; /* button 2,3 on PB0,1 */ DDRD = 0b00111110; /* LEDs, buttons, buzzer, TX/RX */ PORTB = 0b00000011; /* Enable pull-ups on buttons 2,3 */ PORTD = 0b11000000; /* Enable pull-up on button 0,1 */ } #endif /* BOARD_REV_4_9_2009 */ #ifdef BOARD_REV_PTH static void init_gpio(void) { /* 1 = output, 0 = input */ DDRB = 0xFF & ~(1< 70; x--) { for (y = 0; y < 3; y++) { sbi(BUZZER2_PORT, BUZZER2); cbi(BUZZER1_PORT, BUZZER1); delay_us(x); cbi(BUZZER2_PORT, BUZZER2); sbi(BUZZER1_PORT, BUZZER1); delay_us(x); } } } /* Play the winner sound and lights */ void play_winner(void) { set_leds(LED_GREEN|LED_BLUE); winner_sound(); set_leds(LED_RED|LED_YELLOW); winner_sound(); set_leds(LED_GREEN|LED_BLUE); winner_sound(); set_leds(LED_RED|LED_YELLOW); winner_sound(); } /* Plays the current contents of the game moves */ static void play_moves(void) { uint8_t move; for (move = 0; move < nmoves; move++) { toner(moves[move], 150); delay_ms(150); } } /* Adds a new random button to the game sequence, by sampling the timer */ static void add_to_moves(void) { uint8_t new_button; /* Use the lower 2 bits of the timer for the random value */ new_button = 1 << (TCNT2 & 0x3); moves[nmoves++] = new_button; } /* Toggle buzzer every buzz_delay_us, for a duration of buzz_length_ms. */ static void buzz_sound(uint16_t buzz_length_ms, uint16_t buzz_delay_us) { uint32_t buzz_length_us; buzz_length_us = buzz_length_ms * (uint32_t)1000; while (buzz_length_us > buzz_delay_us*2) { buzz_length_us -= buzz_delay_us*2; /* toggle the buzzer at various speeds */ cbi(BUZZER1_PORT, BUZZER1); sbi(BUZZER2_PORT, BUZZER2); delay_us(buzz_delay_us); sbi(BUZZER1_PORT, BUZZER1); cbi(BUZZER2_PORT, BUZZER2); delay_us(buzz_delay_us); } } /* * Light an LED and play tone * * red, upper left: 440Hz - 2.272ms - 1.136ms pulse * green, upper right: 880Hz - 1.136ms - 0.568ms pulse * blue, lower left: 587.33Hz - 1.702ms - 0.851ms pulse * yellow, lower right: 784Hz - 1.276ms - 0.638ms pulse */ static void toner(uint8_t which, uint16_t buzz_length_ms) { set_leds(which); switch (which) { case LED_RED: buzz_sound(buzz_length_ms, 1136); break; case LED_GREEN: buzz_sound(buzz_length_ms, 568); break; case LED_BLUE: buzz_sound(buzz_length_ms, 851); break; case LED_YELLOW: buzz_sound(buzz_length_ms, 638); break; } /* Turn off all LEDs */ set_leds(0); } /* Show an "attract mode" display while waiting for user to press button. */ static void attract_mode(void) { while (1) { set_leds(LED_RED); delay_ms(100); if (check_button() != 0x00) return; set_leds(LED_BLUE); delay_ms(100); if (check_button() != 0x00) return; set_leds(LED_GREEN); delay_ms(100); if (check_button() != 0x00) return; set_leds(LED_YELLOW); delay_ms(100); if (check_button() != 0x00) return; } } /* Wait for a button to be pressed. Returns one of led colors (LED_RED, etc.) * if successful, 0 if timed out */ static uint8_t wait_for_button(void) { uint16_t time_limit = TIME_LIMIT; uint8_t released = 0; uint8_t old_button; while (time_limit > 0) { uint8_t button; /* Implement a small bit of debouncing */ old_button = button; button = check_button(); /* * Make sure we've seen the previous button * released before accepting new buttons */ if (button == 0) released = 1; if (button == old_button && released == 1) { /* Make sure just one button is pressed */ if (button == LED_RED || button == LED_BLUE || button == LED_GREEN || button == LED_YELLOW) { return button; } } delay_ms(1); time_limit--; } return 0; /* Timed out */ } /* Play the game. Returns 0 if player loses, or 1 if player wins. */ static int game_mode(void) { nmoves = 0; while (nmoves < MOVES_TO_WIN) { uint8_t move; /* Add a button to the current moves, then play them back */ add_to_moves(); play_moves(); /* Then require the player to repeat the sequence. */ for (move = 0; move < nmoves; move++) { uint8_t choice = wait_for_button(); /* If wait timed out, player loses. */ if (choice == 0) return 0; toner(choice, 150); /* If the choice is incorect, player loses. */ if (choice != moves[move]) { return 0; } } /* Player was correct, delay before playing moves */ delay_ms(1000); } /* player wins */ return 1; } int main(void) { /* Setup IO pins and defaults */ ioinit(); /* Main loop */ while (1) { /* Wait for user to start game */ attract_mode(); /* Indicate the start of game play */ set_leds(LED_RED|LED_GREEN|LED_BLUE|LED_YELLOW); delay_ms(1000); set_leds(0); delay_ms(250); if (game_mode() != 0) { /* Player won, play winner tones */ play_winner(); } else { /* Player lost, play loser tones */ play_loser(); } } return(0); }