<Appendix A: Code>


Final.c

/***********************************************************************
Final Project ECE 476
Gabriel Rivera gjr8
Tatiana Lamela-Rabell tl74
April 16,2003
***********************************************************************/


#include <Mega32.h>
#include <stdio.h>

#include <lcd.h>
#define bufferLength 1700
#define delayLengthArray 7
#define totalNumEffects 5


#define Reverb 0
#define Distortion 1
#define Tremolo 2
#define Echo 3
#define NoEffect 4

#define NoPush 1
#define MaybePush 2
#define Pushed 3
#define MaybeNoPush 4
#define LCDwidth 16 //characters
#define t0 100 //Debounce Button0
#define t1 100 //Debounce Button1
#define t2 200 //lCD_Display
#define sampleTime 42//this is for 42*4us=168us=5952.38Hz approx 6KHz
#define Out PORTB
#define LCD PORTC
#define MuxSelect PORTA.1
#define Buttons PORTD

#define maxVol 8
#define minVol 0
#asm
.equ __lcd_port=0x15
#endasm


//Function Declarations\
void initialize(void);
char mult(char Num, char multVal);
int multi(int Num, char multVal);
void Button1_Debouncer(void);
void Button0_Debouncer(void);
void LCD_Display(void);

//Variable Declarations
unsigned char Ain,tmpAin;
unsigned char dataBuffer[bufferLength];
unsigned int delay[delayLengthArray]={0,500,700,1000,1300,1500,1594}; //each 100 in delay correspond to actualtime of 17ms
unsigned int i;//delay[delayLengthArray-2]+100;
unsigned int j,k=0,BigOutSignal;
unsigned char OutSignal,direction=1,vol=0;
unsigned char HighVal[]={128,135,145,65000};//Value at which to cut off audiowave for distortion
unsigned char freq[]={30,50,70,90};
unsigned char time0, time1, time2; //timeout counters
unsigned char PushFlag0,PushFlag1; //message indicating a button push
unsigned char PushState0,PushState1;
unsigned char effectIndex=3,propertyIndex[totalNumEffects]={0,0,0,0,0};
unsigned char lcd_buffer[16];
unsigned char SignalGain=0;


char flash *effects[totalNumEffects]={"Reverb","Distort","Tremolo","Echo","NoEffect"};

char flash *properties[totalNumEffects][4]={
{"Church","Auditorium","LargeRoom","Reverse"},
{"High", "Medium","Low","None"},
{"Fast","Medium","Slow","VerySlow"},
{"NoEffect","NoEffect","NoEffect","NoEffect"},
{"NoEffect","NoEffect","NoEffect","NoEffect"}

};


/*****************************void Main(void)****************************
Calls initialize function and then waits for timeN to be zero and calls three more functions:Button0_Debouncer(),Button1_Debouncer(), LCD_Display().
***********************************************************************/


void main(void)
{

initialize();
while(1)
{
if(time0==0)
Button0_Debouncer(); //occur every 50ms

if(time1==0)
Button1_Debouncer();//occur every 50ms

if(time2==0)
LCD_Display();//occur every 100ms

}
}


/****************************void LCD_Display(void)***********************
Updates the
DISPLAY every tenth of a second...t2=200 and checks for button pushes. if Button 0 is pushed then we cycle through
the different effects if we push button 1 we cycle through the effects properties...by this I mean the effect can be Distortion
but what type? High,medium or low distortion
***********************************************************************/

