Created
January 18, 2020 23:47
-
-
Save carlosdelfino/504a05c953b917b3d29aa88fd85e9cee to your computer and use it in GitHub Desktop.
Japanese Taiko Drum Sensei
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
/* | |
File: main.c | |
Date: November 28th, 2012 | |
Description: This file implements the Japanese Taiko Drum Sensei. | |
----------- | |
See Bruce Land's "CORNELL ECE 4760" Website for description of project: | |
< http://people.ece.cornell.edu/land/courses/ece4760/FinalProjects/ > | |
Authors: | |
-------- | |
Adam Harris <awh49> | |
Adam Jelfo <asj42> | |
Lucas Nissenbaum <ln226> | |
Gabriel Soares <gs368> | |
*/ | |
#include <avr/io.h> | |
#include <avr/pgmspace.h> | |
#include <avr/interrupt.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <math.h> | |
#define F_CPU 16000000UL | |
#include <util/delay.h> | |
#include <avr/sleep.h> | |
//Defines for scopes | |
#define begin { | |
#define end } | |
//Boolean variable | |
typedef unsigned char bool; | |
#define TRUE 1 | |
#define FALSE 0 | |
#define SAMPLE_TIME 4000 // @16MHz, 4000 cycles -> 4Khz (1 sample per 250us) | |
#define ADC_BUFFER_SIZE 5000 // 4000 samples at 4KHz = 1sec | |
#define BEAT_TOTAL 1000 // Maximum number of beats that are saved | |
// Analog-to-Digital converter thresholds | |
// To prevent noise, BEAT_UP_THRESH > BEAT_DOWN_THRESH | |
#define BEAT_UP_THRESH 20 // 51 == 0.6V on Oscill | |
#define BEAT_DOWN_THRESH 25 //parameter used to set fraction of peak height | |
#define N_DOWN_SAMPLES 50 // parameter used to prevent double triggers -- use 50 for most drums | |
// Beat classification parameters | |
#define BEAT_LOW_THRESH 110 | |
#define N_RIM_THRESH 100 // 140 | |
// Define threshold for each time comparison in 0.25 ms units | |
#define TORTOISE_THRESH 800 | |
#define SLOTH_THRESH 80 | |
#define HORSE_THRESH -80 | |
#define CHEETAH_THRESH -800 | |
// Define threshold for last beat in repeat-after-me | |
#define LAST_BEAT_THRESH_REPEAT_AFTER_ME (3*TORTOISE_THRESH) | |
// Define LED port | |
#define LED_PORT PORTC | |
// Define pin relative to each beat type | |
#define LED_SOFTSHOT 0x01 | |
#define LED_HARDSHOT 0x02 | |
#define LED_RIMSHOT 0x04 | |
// Define pins for time comparison | |
#define LED_TORTOISE 0x08 | |
#define LED_SLOTH 0x10 | |
#define LED_BEAR 0x20 | |
#define LED_HORSE 0x40 | |
#define LED_CHEETAH 0x80 | |
// Define pin relative to miss beat | |
#define LED_MISS_BEAT 0xD8 | |
// Define pin relative to start metronome mode | |
#define LED_METRONOME 0x20 | |
// Define pin relative to wrong beat type of repeat-after-me | |
#define LED_WRONG_TYPE_REPEAT_AFTER_ME 0x02 | |
#define LED_RIGHT_TYPE_REPEAT_AFTER_ME 0x01 | |
// Define pin relative to cycle of repeat-after-me | |
#define LED_CYCLE_REPEAT_AFTER_ME 0x04 | |
// Define pin relative to reference of repeat-after-me | |
#define LED_REFERENCE_REPEAT_AFTER_ME 0x20 | |
// Define pin relative to initialization of repeat-after-me | |
#define LED_INITIALIZATION_REPEAT_AFTER_ME 0x20 | |
// Define pin relative to start calibration | |
#define LED_INITIALIZATION_CALIBRATE 0x08 | |
// Define pin relative to first beat OK | |
#define LED_FIRST_CALIBRATE 0x10 | |
// Define pin relative to second beat OK | |
#define LED_SECOND_CALIBRATE 0x80 | |
// Define pin relative to third beat OK | |
#define LED_THIRD_CALIBRATE 0x40 | |
// Define pin relative to fourth beat | |
#define LED_FOURTH_CALIBRATE 0x20 | |
// Define pin relative to LED leader follow-the-leader acknowledgement | |
#define LED_LEADER_FOLLOW_THE_LEADER 0x18 | |
// Define pin relative to LED player follow-the-leader acknowledgement | |
#define LED_PLAYER_FOLLOW_THE_LEADER 0x20 | |
// Define button port | |
#define BUTTON_PORT PINB | |
// Define bit relative to each button | |
#define BUTTON_ACTIVE_MODE 0x20 | |
#define BUTTON_START_STOP 0x40 | |
#define BUTTON_RESET 0x80 | |
// Define button check flag | |
#define BUTTON_CHECK (BUTTON_ACTIVE_MODE|BUTTON_START_STOP|BUTTON_RESET) | |
// Define dip-switch port | |
#define DIP_PORT PINA | |
// Define bit relative to dip switches | |
#define BUTTON_DIP_SWITCHES 0xF0 | |
// Metronome light LED length | |
#define METRONOME_LED_DURATION 200 // Turn on LED for 50 ms | |
// Define port for 7 segment display | |
#define SEGMENT_PORT PORTD | |
// Metronome default period | |
#define METRONOME_DEFAULT_PERIOD 20000UL | |
// Calibrate peak factor macro | |
#define CALIBRATE_PEAK_FACTOR(value) (value*4)/3 | |
// Calibrate rim threshold macro | |
#define CALIBRATE_N_RIM_THRESH(value) (value*5)/4 | |
// Define IR pin | |
#define IRPIN 0x02 | |
// Follow the leader duration | |
#define FOLLOW_THE_LEADER_WAIT_DURATION 2000 | |
// Follow the leader infrared debounce | |
#define FOLLOW_THE_LEADER_IR_DEBOUNCE 800 // 200 ms | |
// Beat type enumeration | |
enum _beat_type_ | |
{ | |
inactive, | |
rimshot, // represented by yellow LED, port B.0 | |
softbeat, // represented by green LED, port B.2 | |
hardbeat, // represented by red LED, port B.1 | |
received // represented by reception of a signal | |
}; | |
// Beat structure | |
typedef struct _beat_ | |
{ | |
// Time of the beat | |
long time; | |
// Time of other beat | |
long compare_time; | |
// Type of the beat | |
enum _beat_type_ type; | |
// Type of other beat | |
enum _beat_type_ compare_type; | |
} beat; | |
//Analog-to-Digital Converter Buffer | |
// - weak filter used to measure length of beat | |
// - strong filter used to detect beat | |
volatile unsigned char weak_filter_ADC_samples[ADC_BUFFER_SIZE]; | |
volatile unsigned int index_ADC_samples = 0; | |
volatile unsigned char strong_filter_ADC_sample; | |
// Used to detect duration of samples | |
volatile unsigned char peak_signal = 0; | |
volatile unsigned int max_low_samples = N_DOWN_SAMPLES; | |
volatile unsigned int current_low_samples = 0; | |
volatile unsigned char low_thresh = BEAT_DOWN_THRESH; | |
// Global time | |
volatile unsigned long global_time = 0; | |
// Beat parameter | |
volatile beat beat_vector[BEAT_TOTAL]; | |
volatile unsigned int beat_index = 0; | |
// Used to signal when we are within a beat | |
volatile char sample_flag = 0; | |
volatile enum _sample_sm_ | |
{ | |
off_beat, | |
on_beat | |
} sample_state; // indicates state of sampling state machine | |
// Mode variable | |
volatile enum _active_mode_ | |
{ | |
calibrate_mode, | |
follow_the_leader_mode, | |
metronome_mode, | |
repeat_after_me_mode, | |
free_beat_mode | |
} active_mode; | |
// Debounce state machine | |
volatile enum _debounce_sm_ | |
{ | |
not_pressed_button, | |
pressed_debounce_button, | |
pressed_button, | |
not_pressed_debounce_button | |
} button_state; | |
// Last pressed button | |
volatile unsigned char last_pressed_button; | |
// Change mode variable | |
volatile bool changed_mode = FALSE; | |
// Button activity flag | |
volatile bool button_activity_flag = FALSE; | |
// Jump beat flag | |
volatile bool jump_beat_flag = FALSE; | |
// External interrupt flag | |
volatile bool external_interrupt_flag = FALSE; | |
// Reset flag | |
volatile bool reset_flag = FALSE; | |
// Metronome state machine | |
enum _metronome_sm_ | |
{ | |
wait_metronome, | |
average_metronome, | |
active_metronome | |
} metronome_state; | |
// Metronome variables | |
volatile unsigned long metronome_period = METRONOME_DEFAULT_PERIOD; // Period of the metronome | |
bool metronome_start_flag; // Metronome start/stop flag variable | |
volatile unsigned long metronome_len = 0; // Measures number of "already active" samples | |
// Repeat-after-me state machine | |
enum _repeat_after_me_sm_ | |
{ | |
set_repeat_after_me, | |
wait_repeat_after_me, | |
repeat_repeat_after_me | |
} repeat_after_me_state; | |
// Repeat-after-me variables | |
bool beat_type_flag; | |
unsigned int repeat_after_me_len; | |
// Calibrate variables | |
unsigned char calibrate_peak; | |
// Calibration threshold variables | |
unsigned char beat_low_thresh = BEAT_LOW_THRESH; | |
unsigned char n_rim_thresh = N_RIM_THRESH; | |
// Board ID | |
unsigned char board_id; | |
// Follow-the-leader state machine | |
enum _follow_the_leader_sm_ | |
{ | |
initial_follow_the_leader, | |
wait_leader_follow_the_leader, | |
wait_player_follow_the_leader | |
} follow_the_leader_state; | |
// Follow-the-leader event flags | |
volatile bool follow_the_leader_receiver_beat; | |
// Follow-the-leader leader beat index | |
unsigned int follow_the_leader_leader_beat_index; | |
// Receiver sample state | |
volatile unsigned char previous_receiver_sample; | |
/******************************************************* | |
FUNCTION DECLARATIONS -- All calls for functions below | |
*******************************************************/ | |
void display_segment_id (void); | |
void display_segment_mode (void); | |
void press_button (unsigned char button_input); | |
void display_delay (long time_difference); | |
void display_beat (int to_index); | |
void calibrate (void); | |
bool get_beat (bool ir_flag); | |
unsigned long calculate_average_time (void); | |
void metronome_beat(); | |
void compare_and_display(); | |
void repeat_after_me_beat (); | |
void free_beat(); | |
void read_dip_switches(); | |
void follow_the_leader_leader_beat(); | |
void follow_the_leader_player_beat(); | |
void initialize (void); | |
/******************************************************** | |
Function to display board id in 7 segment display | |
********************************************************/ | |
void display_segment_id () | |
begin | |
// Clear display | |
SEGMENT_PORT &= 0x87; | |
// Set up appropriate pins | |
SEGMENT_PORT |= (board_id<<3); | |
end | |
/******************************************************** | |
Function to display current mode in 7 segment display | |
********************************************************/ | |
void display_segment_mode () | |
begin | |
// Clear display | |
SEGMENT_PORT &= 0x87; | |
// Set up appropriate pins | |
SEGMENT_PORT |= (active_mode)<<3; | |
end | |
/******************************************************** | |
Function to detect pressed button and act upon it | |
*********************************************************/ | |
void press_button (unsigned char button_input) | |
begin | |
last_pressed_button = button_input; | |
button_activity_flag = TRUE; | |
// Check mode input | |
if((button_input&BUTTON_ACTIVE_MODE) != 0) | |
begin | |
changed_mode = TRUE; | |
if(active_mode != free_beat_mode) | |
active_mode++; | |
else | |
active_mode = calibrate_mode; | |
display_segment_mode(); | |
end | |
// Check start input | |
if((button_input&BUTTON_START_STOP) != 0) | |
begin | |
// Each mode should deal with start stop as it wishes to | |
end | |
if((button_input&BUTTON_RESET) != 0) | |
reset_flag = TRUE; | |
end | |
/******************************************************** | |
Function to display time difference | |
********************************************************/ | |
void display_delay (long time_difference) | |
begin | |
// Check for time difference | |
if(time_difference > TORTOISE_THRESH) | |
LED_PORT |= LED_TORTOISE; | |
else if(time_difference > SLOTH_THRESH) | |
LED_PORT |= LED_SLOTH; | |
else if(time_difference < CHEETAH_THRESH) | |
LED_PORT |= LED_CHEETAH; | |
else if(time_difference < HORSE_THRESH) | |
LED_PORT |= LED_HORSE; | |
else if(active_mode == follow_the_leader_mode) | |
LED_PORT = LED_BEAR; | |
end | |
/********************************************************* | |
ISR samples the ADC at a constant rate set by SAMPLE_TIME | |
**********************************************************/ | |
ISR (TIMER1_COMPA_vect) | |
begin | |
// Update timestamp | |
global_time++; | |
// Check if time is multiple of 8ms | |
if((global_time&(0x0000001F)) == 0) | |
begin | |
// If yes, then check button and run button state machine | |
// Get pin B.0 | |
unsigned char button_input = (BUTTON_PORT)&BUTTON_CHECK; | |
// Run button debounce state machine | |
switch(button_state) | |
begin | |
case not_pressed_button: | |
if(button_input != 0) | |
button_state = pressed_debounce_button; | |
break; | |
case pressed_debounce_button: | |
if(button_input != 0) | |
begin | |
button_state = pressed_button; | |
press_button(button_input); | |
end | |
else | |
button_state = not_pressed_button; | |
break; | |
case pressed_button: | |
if(button_input == 0) | |
button_state = not_pressed_debounce_button; | |
break; | |
case not_pressed_debounce_button: | |
if(button_input == 0) | |
button_state = not_pressed_button; | |
else | |
button_state = pressed_button; | |
break; | |
default: | |
button_state = not_pressed_button; | |
break; | |
end | |
end | |
// Check if it is in active metronome mode | |
if((active_mode == metronome_mode)&&(metronome_state == active_metronome)) | |
begin | |
// If it is, blink every metronome_period seconds | |
if(metronome_len == 0) | |
LED_PORT |= LED_METRONOME; | |
if(metronome_len == METRONOME_LED_DURATION) | |
LED_PORT &= ~(LED_METRONOME); | |
metronome_len++; | |
if(metronome_len == metronome_period) | |
metronome_len = 0; | |
end | |
// Check if time for beat was reached for repeat-after-me | |
if((active_mode == repeat_after_me_mode)&&(repeat_after_me_state == repeat_repeat_after_me)&&(sample_state == off_beat)) | |
begin | |
signed long current_reference_time = global_time - beat_vector[0].time; | |
// Check if it is time to turn on reference LED | |
if(current_reference_time == beat_vector[beat_index].compare_time + HORSE_THRESH) | |
LED_PORT |= LED_REFERENCE_REPEAT_AFTER_ME; | |
else if(current_reference_time == beat_vector[beat_index].compare_time) | |
LED_PORT &= ~LED_REFERENCE_REPEAT_AFTER_ME; | |
if(beat_index < repeat_after_me_len - 1) | |
begin | |
// Check if user has "missed" the beat | |
if(current_reference_time == (beat_vector[beat_index].compare_time + beat_vector[beat_index+1].compare_time)/2) | |
begin | |
jump_beat_flag = TRUE; | |
beat_vector[beat_index].type = inactive; | |
beat_vector[beat_index].time = 0; | |
beat_index++; | |
end | |
end | |
else | |
begin | |
// Check if user "missed" last beat | |
if(current_reference_time == (beat_vector[beat_index].compare_time + LAST_BEAT_THRESH_REPEAT_AFTER_ME)) | |
begin | |
jump_beat_flag = TRUE; | |
beat_vector[beat_index].type = inactive; | |
beat_vector[beat_index].time = 0; | |
beat_index++; | |
end | |
end | |
end | |
if((active_mode == follow_the_leader_mode) && (board_id != 0)) | |
begin | |
// Sample pin | |
unsigned char current_receiver_sample = (PIND&0x01); | |
// Check if current sample is transition | |
if((current_receiver_sample == 0) && (previous_receiver_sample == 1)) | |
begin | |
// Check if previous transition was less than 100 ms before to debounce IR | |
if((follow_the_leader_leader_beat_index != 0)&&(global_time - beat_vector[follow_the_leader_leader_beat_index - 1].compare_time <= FOLLOW_THE_LEADER_IR_DEBOUNCE)) | |
; | |
else | |
begin | |
// There was IR reception, set new beat's time | |
beat_vector[follow_the_leader_leader_beat_index].compare_time = global_time; | |
// Set type to active | |
beat_vector[follow_the_leader_leader_beat_index].compare_type = received; | |
// Increment index | |
follow_the_leader_leader_beat_index++; | |
// Activate external interrupt flag | |
external_interrupt_flag = TRUE; | |
end | |
end | |
previous_receiver_sample = current_receiver_sample; | |
end | |
// Check if time for beat was reached on leader for follow-the-leader | |
if((active_mode == follow_the_leader_mode) && (board_id != 0) && (follow_the_leader_state == wait_leader_follow_the_leader) && (sample_state == off_beat)) | |
begin | |
// Check if time is bigger than strict timeout and user did an extra beat | |
signed long current_reference_time = global_time - beat_vector[follow_the_leader_leader_beat_index].time; | |
// If reference time is too long, jump sample | |
if(current_reference_time == FOLLOW_THE_LEADER_WAIT_DURATION) | |
begin | |
jump_beat_flag = TRUE; | |
beat_vector[follow_the_leader_leader_beat_index].compare_type = inactive; | |
beat_vector[follow_the_leader_leader_beat_index].compare_time = 0; | |
follow_the_leader_leader_beat_index++; | |
end | |
end | |
// Check if time for beat was reached on player for follow-the-leader | |
if((active_mode == follow_the_leader_mode) && (board_id != 0) && (follow_the_leader_state == wait_player_follow_the_leader) && (sample_state == off_beat)) | |
begin | |
// Check if time is bigger than strict timeout and user did an extra beat | |
signed long current_reference_time = global_time - beat_vector[beat_index].compare_time; | |
if(current_reference_time == FOLLOW_THE_LEADER_WAIT_DURATION) | |
begin | |
jump_beat_flag = TRUE; | |
beat_vector[beat_index].type = inactive; | |
beat_vector[beat_index].time = 0; | |
beat_index++; | |
end | |
end | |
// Check type of sample | |
switch(sample_state) | |
begin | |
case on_beat: | |
// In case of a weak filter sample | |
// Sample the ADC from weak filter and store to vector | |
weak_filter_ADC_samples[index_ADC_samples] = ADCH; | |
ADCSRA |= (1<<ADSC); | |
// Check if peak was found | |
if(weak_filter_ADC_samples[index_ADC_samples] > peak_signal) | |
begin | |
// If it is peak, reset peak value | |
peak_signal = weak_filter_ADC_samples[index_ADC_samples]; | |
current_low_samples = 0; | |
end | |
else | |
begin | |
// If not peak, check if below threshold | |
if(weak_filter_ADC_samples[index_ADC_samples] < low_thresh) | |
begin | |
current_low_samples++; | |
if(current_low_samples > max_low_samples) | |
begin | |
// Beat is over. | |
// First, classify beat | |
if(peak_signal < beat_low_thresh) | |
beat_vector[beat_index++].type = softbeat; | |
else if(index_ADC_samples < n_rim_thresh) | |
beat_vector[beat_index++].type = rimshot; | |
else | |
beat_vector[beat_index++].type = hardbeat; | |
// Second, set mode back to off | |
sample_state = off_beat; | |
// Third, change ADC sampling channel | |
ADMUX ^= 0x01; | |
// Reset variables | |
peak_signal = 0; | |
current_low_samples = 0; | |
index_ADC_samples = 0; | |
end | |
end | |
else | |
// If not below threshold, reset low samples | |
current_low_samples = 0; | |
end | |
index_ADC_samples++; | |
break; | |
case off_beat: | |
//Sample the ADC and store to vector | |
strong_filter_ADC_sample = ADCH; | |
ADCSRA |= (1<<ADSC); | |
// Check if signal is on | |
if(strong_filter_ADC_sample > BEAT_UP_THRESH) | |
begin | |
// If yes, save timestamp | |
beat_vector[beat_index].time = global_time; | |
// Change to weak filter mode | |
sample_state = on_beat; | |
// Change ADC sampling channel | |
ADMUX ^= 0x01; | |
end | |
end | |
end | |
/****************************************************** | |
Calibration function | |
********************************************************/ | |
void calibrate () | |
begin | |
// Reset beat index | |
beat_index = 0; | |
// Signal initialization of calibrate and soft beat | |
LED_PORT = LED_INITIALIZATION_CALIBRATE|LED_SOFTSHOT; | |
// First, two soft beats and save their peaks | |
calibrate_peak = 0; | |
if(get_beat(FALSE) == FALSE) | |
return; | |
unsigned char first_peak_signal = calibrate_peak; | |
// Signal first beat as ok | |
LED_PORT = LED_FIRST_CALIBRATE|LED_SOFTSHOT; | |
calibrate_peak = 0; | |
if(get_beat(FALSE) == FALSE) | |
return; | |
unsigned char second_peak_signal = calibrate_peak; | |
// Given two samples, treat peak as calibration | |
beat_low_thresh = CALIBRATE_PEAK_FACTOR((((unsigned int)first_peak_signal + (unsigned int)second_peak_signal)/2)); | |
// Signal second beat is OK | |
LED_PORT = LED_SECOND_CALIBRATE|LED_RIMSHOT; | |
// Sample twice for rimshots | |
if(get_beat(FALSE) == FALSE) | |
return; | |
unsigned long first_rim_thresh = global_time - beat_vector[beat_index-1].time; | |
// Signal third beat is OK | |
LED_PORT = LED_THIRD_CALIBRATE|LED_RIMSHOT; | |
if(get_beat(FALSE) == FALSE) | |
return; | |
unsigned long second_rim_thresh = global_time - beat_vector[beat_index-1].time; | |
// Given two samples, treat peak as calibration | |
n_rim_thresh = CALIBRATE_N_RIM_THRESH((((unsigned int)first_rim_thresh + (unsigned int)second_rim_thresh)/2)); | |
LED_PORT = LED_FOURTH_CALIBRATE; | |
// Set mode as free beat mode | |
while(get_beat(FALSE) == TRUE); | |
end | |
/******************************************************* | |
Get beat function | |
*******************************************************/ | |
bool get_beat(bool ir_flag) | |
begin | |
button_activity_flag = FALSE; | |
jump_beat_flag = FALSE; | |
external_interrupt_flag = FALSE; | |
// Wait for beat | |
while(sample_state == off_beat) | |
begin | |
if(button_activity_flag == TRUE) | |
return FALSE; | |
if(jump_beat_flag == TRUE) | |
return FALSE; | |
if(external_interrupt_flag == TRUE) | |
return FALSE; | |
end | |
// Clear LED's at beat detection | |
LED_PORT &= ~(LED_RIMSHOT + LED_HARDSHOT + LED_SOFTSHOT); | |
// Turn IR on to send beat information | |
if(ir_flag == TRUE) | |
PORTD |= IRPIN; | |
// Wait until beat capture is done | |
while(sample_state == on_beat) | |
begin | |
// Get calibrate peak | |
if(peak_signal > calibrate_peak) | |
calibrate_peak = peak_signal; | |
end | |
// Turn off IR as soon as beat is over | |
if(ir_flag == TRUE) | |
PORTD &= ~IRPIN; | |
return TRUE; | |
end | |
/******************************************************* | |
Display beat type | |
*******************************************************/ | |
void display_beat (int to_index) | |
begin | |
// Show classified beat | |
switch(beat_vector[(to_index)].type) | |
begin | |
case rimshot: | |
LED_PORT |= LED_RIMSHOT; | |
break; | |
case hardbeat: | |
LED_PORT |= LED_HARDSHOT; | |
break; | |
case softbeat: | |
LED_PORT |= LED_SOFTSHOT; | |
break; | |
default: | |
LED_PORT |= LED_RIMSHOT + LED_HARDSHOT + LED_SOFTSHOT; // ERROR case | |
break; | |
end | |
end | |
/************************************************* | |
Average between samples if possible | |
***********************************************/ | |
unsigned long calculate_average_time () | |
begin | |
// Check if any sample exists | |
// If yes, set as average | |
// Otherwise, set as 12 bpm or 0.2 bps | |
if(beat_index > 1) | |
return ((beat_vector[beat_index - 1].time - beat_vector[0].time)/(beat_index-1)); | |
else | |
return METRONOME_DEFAULT_PERIOD; | |
end | |
/**************************************************** | |
Function to play metronome mode | |
***************************************************/ | |
void metronome_beat() | |
begin | |
// Check current state | |
switch(metronome_state) | |
begin | |
case wait_metronome: | |
// For testing purposes, turn on LED indicating waiting mode | |
LED_PORT = 0x08; | |
// Set to get first beat | |
beat_index = 0; | |
// Waits until first beat | |
if(get_beat(FALSE) == FALSE) | |
begin | |
if(last_pressed_button != BUTTON_START_STOP) | |
begin | |
LED_PORT = 0x00; | |
return; | |
end | |
else | |
begin | |
// If it is a start stop button, check new state | |
metronome_period = calculate_average_time(); | |
metronome_state = active_metronome; | |
metronome_len = 0; | |
end | |
end | |
else | |
metronome_state = average_metronome; | |
break; | |
case average_metronome: | |
LED_PORT = 0x10; | |
if(get_beat(FALSE) == FALSE) | |
begin | |
//If there is a button press, check the kind of press | |
if(last_pressed_button != BUTTON_START_STOP) | |
begin | |
metronome_state = wait_metronome; | |
beat_index = 0; | |
LED_PORT = 0x00; | |
return; | |
end | |
else | |
begin | |
// If it is a start stop button, check new state | |
metronome_period = calculate_average_time(); | |
metronome_state = active_metronome; | |
metronome_len = 0; | |
end | |
end | |
break; | |
case active_metronome: | |
LED_PORT = 0x40; | |
// Get beats until button press | |
while(get_beat(FALSE) == TRUE) | |
display_beat(beat_index - 1); | |
metronome_state = wait_metronome; | |
metronome_period = 0; | |
beat_index = 0; | |
LED_PORT = 0x00; | |
break; | |
end | |
end | |
/******************************************************** | |
Compare and display | |
********************************************************/ | |
void compare_and_display() | |
begin | |
// Check if beat is available | |
if((beat_vector[beat_index - 1].type == inactive)||(beat_vector[beat_index - 1].compare_type == inactive)) | |
begin | |
LED_PORT = LED_MISS_BEAT; | |
return; | |
end | |
// Check beat type | |
if((beat_vector[beat_index - 1].type == beat_vector[beat_index - 1].compare_type)||(beat_vector[beat_index - 1].compare_type == received)) | |
LED_PORT = LED_RIGHT_TYPE_REPEAT_AFTER_ME; | |
else | |
LED_PORT = LED_WRONG_TYPE_REPEAT_AFTER_ME; | |
if((active_mode == repeat_after_me_mode)&&(beat_index != 1)) | |
begin | |
signed long time_difference = (signed long)beat_vector[beat_index - 1].time - (signed long)beat_vector[0].time; | |
display_delay(-(time_difference - (signed long)beat_vector[beat_index - 1].compare_time)); | |
end | |
if(active_mode == follow_the_leader_mode) | |
begin | |
display_delay((signed long)beat_vector[beat_index - 1].compare_time -(signed long)beat_vector[beat_index - 1].time); | |
end | |
end | |
/******************************************************** | |
Repeat after me mode | |
********************************************************/ | |
void repeat_after_me_beat () | |
begin | |
// Check current repeat_after_me_state | |
switch(repeat_after_me_state) | |
begin | |
case set_repeat_after_me: | |
// Reset beat index and settle mode | |
beat_index = 0; | |
LED_PORT = LED_INITIALIZATION_REPEAT_AFTER_ME; | |
// Wait for button | |
while(get_beat(FALSE) == TRUE) | |
display_beat(beat_index - 1); | |
LED_PORT = 0; | |
if(last_pressed_button != BUTTON_START_STOP) | |
return; | |
if(beat_index < 2) | |
return; | |
// Copy all information to compare variables | |
int i = 0; | |
for(; i < beat_index; i++) | |
begin | |
beat_vector[i].compare_time = beat_vector[i].time - beat_vector[0].time; | |
beat_vector[i].compare_type = beat_vector[i].type; | |
end | |
repeat_after_me_len = beat_index; | |
repeat_after_me_state = wait_repeat_after_me; | |
break; | |
case wait_repeat_after_me: | |
// Reset beat index to get first beat | |
beat_index = 0; | |
// Turn on LED indicating cycle | |
LED_PORT |= LED_CYCLE_REPEAT_AFTER_ME; | |
// Wait for beat | |
if(get_beat(FALSE) == FALSE) | |
begin | |
// If button press, set state back to set_repeat_after_me | |
repeat_after_me_state = set_repeat_after_me; | |
return; | |
end | |
else | |
begin | |
// Move to next state | |
repeat_after_me_state = repeat_repeat_after_me; | |
compare_and_display(); | |
end | |
break; | |
case repeat_repeat_after_me: | |
while(beat_index < repeat_after_me_len) | |
begin | |
// Get beat | |
if(get_beat(FALSE) == FALSE) | |
begin | |
// Check if button | |
if(button_activity_flag == TRUE) | |
begin | |
repeat_after_me_state = set_repeat_after_me; | |
return; | |
end | |
end | |
// Display beat even if missed | |
compare_and_display(); | |
end | |
repeat_after_me_state = wait_repeat_after_me; | |
break; | |
end | |
end | |
/******************************************************* | |
Execute free beat mode | |
*******************************************************/ | |
void free_beat() | |
begin | |
// Turn off LED's | |
LED_PORT = 0; | |
// Wait for beats and signal them | |
while(get_beat(FALSE) == TRUE) | |
display_beat(beat_index - 1); | |
end | |
/******************************************************** | |
Read dip switches | |
*********************************************************/ | |
void read_dip_switches() | |
begin | |
// Check DIP switch locations | |
unsigned char read_dips = (DIP_PORT&BUTTON_DIP_SWITCHES); | |
//Following lines reverse the bit orders (due to way it is connected on board)g | |
read_dips = (read_dips & 0x0F) << 4 | (read_dips & 0xF0) >> 4; | |
read_dips = (read_dips & 0x33) << 2 | (read_dips & 0xCC) >> 2; | |
read_dips = (read_dips & 0x55) << 1 | (read_dips & 0xAA) >> 1; | |
board_id = read_dips; | |
end | |
/******************************************************** | |
Follow the leader - leader board | |
********************************************************/ | |
void follow_the_leader_leader_beat() | |
begin | |
// First, reset beat index | |
beat_index = 0; | |
// Signal current user | |
LED_PORT = LED_LEADER_FOLLOW_THE_LEADER; | |
// While there is no button press | |
while(get_beat(TRUE) == TRUE) | |
display_beat(beat_index - 1); | |
end | |
/******************************************************** | |
Follow the leader - player board | |
*********************************************************/ | |
void follow_the_leader_player_beat () | |
begin | |
// First, reset beat index | |
beat_index = 0; | |
follow_the_leader_leader_beat_index = 0; | |
// Set break flag to false | |
bool break_flag = FALSE; | |
// Turn on middle LED to indicate ready | |
LED_PORT = LED_PLAYER_FOLLOW_THE_LEADER; | |
// Call get beat | |
while(break_flag == FALSE) | |
begin | |
// Check follow-the-leader state | |
switch(follow_the_leader_state) | |
begin | |
case initial_follow_the_leader: | |
// Try to get beat | |
if(get_beat(FALSE) == FALSE) | |
begin | |
// If button was pressed, leave mode | |
if(button_activity_flag == TRUE) | |
break_flag = TRUE; | |
// If leader beat, go to wait for player mode | |
if(external_interrupt_flag == TRUE) | |
follow_the_leader_state = wait_player_follow_the_leader; | |
end | |
else | |
begin | |
// Simultaneous beat and receiver could happen | |
// If player beat only, go to wait for leader mode | |
// If both, stay at this state and display difference | |
if(external_interrupt_flag == TRUE) | |
compare_and_display(); | |
else | |
follow_the_leader_state = wait_leader_follow_the_leader; | |
end | |
break; | |
case wait_leader_follow_the_leader: | |
// Try to get beat | |
if(get_beat(FALSE) == FALSE) | |
begin | |
// If button was pressed, leave mode | |
if(button_activity_flag == TRUE) | |
break_flag = TRUE; | |
// If leader beat, display time difference and restart state machine | |
if(external_interrupt_flag == TRUE) | |
begin | |
compare_and_display(); | |
follow_the_leader_state = initial_follow_the_leader; | |
end | |
// If timeout, display miss (extra beat) and restart state machine | |
if(jump_beat_flag == TRUE) | |
begin | |
compare_and_display(); | |
follow_the_leader_state = initial_follow_the_leader; | |
end | |
end | |
else | |
begin | |
// Check for simultaneous beat and receive | |
if(external_interrupt_flag == FALSE) | |
begin | |
// If beat before deadline, then display miss and restart state | |
beat_vector[follow_the_leader_leader_beat_index].type = inactive; | |
beat_vector[follow_the_leader_leader_beat_index].time = 0; | |
follow_the_leader_leader_beat_index++; | |
compare_and_display(); | |
end | |
else | |
begin | |
// If there was leader beat within player's second beat, then mark first as wrong, second as | |
beat_vector[follow_the_leader_leader_beat_index].compare_type = beat_vector[follow_the_leader_leader_beat_index - 1].type; | |
beat_vector[follow_the_leader_leader_beat_index].compare_time = beat_vector[follow_the_leader_leader_beat_index - 1].time; | |
beat_vector[follow_the_leader_leader_beat_index - 1].compare_type = inactive; | |
beat_vector[follow_the_leader_leader_beat_index - 1].compare_time = 0; | |
// Display only last "correct" beat | |
follow_the_leader_leader_beat_index++; | |
beat_index--; | |
compare_and_display(); | |
beat_index++; | |
end | |
end | |
break; | |
case wait_player_follow_the_leader: | |
// Try to get beat | |
if(get_beat(FALSE) == FALSE) | |
begin | |
// If button was pressed, leave mode | |
if(button_activity_flag == TRUE) | |
break_flag = TRUE; | |
// If leader beat, display time difference and restart state | |
if(external_interrupt_flag == TRUE) | |
begin | |
beat_vector[beat_index].type = inactive; | |
beat_vector[beat_index].time = 0; | |
beat_index++; | |
compare_and_display(); | |
end | |
// If timeout, display miss (missed beat) and restart state machine | |
if(jump_beat_flag == TRUE) | |
begin | |
compare_and_display(); | |
follow_the_leader_state = initial_follow_the_leader; | |
end | |
end | |
else | |
begin | |
// If beat before deadline, then display miss and restart state machine | |
// Check if external interrupt happened during beat | |
if(external_interrupt_flag == FALSE) | |
begin | |
compare_and_display(); | |
follow_the_leader_state = initial_follow_the_leader; | |
end | |
else | |
compare_and_display(); | |
end | |
break; | |
end | |
end | |
// Restart state machine for next run | |
follow_the_leader_state = initial_follow_the_leader; | |
end | |
/*********************************************************** | |
Initialize global variables and ports | |
***********************************************************/ | |
void initialize(void) | |
begin | |
/******************** | |
Setup TIMER1 | |
- Prescalar = 1; Clear on Compare Match | |
- Output Compare A Match interrupt enabled | |
********************/ | |
TCCR1B = (1<<CS10) | (1<<WGM12); | |
OCR1A = SAMPLE_TIME; | |
TIMSK1 = (1<<OCIE1A); | |
/******************** | |
Setup ADC: | |
- Left Adjust Result | |
- Ref. Voltage is AVcc | |
- ADC Enable | |
- Prescalar = 16 (ADC @ 1MHz) | |
********************/ | |
ADMUX = (1<<ADLAR) | (1<<REFS0); | |
ADCSRA = ((1<<ADEN) | (1<<ADSC)) + 4; | |
// The code in the following lines was found November 17th at: | |
// http://people.ece.cornell.edu/land/courses/ece4760/IR_comm/IR_comm_GCC644_v8.c | |
/******************** | |
IR Carrier Generator | |
- Set up Timer2 for square wave with NO ISR | |
- 56 KHz => 1/2 cycle 8.928 microsec = 143 cycles | |
- D.7 -> 56KHz | |
********************/ | |
OCR2A = 142 ; //143 cycles/half-period | |
// count at full rate | |
TCCR2B = 1; | |
// set to toggle OC2A, clear on match, | |
TCCR2A = (1<<COM2A0) | (1<<WGM21) ; | |
// End of referenced code | |
/******************** | |
Setup Ports | |
********************/ | |
/*PORT A | |
A.0: ADC Input Strong Filter | |
A.1: ADC Input Weak Filter | |
A.4-A.7: Dip switches*/ | |
DDRA = 0x00; | |
/*PORT D | |
D.0: IR RX | |
D.1: IR TX | |
D.2: | |
D.3-D.6: 7-segment display | |
D.7: 56KHz Wave */ | |
DDRD = 0xFE; | |
//Setup LED ports | |
DDRC = 0xFF; | |
/*PORT B | |
B.5: MODE SELECT BUTTON | |
B.6: START/STOP BUTTON | |
B.7: RESET BUTTON*/ | |
DDRB = 0x00; | |
// Reset beat index | |
beat_index = 0; | |
//Setup Initial State Machine | |
sample_state = off_beat; | |
//Setup Initial Mode | |
active_mode = calibrate_mode;//inactive_mode; | |
//Setup Initial Metronome State | |
metronome_state = wait_metronome; | |
// Setup Initial Repeat-after-me State | |
repeat_after_me_state = set_repeat_after_me; | |
// Setup Initial Follow-the-leader State | |
follow_the_leader_state = initial_follow_the_leader; | |
// Get and indicate board ID | |
read_dip_switches(); | |
display_segment_id(); | |
// Wait for user to read ID | |
_delay_ms(1000); | |
// Display active mode | |
display_segment_mode(); | |
//Enable Interrupts | |
sei(); | |
end | |
/******************************************************* | |
Main function | |
********************************************************/ | |
int main() | |
begin | |
while(1) | |
begin | |
reset_flag = FALSE; | |
//Set Up The Board | |
initialize(); | |
// Check for reset button press | |
do | |
begin | |
// Check mode and call relative function | |
switch(active_mode) | |
begin | |
case calibrate_mode: | |
calibrate(); | |
break; | |
case follow_the_leader_mode: | |
// Check if leader or player | |
if(board_id == 0) | |
follow_the_leader_leader_beat(); | |
else | |
follow_the_leader_player_beat(); | |
break; | |
case metronome_mode: | |
metronome_beat(); | |
break; | |
case repeat_after_me_mode: | |
repeat_after_me_beat(); | |
break; | |
case free_beat_mode: | |
free_beat(); | |
break; | |
end | |
end while(reset_flag == FALSE); | |
// If reset_flag is false, run again | |
end | |
return 0; | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment