Lecture: 29
Objective: To provide operational understanding of how to adjust DDS parameters to generate waveforms with varying frequencies given a table size and update rates.

Direct Digital Synthesis (DDS) algorithm

The last lecture presented a schematic descrption of the DDS architecture using digital hardware. The DDS hardware uses a phase accumulator that counts up by the phase increment at the update rate. The integer portion of the phase accumulator is used to as the address to a ROM containing one period of waveform you are trying to reproduce. The output of the ROM is sent to a digital to analog converter (DAC) to produce the analog waveform. In our case, the ADC consisted of the PWM subsystem of the PIC coupled to a low pass filter (LPF), that converted a D% duty cycle waveform from the PWM subsystem into a 3.3V * D% analog output.

Fine frequency control over the output waveform is made possible by having fractional values for the phase accumulator and phase increment. While only the integer portion of the phase accumulator is used as the address to the ROM, the fractional portion of the phase accumulator allows fractional portions of the phase increment to accumulate and eventually manifest themselves in the integer portion.

To make this discussion concrete, let's examine the DDS system implemented in the following algorithm. In this example, we are trying to reproduce a sine wave using a 16 element ROM. This ROM will require 4-bits of address to access all 16 elements. For this example, we will use a 4.12 format number for both the phase increment and phase accumulator. We need the 4 integer bits of the fixed-point number because we need to access all 16 elements in the ROM. I had a choice of 4 fractional bits (to form an 8-bit variable), or 12-bits (to form a 16-bit variable). I choose 12-bits because I wanted very precise frequency control (more on this later).

In order to access the array, we have to remove the fractional bits from the phase accumulator. This is accomplished in the algorithm by shifting the phase accumulator right by 12-bits and storing the resulting value in a temporary register.
    int8 sin[16] = {127,176,217,245,254,245,217,176,127,78,37,10,0,1,37,78};

    // Since there are 16 entries in ROM, we will use 4.12 format
    int16 phaseAccumulator, phaseIncrement, index;

    phaseAccumulator = 0;
    phaseIncrement = <value>

    for(;;) {
	delayTenMicroSeconds();
	phaseAccumulator = phaseAccumulator + phaseIncrement;
	index = phaseAccumulator >> 12;
	PWM4_LoadDutyValue(sin[index]);		// Assume an 8-bit duty cycle
    }
The code shown in the algorithm is a mix of real-code and pseudo-code. The call to "delayTenMicroSeconds();" will be replaced with an interrupt call in future algorithms (the infinite loop will also be removed). The value of phaseIncrement is set by some actor who controls the frequency of the output waveform - usually main. Remember that the duty cycle of a PWM waveorm is set by the 8-bit value in the CCPx register (from a previous lecture) or by using the built-in functions provided by MCC.

To better understand what is happening with the code, try to complete the following tables which describe the values of the phaseAccumulator through time and the values of the ROM it selects.
phaseIncrement = 0x0D40
Time (us)phaseAccumulator index CCPx
10 0x0D40 0x00  
20      
30      
40      
50   0x04  
60 0x4F80    
70      
80     217
To see the solution, right-mouse click and select "view page source".

DDS calculations

Let's examine the phase increment in our algorithm above and its relationship to the frequencies produced by the system. Note, in the algorithm that the update rate is set at 10us and the ROM has 16 entries.

Question:
What phase increment will produce an output waveform with 440 Hz? Represent the phase increment in decimal and in 4.12 format.

Answer:
 1 update   10^6 us   X phase inc      1 wave    
 -------- * ------- * ----------- * -------------  = 440 Hz
   10us       1s       1 update      16 phase inc  
X = 0.0704
In 4.12 format, 0.0704*212 = 288.4 which rounds to 288.
288 in binary is 0000 0001 0010 0000, shifting the decimal point left by 12 bits yields 0000.000100100000

Question:
What output waveform frequency will a phase increment of 0.1234 produce?

Answer:
 1 update   10^6 us   0.1234 phase inc     1 wave    
 -------- * ------- * ---------------- * ------------  = 771.25 Hz
   10us       1s           1 update      16 phase inc  

Frequency resolution

Now let's examine some performance metrics associated with a DDS system. One of the most important is the frequency resolution; the smallest change possible with a DDS setup. In order to answer this question we need to introduce the concept of consectutive representations for fixed-point numbers. Two fixed-point numbers will be said to have consectutive representations if one of them can be derived by adding binary 0000001 to the other. For example, in a 4.4 format numbers 0010 1101 and 0010 1100 are consectutive representations (since you can add binary 00000001 to the second number and arrive at the first number) while 0010 1101 and 0010 1110 are not consectutive representations.

Question:
Given a 4.12 format number what is the change in value between consectutive representations in 4.12 format and in decimal?

Answer:
Given two consectutive representations of 4.12 numbers, the difference between them will always be 0000.000000000001 or 2-12. This works out to 244*10-6.

Question:
Given a 4.12 format number for the phaseIncrement and phaseAccumulator, a 10us update rate, and a 16 entry ROM, what is the frequency change between consectutive representations of the phaseIncrement?

Answer:
From the previous question we know that the difference between consectutive 4.12 numbers is 0000.000000000001 or 2-12 = 244*10-6. Using this difference as the phase increment in our DDS equations yields:
 1 update   10^6 us   244*10-6phase inc      1 wave    
 -------- * ------- * ----------------- * -----------  = 1.53 Hz
    10us     1s            1 update       16 phase inc  


This is a general result, there will always be 1.53Hz difference between consectutive representation. This value is so important, it is given a name, frequency resolution. To recap, the frequency resolution of a DDS system is the frequency change between consectutive representations of the phase increment. The frequency resolutions is affected by update rate, fixed point format of the phase increment and ROM size. Let's see how one of these factors affects the frequency resolution of our DDS algorithm.

Question:
Let's change the datatype of phaseIncrement and phaseAccumulator from int16 to int8. What is the frequency resolution of the resulting algorithm?

Answer:
Our phase increment will no longer be a 4.12 format number because these 16-bits will not fit into an int8. Since we need 4 integer bits to access the ROM, this leaves only 4 fractional bits. Thus, the phase increment is a 4.4 format number.

The change in value between consectutive 4.4 representations is 0000.0001 = 2-4 = 0.0625

Now we can use our DDS equation to determine the frequency resolution using the phase increment between consectutive representations of the phase increment.
 1 update   10^6 us   0.0625 phase inc      1 wave    
 -------- * ------- * ---------------- * -----------  = 391 Hz
    10us     1s            1 update      16 phase inc  

DDS with interrupts

uint8_t sin[16] = {127,176,217,245,254,245,217,176,127,78,37,10,0,1,37,78};

// This need to be global because they are shared between ISR and main
uint16_t phaseIncrement = 0x0123;

//*****************************************************************
//*****************************************************************
void high_priority interrupt tmr0_isr(void) {
 
    static uint16_t phaseAccumulator = 0;
    uint16_t index;

    phaseAccumulator = phaseAccumulator + phaseIncrement;
    index = phaseAccumulator >> 12;
    PWM4_LoadDutyValue(sin[index]);		// Assume an 8-bit duty cycle

    TMR0_WriteTimer(TMR0_ReadTimer() + (0xFFFF - RATE));
    INTCONbits.TMR0IF = 0;		// Clear the timer rollover flag

} // end timer compare interrupt

Test your understanding

You can find the solutions embedded in the "source code" for this web page by right mouse clicking on this web page and selecting "view source". The solutions are in HTML comments.