Guitar

34
Guitar Tuner Rodrigo Sierra Chavera, Electrical Engineering Project Advisor: Dick Blandford April 16, 2012 Evansville, Indiana

Transcript of Guitar

Page 1: Guitar

Guitar Tuner

Rodrigo Sierra Chavera, Electrical Engineering

Project Advisor: Dick Blandford

April 16, 2012

Evansville, Indiana

Page 2: Guitar

Table of Contents

I. Introduction

II. Background

III. Design Approach

A.Hardware

B. Software

C. Standards

IV. Results

V. Conclusion

Appendix A

Appendix B

Appendix C

Appendix D

Appendix E

List of Figures

Figure 1: Sketch of the Product

Figure 2: Basic Block Diagram

Figure 3: Pickup Representation

Figure 4: Non-inverting Op-amp Diagram

Figure 5: Final product

Figure 6: LPC1768 ARM7 Schematic

Figure 7: LM238 operational amplifier

Figure 8: AC signal circuit

Figure 9: Output Signal from AC Circuit

Figure 10: Spectrum of a Guitar Frequency

Page 3: Guitar

List of Tables

Table 1: Chromatic Scale of Notes

Table 2: Standard E Tuning

Table 3: Program Description. Tasks and public functions

Page 4: Guitar

I. Introduction

Musicians need to have their instruments perfectly tuned so they can play different types

of music. Anyone who is starting to learn to play any instrument knows that tuning it can be

difficult and many times stressful. One of the reasons is that these novice musicians have not yet

acquired what experts call ‘musical ear’ or ‘trained ear’. The conventional tuning process done

by ear can be useful and practical in many situations; however, this can produce many

inaccuracies and if not good enough, it may require a significant amount of time.

An electronic guitar tuner functions as a device used to help musicians tune instruments.

In this particular case, this designed device can tune any electric guitar string to its ideal

frequency. This device detects whether a string of an electric guitar is in tune or not by

displaying the frequency difference (fundamental frequency – current frequency). Also, a certain

number of LEDs will aid the user to decide whether the tuning pegs of a guitar need to be turned

clockwise or counterclockwise. When holding the device, the user will see a power on/off

switch, 3 LEDs that indicate whether the string is in tune, sharp, or flat, and a push button to

select any of the 6 strings.

Figure 1: Sketch of the Product

Page 5: Guitar

The components used to design and build the electronic guitar tuner are a microcontroller

(mbed LPC1768), liquid crystal display (LCD) panel for display, light-emitting diodes (LED),

user interface button, power switch, and many analog components for the guitar signal

amplification. These analog components turn the incoming signal from the guitar into usable data

for the microcontroller’s analog to digital converter (ADC).

Then, the microcontroller runs the program (see appendix E) stored in it which has two

main stages. The first one reads the signal from the ADC port and runs the Goertzel algorithm

(appendix D shows how this routine works) to it to find its harmonics. Next, the program does

the necessary calculations to find the frequency difference and sends the data to the digital to

analog converter (DAC) of the microcontroller which is displayed on an LCD panel.

LPC1768

MCU

Power

Source

Guitar

Signal

Amplification

DC

BIAS User

interface

Figure 2: Basic Block Diagram

Page 6: Guitar

II. Background

A note is a name given to describe a musical frequency [1]. The chromatic scale is

frequently used in Western music and it consists of the following twelve notes: A, A#, B, C#, D,

D#, E, E#, F, F#, G, G#. In general, the note ‘A’ is chosen as the standard frequency, and from

this note, the other notes’ frequencies are calculated. It is possible to find the distance between

the frequencies of two sounds by dividing the frequency of one by the frequency of the other.

This distance is called interval. An octave is the interval between two sounds one of which has

twice the frequency of the other [2]. These 12 notes mentioned before repeat for each octave.

Notes are assigned by their letters, and the number after them represents the octave they reside

in. For instance, B = 246.94 is in the 2nd octave and is called B3. Table 1 [1] lists the chromatic

scale of notes over a three octave range.

Table 1: Chromatic Scale of Notes

Note Octave1 Octave2 Octave3

A 110.00 220.00 440.00

A# 116.54 233.08 466.16

B 123.47 246.94 493.88

C 130.81 261.63 523.25

C# 138.59 277.18 554.37

D 146.33 293.66 587.33

D# 155.56 311.13 622.25

E 164.81 329.63 659.26

F 174.61 349.23 698.46

F# 185.00 369.99 739.99

G 196.00 392.00 783.99

G# 207.65 415.30 830.61

Page 7: Guitar

A guitar is a string instrument which usually has 6 strings. Because of the strings, the

sounds waves that a guitar emits are harmonic in nature. When a guitar string is plucked at a

random location on a guitar, the 1st harmonic or fundamental frequency, and also integer

multiples of it are propagated. These locations are where notes reside are made by frets which

are the raised portion on the neck of a stringed instrument. Frets divide the neck into fixed

segments; on a guitar, each fret represents one semitone. An electric guitar’s fretboard is

designed so that the 12 notes of the chromatic scale are located from the zero position (open

note) to the 12th fret. After the 12th fret, all the notes repeat at the next octave. When someone

tunes a guitar, what it basically happens is that the string acquires the proper tension by adjusting

the tuning pegs of the guitar. Ideally, once a guitar is tuned, it should stay in tune forever.

Unfortunately, due to environment (humidity, temperature, time) and other physical factors, the

strings lose tension periodically and they need to be tuned again depending on how often they are

used.

An electric guitar is normally tuned to standard tuning (E). There are many other tunings

which are specialized for certain types of music, such as Open D for folk guitarists. Table 2 lists

the standard tuning for a regular electric guitar [1].

Table 2: Standard E Tuning

String # Note Frequency (Hz)

1 E 329.6

2 B 246.9

3 G 196.0

4 D 146.8

5 A 110.0

6 E 82.4

Page 8: Guitar

III. Design Approach

A. Hardware

The way to get the electric guitar output is by getting in contact with the guitar’s pickups.

These are a type of transducer which gets the different guitar vibrations and turns them into an

electric signal [3]. Different guitars have different pickups (magnetic and piezoelectric). But in

general, they are mostly the same; they have magnetic windings to induce magnetic field.

Figure 3: Pickup Representation

An audio cable was connected to the electric guitar’s output jack to get the signal. The

problem is that typically the signal coming from the electric guitar is really low (between 5 to

100 mV) and very noisy. From the LPC1768 datasheet, this signal is not enough to be sent to the

ADC. The signal had to be amplified to reach the 3.3 V peak-to-peak requirement of the ADC

port. To do so, the LM358 operational amplifier was used. Its schematic can be found in

appendix A. This specific op-amp was chosen because it works well in this specific frequency

range (0 to 1000 Hz). Moreover, they were available in the stockroom.

The design of the circuit was basically divided into three stages. The first stage was

designed to provide a DC offset of 1.65V to the signal. When designing the circuit, certain

Page 9: Guitar

values of capacitors and resistors were used to avoid filtering any undesired frequency out. In

this case, a couple of 1MΩ resistors and a 1µF capacitor were used.

Then, the signal goes through an amplification circuit. Two inverting op-amps that made

the signal go up to 3.3V were used. The circuit was designed in a way that the overall gain is 33.

Finally, a pair of diodes (1N914) with a 3.3V input were added to rectify any signal that

goes beyond the 3.3V threshold. This whole circuit creates a nice waveform oscillating between

0 and 3.3V.

The microcontroller chosen for this design was the LPC1768 from NXP. The reason why

this microcontroller was chosen was because it is a powerful chip. The LPC1768 is based on a

32-bit ARM7 cortex-M3 core running at 96 MHz central processing unit (CPU) with real time

emulation and embedded trace support that combines the microcontroller with 512 kB of

embedded high-speed flash memory, 32kB of SRAM. It also has two UARTs, three 32-bit timers

and a real time clock. The MCU operates at +5V; in this case a 5V regulator was used to bring

the input voltage from the fours AA batteries to 5V. Moreover, the ADC is included in the chip

as an analog block. There are two channels with up to 12-bit resolution each. The input for the

ADC +

_ Guitar

Signal

Figure 4: Non-inverting Op-amp Diagram

Page 10: Guitar

ADC is specified in the schematic of the MCU found in appendix A [4].A sampling rate of

20000 samples/second was used, and it satisfies the Nyquist sampling theorem.

The electronic guitar tuner is powered by a set of four AA batteries (6V). It also has a

power switch which turns the device on and off. Then, the device has a switching voltage

regulator that drops the voltage down to +5V respectively. I chose this type of regulator due to its

efficiency and heat dissipation. Everything runs at +5V (MCU, op-amps) but the inputs for

rectifier, LCD and DC bias configuration (powered by the 3.3V output of the LPC1768).

B. Software

The program design for this project has been written in C using µVision 4. The incoming

AC electric signal is converted into digital to be used by digital signal processing (DSP).

The use of the FFT and Goertzel algorithm was critical in the software design part of the

project. First, the fast Fourier transform (FFT) was used to determine the spectrum of the signals.

Appendix C shows how this technique works. In DSP, the user deals with discreet signal; that is

why the discrete Fourier transform (DFT) formula was invented. Due to many unnecessary

operations, the FFT algorithm came to life. As mentioned before, the FFT task (algorithm) is

very important because it controls the sharp/flat/in-tune LEDs and the display, based on the

program results. The sample frequency used in the design is 20000 Hz with 4096 samples.

Unfortunately, this algorithm requires mid to high end microcontrollers that can handle

many calculations. That is why, the Goertzel algorithm came in in very handy. It basically gives

the same result of one frequency bin of the FFT and it is more efficient. Plus, it takes less than

half of the time that the FFT takes. Thus, this algorithm was picked to transform the time domain

signal to a frequency domain one.

Page 11: Guitar

The whole program has four main tasks. The first one is the initialize function; it starts

the system and only runs once. The second task sets the parameters of each string and also sets

the baud rate of the LCD for serial communication. Then, the Goertzel task does the necessary

calculations to turn the data stored in the buffer into the frequency domain. And, the last task

displays all the important information on a LCD and controls the LEDs.

Table 3: Program Description. Tasks and public functions

File Tasks Public Functions

Main.c Initialize variables

Set parameters for each string

Set LCD baud rate

Goertzel algorithm

Display messages

ADC.c ADC()

C. Standards

My project conformed to the ARM7TDMI power consumption standard. This is about

portable and handheld products require processors that consume less power than those in desktop

and other powered applications. It states that designers must analyze power use in the early stage

of design both at the circuit and system levels. RISC processors, such as our ARM7TDMI, have

both strengths and weaknesses as far as power consumption is concerned [5].

Page 12: Guitar

IV. Results

The designed guitar tuner worked as expected. The incoming signal from an electric

guitar was successfully amplified and centered at 1.65 V. Since only one power source was used

to power the operational amplifier, the signal didn’t go all the way down to ground. However,

this wasn’t needed since it met the basic requirements of the ADC of the mbed microcontroller.

Also, the Goertzel algorithm worked as expected. It did a good job in calculating the

different frequencies. After some tests, it was found that the FFT had a finite frequency

resolution and it was hard to detect small frequency changes without increasing the time window

which led to a sluggish program. As a result, the Goertzel algorithm performed exceptionally

well and took less time than the FFT. To make it even more accurate, the designed program ran

this algorithm six times with six different frequency targets, and compared them to find out

whether the string was in tune or not.

When comparing this device with others found in the current market, it is found that it

has many customizable features. For instance, tuning types can be changed at any time by just

changing the frequency target value son the program. Also, it can be built with less than 20

dollars since the Goertzel algorithm doesn’t require a powerful processor.

V. Conclusion

An accurate, real-time tuner was designed using the guitar output signal, operational

amplifiers, NXP LPC1768 processor, LCD panel and LEDs. It has been found that even though

there is noise in the signal, the fundamental frequencies will still be the same. Also that other

FFT algorithms and approaches gave more accurate results when implemented in the right way.

Page 13: Guitar

In the real world, users look for cheap and accurate devices and this designed device could easily

fit in this area. Moreover, a better signal-to-noise ratio of the signal could have been gotten by

probably using a higher sampling rate or maybe more points. Less noise in the signal might have

given a cleaner FFT spectrum to be compared with the Goertzel algorithm.

The FFT is not only way and best way to design a guitar tuner and this device is a perfect

example. It behaves really well and does the calculations fast enough to make the tuning process

enjoyable and not sluggish at all.

Figure 5: Final product

Page 14: Guitar

Appendix A

LPC1768 Schematic [6]:

Figure 6: LPC1768 ARM7 Schematic

Page 15: Guitar

Figure 7: LM238 operational amplifier

Page 16: Guitar

Appendix B

Simulation results:

Figure 8: AC signal circuit

Page 17: Guitar

Figure 9: Output Signal from AC Circuit

Page 18: Guitar

Appendix C

DFT and FFT algorithm

While the DFT transform above can be applied to any complex valued series, in practice

for large series it can take considerable time to compute, the time taken being proportional to the

square of the number on points in the series. A much faster algorithm has been developed by

Cooley and Tukey around 1965 called the FFT (Fast Fourier Transform). The only requirement

of the the most popular implementation of this algorithm (Radix-2 Cooley-Tukey) is that the

number of points in the series be a power of 2. The computing time for the radix-2 FFT is

proportional to .So for example a transform on 1024 points using the DFT takes about

100 times longer than using the FFT, a significant speed increase. Note that in reality comparing

speeds of various FFT routines is problematic, many of the reported timings have more to do

with specific coding methods and their relationship to the hardware and operating system.

Sample transform pairs and relationships

• The Fourier transform is linear, that is

a f(t) + b g(t) ---> a F(f) + b G(f)

a xk + b yk ---> a Xk + b Yk

• Scaling relationship

f(t / a) ---> a F(a f)

f(a t) ---> F(f / a) / a

• Shifting

f(t + a) ---> F(f) e-j 2 pi a f

• Modulation

f(t) ej 2 pi a t ---> F(t - a)

• Duality

Xk ---> (1/N) xN-k

Page 19: Guitar

Sampling theorem

The sampling theorem (often called "Shannons Sampling Theorem") states that a continuous

signal must be discretely sampled at least twice the frequency of the highest frequency in the

signal.

More precisely, a continuous function f(t) is completely defined by samples every 1/fs (fs is the

sample frequency) if the frequency spectrum F(f) is zero for f > fs/2. fs/2 is called the Nyquist

frequency and places the limit on the minimum sampling frequency when digitising a continuous

sugnal.

Figure 10: Spectrum of a Guitar Frequency

Page 20: Guitar

Appendix D

Goerzel algorithm [7]:

There are various ways to detect the presence of a special known frequency in a monitored signal. A simplistic way is to take an FFT (Fast Fourier Transform) of the signal and check whether the desired frequency is present. That is not very efficient, however, because most of the computed results are ignored. A discrete Fourier transform (DFT) produces the same numerical result for a single frequency of interest, making it a better choice for tone detection. The Goertzel Algorithm is a DFT in disguise, with some numerical tricks to eliminate complex number arithmetic, roughly doubling the efficiency. This note presents the Goertzel Algorithm [1,2], and in particular, ways to improve its ability to isolate frequencies of interest. The Goertzel Algorithm has received a lot of attention recently for mobile telephone applications, but there are certainly many other ways it can be used. This section summarizes very briefly how an ordinary DFT is transformed into Goertzel filter form. If you don't care about these details, you can skip this section.We start from the DFT equation in its natural form.

The frequency of interest is located at term k.

f = k/N fsample

The weighting factors in the DFT equation are complex values obtained by sampling the cosine and sine functions. These complex numbers have the property that indexing them in the reverse direction generates the complex conjugates; the negative powers also produce complex conjugates. Doing both results in the original weighting factors, only with a different way of thinking about how they are generated.

In something of a role reversal, we can treat the powers of W in the DFT summation as the power terms in a polynomial, with the x terms as the multiplier coefficients. An efficient way to evaluate this polynomial is the nested form.

Page 21: Guitar

As the nested form is evaluated term by term, it produces a sequence of intermediate results y:

y-1 = 0 y0 = W-k y-1 + x0 y1 = W-k y0 + x1 ... yN-1 = W-k yN-2 + xN-1 yN = W-k yN-1

The final result yN equals the desired DFT sum. The N evaluation steps can be summarized by iteration

yn = W-k yn-1 + xn n = 0, ... , N-1

While simple, this is still no more efficient than the original DFT.The iteration equation has an equivalent transfer function in terms of discrete domain variable z.

Multiplying top and bottom by the conjugate of the denominator terms, and incorporating the multiply operation that follows the final step of the nested sequence, we get the final Goertzel filter equation.

Let us review the minor miracles.

1. The filtering is split into two simple cascaded parts. 2. The part of the filtering applied directly to the input sequence uses only real numbers. 3. One of the filter multipliers has reduced to a constant value of 1.0, eliminating half of the

multiply operations. 4. The other multiplier does not change. It is not necessary to use a table of pre-computed

coefficients as in an FFT or DFT. 5. The only value of y we really care about is the final one, yN. The second filter stage is a

two-term FIR filter evaluated just once.

Page 22: Guitar

Appendix E Source code: ///////////////////////Electric Guitar Tuner////////////////////// /////////////////Rodrigo Sierra Chavera////////////////////////// #include "mbed.h" #include "adc.h" #include <math.h> #define PI 3.1416 #define fs 20000 InterruptIn selection(p12); Serial pc(USBTX, USBRX); // tx, rx //to communicate with the putty //for debugging Serial device(p13, p14); // tx, rx //LCD serial communication //DebounceIn pb(p12); DigitalOut lowLED(p28); ///leds ports DigitalOut tuneLED(p29); DigitalOut highLED(p30); //Initialize variables int Counter = 0; int string = 0; int DataBuffer[5000]; float low, lowok; float intune, intuneA, intuneB, intuneC, note_freq; float high, highok; char* note_played; void selection_pressed() // push button routine string++; if (string > 5) string = 0; //////////////////////////////////////////////////////////////////// //////////////////////GOERTZEL ALGORITHM//////////////////////////// //////////////////////////////////////////////////////////////////// float Goertzel(int samples[], float freq, int N) float s_prev = 0.0; float s_prev2 = 0.0; float coeff,normalizedfreq,power,s; int i; normalizedfreq = freq / fs; coeff = 2*cos(2*PI*normalizedfreq); for (i=0; i<N; i++) s = samples[i] + coeff * s_prev - s_prev2; ///////////////////// ///Formula: Q0=s=coeff*Q1-Q2+buffer//////////////////////////////

