<Appendix A: Code>
/***********************************************************************
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;
}