User Tools

Site Tools


projects:fpga_note_generator

This is an old revision of the document!


What I Did

I turned my FPGA into an instrument capable of playing eight notes. The eight switches on the FPGA are set to correspond to a note in an A natural minor scale. When a switch is on, the FPGA outputs an 8-bit quantized sine wave corresponding to that note. The sine wave is then converted into an analog signal that drives a speaker.

Why I Did It

I wanted to do a project that would introduce me to working with an FPGA. Making a note generator sounded very interesting to me and seemed well scoped for one person with no prior FPGA experience. I also like that there were many options for improvement beyond the minimum deliverable.

How I Did It

Overview

In order to generate a sine wave and convert it to an analog signal, I used a direct digital synthesizer. The DDS I implemented has four major components - a frequency reference (the clock), a phase accumulator, a sine lookup table, and a digital-to-analog converter.

Frequency Reference

The clock is the frequency reference that provides the timing for everything that the FPGA does. I chose somewhat arbitrarily to use a 50 MHz clock signal. The clock signal is generated by a silicon oscillator on the FPGA board.

Phase Accumulator

The phase accumulator takes in the phase increment value and increments the phase angle by that much every cycle. For example, if the phase angle is n-bits and the phase increment value is 1, then it will increase the phase angle by 1*360o/2n degrees every cycle. This means that the frequency of the outputted sine wave is determined by phase increment*fclk/2n and the frequency resolution is determined by fclk/2n.

A phase accumulator is included in the drop-in Sine LUT module provided by Xilinx, so I chose to use that rather than implementing my own.

Sine Lookup Table

The sine LUT is simply a lookup table that stores uniformly spaced samples of a sine wave. It takes in the phase angle from the phase accumulator as the address and outputs the sine.

In order to shorten the table and make it more space efficient, quarter and half wave symmetry can be exploited. This works because sin(a) = sin(pi-a) and sin(-a) = -sin(a). The two most significant bits of the angle are used for quadrant mapping, so it is not necessary to store values for all four quadrants in the LUT.

I used the sine lookup table provided by Xilinx in the CORE generator system. This automatically utilizes half or quarter wave symmetry based on what it determines is most appropriate.

DAC

I ended up using an R-2R resistor ladder as the DAC for my system. Using an R-2R resistor ladder is a simple way to convert from digital to analog. It works by weighting the contribution of each bit to the final output voltage. The most significant bit contributes the most to the output voltage and the least significant bit contributes the least. An op amp buffer was placed between the resistor ladder and the speaker to prevent the speaker from acting as another resistor and distorting the output of the DAC. Although an R-2R ladder is not as accurate as other methods of digital to analog conversion, it was good enough for my purposes.

Wikipedia

How Can It Be Built Upon?

Code, Schematics and Build Instructions

Code

Switches were set to play a specific note by hard coding the phase increment value that would be provided to the phase accumulator when that switch is set high.

always @(posedge clk)
begin
    if (switch[0] == 1)            //If 0th switch is high, generate A.
    begin
        p_inc = 26'b1001001111;    //A (440 Hz)
    end
    else if (switch[1] == 1)       //If 1st switch is high, generate B.
    begin
        p_inc = 26'b1010010111;    //B
    end
    else if (switch[2] == 1)       //If 1st switch is high, generate C.
    begin
        p_inc = 26'b1010111110;    //C
    end
    else if (switch[3] == 1)       //If 1st switch is high, generate D.
    begin
        p_inc = 26'b1100010100;    //D
    end
    else if (switch[4] == 1)       //If 1st switch is high, generate E.
    begin
        p_inc = 26'b1101110100;    //E
    end
    else if (switch[5] == 1)        //If 1st switch is high, generate F.
    begin
         p_inc = 26'b1110101001;    //F
    end
    else if (switch[6] == 1)        //If 1st switch is high, generate G.
    begin
        p_inc = 26'b10000011100;    //G
    end
    else if (switch[7] == 1)        //If 1st switch is high, generate A.
    begin
        p_inc = 26'b10010011101;    //A
    end
    else
    begin
        p_inc = 26'b0;
    end
end

All of the code used in the project can be downloaded below.

Schematics

Build Instructions

Gotchas

Work Plan Reflection

To Do

projects/fpga_note_generator.1387744329.txt.gz · Last modified: 2013/12/22 15:32 by criley1