Page 23: Guitar

s_prev2 = s_prev; s_prev = s; power = s_prev2*s_prev2+s_prev*s_prev-coeff*s_prev*s_prev2; ////Optimized Goertzel/////////////////////////////////////////////// ////magnitude2 = Q12 + Q22-Q1*Q2*coeff/////////////////////////////// return power; ////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// ADC adc(fs, 1); //set ADC using .h file void sample_data(int chan, uint32_t value) DataBuffer[Counter] = adc.read(p15); Counter += 1;//////////////number of samples int main() //LCD device.baud(4800); device.putc('/'); //clear LCD //Interupt for Switching Strings selection.mode(PullDown); selection.rise(&selection_pressed); while (1) //when button is pressed, the program goes through this different cases //and it sets frequency values switch (string) case 0: note_freq = 82; note_played= "E2"; break; case 1: note_freq = 110; note_played= "A2"; break; case 2: note_freq = 147; note_played= "D3"; break; case 3: note_freq = 196; note_played= "G3"; break; case 4: note_freq = 247; note_played= "B3"; break;

Page 24: Guitar

case 5: note_freq = 330; note_played= "E4"; break; ////////////////////////////////////ADC setup/////////////////////////////////////////////////////////////// //////////////////to get data from port 15////////////////////////////////////////////////////////////////// //Prepare for burst mode on all ADC pins and set up interrupt handler adc.append(sample_data); adc.startmode(0,0); //set up mbed adc.burst(1); adc.setup(p15,1); //ADC on adc.interrupt_state(p15,1); //interrupt start up wait(.2); //delay so microcontroller can get a total of 4096 samples adc.interrupt_state(p15,0); //Finish the interrupt adc.setup(p15,0); //turn off ADC int actualfs = adc.actual_sample_rate(); //find the actual sampling rate used for calculations /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// high = 0; low = 0; int while1 = 2; while(while1<48) //low note scenario lowok = Goertzel(DataBuffer, (note_freq + while1 ), Counter); if (lowok > low) low=lowok; while1+=2; intuneA = Goertzel(DataBuffer, (note_freq+1), Counter); //Close frequencies from Target Frequency intuneB = Goertzel(DataBuffer, note_freq, Counter); //Close frequencies from Target Frequency intuneC = Goertzel(DataBuffer, (note_freq-1), Counter); //Close frequencies from Target Frequency if ((intuneA > intuneB) && (intuneA > intuneC)) intune = intuneA; else if ((intuneB > intuneA) && (intuneB > intuneC)) intune = intuneB; else intune = intuneC; int while2 = 2; while(while2<48) //high note scenario highok = Goertzel(DataBuffer, (note_freq + while2 ), Counter); if (highok > high) high=highok; while2+=2; ////////////////////////////////////For degubbing purposes///////////////////////////////////////

Page 25: Guitar

////////////////////////////////print low, in tune, high values////////////////////////////////// pc.printf("low = %.2f, in tune = %.2f, high = %.2f", low, intune, high); //////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////// if ((intune > high) && (intune > low)) //comparison of magnitudes to decide whether a string //is high or low or in tune highLED = 0; tuneLED = 1; lowLED = 0; else if (high > intune) highLED = 1; tuneLED = 0; lowLED = 0; else if (low > intune) highLED = 0; tuneLED = 0; lowLED = 1; else highLED = 0; tuneLED = 0; lowLED = 0; // Display on the LCD if (tuneLED) device.printf("Tuning string: %iIn Tune! ", (6-string)); else if (highLED) device.printf("Tuning string: %iToo High ", (6-string)); else if (lowLED) device.printf("Tuning string: %iToo Low ", (6-string)); else device.printf("Try again"); Counter = 0; //reset counter parameter

ADC.c:

#include "mbed.h" #include "adc.h"

Page 26: Guitar

