/* * Metronome_v1.c * * Copyright (c) 2012 Chris Monaco * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Created: 6/16/2012 5:29:18 PM * Author: Christopher Monaco */ //Define the clock speed for the interrupt library. //We're using the 1MHz internal oscillator #define F_CPU 1000000UL //Include AVR Libraries #include #include #include //Define values to be used in programming #define MAX_SPEED 250 //Max metronome speed in BPM #define MIN_SPEED 30 //Min metronome speed in BPM #define DISP_CATH_PORT PORTD //The Display cathodes are connected to pins PD0-PD6 #define DISP_AN_PORT PORTC //The Display anodes are connected to pins PC0-PC2 #define DIGIT1 0 //PC0 #define DIGIT2 1 //PC1 #define DIGIT3 2 //PC2 #define OUTPUT_PORT PORTB //The Buzzer is connected to pin PB1/OCA1, LED is connected to PB0 #define INPUT PINB //Buttons are on PB2-PB4 #define LED_PIN 0 //PB0 #define DEBOUNCE_COUNT_MAX 240 //Number used for debouncing the buttons #define TIMESIG_DISPLAY_LENGTH 500 //Number of loops during which to display the current time signature //Enumerated Values for use in programming enum {BUTTON_UP = 2, BUTTON_DOWN, BUTTON_TIMESIG, NUM_BUTTONS} buttons; enum {FOUR_FOUR, TWO_FOUR, TWO_TWO, THREE_EIGHT, FIVE_EIGHT, SIX_EIGHT, NUM_TIMESIG} timeSigs; //Global Variables Declarations //State Variables uint8_t speed = 60; //Metronome speed, initialized to 60 BPM uint8_t current_display = 1; //The current display we are on, used for multiplexing the display uint16_t button_counter[NUM_BUTTONS]; //Array containing the amount of time each button is held down for //Tone Generations Variables uint16_t toneFreq = 2000; //Frequency of the tone sent to the buzzer, in Hz uint8_t buzzPeriod = 40; //The length of the tone, toneFreq * 20ms uint16_t iCounterMax; //Used for sounding the tone, see the timer1 ISR uint16_t iCounter = 0; uint8_t buzzerflag = 0; //Time Signature Count Tone Change Variables uint8_t current_TimeSig = FOUR_FOUR; uint8_t beatCounter = 0; //Counts the number of beats uint8_t divisor = 4; //Determined based on time signature //Function Prototypes void setup(); //Setup I/O and timers void setDigit(int digit, int value); //sets the given value to the display at a given digit void displayTimeSig(); //Displays the current time signature to the display /************************************************************************/ /* Main Entry Point Into Program */ /************************************************************************/ int main() { setup(); //call setup function to get everything ready while(1) { //The main program loop will handle input and debouncing of the buttons /* The for loop here increments a the counter variable corresponding to a specific button every time the main loop is cycled and a button is depressed. This way we can debounce a button by making sure it is reading a single value for a certain amount of time. */ for(int i = 2; i < NUM_BUTTONS; i++) { if((INPUT & (1<= NUM_TIMESIG) { current_TimeSig = FOUR_FOUR; } else { current_TimeSig++; } /* Next, depending on the time signature chosen, update the divisor variable. This will be explained in more detail in the timer1 ISR below. */ switch(current_TimeSig) { case FOUR_FOUR: divisor = 4; break; case TWO_TWO: ; case TWO_FOUR: divisor = 2; break; case THREE_EIGHT: divisor = 3; break; case FIVE_EIGHT: divisor = 5; break; case SIX_EIGHT: divisor = 6; } //Put the current time signature on the display displayTimeSig(); } } return 0; } /************************************************************************/ /* Setup() */ /* Handles all initial set up of registers and timers. */ /************************************************************************/ void setup() { //Handle Pin Direction Settings DDRD = 0xFF; //DISP_CATH_PORT, set all pins as output DDRC = 0x07; //DISP_AN_PORT, set pins PC0, PC1, PC2 as output DDRB = 0x03; //I/O pins on port B, set pins 0 and 1 as output PORTB = 0x1C; //Set internal pull-ups on pins PB2, PB3, PB4 //Initialize iCounterMax for tone generation iCounterMax = toneFreq*((float)60/(float)speed); //Initialize all button counters to 0 for(int i = 0; i < NUM_BUTTONS; i++) { button_counter[i] = 0; } //Set-up Timers and Interrupts //Timer0 for display multiplexing TCCR0A = (1<= 100) { DISP_AN_PORT &= ~((1<= 10) { DISP_AN_PORT &= ~((1<