void LCD_Display(void)
{

if(PushFlag0|PushFlag1) //dont want to spend cycles here if not needed(no buttons has been pressed/most of the time)
{
SignalGain=0; //everytime any button is pushed the AGC(Automatic Gain Control) clears again. The logic behind this is that
//every effect has it's own DIFFERENT sound level so we set the gain to 0 or normal each time any button is pressed. THis
//takes about 168us*7=1millisecond

if(PushFlag0) //if button0 push cycle through effects.effectsIndex holds the current effect
{
PushFlag0=0; //clear flag
if(effectIndex!=totalNumEffects-1)//if we reach the end start over "cycle through"
effectIndex++;
else
effectIndex=0;
}


else if(PushFlag1) //same thing as button0 but now for button1
{
PushFlag1=0;//clear flag

switch(effectIndex)
{
case Reverb:
if(propertyIndex[Reverb]!=3)//for all effects the array propertyIndex[effectIndex] holds the current property
propertyIndex[Reverb]++;
else
propertyIndex[Reverb]=0;
break;

case Distortion:
if(propertyIndex[Distortion]!=3)
propertyIndex[Distortion]++;
else
propertyIndex[Distortion]=0;
break;


case Tremolo:
if(propertyIndex[Tremolo]!=3)
propertyIndex[Tremolo]++;
else
propertyIndex[Tremolo]=0;

break;


case Echo:
if(propertyIndex[Echo]!=3)
propertyIndex[Echo]++;
else
propertyIndex[Echo]=0;

break;

case NoEffect:
if(propertyIndex[NoEffect]!=3)
propertyIndex[NoEffect]++;
else
propertyIndex[NoEffect]=0;

break;
}//end switch
}//end elseif


//writing to LCD every tenth of a second
lcd_clear();

lcd_gotoxy(0,0);
lcd_putsf("Effect:");

lcd_gotoxy(8,0); //effectIndex
lcd_putsf(effects[effectIndex]);

lcd_gotoxy(0,1);
lcd_putsf("Sel:");

lcd_gotoxy(5,1);
lcd_putsf(properties[effectIndex][propertyIndex[effectIndex]]);



}//end outer if

}//end LCD_Display


/*****************************void initialize(void)**************************
Starts A/D conversion starts Timer0 and Timer1
***********************************************************************/

void initialize(void)
{
// clearing buffer
lcd_init(LCDwidth); //initialize the display
lcd_clear(); //clear the display

lcd_gotoxy(0,4); //welcome screen
lcd_putsf("Welcome!");
lcd_gotoxy(0,1);
lcd_putsf("Select Effects");

for(j=0;j<bufferLength;j++)//clearing the buffer initially
dataBuffer[j]=0;


//SETTING UP TIMER 1 setting values for registers
OCR1A = sampleTime; //time between samples 168 microseconds =5952samples/second
TCCR1B = 11; //divide by 64, clear-on-match (see page 110 textbook)OK
TCCR1A = 0x00; //turn off pwm and oc lines
TIMSK = 0x10; //enable interrupt T1 cmp

//SETTING UP TIMER 0 value for 1/2 Msec at 8 MHz
TCNT0= 131;// reload=256-125;
TIMSK=TIMSK|1;//turn on timer 0 overflow ISR see page 80 in data sheet
TCCR0=3; //divide by 64


ADCSR=0b11001111; //turn on ADC,allow interrupt at } of conversion,prescaler set to divide by 128
ADMUX=0b00100000; //channel zero/ left adj ADLRA /External Aref
DDRC=0xff;//output LCD
DDRD=0x00; //input buttons
DDRB=0xff; //output to D/A
DDRA.1=1;//output MuxSelect
//Buttons=0xff;

i=delay[delayLengthArray-2]+100; //this is the index for the buffer it needs to start after the delay indexes
time0=100;
time1=100;
time2=200;

PushState0=NoPush; //initializing Debounce StateMachines
PushState1=NoPush;
SignalGain=0;

#asm ("sei");
}

/****************interrupt [TIM0_OVF] void timer0_overflow(void)**************
this interrupt occurs every half millisecond and is used to call tasks.
***********************************************************************/
interrupt [TIM0_OVF] void timer0_overflow(void)
{

if(time0>0)
time0--;
if(time1>0)
time1--;
if(time2>0)
time2--;

TCNT0=131; //reload to force 1/2 mSec overflow

}


/*****************interrupt [TIM1_COMPA] void t1_cmpA(void)****************
This is the IMPORTANT CODE! This occurs every 168us so we have to be in and out FAST!
Here we alter the digital audio signal and output it.
***********************************************************************/