ADC *ADC::instance; ADC::ADC(int sample_rate, int cclk_div) int i, adc_clk_freq, pclk, clock_div, max_div=1; //Work out CCLK adc_clk_freq=CLKS_PER_SAMPLE*sample_rate; int m = (LPC_SC->PLL0CFG & 0xFFFF) + 1; int n = (LPC_SC->PLL0CFG >> 16) + 1; int cclkdiv = LPC_SC->CCLKCFG + 1; int Fcco = (2 * m * XTAL_FREQ) / n; int cclk = Fcco / cclkdiv; //Power up the ADC LPC_SC->PCONP |= (1 << 12); //Set clock at cclk / 1. LPC_SC->PCLKSEL0 &= ~(0x3 << 24); switch (cclk_div) case 1: LPC_SC->PCLKSEL0 |= 0x1 << 24; break; case 2: LPC_SC->PCLKSEL0 |= 0x2 << 24; break; case 4: LPC_SC->PCLKSEL0 |= 0x0 << 24; break; case 8: LPC_SC->PCLKSEL0 |= 0x3 << 24; break; default: LPC_SC->PCLKSEL0 |= 0x1 << 24; break; pclk = cclk / cclk_div; clock_div=pclk / adc_clk_freq; if (clock_div > 0xFF) clock_div=0xFF; if (clock_div == 0) fprintf(stderr, "Warning: Clock division is 0. Re-Setting to 1.\n"); clock_div=1; _adc_clk_freq=pclk / clock_div; if (_adc_clk_freq > MAX_ADC_CLOCK) fprintf(stderr, "Warning: Actual ADC sample rate of %u which is above %u limit\n", _adc_clk_freq / CLKS_PER_SAMPLE, MAX_ADC_CLOCK / CLKS_PER_SAMPLE); while ((pclk / max_div) > MAX_ADC_CLOCK) max_div++; fprintf(stderr, "Maximum recommended sample rate is %u\n", (pclk / max_div) / CLKS_PER_SAMPLE);

Page 27: Guitar

LPC_ADC->ADCR = ((clock_div - 1 ) << 8 ) | //Clkdiv ( 1 << 21 ); //A/D operational //Default no channels enabled LPC_ADC->ADCR &= ~0xFF; //Default NULL global custom isr _adc_g_isr = NULL; //Initialize arrays for (i=7; i>=0; i--) _adc_data[i] = 0; _adc_isr[i] = NULL; //* Attach IRQ instance = this; NVIC_SetVector(ADC_IRQn, (uint32_t)&_adcisr); //Disable global interrupt LPC_ADC->ADINTEN &= ~0x100; ; void ADC::_adcisr(void) instance->adcisr(); void ADC::adcisr(void) uint32_t stat; int chan; // Read status stat = LPC_ADC->ADSTAT; //Scan channels for over-run or done and update array if (stat & 0x0101) _adc_data[0] = LPC_ADC->ADDR0; if (stat & 0x0202) _adc_data[1] = LPC_ADC->ADDR1; if (stat & 0x0404) _adc_data[2] = LPC_ADC->ADDR2; if (stat & 0x0808) _adc_data[3] = LPC_ADC->ADDR3; if (stat & 0x1010) _adc_data[4] = LPC_ADC->ADDR4; if (stat & 0x2020) _adc_data[5] = LPC_ADC->ADDR5; if (stat & 0x4040) _adc_data[6] = LPC_ADC->ADDR6; if (stat & 0x8080) _adc_data[7] = LPC_ADC->ADDR7; // Channel that triggered interrupt chan = (LPC_ADC->ADGDR >> 24) & 0x07; //User defined interrupt handlers if (_adc_isr[chan] != NULL) _adc_isr[chan](_adc_data[chan]); if (_adc_g_isr != NULL) _adc_g_isr(chan, _adc_data[chan]); return;

Page 28: Guitar

int ADC::_pin_to_channel(PinName pin) int chan; switch (pin) case p15://=p0.23 of LPC1768 default: chan=0; break; case p16://=p0.24 of LPC1768 chan=1; break; case p17://=p0.25 of LPC1768 chan=2; break; case p18://=p0.26 of LPC1768 chan=3; break; case p19://=p1.30 of LPC1768 chan=4; break; case p20://=p1.31 of LPC1768 chan=5; break; return(chan); PinName ADC::channel_to_pin(int chan) const PinName pin[8]=p15, p16, p17, p18, p19, p20, p15, p15; if ((chan < 0) || (chan > 5)) fprintf(stderr, "ADC channel %u is outside range available to MBED pins.\n", chan); return(pin[chan & 0x07]); int ADC::channel_to_pin_number(int chan) const int pin[8]=15, 16, 17, 18, 19, 20, 0, 0; if ((chan < 0) || (chan > 5)) fprintf(stderr, "ADC channel %u is outside range available to MBED pins.\n", chan); return(pin[chan & 0x07]); uint32_t ADC::_data_of_pin(PinName pin) //If in burst mode and at least one interrupt enabled then //take all values from _adc_data if (burst() && (LPC_ADC->ADINTEN & 0x3F)) return(_adc_data[_pin_to_channel(pin)]); else //Return current register value or last value from interrupt switch (pin) case p15://=p0.23 of LPC1768 default:

