I Introduction
The MIDI Maestro is a MIDI sequence recognizing and comparison entertainment
device that is to be used in unison with a MIDI keyboard. The MIDI Maestro has
two modes of operation. The first mode is a basic sequence building mode which
reads successively longer sequences of note data into a statically allocated circular
buffer, compares previous sequence values with current sequence values, kicks
the current player out of the game if their sequence doesn’t match, and moves on
to the next player. The second mode of operation is advanced multi-player mode.
Both players play phrases over MIDI backing rhythms that are stored in statically
allocated arrays and try to reproduce both the notes and the timings that their
opponent has played. The players’ input notes are recorded into a circular buffer
and their timings are stored in a statically allocated array of booleans
corresponding to time ticks (divisions of measures). MIDI output (backing
rhythms) are polled against a timer while MIDI input is interrupt driven. RPG
movement and pushbuttons are also interrupt driven in advanced multi-player
mode and in the menu navigation system. The body of this report details these
major concepts and provides an overview of the flow of embedded software in the
MIDI Maestro.
II Software Design Considerations
Memory Sections/Mappings:
The memory on the Atmel ATMega8535 is divided up into three regions.
These regions are the Flash Program Memory Space, the SRAM Data
Memory Space, and the EEPROM Memory Space.
Flash Program Memory Space:
$000 - $FFF (4Kx16bits)
Boot Sector resides in upper portion of Flash Program Memory
Space
SRAM Data Memory Space:
$0000 - $001F is the 32 entry registry file
$0020 - $005F are the I/O registers (addressed as $00 - $3F)
$0060 - $025F is the internal SRAM (512 x 8bits)
EEPROM Memory Space:
512 x 8bits addressed through I/O registers
EEAR register 8..0 is the read/write address
EEDR register 7..0 is the data read/written
Memory Models (Data Structures):
The primary data structures that are used in the MIDI Maestro exist to
store and/or analyze MIDI data based on the current mode of game play.
Since both the microcontroller generated backing rhythm and the user
provided MIDI input are strongly dependant on the same real-time
constraints, the MIDI streams in both cases are stored in similar ways.
The slight difference between the two is that the pre-programmed backing
rhythm data is stored in statically allocated arrays and the real-time user
input note data is stored in circular buffers (two). Both will use a
statically allocated array of booleans with entries corresponding to
divisions of a measure (i.e. 16 ticks per measure for 4/4 time or 12 ticks
per measure for 3/3 time). In the case of the backing rhythm, if the
boolean value is true for the current time tick (based on Timer 0), the next
note in the statically allocated array will be played. An example of this
storage scheme is shown in Figure 1.
Figure 1: Backing Rhythm Data Storage
In advanced multi-player mode, where key sequences are compared
between two users, if user input is received, the corresponding boolean
will be set in the time tick array (again based on Timer 0) and the note
data will be added to the current player’s circular buffer. This user data
can be analyzed after it is stored.
All of the pre-programmed MIDI data will be stored in Flash Program
Memory along with the users’ boolean tick arrays. The circular buffers
(two) to store user input note data will reside in SRAM. All of these data
structures are global.
NOTE: In the sequence building mode, the boolean tick arrays will not be
needed and will be unused.
Startup Code:
The startup code below initializes the correct input and output pins on
ports A through D, enables the UART with an 8 bit buffered interrupt
driven receive and a baud rate of 31250, enables the SPI as master with a
clock of 2MHz, and sets up Timer 0 with an initial value of 0. The UART
is used to send and receive MIDI data, the SPI is used to communicate
with the ISD4003 Voice Record/Playback Chip, and Timer 0 is used to
keep track of timing of MIDI data.
#include <mega8535.h>
#define RXB8 1
#define TXB8 0
#define UPE 2
#define OVR 3
#define FE 4
#define UDRE 5
#define RXC 7
#define FRAMING_ERROR (1<<FE)
#define PARITY_ERROR (1<<UPE)
#define DATA_OVERRUN (1<<OVR)
#define DATA_REGISTER_EMPTY (1<<UDRE)
#define RX_COMPLETE (1<<RXC)
// USART Receiver buffer
#define RX_BUFFER_SIZE 8
char rx_buffer[RX_BUFFER_SIZE];
unsigned char rx_wr_index,rx_rd_index,rx_counter;
// This flag is set on USART Receiver buffer overflow
bit rx_buffer_overflow;
// USART Receiver interrupt service routine
#pragma savereginterrupt [USART_RXC] void uart_rx_isr(void)
{
char status,data;
#asm
push r26
push r27
push r30
push r31
in r26,sreg
push r26
#endasm
status=UCSRA;
data=UDR;
if ((status & (FRAMING_ERROR | PARITY_ERROR |
DATA_OVERRUN))==0)
{
rx_buffer[rx_wr_index]=data;
if (++rx_wr_index == RX_BUFFER_SIZE) rx_wr_index=0;
if (++rx_counter == RX_BUFFER_SIZE)
{
rx_counter=0;
rx_buffer_overflow=1;
};
};
#asm
pop r26
out sreg,r26
pop r31
pop r30
pop r27
pop r26
#endasm
}
#pragma savereg+
#ifndef _DEBUG_TERMINAL_IO_
// Get a character from the USART Receiver buffer
#define _ALTERNATE_GETCHAR_
#pragma used+
char getchar(void)
{
char data;
while (rx_counter==0);
data=rx_buffer[rx_rd_index];
if (++rx_rd_index == RX_BUFFER_SIZE) rx_rd_index=0;
#asm("cli")
--rx_counter;
#asm("sei")
return data;
}
#pragma used#endif
// Standard Input/Output functions
#include <stdio.h>
// Declare your global variables here
void main(void)
{
// Declare your local variables here
// Input/Output Ports initialization
// Port A initialization
// Func0=Out Func1=Out Func2=Out Func3=Out Func4=Out Func5=Out
Func6=Out Func7=Out
// State0=0 State1=0 State2=0 State3=0 State4=0 State5=0 State6=0
State7=0
PORTA=0x00;
DDRA=0xFF;
// Port B initialization
// Func0=In Func1=In Func2=In Func3=Out Func4=Out Func5=Out
Func6=In Func7=Out
// State0=T State1=T State2=T State3=0 State4=0 State5=0 State6=T
State7=0
PORTB=0x00;
DDRB=0xB8;
// Port C initialization
// Func0=Out Func1=Out Func2=Out Func3=Out Func4=Out Func5=Out
Func6=Out Func7=Out
// State0=0 State1=0 State2=0 State3=0 State4=0 State5=0 State6=0
State7=0
PORTC=0x00;
DDRC=0xFF;
// Port D initialization
// Func0=In Func1=Out Func2=In Func3=In Func4=In Func5=In
Func6=In Func7=In
// State0=T State1=0 State2=T State3=T State4=T State5=T State6=T
State7=T
PORTD=0x00;
DDRD=0x02;
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 7.813 kHz
TCCR0=0x05;
TCNT0=0x00;
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=FFh
// OC0 output: Disconnected
TCCR0=0x00;
TCNT0=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// INT2: Off
MCUCR=0x00;
MCUCSR=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x00;
// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: On
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud rate: 31250
UCSRA=0x00;
UCSRB=0x98;
UCSRC=0x86;
UBRRH=0x00;
UBRRL=0x0F;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
// Analog Comparator Output: Off
ACSR=0x80;
SFIOR=0x00;
// SPI initialization
// SPI Type: Master
// SPI Clock Rate: 2000.000 kHz
// SPI Clock Phase: Cycle Half
// SPI Clock Polarity: Low
// SPI Data Order: LSB First
SPCR=0x70;
SPSR=0x00;
// Global enable interrupts
#asm("sei")
Organization of Embedded Code:
The MIDI Maestro’s run-time software is broken down into the following
functional blocks:
MIDI Input
MIDI Output
MIDI Data Analysis
LCD Output
Analog Audio Message Initiation
Menu Navigation
Mid-Game Control
The MIDI Input Block is responsible for obtaining MIDI data and filtering
unused input, the MIDI Output Block is responsible for sending the
backing rhythm to the MIDI keyboard, the MIDI Data Analysis Block will
either compare users’ real-time input streams or compare non-real-time
sequences (dependant on game mode), the LCD Output Block is
comprised of character and graphic generation routines, the Analog Audio
Message Initiation Block is responsible for sending the correct audio clip
request to the ISD4003 Voice Record/Playback Chip, the Menu
Navigation Block is responsible for setting up initial game settings, and
the Mid-Game Control Block handles user control requests during game
play.
III Software Design Narrative
MIDI Input Block:
This is a polled interrupt generating block that reads MIDI data on the
UART input of the microcontroller that is sent from the MIDI keyboard.
If the input is invalid (i.e. pitchbend wheel data, MIDI control knob data),
it is discarded. If the input is valid, the note data is placed in one of two
circular note data buffers so that it can be analyzed at a later time. In
multi-player advanced mode, each player will get a buffer and in basic
sequence building mode, a current buffer and a past sequence buffer exist.
In advanced multi-player mode, the tick corresponding to Timer 0’s value
will be set in the tick array.
NOTE: Only MIDI start messages will be recorded. MIDI stop messages
need not be recorded since simultaneously requiring users to press and
depress keys at the same time is not the Midi Maestro’s intention.
MIDI Output Block:
This is a polled block that works closely with Timer 0 to generate the
backing rhythm in multi-player advanced mode. If Timer 0’s value has
not yet reached a value allowing the MIDI Output Block to move to the
next tick in the tick array, nothing is done. If Timer 0 reached the value
corresponding to the next tick, the MIDI Output Block resets Timer 0 and
moves to the next index in the tick array. If the corresponding value is
false, nothing is done. If that value is true, the next MIDI message is sent
to the keyboard via the UART transmit port. This block then waits again
until it can move to the next index in the tick array and repeats.
NOTE: Both MIDI start and MIDI stop messages are sent. Also, the
backing rhythm data is sent on MIDI ports 2 through 16 with one
instrument per channel. Data is not sent on port 1 (excluding during
initializations) because this software assumes that the user’s MIDI
keyboard is set with the ‘Remote Input’ option set to port 1. Any sent
program change MIDI messages (instrument changes) on port 1 will then
result in changing the user’s selected instrument, which is not the MIDI
Maestro’s intent.
MIDI Data Analysis:
The MIDI Data Analysis Block assumes two roles based on the mode of
game play. In advanced multi-player mode, after one player has provided
a key sequence and the other player has attempted to duplicate it, the
MIDI Data Analysis Block compares the two. It does this by stepping
through both the tick arrays and the circular buffers to determine if the
attempted sequence duplication is sufficient in both timing and note data.
This block is slightly forgiving in its comparisons since requiring exact
duplication would make game play impossible and boring. More leeway,
however, is given to slight timing differences than to note differences. In
basic sequence building mode, the data stored in the current buffer is
compared with the data stored in the past buffer. If duplication is not
exact, the sequence is considered invalid.
LCD Output:
This block is a collection of routines (character generation, graphic
generation, and LCD control) that aid in displaying data on the LCD. The
display can be easily turned on and off, sides of the LCD can be chosen
(the LCD is divided into two halves vertically), the LCD can be cleared,
and lines of characters and graphics can be generated. These routines send
all required instructions to the LCD, assert all data/address clock signals,
set all address/data lines, and most importantly, separate the programmer
from the inner workings of the LCD.
Analog Audio Message Initiation:
When user’s need to be alerted of game messages via the internal MIDI
Maestro speaker, the Analog Audio Message Initiation block provides a
wrapper of control for the ISD4003 Voice Record/Playback Chip. This
block places the ISD4003 in play mode by sending a single ‘powerup’
instruction via the microcontroller’s SPI port, sets the play address for the
desired message (based on passed parameters), and initiates playback by
sending the ‘play’ instruction.
Menu Navigation:
When the MIDI Maestro is powered on, a game setup screen is displayed.
The Menu Navigation Block is responsible for displaying the menus,
reading interrupt driven input from the RPG, moving cursors, debouncing
button presses, and selecting menu items. When information is entered by
the user, the Menu Navigation block stores game data such as number of
players, game mode, game personality, number of challenges, and
difficulty levels in global variables that can be used by the rest of the
software. Copies of these values are stored in EEPROM so that they can
be set as defaults upon the next powerup.
Mid-Game Control:
The Mid-Game Control Block is responsible for initiating game play,
handling user requests during game play, invoking the Analog Audio
Message Initiation Block, and ending game play. In order to initiate game
play, the Mid-Game Control Block sets empty conditions on circular
buffers (sets head and tail pointers accordingly), initializes boolean tick
arrays to false, sets the current player, and resets Timer 0. During game
play, the Mid-Game Control Block handles user control requests. These
can be game exit requests, tempo change requests, or challenge requests
(in advanced multi-player mode). If one user plays a bogus sequence, the
other player can challenge. If the original user cannot repeat the sequence,
the original user loses their turn. Each player only has a small number of
challenge requests, so they must be used wisely. Game messages (played
on the internal MIDI Maestro speaker) may also be initiated during game
play. Finally, the Mid-Game Control Block is responsible for determining
the end of game play, assigning a winner, and generating appropriate
game messages. After game play is complete, this block relinquishes
control to the Menu Navigation Block.
NOTE: While in basic sequence building mode, the Mid-Game Control
Block retains the underlying flow of control. This is different in advanced
multi-player mode where the underlying flow of control is relinquished to
the MIDI Output Block.
IV Software Flowcharts
Basic Sequence Building Mode (3 or more players):
Menu
Navigation and
Game Setup
Game
Mode?
Basic
Set Initial System Values
and Initiate Play
Next Player
Current Player Adds to
Sequence
Current Player Adds
to Sequence
Yes
Next Player
Yes
Sequence
Match?
No
No
Remove Player
Yes
>2 Players
Remain?
Sequence
Match?
Determine
Winner
No
Output Game
Messages
END
Advanced Multi-Player Mode (Offense Player):
Menu
Navigation and
Game Setup
Got
Offense
Player
Service
(From Next
Pages)
Game
Mode?
No
End of Turn
Reached?
Yes
Switch Players
& Service
Defense Player
(2 pages ahead)
Yes
Service RPG
Interrupt
(Next Page)
Yes
Service MIDI
Input Interrupt
(Next Page)
Advanced
No
Reset Timer 0
RPG
Interrupt?
Current Timer
0 Value
Reached
Apex?
Yes
Increment Tick
Array Index
No
Tick Array
Value True?
Yes
Send Current MIDI Data
Item & Increment MIDI
Data Array Index
No
No
MIDI Input
Interrupt?
Advanced Multi-Player Mode (Interrupt Service Offense):
Got RPG Interrupt
(From Previous Page)
Tempo
Interrupt?
Yes
Increase or Decrease
Timer 0 Apex
Return From
Interrupt
No
Yes
Keep Background
Rhythm Alive As Done
on Previous Page
No
Valid
Challenge
Request?
No
No
End of
Challenge?
Set Next Player as Defense,
Remove 1 Defense
Challenge, Service Defense
(Next Page)
Yes
MIDI
Interrupt?
Yes
Yes
Compare New
Data to Old
Data
Got MIDI Input
Interrupt (From
Previous Page)
Data
Valid?
Remove one Defense
Challenge, Set Next Player
as Offense, Service
Offense (Previous Page)
No
Valid MIDI
Data?
No
Yes
Set True at Current Tick
Array Index and Put
Data Item Into Offense
Circular Buffer
Return From
Interrupt
Advanced Multi-Player Mode (Defense Player):
Got Defense
Player Service
(From Previous
Pages)
No
End of Turn
Reached?
Yes
Service End of
Current Phrase
Contest
(Next Page)
No
Reset Timer 0
RPG
Interrupt?
Current Timer
0 Value
Reached
Apex?
Yes
Increment Tick
Array Index
No
Tick Array
Value True?
Yes
Send Current MIDI Data
Item & Increment MIDI
Data Array Index
No
Yes
Service RPG
Interrupt
(Next Page)
Yes
Service MIDI
Input Interrupt
(Next Page)
No
MIDI Input
Interrupt?
Advanced Multi-Player Mode (Interrupt Service Defense):
Got RPG Interrupt
(From Previous Page)
Tempo
Interrupt?
Yes
Increase or Decrease
Timer 0 Apex
Return From
Interrupt
No
Got MIDI Input
Interrupt (From
Previous Page)
No
Valid MIDI
Data?
Yes
Set True at Current Tick
Array Index and Put
Data Item Into Defense
Circular Buffer
Return From
Interrupt
Advanced Multi-Player Mode (End of Current Phrase Service):
Got End of Phrase
Service Request
(Previous Page)
Defense
Sequence
Match Offense?
Yes
Set Current Player to
Offense, Service Offense
(3 Pages Back)
No
Decrease Defense
Life Score
Defense
Dead?
No
Service Offense
(3 Pages Back)
Yes
Declare Offense
Winner, Play
Game Messages
END
NOTE: End can be considered a path back to Game Setup Screen
V List of References
Atmel ATmega8535 Datasheet
http://shay.ecn.purdue.edu/~477grp3/datasheets/at8535.pdf
ISD4003 Series Datasheet
http://shay.ecn.purdue.edu/~477grp3/datasheets/ISD4003Rev1.0.pdf
CodevisionAVR C Compiler
Startup Code
Version 1.23.8d Standard
© Copyright 2026 Paperzz