interrupt [TIM1_COMPA] void t1_cmpA(void)
{

tmpAin=Ain; //taking the digital value from the ADC
OutSignal=0;//this is the tmp variable where we hold the sample while we do stuff on it
BigOutSignal=0;//same as above but and integer... we didn't need this initially but then we figured out we did.
//many of our signal processing is adding signals to past signals so do the math if we have a wave
//of 128 or 2.5 volts and add it to another on of 128 or 2.5 volts what do we get 5 volts right?
// wrong 128+128=256 and if our variable is a char then it rolls over and it is zero volts
// so we use an int and then scale the signal w/ the AGC and turn it back into a char when outputting...

//moving circular buffer indexing
if (i!=bufferLength-1)
i++;
else
i=0;

//if the effect is an ECHO we need a feedback buffer
if(effectIndex!=Echo)
dataBuffer[i]=tmpAin;

else
{
BigOutSignal= ( ((int)tmpAin) + ((int) (dataBuffer[delay[0]]*.30)) );//delete tmpAin when we have switch.
dataBuffer[i]=( ((char)(BigOutSignal)) ) ;
}

switch(effectIndex)//what effect are we in now?
{


case Reverb:

switch(propertyIndex[effectIndex])//what property in Reverb are we in now?
{

case 0: //Church-more additions of past samples ==bigger room
BigOutSignal= (( ((int)tmpAin)+ (multi(dataBuffer[delay[0]],2))+ ((int)multi(dataBuffer[delay[1]],1))+ ((int)multi(dataBuffer[delay[2]],2)) + ((int)multi(dataBuffer[delay[3]],1))+ ((int)multi(dataBuffer[delay[4]],0)) + ((int)dataBuffer[delay[5]])) -400) ;
break;

case 1: //Auditorium
BigOutSignal= ( ((int)tmpAin)+ (int)multi(dataBuffer[delay[2]],2)) + ((int)multi(dataBuffer[delay[3]],1))+ ((int)multi(dataBuffer[delay[4]],0)) + ((int)dataBuffer[delay[5]]) -300 ;

break;

case 2: //Large Room
BigOutSignal= ( ((int)tmpAin)+ (int)multi(dataBuffer[delay[3]],1))+ ((int)multi(dataBuffer[delay[4]],0)) + ((int)dataBuffer[delay[5]]) -250 ;

break;

case 3://Reverse Rev-like church but inversed scaling
BigOutSignal= (( ((int)tmpAin)+ (multi(dataBuffer[delay[0]],0))+ ((int)multi(dataBuffer[delay[1]],1))+ ((int)multi(dataBuffer[delay[2]],2)) + ((int)multi(dataBuffer[delay[3]],3))+ ((int)multi(dataBuffer[delay[4]],4)) + ((int)multi(dataBuffer[delay[5]],5))) -400) ;
break;


}//end switch
break;


case Distortion: //pretty self explanatory...just clipping the wave
if(tmpAin>HighVal[propertyIndex[effectIndex]])
BigOutSignal=HighVal[propertyIndex[effectIndex]];
else
BigOutSignal=tmpAin;
break;



case Tremolo:

k++;

//all this ugly code below just choses a volume or a gain for the signal
//so the volume function is a simple triangle wave of height 1 and amplitude one
if(k==freq[propertyIndex[effectIndex]])
{

k=0;
if(direction ==1)//countingup
vol++;
else
vol--;
if(vol==maxVol)
direction=0;//1 is to count up 0 counts down
else if (vol==minVol)
direction=1;
}

BigOutSignal= ((int)mult(tmpAin,vol));//+OutSignal;sinArr[time]
break;

case Echo://most of the echo processing happens above..see above
SignalGain=0;//don't want to mess around w/ AGC since the echo is afeedback loop we can easily distort everything!
break;

case NoEffect: //out=in basically
BigOutSignal=tmpAin;
break;


}//end switch


//here we move the delays foward and when they reach the end we start over again.
for(j=0;j<delayLengthArray;j++)
{
if (delay[j]!=bufferLength-1)
delay[j]++;
else
delay[j]=0;
}



//this is the AGC we simply increase the signal gain until we are happy
if( ((BigOutSignal>255) & SignalGain<7) ) //Automatic Gain Control.
SignalGain++;

//here we convert the int to a char and scale the int so that it can fit in the char.
OutSignal=( ( (char) ( multi(BigOutSignal,SignalGain)) ) );

Out=OutSignal;//Out to DAC


}// interrupt