Page 29: Guitar

return(LPC_ADC->ADINTEN & 0x01?_adc_data[0]:LPC_ADC->ADDR0); case p16://=p0.24 of LPC1768 return(LPC_ADC->ADINTEN & 0x02?_adc_data[1]:LPC_ADC->ADDR1); case p17://=p0.25 of LPC1768 return(LPC_ADC->ADINTEN & 0x04?_adc_data[2]:LPC_ADC->ADDR2); case p18://=p0.26 of LPC1768: return(LPC_ADC->ADINTEN & 0x08?_adc_data[3]:LPC_ADC->ADDR3); case p19://=p1.30 of LPC1768 return(LPC_ADC->ADINTEN & 0x10?_adc_data[4]:LPC_ADC->ADDR4); case p20://=p1.31 of LPC1768 return(LPC_ADC->ADINTEN & 0x20?_adc_data[5]:LPC_ADC->ADDR5); //Enable or disable an ADC pin void ADC::setup(PinName pin, int state) int chan; chan=_pin_to_channel(pin); if ((state & 1) == 1) switch(pin) case p15://=p0.23 of LPC1768 default: LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 14); LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 14; LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 14); LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 14; break; case p16://=p0.24 of LPC1768 LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 16); LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 16; LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 16); LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 16; break; case p17://=p0.25 of LPC1768 LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 18); LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 18; LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 18); LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 18; break; case p18://=p0.26 of LPC1768: LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 20); LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 20; LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 20); LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 20; break; case p19://=p1.30 of LPC1768 LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 28); LPC_PINCON->PINSEL3 |= (unsigned int)0x3 << 28; LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 28); LPC_PINCON->PINMODE3 |= (unsigned int)0x2 << 28; break; case p20://=p1.31 of LPC1768 LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 30); LPC_PINCON->PINSEL3 |= (unsigned int)0x3 << 30; LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 30);

Page 30: Guitar

LPC_PINCON->PINMODE3 |= (unsigned int)0x2 << 30; break; //Only one channel can be selected at a time if not in burst mode if (!burst()) LPC_ADC->ADCR &= ~0xFF; //Select channel LPC_ADC->ADCR |= (1 << chan); else switch(pin) case p15://=p0.23 of LPC1768 default: LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 14); LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 14); break; case p16://=p0.24 of LPC1768 LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 16); LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 16); break; case p17://=p0.25 of LPC1768 LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 18); LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 18); break; case p18://=p0.26 of LPC1768: LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 20); LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 20); break; case p19://=p1.30 of LPC1768 LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 28); LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 28); break; case p20://=p1.31 of LPC1768 LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 30); LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 30); break; LPC_ADC->ADCR &= ~(1 << chan); //Return channel enabled/disabled state int ADC::setup(PinName pin) int chan; chan = _pin_to_channel(pin); return((LPC_ADC->ADCR & (1 << chan)) >> chan); //Select channel already setup void ADC::select(PinName pin) int chan; //Only one channel can be selected at a time if not in burst mode if (!burst()) LPC_ADC->ADCR &= ~0xFF; //Select channel chan = _pin_to_channel(pin); LPC_ADC->ADCR |= (1 << chan);

Page 31: Guitar

//Enable or disable burst mode void ADC::burst(int state) if ((state & 1) == 1) if (startmode(0) != 0) fprintf(stderr, "Warning. startmode is %u. Must be 0 for burst mode.\n", startmode(0)); LPC_ADC->ADCR |= (1 << 16); else LPC_ADC->ADCR &= ~(1 << 16); //Return burst mode state int ADC::burst(void) return((LPC_ADC->ADCR & (1 << 16)) >> 16); //Set startmode and edge void ADC::startmode(int mode, int edge) int lpc_adc_temp; //Reset start mode and edge bit, lpc_adc_temp = LPC_ADC->ADCR & ~(0x0F << 24); //Write with new values lpc_adc_temp |= ((mode & 7) << 24) | ((edge & 1) << 27); LPC_ADC->ADCR = lpc_adc_temp; //Return startmode state according to mode_edge=0: mode and mode_edge=1: edge int ADC::startmode(int mode_edge) switch (mode_edge) case 0: default: return((LPC_ADC->ADCR >> 24) & 0x07); case 1: return((LPC_ADC->ADCR >> 27) & 0x01); //Start ADC conversion void ADC::start(void) startmode(1,0); //Set interrupt enable/disable for pin to state void ADC::interrupt_state(PinName pin, int state) int chan; chan = _pin_to_channel(pin); if (state == 1) LPC_ADC->ADINTEN &= ~0x100; LPC_ADC->ADINTEN |= 1 << chan; /* Enable the ADC Interrupt */ NVIC_EnableIRQ(ADC_IRQn); else

