Digital Guitar Tuner
Eric Tai | Daniel Tsui
ECE 476: Spring 2005
Introduction | High Level Design | Program/Hardware Design | Results | Conclusions | Appendix
Appendix
Appendix A: Commented program listing
//time base will
be 10e-4
//stuff that
needs to be added in:
//
//
//PORT hookups:
//Port A:
ADC
//Port B: LED
outputs
//Port D:
pushbutton inputs
#asm
.equ __lcd_port=0x15
#endasm
#include <lcd.h> // LCD driver routines
#include <Mega32.h>
#include <stdio.h>
#define begin {
#define end }
//signal timer
state machine
#define start 1
#define counting 2
//pushbutton
state machine
#define release 1
#define debounce 2
#define detect_t 3
#define done 4
#define still_p 5
#define debounce_r 6
//guitar
frequencies
#define E2 12134 // period
in us. E2 period is 12134x10^-6
#define A2 9090
#define D3 6180
#define G3 5102
#define B3 4049
#define E4 3033
#define STIME 2
#define BUTTONTIME 500 //ever 50ms
#define mult(a,b) ((char)((((long)(a))*((long)(((int)b)<<8)))>>16)) //multiply 8-bit
integer by 8-bit fraction
//declaring functions
char lcd_buffer[17];
void initialize(void);
void pollbuttons(void);
void dosignal(void);
void eval_timer(void);
//variables
unsigned char
x, lastx, y, lasty; //raw A to D number
float a,b;
char state, buttonstate, count; //timing
state machine
unsigned int
timer, endtime; //unsigned int from 0 to 65535
char threshold, thres;
//counters
char signal_time;
char led;
char buttons, maybe; //store states for pushbutton
int stringval; //the string being hit
int time1; //timer1
unsigned int
temp;
unsigned int
lower2, upper2, lower1, upper1, lower0, upper0, cal;
//*****************************************************
//timer 0 compare
ISR
interrupt [TIM0_COMP] void timer0_compare(void)
begin
if (time1 > 0)
--time1;
if
(signal_time > 0) --signal_time;
timer++;
end
//*******************************************************
void initialize(void)
begin
lcd_init(16);
//init the A to D
converter
TIMSK = 2;
OCR0 = 25; //count
up to 25
TCCR0=0b000001011; //prescale by 64
ADMUX = 0b01100000; //enable
ADC and set prescaler to 1/128*16MHz=125,000
//user internal vref
//and clear interupt enable
//and start a conversion
ADCSR = 0b11000111;
//initialize
buttons
DDRB = 0xff; //output LED's
PORTB = 0x00; //start all
off
DDRD = 0x00; //input pushbuttons
PORTD = 0xff;
//initialize
variables
time1 = BUTTONTIME;
signal_time = STIME;
threshold = 127; //digitized value for
zero ----CHANGE THIS VALUE
thres = 0; // the amount of leeway on tuning
led = 0x00;
PORTB = ~led;
state = start;
b=0.0305;
a=0.9391;
buttonstate
= release;
upper0
= 0;
upper1
= 0;
upper2
= 0;
lower0
= 0;
lower1
= 0;
lower2
= 0;
#asm
sei
#endasm
temp = 0;
count=0;
end
void pollbuttons(void)
//function: to
set the correct string that is being checked.
Can occur every 30 ms. We will
have to debounce.
begin
time1 = BUTTONTIME;
buttons = PIND;
//debounce
switch (buttonstate)
begin
case
release:
//PORTB
= 0b11111111;
//pushbuttons not pressed
if
(buttons != 0xff) //if something pressed
begin
maybe = buttons; //keep old buttonpressed number
buttonstate = debounce;
//change states
end
break;
case
debounce:
//is
pushbuttons pressed?
if
(buttons == maybe) buttonstate = still_p;
//button
pressed
else
buttonstate = release;//button not pressed
break;
case
still_p:
//press
confirmed
if (buttons == maybe) //button
IS pressed
begin
switch (buttons) //button capture
begin
case 0b11111110:
stringval = E2;
PORTB
= 0b11111110;
lcd_clear();
lcd_gotoxy(0,0);
sprintf(lcd_buffer,"String
E2");
lcd_puts(lcd_buffer);
b=0.0450;
a=0.9099;
//passing frequency bounds
upper2 = 2527;
upper1 = 2498;
upper0 = 2470;
lower0 = 2386;
lower1 = 2358;
lower2
= 2331;
cal=20;
threshold = 127;
break;
case 0b11111101:
stringval = A2;
lcd_clear();
lcd_gotoxy(0,0);
lcd_putsf("String
A2");
b=0.0592;
a=0.8816;
//passing frequency
bounds
upper2 = 1893;
upper1
= 1871;
upper0 = 1850;
lower0 = 1786;
lower1 = 1766;
lower2 = 1476;
cal=30;
threshold = 127;
break;
case 0b11111011:
stringval = D3;
lcd_clear();
lcd_gotoxy(0,0);
lcd_putsf("String
D3");
b=0.0797;
a=0.8406;
//passing frequency
bounds
upper2 =
1419;
upper1 =
1402;
upper0 = 1386;
lower0 =
1339;
lower1 =
1324;
lower2 = 1308;
cal=30;
threshold = 127;
break;
case 0b11110111:
stringval = G3;
lcd_clear();
lcd_gotoxy(0,0);
lcd_putsf("String
G3");
b=0.0730;
a=0.8541;
//passing frequency bounds
upper2 = 1062;
upper1 = 1050;
upper0 = 1038;
lower0 = 1003;
lower1 = 991;
lower2 = 980;
cal=10;
threshold = 80;
break;
case 0b11101111:
stringval = B3;
lcd_clear();
lcd_gotoxy(0,0);
lcd_putsf("String
B3");
b=0.1270;
a=0.7459;
//passing frequency
bounds
upper2 = 843;
upper1
= 834;
upper0 = 824;
lower0 = 796;
lower1 = 787;
lower2 = 778;
cal=10;
threshold = 127;
break;
case 0b11011111:
stringval = E4;
lcd_clear();
lcd_gotoxy(0,0);
lcd_putsf("String
E4");
b=0.1648;
a=0.6705;
//passing frequency
bounds
upper2 = 632;
upper1 = 624;
upper0 = 617;
lower0 = 596;
lower1 = 589;
lower2 = 583;
cal=10;
threshold = 127;
break;
default: break;
end
end
else buttonstate =
debounce_r;
break;
case
debounce_r:
if (buttons == maybe)
buttonstate = still_p;
else buttonstate = release;
break;
end //switch */
end //pollbuttons
void dosignal(void)
//to filter then
time the input frequency. Needs to be
occurring every 1 microsecond.
begin
signal_time = STIME;
lastx=x;
lasty=y;
x=ADCH; //get signal
ADCSR.6=1; //get next sample
endtime=timer; //get current timer
value for the period
//digital filter
y=((char)(b*x+b*lastx+a*lasty));
if(lasty<threshold
&& y > threshold) //cross zero from negative to positive
begin
count++;
if(count==20)
begin
eval_timer(); //check
if timer value is in bounds
count=0;
timer=0;
//reset timer and begin measurement for next
20 periods
end
end
end
void eval_timer(void)
begin
endtime = endtime + cal;
lcd_clear();
lcd_gotoxy(0,0);
sprintf(lcd_buffer,"targ:
%d-%d",lower0,upper0);
lcd_puts(lcd_buffer);
lcd_gotoxy(0,1);
sprintf(lcd_buffer,"%d",endtime);
lcd_puts(lcd_buffer);
if
(endtime > lower0 && endtime < upper0) PORTB = 0b11110111; //light up middle
LED
else if (endtime >
upper0 && endtime < upper1) PORTB
= 0b11111011; //light
up +30 cent
else if (endtime >
upper1 && endtime < upper2) PORTB
= 0b11111101; //light up +50 cent
else if (endtime >
upper2) PORTB = 0b11111110; //light up
+70cent
else if (endtime >
lower1 && endtime < lower0)
PORTB = 0b11101111; //light up -30 cent
else if (endtime >
lower2 && endtime < lower1)
PORTB = 0b11011111; //-50
else if (endtime <
lower2) PORTB =
0b10111111; //-70
end
void main(void)
begin
initialize();
// measure and
display loop
while (1)
begin
if(signal_time==0) dosignal(); //get and process
signal
if(time1==0) pollbuttons(); //get input
from buttons
end
end
Appendix B: Schematics
Figure: Flow Diagram
Picture: Completed Circuit
Appendix C: Cost details with all part numbers and their price.
This cost will
include components supplied by the lab, including MCU and power supplies but excluding
the STK500.
Item |
Cost |
1 x breadboard |
6.00 |
1 x Power Supply |
5.00 |
1 x Mega32 |
8.00 |
1 x LCD Display (16x2) |
8.00 |
1 x Custom PC Board |
5.00 |
11 x Resistors |
0.11 |
2 x Capacitors |
0.50 |
2 x 1N914 Diodes |
0.02 |
1 x LMC7111 Op Amp |
1.55 |
1 x Audio Jack |
2.00 |
2 x Solder-linked breadboards |
5.00 |
TOTAL: |
41.18 |
Appendix D: Specific Tasks
Programming Code – ejt22, ddt7.
Analog Op Amp – ddt7, etj22.
Digital Filter – ddt7
Web Site – ejt22.
Pushbuttons and LCD – ejt22, ddt7
Appendix E: References
Data sheets
Vendor sites
Radio Shack. www.radioshack.com
Background
sites/papers
Reeves, Galen and Dimitry Berenson. “Digital Guitar Tuner”. Spring 2004. http://instruct1.cit.cornell.edu/courses/ee476/FinalProjects/s2004/ddb25/index.htm
Wolfe, Joe. “Note Names, MIDI Numbers and Frequencies.” http://www.phys.unsw.edu.au/~jw/notes.html