Skip to content

Instantly share code, notes, and snippets.

@carlosdelfino
Created January 18, 2020 23:47
Show Gist options
  • Save carlosdelfino/504a05c953b917b3d29aa88fd85e9cee to your computer and use it in GitHub Desktop.
Save carlosdelfino/504a05c953b917b3d29aa88fd85e9cee to your computer and use it in GitHub Desktop.
Japanese Taiko Drum Sensei
/*
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