Page 32: Guitar

LPC_ADC->ADINTEN &= ~( 1 << chan ); //Disable interrrupt if no active pins left if ((LPC_ADC->ADINTEN & 0xFF) == 0) NVIC_DisableIRQ(ADC_IRQn); //Return enable/disable state of interrupt for pin int ADC::interrupt_state(PinName pin) int chan; chan = _pin_to_channel(pin); return((LPC_ADC->ADINTEN >> chan) & 0x01); //Attach custom interrupt handler replacing default void ADC::attach(void(*fptr)(void)) //* Attach IRQ NVIC_SetVector(ADC_IRQn, (uint32_t)fptr); //Restore default interrupt handler void ADC::detach(void) //* Attach IRQ instance = this; NVIC_SetVector(ADC_IRQn, (uint32_t)&_adcisr); //Append interrupt handler for pin to function isr //////////////////////////////////////////////////////////////// void ADC::append(PinName pin, void(*fptr)(uint32_t value)) int chan; chan = _pin_to_channel(pin); _adc_isr[chan] = fptr; //Append interrupt handler for pin to function isr void ADC::unappend(PinName pin) int chan; chan = _pin_to_channel(pin); _adc_isr[chan] = NULL; //Unappend global interrupt handler to function isr void ADC::append(void(*fptr)(int chan, uint32_t value)) _adc_g_isr = fptr; //Detach global interrupt handler to function isr void ADC::unappend() _adc_g_isr = NULL;

Page 33: Guitar

//Set ADC offset void offset(int offset) LPC_ADC->ADTRM &= ~(0x07 << 4); LPC_ADC->ADTRM |= (offset & 0x07) << 4; //Return current ADC offset int offset(void) return((LPC_ADC->ADTRM >> 4) & 0x07); //Return value of ADC on pin int ADC::read(PinName pin) //Reset DONE and OVERRUN flags of interrupt handled ADC data _adc_data[_pin_to_channel(pin)] &= ~(((uint32_t)0x01 << 31) | ((uint32_t)0x01 << 30)); //Return value return((_data_of_pin(pin) >> 4) & 0xFFF); //Return DONE flag of ADC on pin int ADC::done(PinName pin) return((_data_of_pin(pin) >> 31) & 0x01); //Return OVERRUN flag of ADC on pin int ADC::overrun(PinName pin) return((_data_of_pin(pin) >> 30) & 0x01); int ADC::actual_adc_clock(void) return(_adc_clk_freq); int ADC::actual_sample_rate(void) return(_adc_clk_freq / CLKS_PER_SAMPLE);

Page 34: Guitar

References

[1] T. Yahaya, Abdullah. "Music Scales." Music Scales. Web. 10 Mar. 2012. Available: http://www.angelfire.com/in2/yala/4scales.htm.

[2] "First Music Lesson - Frequency, Octave, Semitone, Sound Names, Major Scale - Apronus.com." Apronus.com. TargetSoft. Web. 11 Mar. 2012. Available: http://www.apronus.com/music/lessons/unit01.htm.

[3] Wagner, David. "Electric Guitar Pickups 101." Gear-Vault. Web. 09 Mar. 2012. Available: http://gear-vault.com/electric-guitar-pickups-101/.

[4] NXP. LPC1768. mbed. Web. Available: http://mbed.org/media/uploads/chris/mbed-005.1.pdf

[5] IEEE. "ARM7TDMI Power Consumption." ARM7TDMI Power Consumption. Aug. 1997. Web. 14 Mar. 2012. Available: http://ieeexplore.ieee.org/xpl/freeabs_all.jsp?arnumber=612178&abstractAccess=no&userType=inst.

[6] mbed website. NXP LCP1768.10 Mar. 2012. Avialable: http://mbed.org/

[7] "Detecting A Single Frequency Efficiently." Frequency Detection. Web. 12 Apr. 2012. <http://www.mstarlabs.com/dsp/goertzel/goertzel.html>.