Skip to main content
Library homepage
 
Loading table of contents menu...
Engineering LibreTexts

14.2: Parte Uno – A Simple Sine Generator

  • Page ID
    25635
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    Here is a short Python program that will create a sine table for us. Each data point is an unsigned char so they range from a minimum of 0 to a maximum of 255 with an effective DC offset of 128. This will work perfectly with a simple unipolar 8 bit DAC.

    # Python 3.3 wavetable creator for C language data array using unsigned bytes
    # 256 entry wave table, amplitude range is 0-255
    # Note: Rename array as needed
    
    import math
    
    fn = input("Enter name of the wavetable file to be created: ")
    fil = open( fn, "w+" )
    fil.write( "unsigned char sinetable[256]={" )
    
    for x in range( 256 ):
    
        # each data point is 1/256th of a cycle, peak value is 127
        f = 127.0 * math.sin( 6.28319 * x / 256.0 )
    
        # turn into an integer and add DC offset
        v = int( round( f, 0 ) ) + 128
    
        # guarantee no out-of-bounds values just in case
        if v > 255:
            v = 255
        if v < 0:
            v = 0
    
        if x < 255:
            fil.write( str(v) + ", " )
        else:
            fil.write( str(v) + "};" )
    
    fil.close()
    

    To generate the sine table we simply run this program, enter an appropriate file name when prompted, and literally wait a second for it to process. We then open the resulting file with a text editor and paste the contents into the Arduino editor. We can use this program as a template to generate other wave tables as needed.

    Now that we have the wave data, what sort of circuitry will we use for the DAC? Obviously, we can use an off-the-shelf 8 bit DAC but we can also achieve workable results by using a simple R-2R ladder network as shown in Figure \(\PageIndex{1}\). R might be around 10 k\(\Omega\) thus making 2R 20 k\(\Omega\). While resistor tolerance and port output voltage fluctuations will add some non-linearity, the results for 8 bit data are good enough for now.

    Choose an appropriate value for R and build the ladder network. Wire the bits to port D of the Arduino Uno. Port D corresponds to pins 0 (LSB) through 7 (MSB). The Analog Output connection should be monitored with an oscilloscope.

    clipboard_e9498bd8cb5c18567e024694c6699e53b.png

    Figure \(\PageIndex{1}\): R-2R Ladder

    The code to generate the sine wave is fairly straightforward, in fact it is actually quite sparse. Most of the space is taken up by the data array. Here is the first version:

    unsigned char sinetable[256]={128, 131, 134, 137, 140, 144, 147, 150, 153, 
    156, 159, 162, 165, 168, 171, 174, 177, 179, 182, 185, 188, 191, 193, 196, 
    199, 201, 204, 206, 209, 211, 213, 216, 218, 220, 222, 224, 226, 228, 230, 
    232, 234, 235, 237, 239, 240, 241, 243, 244, 245, 246, 248, 249, 250, 250, 
    251, 252, 253, 253, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 254, 
    254, 254, 253, 253, 252, 251, 250, 250, 249, 248, 246, 245, 244, 243, 241, 
    240, 239, 237, 235, 234, 232, 230, 228, 226, 224, 222, 220, 218, 216, 213, 
    211, 209, 206, 204, 201, 199, 196, 193, 191, 188, 185, 182, 179, 177, 174, 
    171, 168, 165, 162, 159, 156, 153, 150, 147, 144, 140, 137, 134, 131, 128, 
    125, 122, 119, 116, 112, 109, 106, 103, 100, 97, 94, 91, 88, 85, 82, 79, 77, 
    74, 71, 68, 65, 63, 60, 57, 55, 52, 50, 47, 45, 43, 40, 38, 36, 34, 32, 30, 
    28, 26, 24, 22, 21, 19, 17, 16, 15, 13, 12, 11, 10, 8, 7, 6, 6, 5, 4, 3, 3, 
    2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 5, 6, 6, 7, 8, 10, 11, 12, 
    13, 15, 16, 17, 19, 21, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 43, 45, 47, 
    50, 52, 55, 57, 60, 63, 65, 68, 71, 74, 77, 79, 82, 85, 88, 91, 94, 97, 100, 
    103, 106, 109, 112, 116, 119, 122, 125};
    
    void setup()
    {
          // set all of port D for output
          DDRD = 0xff;
    }
    void loop()
    {
          unsigned char u=0;
          unsigned char *p;
    
          p = sinetable;
    
          // loop forever
          while( 1 )
          {
                // set data and wait one sample period
                PORTD = p[u++];
                delayMicroseconds(100);
          }
    }
    

    The code first sets all of port D to output mode. The loop() function consists of a while() loop that never terminates. Interestingly, loop() doesn’t loop at all, it is only entered once. The contents of the loop() function could just as easily be placed in the setup() function but they are placed here simply as a means of visually separating the initialization code from the looping code.

    A pointer is used to identify the start of the wave table. Strictly speaking this is not necessary here as we could just as well use sinetable[]. The reason for this will become apparent in a moment. The first value of the wave table array is copied to port D. The ladder network transforms the eight 0 volt/5 volt combinations into a single voltage. Notice the efficiency of the single write to the port when compared to the bit oriented function digitalWrite() examined earlier. Trying to write a single bit at a time would create a complete mess as the various bits would not all change together but would create seven wrong values leading up to the correct value once all eight bits had been written. In any case, after the value is written, the program pauses for 100 microseconds. This is the sample period. After the pause, the next array value is copied to the port and the process repeats. Ignoring the looping and accessing/writing overhead, a single pass of the table requires 256 entries times 100 microseconds each, or 25.6 milliseconds. This is an output frequency of approximately 39 Hz.

    Enter the code, compile and transfer it the board. Hook up an oscilloscope to the Analog Output connection. You should see a fairly nice 39 Hz sine wave of about 5 volts peak to peak riding on a 2.5 volts DC offset. Zoom in on the time base to inspect the wave. The individual stair steps of the R-2R ladder should be apparent. Also, the timing of 100 microseconds per step should be obvious.

    We can increase or decrease the frequency by adjusting the value fed to the delayMicroseconds() function. Try a higher value and see what you get. 10 microseconds should yield a frequency of about 390 Hz. Using this frequency, measure the residual THD of the waveform. With typical 5% resistors the total harmonic distortion will likely be less than 1%. A portion of this is due to the variance of the resistors as well as the port pin output voltages not all being identical.

    A different wave shape can be generated and added to the program. The only change to the body of the program would be to change what the pointer p is referencing. For example, you could copy and paste the sine table, change a few of the values by extreme amounts so they’re obvious to the eye, and then change the name to something like wackysinetable[]. Finally, change the pointer reference to p=wackysinetable; Try this. If available, you might consider connecting the output to a small amplifier and headphones and listen to the newly altered wave. Why, it’s hours of fun for the making! Call your spouse/bf/gf/so, get the kids or siblings, grab the neighbors and unleash the dog; they’ll all agree that it’s way better than watching another inane game show, “reality” series or TV evangelist.


    This page titled 14.2: Parte Uno – A Simple Sine Generator is shared under a CC BY-NC-SA 4.0 license and was authored, remixed, and/or curated by James M. Fiore via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.