/* * main.c * * Created: 8/9/2022 1:35:03 AM * Author: Eric Ljungquist * License: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication creativecommons.org/publicdomain/zero/1.0/ */ #define F_CPU 32768 #include #include #include #define START_TONE_TIME 500 //The starting time the tones will play for. #define MIN_TONE_TIME 150 //The fastest time the tones will play for. //Port designations. #define GREEN_BUTTON_PIN 7//PA7 - pin 3 #define RED_BUTTON_PIN 1 //PA1 - pin 4 #define YELLOW_BUTTON_PIN 6 //PA6 - pin 2 #define BLUE_BUTTON_PIN 2 //PA2 - pin 5 #define BUZZER_PIN 3 //PA3 - pin 7 //PA0 - pin 6 is dedicated to programming. //Bit masks for port designations. #define GREEN_BUTTON_MASK (1<> 17; shifter ^= shifter << 5; //Only use the first 2 bits to get a value between 0 and 3. return shifter & 3; } //Light the LED for the given button (mask). void light_button(int8_t button){ //To light a button LED we set the pin to ground. //set direction to output PORTA.DIR = PORTA.DIR | button; //set output to low PORTA.OUT = PORTA.OUT & ~button; } //Turn off the LED for the given button (mask). void unlight_button(int8_t button){ PORTA.DIR = PORTA.DIR & ~button; } //Can't pass values that are not hard coded to _delay_ms. This is a work-around. void delay(uint16_t time){ for(int i = 0; i < time; i++){ _delay_ms(1); } } //Sound the buzzer at the given frequency. void start_buzzer(uint16_t frequency){ //Use the TCA0 Timer frequency generator to output a square wave to the default PWM pin. //reset count to 0 to avoid timer hang ups TCA0.SINGLE.CNT = 0; //Change frequency TCA0.SINGLE.CMP0 = ((F_CPU / 2) / frequency) - 1; //equation assumes no timer prescaler. //set pin as timer output TCA0.SINGLE.CTRLB = 0b00010001; //enable the timer TCA0.SINGLE.CTRLA = 0b00000001; } //Stop making buzzer noise. void stop_buzzer(){ //disable pin as timer output TCA0.SINGLE.CTRLB = 0b00000001; //disable the timer TCA0.SINGLE.CTRLA = 0b00000000; } //Make the given 'button' play a tone at 'frequency' for the given 'time'. void play_button(int8_t button, uint16_t frequency, uint16_t time){ light_button(button); start_buzzer(frequency); delay(time); stop_buzzer(); unlight_button(button); } //Button press detection done via input polling. //Handles button press detection. void poll_buttons(){ //Get the current state of the buttons. btnstate_c = PORTA.IN & ALL_BUTTONS_MASK; //If the current state and last state don't match then something happened with the buttons. if(btnstate_c != btnstate_l){ //Faling edge checks. if(((btnstate_c & GREEN_BUTTON_MASK) == 0) & ((btnstate_l & GREEN_BUTTON_MASK) != 0)){ start_buzzer(tone_list[0]); } if(((btnstate_c & RED_BUTTON_MASK) == 0) & ((btnstate_l & RED_BUTTON_MASK) != 0)){ start_buzzer(tone_list[1]); } if(((btnstate_c & YELLOW_BUTTON_MASK) == 0) & ((btnstate_l & YELLOW_BUTTON_MASK) != 0)){ start_buzzer(tone_list[2]); } if(((btnstate_c & BLUE_BUTTON_MASK) == 0) & ((btnstate_l & BLUE_BUTTON_MASK) != 0)){ start_buzzer(tone_list[3]); } //Rising edge checks if(((btnstate_c & GREEN_BUTTON_MASK) != 0) & ((btnstate_l & GREEN_BUTTON_MASK) == 0)){ button_press = 0; stop_buzzer(); _delay_ms(100); } if(((btnstate_c & RED_BUTTON_MASK) != 0) & ((btnstate_l & RED_BUTTON_MASK) == 0)){ button_press = 1; stop_buzzer(); _delay_ms(100); } if(((btnstate_c & YELLOW_BUTTON_MASK) != 0) & ((btnstate_l & YELLOW_BUTTON_MASK) == 0)){ button_press = 2; stop_buzzer(); _delay_ms(100); } if(((btnstate_c & BLUE_BUTTON_MASK) != 0) & ((btnstate_l & BLUE_BUTTON_MASK) == 0)){ stop_buzzer(); button_press = 3; _delay_ms(100); } } //The current state is now considered the old state. btnstate_l = btnstate_c; } //Game State - Wait for the user to start a new game. void state_wait_for_start(){ //TODO: use a timer and put into sleep //Give the seed a ramdomish number. seed++; poll_buttons(); //Wait for any button press if(button_press != -1){ //make sure the seed isn't 0. The random number generator doesn't work shifting about a bunch of 0's if(seed == 0){ seed = 253; } //Reset game variables. current_level = 0; tone_time = START_TONE_TIME; //Delay a short bit to give the user some time to adjust. _delay_ms(500); button_press = -1; //Change game state. current_state = state_play_sequence; } } //Game State - Play a tone sequence for the user. void state_play_sequence(){ uint8_t current_sequence = 0; uint8_t currentValue = 0; //Reset the random number generator to the seed value. reset_random_value(); //Play all tones up to the current level. for(current_sequence = 0; current_sequence <= current_level; current_sequence++){ currentValue = random_value(); play_button(button_map[currentValue], tone_list[currentValue], tone_time); delay(tone_time / 2); } //Reduce the time the tone is played by 10% from the previous value. tone_time = (tone_time * 9) / 10; if(tone_time < MIN_TONE_TIME){ tone_time = MIN_TONE_TIME; } //Reset the random number generator to prepare for user input. reset_random_value(); current_input_level = 0; //Change game state current_state = state_users_turn; } //Game State - Wait for and process user input. void state_users_turn(){ poll_buttons(); if(button_press != -1){ uint8_t current_level_value = random_value(); //Correct button press in the sequence if(button_press == current_level_value){ //Passed current level if(current_input_level == current_level){ current_level++; _delay_ms(500); current_state = state_play_sequence; } //User entered correct value for level but has not completed the level. //Wait for more user input. current_input_level++; } else { //Wrong input, game over. _delay_ms(500); current_state = state_game_over; } button_press = -1; } } //Game State - Game over. Play some tones to signal to the user that they //made an error. void state_game_over(){ play_button(RED_BUTTON_MASK, 880, 500); _delay_ms(300); play_button(RED_BUTTON_MASK, 783, 400); _delay_ms(300); play_button(RED_BUTTON_MASK, 460, 700); _delay_ms(300); current_state = state_wait_for_start; } //Initialization at start up. void init(){ //Set the main clock frequency to the low power oscillator. Simon is not very CPU intensive. CPU_CCP = 0xD8; //unlock write protection. 0xD8 is the key given in the data sheet. CLKCTRL.MCLKCTRLA = CLKCTRL_CLKSEL_OSCULP32K_gc; //set clock to the ultra low power internal oscillator. CPU_CCP = 0xD8; //unlock write protection. 0xD8 is the key given in the data sheet. CLKCTRL.MCLKCTRLB = 0b00000000; //disable clock prescale. //Start buzzer as an output. PORTA.DIR = PORTA.DIR | BUZZER_MASK; //Make sure button pins start as inputs PORTA.DIR = PORTA.DIR & ~ALL_BUTTONS_MASK; //enable the pull up resistors on inputs now so that we don't trigger an input later when we turn them on. *(&(PORTA.PIN0CTRL) + GREEN_BUTTON_PIN) = PORT_PULLUPEN_bm; *(&(PORTA.PIN0CTRL) + RED_BUTTON_PIN) = PORT_PULLUPEN_bm; *(&(PORTA.PIN0CTRL) + YELLOW_BUTTON_PIN) = PORT_PULLUPEN_bm; *(&(PORTA.PIN0CTRL) + BLUE_BUTTON_PIN) = PORT_PULLUPEN_bm; //Game wont start until the user clicks a button. current_state = state_wait_for_start; } int main(void) { init(); while(1) { current_state(); } }