/*****************char mult(char Num, char multVal)***********************
This function basically just multiply a value using shifts
the +16/32/48 is needed because if we have something like our input like x[n]= 2.5sin(f*n)+2.5 volts and we divide by 2 or shift by 1
to the left we get x[n]/2= 1.25sin(f*n)+1.25 hence we need to bring it back up to a 2.5 offset or when adding our waves we
get garbage. so we add 1.25V or 64 see case 4 which demonstrates the operation
***********************************************************************/

char mult(char Num, char multVal)
{

switch (multVal)
{
case 0: //'1'
return Num;
break;
case 1: //'.875':
return ( ( ((Num>>1)+(Num>>2)+(Num>>3))+16) );
break;
case 2://'.75':
return ( ( ((Num>>1)+(Num>>2))+32));
break;
case 3: //.675
return (( ((Num>>1)+(Num>>3))+48));
break;
case 4://'.5':
return (((Num>>1) + 64));
break;
case 5://'.375':
return ((((Num>>2)+ (Num>>3))+80));
break;
case 6://'.25':
return (((Num>>2)+96));
break;
case 7://'.125':
return ( ((Num>>3)+112) );
break;
case 8://'0'
return 128;
break;
}


}

/*********************char multi(int Num, char multVal)**********************
this is the same as the mult function but for ints
***********************************************************************/

int multi(int Num, char multVal)
{

switch (multVal)
{
case 0: //'1'
return Num;
break;
case 1: //'.875':
return ( (((Num>>1)+(Num>>2)+(Num>>3))+16) );
break;
case 2://'.75':
return ( ( ((Num>>1)+(Num>>2))+32));
break;
case 3: //.675
return ((((Num>>1)+(Num>>3)+48)));
break;
case 4://'.5':
return (((Num>>1) + 64));
break;
case 5://'.375':
return ((((Num>>2)+ (Num>>3))+80));
break;
case 6://'.25':
return (((Num>>2)+96));
break;
case 7://'.125':
return ( ((Num>>3)+112) );
break;
case 8://'0'
return 128;
break;
}


}


/**********************void Button1_Debouncer(void)* **********************
Debounces button1 every 50ms..t1=100. Button1 is the property select for each effect.
***********************************************************************/


void Button1_Debouncer(void)
{
/*lcd_clear();
lcd_gotoxy(0,0);
sprintf(lcd_buffer,"%i",PushState1);
lcd_puts(lcd_buffer);
*/
time1=t1; //reset the task timer
switch (PushState1)
{
case NoPush:
if (~PIND == 0x02) PushState1=MaybePush;
else PushState1=NoPush;
break;
case MaybePush:
if (~PIND == 0x02)
{
PushState1=Pushed;
PushFlag1=1;
}
else PushState1=NoPush;
break;
case Pushed:
if (~PIND == 0x02) PushState1=Pushed;
else PushState1=MaybeNoPush;
break;
case MaybeNoPush:
if (~PIND == 0x02) PushState1=Pushed;
else
{
PushState1=NoPush;
PushFlag1=0;
}
break;
}
}

/*********************void Button0_Debouncer(void)*************************
Debounces button0 every 50ms..t0=100. Button0 is the effect select button
***********************************************************************/


void Button0_Debouncer(void)
{
time0=t0; //reset the task timer
switch (PushState0)
{
case NoPush:
if (~PIND == 0x01) PushState0=MaybePush;
else PushState0=NoPush;
break;
case MaybePush:
if (~PIND == 0x01)
{
PushState0=Pushed;
PushFlag0=1;
}
else PushState0=NoPush;
break;
case Pushed:
if (~PIND == 0x01) PushState0=Pushed;
else PushState0=MaybeNoPush;
break;
case MaybeNoPush:
if (~PIND == 0x01) PushState0=Pushed;
else
{
PushState0=NoPush;
PushFlag0=0;
}
break;
}
}


/********************interrupt [ADC_INT] void adc_done(void)*****************
gets value from A/D and exits...Interrupt constantly executes.
***********************************************************************/
interrupt [ADC_INT] void adc_done(void)
{
Ain = ADCH;
ADCSR= ADCSR|0x40;
}


AppendixA AppendixB AppendixC AppendixD