Skip to main content
Engineering LibreTexts

14.3: Parte Due – Changing Wave Shape

  • Page ID
    25636
  • \( \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}}\)

    While it can be fun to change the delay, edit the wave tables by hand and then listen to the results, what would be more useful would be to control both the frequency and wave shape with some external controls. This is more in keeping with a standard lab function generator.

    How might we offer the user control over the wave shape? Many lab function generators use a set of interdependent push-buttons where only one button can be depressed at a time. This offers nice visual and tactile feedback. A simpler and less expensive technique utilizes a single button that cycles through the choices, flipping back to the first choice once the last one is reached. This is particularly effective if the number of choices is fairly small. We could couple this to a series of LEDs to indicate the selected item. Behind the scenes a single variable will hold the “state” of the selection and will be updated as the switch as activated. This switch should be debounced in hardware as a software debounce will require too much time. We can check the state of this switch at the start of each cycle. This has two potential advantages over checking it after each value is written to the output port. The first advantage is less processing. The second advantage is that if we set up the tables correctly, we can guarantee that wave shape switches will always occur at a positive slope zero volt crossing thus avoiding potentially dangerous transients.

    Let’s use three wave shapes: sine, square and ramp. We’re going to need three LEDs to indicate the shape so that’s three output pins. We’ll also need an input pin for the shape switch. Although our wave shape variable could just use the numbers 1, 2 and 3 to represent these three waveforms, with a little forethought we can save ourselves some work. Why not just use the corresponding LED port bit value to represent the wave choice? Doing so means there will be no translation needed between the wave shape choice and which LED to light. Let’s use port B.1 (port B bit 1) for the input switch and B.2:4 for the sine, square and ramp LEDs, respectively. We could use the following:

    // all port B
    #define WAVEBIT         0x02
    #define SINEWAVE        0x04
    #define SQUAREWAVE      0x08
    #define RAMPWAVE        0x10
    

    We then need to modify the setup() function with the following:

    // set B.2:4 for output
    DDRB |= (SINEWAVE | SQUAREWAVE | RAMPWAVE);
    
    // set B.1 for input, enable pull-up
    DDRB &= (~WAVEBIT);
    PORTB |= WAVEBIT;
    

    In the loop() function we will need several new variables:

    unsigned char wavechoice=SINEWAVE;
    unsigned char waveswitch=WAVEBIT, currentwaveswitch=WAVEBIT;
    

    wavechoice is the variable that holds the user’s current choice of wave shape. It is initialized to sine. It is important to remember that we’ll only change the wave shape on a push-button transition, not on it being high or low per se. Thus, we need to know the switch’s prior state as well as its current state. The two waveswitch variables are used to hold these two states, pressed or released. Instead of setting these to 1/0 (true/false), we use the actual bit value to represent true. This will make coding a little more transparent. So, before entering the while() loop we need to set the wave table pointer to the current default:

    // select default sine and light corresponding waveshape LED
    PORTB |= wavechoice;
    p = sinetable;
    

    Because the wavechoice variable uses the port bit values for the LEDs, we can just copy the value to the output port to light it (remember, by default on reset all other bits are zero); nice and simple.

    Now let’s take a look at the switch processing. First we need to determine the current state:

    currentwaveswitch = PINB & WAVEBIT;
    

    We will only process the switch on a depressed state (i.e., a high to low transition). Note that if you’re using a hardware debounce circuit that inverts, this logic will also be inverted (remove the “not”).

    if( !currentwaveswitch ) // selected (down)
    {
          if( currentwaveswitch != waveswitch ) // transition
          {
    

    We then check the current state of the wavechoice variable and cycle through the available values, updating the generic wave table pointer as well:

                if( wavechoice == SINEWAVE )
                {
                      wavechoice = SQUAREWAVE;
                      p = squaretable;
                }
                else
                {
                      if( wavechoice == SQUAREWAVE )
                      {
                            wavechoice = RAMPWAVE;
                            p = ramptable;
                      }
                      else
                      {
                            wavechoice = SINEWAVE;
                            p = sinetable;
                      }
                }
    

    We turn off all of the LEDs and activate the newly chosen item:

                //turn off all LEDs
                PORTB &= (~(SINEWAVE| SQUAREWAVE| RAMPWAVE));
    
                // turn on newly selected LED
                PORTB |= wavechoice;
          }
    }
    

    Lastly, we update the waveswitch variable so our transition detection logic properly works:

    waveswitch = currentwaveswitch;
    

    The only thing left is to simply place an if() clause around this chunk of code so that we only check the switch at the start of the wave table. This occurs when the index variable u is zero:

    if( !u )
    {
          // wave shape switch code goes here
    }
    

    Create two new wave tables for the square and ramp, and add the code above. Also wire in the three LEDs and wave shape switch. Compile, transfer and test the operation using an oscilloscope. The Python program presented earlier can be used to create the new arrays. The square wave consists of 128 entries of 255 followed by 128 entries of 0. The ramp wave consists of 256 entries starting at 0 and incrementing by 1 each time to end at the value 255.


    This page titled 14.3: Parte Due – Changing Wave Shape 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.