Skip to content

Latest commit

 

History

History
executable file
·
148 lines (102 loc) · 7.43 KB

content.asc

File metadata and controls

executable file
·
148 lines (102 loc) · 7.43 KB

Generating analogue signals

One of the nice features of FPGAs is how flexible the I/O pins are. In this chapter we will make a standard I/O pin generate an analogue signal, playing a tone using a waveform that is stored in block RAM.

This module is largely based on Xilinx’s AppNote xapp154.pdf.

One bit (Delta Sigma) DAC

You are most probably familiar with Pulse Width Modulation (PWM), when a signal of a constant frequency has its duty cycle modulated to generate different power levels. If a PWM signal is passed through a low pass filter you end up with an analogue voltage that is proportional to the duty cycle. PWM is used in power supplies, light dimmers and motor controllers and such.

Delta Sigma modulation is a lot like that, but without the constant frequency of PWM. It has an output that 'hunts' for the desired output value. A one bit DAC has only two output values (1 or 0), and it generates the value which when included in a running average brings it closest to the desired level:

  • To generate a level of 0.5 the output will be "10101010101…​"

  • To generate 0.25 the output will be "000100010001…​"

  • To generate 0.66 the output will be "110110110110110…​"

All of these signals average out to the desired value but have different frequencies.

Um, that looks really hard to do

It’s not that hard at all. For this example, work in decimal to make it clearer, but implementation in binary is just the same.

To make a Delta Sigma DAC with 100 output levels you need an accumulator with two decimal digits, and you use the "carry to the hundreds" as the output. Just keep adding the desired output level to the two digits and the "carry to the hundreds" will be a stream of ones and zeros that averages to the desired level.

Here’s a two decimal digit DAC generating the output of 33:

Iteration

Digits

Carry/Output

0

50

0

1

83

0

2

16

1

3

49

0

4

82

0

5

15

1

6

48

0

7

81

0

Pretty simple!

Of course there are a few little tricks:

  • Do it quick enough so that at the highest required frequency you have enough \'1’s and \'0’s to average over

  • Careful design of an analogue output filter is required for best performance

  • Do not use all the DAC’s range, as the spectrum of noise at either end is problematic

Rough back-of-the-envelope bandwidth and effective resolution calculation

If you need to produce signals at 22kHz, you have to use at least a 44kHz playback frequency. If the one-bit DAC runs at 25MHz there is a just over five hundred output values (ones and zeros) per 1/44000th of a second at best you have nine-bit resolution at that frequency.

Doing it in VHDL

Here is the code for an 8 bit DAC. It is pretty much a "count by 'n'" counter:

 library IEEE;
 use IEEE.STD_LOGIC_1164.ALL;
 use IEEE.STD_LOGIC_ARITH.ALL;
 use IEEE.STD_LOGIC_UNSIGNED.ALL;

 entity dac8 is
    Port ( Clk : in  STD_LOGIC;
           Data : in  STD_LOGIC_VECTOR (7 downto 0);
           PulseStream : out  STD_LOGIC);
 end dac8;

 architecture Behavioral of dac8 is
    signal sum : STD_LOGIC_VECTOR (8 downto 0);
 begin
    PulseStream <= sum(8);

   process (clk, sum)
   begin
      if rising_edge(Clk) then
         sum <= ("0" & sum(7 downto 0)) + ("0" &data);
      end if;
   end process;
end Behavioral;

Connecting up the headphones

On the Papilio One, just plug amplified speakers into the jack and use the following constraint:

Note
Constraint for the Papilo One
NET "Audio" LOC = "P41";

Connecting headphones to the Basys2

Unlike the Papilio One + LogicStart MegaWing combo the Basys2 does not have an audio output, so we need to use a PMOD port. The PMODs on the Basys2 board have four signal wires from the FPGA, a ground and a 3.3V power connection. For the JA header on the Basys2 board the constraints are:

Note
Contraints for the Basys2
NET "JA<0>" LOC = "B2";
NET "JA<1>" LOC = "A3";
NET "JA<2>" LOC = "J3";
NET "JA<3>" LOC = "B5";
Caution
Make sure that you don’t short the power pins. Shorting out ground and power will upset your USB port and/or your FPGA board

For this project connect a set of stereo earphones between pin 0 and pin 1 and the ground. To do this I used a header strip, 3.5mm jack and a length of wire:

m12s1

If you pull the unused pins out of the header strip you might just be able to hold the 3.5mm jack in place at the correct time…​

The inductive nature of the headphones/earphones proves to be a pretty good low pass filter for the high frequency signals so no additional components are needed - but if you want to you can include a suitable capacitor in series to prevent average DC voltage running through them.

The Basys2 board has a 200 ohm resistor in series with the FPGA output pin. This makes the PMOD connectors somewhat protected against ESD, overvoltage and shorts. For this project it also acts as a voltage divider reducing the DC bias and the peak to peak voltages that go through the headphones/earphones.

Project - Wave file generation

In the prior project we hooked a block RAM to the LEDs, and used it to flash them. We can do the same to generate an audio waveform.

  • Make a COE file containing the samples for a sine wave (something like "f(n) = int((sine(n*PI()/1024)+1)*100)+128" will give you values between 28 and 228 that you can use).

  • Load it into the flashylights project and check that the lights look OK.

  • To generate an audible tone we need to cycle through this somewhere around 400 times per second - so we need to use counter(15 downto 6) to address the ROM component. This should generate a tone of one cycle every 65536 clocks = 381.4Hz

  • Add an 8 bit DAC to your project and connect it to the audio output. Remember to add the appropriate constraints to your project!

  • Build and download the design. If you connect your headphones you should have a tone!

Challenges

  • At the moment we can only generate one frequency. Design and try out ways to make different frequencies.

  • The Spartan 3E-250 has 24K of on-chip memory. That’s enough for 2 seconds of telephone quality 11kHz/8 bit audio…​.

  • If you connect the two high address bits on RAM to switches you can have four different waveforms, each with 256 samples per cycle, possibly allow you to generate Square, Saw, Ramp and Sine waves from one project.

  • By right-shifting the samples you can control the volume - and with a 'wider' DAC you can keep the least significant bits. Remember to 'sign-extend' the sample when you shift it (e.g. y(8 downto 0) = x(7) & x(7 downto 0)).

  • The design is quite lo-fi - very 8 bit! You could extend the DAC to 16 bits, and of course changing the ROM to have a data width of 16 (you will also need a new '.coe' file with samples expanded out to match the range of the 16 bit values).