Marc-Julien objois, Catherine Single, Charlena Fong, and Mariya Shterngartz
Playing music from a digital circuit is easy. Especially if you only use square waves. In this form, a digital output can simply be amplified as-is. However, even this requires some musical background.
All musical notes in Western notation correspond to certain base frequencies. "Middle A" is the standard by which most instruments are tuned. It corresponds to a frequency of 440 Hz. Consecutive "A" notes above middle A correspond to multiples of 440 Hz. For instance, the next A up is 880 Hz. Similarly, halving a frequency gives the frequency corresponding to an octave below. The A below middle A has a base frequency of 220 Hz. Middle A is usually referred to as A 2. The range of notes is divided numbered octaves, separated at each C.
This is a logarithmic scale. In Western music, the frequencies between octaves are separated
into twelve semi-tones. To find the frequency of any given note, use the
following formula, where n
is the "distance" from middle A.
So for instance, A#2 is +1 away from middle A, whereas C 2 (C of same octave as middle A) is -9.
Provided is a chart of frequencies for one octave above and one below middle A.
To output a note of a given frequency (using square waves), one must simply
use a clock divider to create a clock signal with the desired period. Let us
take the example of middle A (440 Hz).
Given a master clock of 25MHz, 25,000,000 Hz / 440 Hz
gives us
approximately 56818
. Thus, every 56818 clock cycles must contain
a complete period. To do this, halve the number (in this case, to 28409) and
count up to this number repeatedly, looping back to 0 every time. Every time
the code loops back to 0, invert the current logic value of the note output clock.
This will generate a note of the desired frequency. This can be applied to
any note.
Here is a sample block of VHDL code that outputs a square wave at 440 Hz.
signal middle_a : std_logic := '1'; process (clk) -- Going at 25 MHz variable count : natural := 0; begin if rising_edge(clk) then if count = 28409 then count := 0; middle_a <= not middle_a; else count := count + 1; end if; end if; end process;