Clifton Laboratories 7236 Clifton Road  Clifton VA 20124 tel: (703) 830 0368 fax: (703) 830 0711

E-mail: Jack.Smith@cliftonlaboratories.com


 

Home
Updates
Prior Products - no longer available
Documents
Book
Modulation
Loads
Lattice Crystal Filter
IMD Measurements
Using LP100 Coupler
Prototyping
Software Updates
K2 Measurements
Oscillator Noise Measurements
Bypassing
Capacitor Voltage Change
K2 Freq Stability
Cohn Crystal Filter
Receiver AGC Curves
K2 RX Sensitivity
Canned Osc Phase Noise
K2 Interface
K2 Filter
Surface Mount Assembly
TL750L Low Dropout Regulator
Swordfish DDS
Swordfish GLCD Module
Bessel Nulls
AM Modulation
Z10000 with FT-920
Z100 Tuning Aid
Dayton 2007
Softrock Lite 6.2
Header Adapter
Carbon Composition Resistors
Thermometers
Hakko FT-800 Thermal Wire Stripper
Heat Sinks
Diode Turn-On Time
Bill Hewlett and his Magic Lamp
Tektronix P6022 Current Probe
1N400x Diode Family Forward Voltage
Temperature Chamber
Diode Vf vs If
Ferrite Transformers
6 dB Hybrid Combiner
Type 43 Ferrite B-H Curve
K3 IF Bandpass Filter
Estimating Q of Ferrite Cores
Z10000 Buffer Amp
Z10010 Bandpass Filter
Using Softrock as a Panadapter for the K2
Signal Generator Phase Noise & Elecraft K2
Audio Transformer Data and Modeling
Measuring 60 Hz Frequency
Compact Fluorescent Lamp
Z10000-U Buffer Amp and FT-1000MP
WJ-8617B Receiver Impressions
Weather in Clifton VA
Radio Intelligence Example
Diodes for RF Probes
PIC Development Boards and Programmers
Elecraft K3 and Panadapters
Elecraft K3 AGC and S-Meter
Elecraft K3 Noise Blanker and Crystal/DSP Filtering
Jackson Harbor Press VLF Converter
Elecraft K3 Receive Audio
Headphone Impedance

 

 
What is DDS?

DDS is an acronym standing for "direct digital synthesis." It's sometimes called a "numerically controller oscillator," but DDS seems to be the more popular term.

I devoted most of a chapter in my book to using a 16F877 PIC to build an audio DDS generator, and I don't propose to duplicate that effort here. In brief, a DDS generates its output by computing the value of the desired sine wave (or other waveform, as DDS is not limited to sinusoidal waveforms) at specific time intervals and then sending the value to a digital-to-analog converter, where the numerical data is converted to a corresponding voltage level.

 

The upper trace in the oscilloscope capture shows a typical DDS output. Note that the desired sine wave is approximated by discrete voltage steps.  The PIC calculates the appropriate voltage level as a byte-range number (0...255) and sends it to the DAC where it is converted to a voltage. In this case, the output is a 730 Hz sine wave.

The lower trace is the DAC's output after it is run through a low pass filter, which cleans up most of the digital step artifacts seen in the raw DAC's output.
 

To show you that the DDS is not restricted to sine wave outputs, the lower trace in the following oscilloscope capture is a DDS-generated sawtooth or ramp waveform. The upper trace is a synchronization waveform.

 

That's enough for tonight. You can think about what uses you might make of a PIC-based audio DDS

.

DAC - Digital to Analog Converter

Let's look at how a DAC works. We'll start with a simple 4-bit R-RL ladder type DAC. The two oscilloscope plots above were taken using this circuit, breadboarded up with five discrete resistors.

The four switches, DB0...DB3 switch between ground and +5V. Of course, our circuit will not use real SPDT switches, but rather the PIC's output pins will switch between logic high (+5V, more or less) and logic low (0.2V or so).
How does this simple circuit work? The easiest way to understand how it works is to regard the four resistors as current sources. Into a low impedance load (such as Rload, the 47 ohm resistor), when DB3 is switched to the +5V source, it will force 5 mA (5V/1000 ohms) through Rload. In this case, the output voltage is 235 mV, calculated as 0.005 A * 47 ohms = 0.235V

Likewise, when DB2 is switched to +5V, Rload has 2.5 mA through it, DB1 controls a 1.25 mA source and DB0 controls a 0.63 mA source.

With the switches set as shown in the diagram, the total current through Rload is 2.5 mA (from DB2, through R2) and 1.25 mA (from DB1, through R3), totaling 3.75 mA, for an output voltage of 176 mV.

What about the shunting effect of R1 and R4, you may be thinking. These clearly drain current away from Rload. And, how can you just sum the currents?

The answer is yes, these are all real effects. However, we've chosen R1...R4 and Rload carefully. A 1K resistor shunting 47 ohms is negligible, at least at the resolution we expect from a crude 4-bit DAC. Likewise, a couple hundred millivolts across Rload won't make our assumption that R1...R4 are current sources that bad. Hence, we can simply sum the currents and be "close enough" for our purposes.

Don't believe me? Dig out your calculator and work through Ohm's law and send me an E-mail with the exact answer. It'll be within 10% of our simplistic analysis.

Oh yes, why is this called an R-2R ladder? Looking at the resistor values and diagram should answer this question for you. (I used real values at 3.9K and 8.2K; in theory these should be 4.0K and 8.0K).

And, why make the ratios 2:1? It has something to do with the binary system doesn't it? Yes, indeed it does. If we look at a 4-bit binary number (usually called a nibble) defined by DB3...DB0, we see that the DC output of our R-2R ladder DAC automatically tracks the binary value of the nibble. The binary value to voltage multiplier is the current generated by the least significant bit (R4, or 0.625 mA) times Rload, or 29.4 mV.

Let's see if this works. The nibble data value represented by these switches is 0110 binary, or in decimal 6. Hence, the output voltage should be 6 * 29.4 mV = 176 mV, exactly what we calculated before.

Nice when it all fits together, isn't it?

 
Simple Swordfish DDS Code--Program DDS-1

OK, time for some code. I'll first list the complete program and then explain what is behind it. The program assumes we have two of the simple 4-bit resistive DAC circuits connected to the PIC's Port B.

The following schematic fragment shows the resistive DAC connection.

 
Our first code generates two 4-bit sine waves, one on Chan_A_Out and one on Chan_B_Out, at different frequencies.
 

{

****************************************************************

* Name : Toner.BAS *

* Author : Jack R. Smith *

* Notice : Copyright (c) 2006 Clifton Laboratories *

* : All Rights Reserved *

* Date : 7/22/2006 *

* Version : 1.0 *

* Notes : *

* : *

****************************************************************

}

'Program to output two simultaneous DDS-generated sine waves

'into 4-bit DACs on Port B. Channel A is lower nibble

'Channel B is upper nibble.

 

Device = 18F4620

Clock = 40 'remember to enable HSPLL in configuration

 

Config

OSC = HSPLL, //turn 4x PLL ON

LVP = OFF,

DEBUG = OFF,

PWRT = ON,

BOREN = ON,

WDT = OFF,

PBADEN = OFF, 'for 18F4620 - makes port B digital

XINST=OFF

Const

SinTable(16) As Byte = (8,10,13,14,15,14,13,10,8,5,2,1,0,1,2,5)

 

Dim

aStep, bStep As Word,

PhaseAccumA As Word, 'use these as a 4.12 (15 bit total)

PhaseAccumB As Word, 'phase accumulators

Temp As Byte

 

TRISB = $00 'make output

'adjust these values when loop code is finished as the

'output frequency depends on Repeat / Until loop timing

aStep = 935 'generates 2.56 KHz with current timing

bStep = 267 'generates 730 Hz with current timing

PhaseAccumA = 0

PhaseAccumB = 0

 

Repeat

   High(PORTC.0)

   PhaseAccumA = PhaseAccumA + aStep

   PhaseAccumB = PhaseAccumB + bStep

   Temp = SinTable(PhaseAccumA >> 12)

   Temp = Temp + 16 * (SinTable(PhaseAccumB >> 12))

   PORTB = Temp

   Low(PORTC.0)

Until False

End

 
Tomorrow, we'll start analyzing the code.

[Note added 06 October 2006. I fixed an error in the main code body, replacing erroneous references to variables i and j with aStep and bStep respectively.

 

 

Program DDS-1 Analysis

 

Device = 18F4620

Clock = 40 'remember to enable HSPLL in configuration

 

 
The first part of a Swordfish program identifies the PIC to be used and the clock speed in MHz. This is done using Swordfish's keywords "Device" to identify the PIC and "Clock" to define the clock speed.  I used an 18F4620 for the DDS experiments, because I happened to have one set up in a plug-type breadboard, but an 18F4620 is gross over-kill to demonstrate how a DDS works.

DDS is a demanding application for a PIC, and the faster the clock, the better off you are, particularly when programming in a high level language, such as Swordfish. If you look at Chapter 16 of my book, Programming the PIC Microcontroller with MBasic, you will find a chapter on Digital-to-Analog conversion and several DDS programs written in MBasic, for the 16F877 PICs. In order to wring acceptable performance, I had to code time-sensitive segments in assembler. We'll see later that Swordfish produces such efficient code that we do not need assembler routines to achieve respectable performance from the 18F devices.

 

Config

 

The next part of our program follows Swordfish's keyword "Config," establishes the PIC's "configuration" which sets certain options that must be set during programming. This process is also known as setting the configuration "fuses."

Swordfish will supply a set of default configuration fuses, but it's a good idea to establish any necessary configuration settings in your code, rather than depend on defaults.

 

OSC = HSPLL, //turn 4x PLL ON

 

Microchip added a built-in clock multipler phase locked loop to the 18F series chips. Thus, to obtain a 40 MHz clock rate, we have two options:
  • Use a 40 MHz crystal or resonator or external clock; or
  • Use a 10 MHz crystal, or resonator or clock source and enable the built-in 4x PLL multiplier

It's much easier to use a 10 MHz resonator and enable the PLL, via the OSC=HSPLL statement.

 

LVP = OFF,

 

LVP is the "low voltage programming" command. I won't delve into the details of low voltage programming versus high voltage programming, but our program requires LVP to be disabled.

LVP is a feature added by Microchip that turns out to be more of a hindrance than a benefit. Microchip ships its devices with LVP enabled, so it's necessary to explicitly turn it off the first time it is programmed.

 

DEBUG = OFF,

The 18F-series, along with some late 16F PICs has an internal option that allows easy extraction of internal settings and variables when DEBUG is enabled. However, the compiler and the test board must also support DEBUG, so we disable this feature.

 

PWRT = ON,

 

 

This line enables the "power-up" timer (PWRT). The power-up timer provides a 65 ms (nominal) delay from the time the PIC detects that it has been powered up until program code begins execution. This delay allows the device's power supply to stabilize and you will normally wish to set the PWRT to ON.
 

BOREN = ON,

BOREN is Microchip's mnemonic for the "brownout reset" function. A "brown-out" is detected by the PIC when the supply voltage drops below safe operating levels, but not necessarily down to zero. Enabling BOREN, as in our test program, is the safest option, as a momentary power interruption may cause unknown side effects. A clean reset on brown-out (re-starting the code as if the circuit had its power removed) will help ensure proper code operation should a power abnormality occur.
 

WDT = OFF,

 

WDT is the mnemonic for the "watch-dog timer." The watch-dog timer causes the PIC to reset, at a specific time interval. The reset interval has several optional durations settable by the programmer.

Why would you want your PIC code to reset? The answer is normally you don't want it to reset, so if you wish to use the WDT, your code will periodically disarm the WDT and thus prevent it from resetting the PIC. But, if your code has a problem and enters an endless loop, or gets hung up waiting for an external input, it won't be able to disarm the WDT and hence the WDT will force a reset, thereby extricating your program from an unexpected freeze.

If you do not need this level of supervision, keep the watch-dog timer disabled, as in the example. Otherwise, you will have to add code to periodically disarm the WDT.
 

PBADEN = OFF, 'for 18F4620 - makes port B digital

 

 

Reading the 18F4620 data sheet quickly shows that each pin has several potential uses. In the 18F4620's case, for example, the Port B pins may either be used for normal digital logic input and output, or they may serve as additional analog inputs to the PIC's analog-to-digital converter.

The default configuration is for the Port B pins RB0...RB4 to be analog inputs on power-on reset. Since we use these pins for digital output, we must over-ride the default configuration and force these pins to be digital. (RB5...RB7 are assigned to digital mode at power up.)
 

XINST=OFF The last configuration control we set is to disable the "extended instruction set." The extended instruction set allows the 18F-series chips to execute assembler instructions that were not available in older devices. Unfortunately, some of these extended instruction set operations have proven to be buggy, and to avoid trouble we'll disable them.
 
   
If I were writing a book on programming the 18F Microcontroller with Swordfish Basic, I would have covered the configuration settings in a separate chapter, and we could therefore jump directly into the DDS-related code. Since I'm not writing that book, I've tried to make this page more stand-alone and unfortunately this means we have to slog through some things related to the nuts and bolts of how a PIC works before we get to the interesting code.

One more thing. I've mentioned that Swordfish will automatically set many configuration bits for you. Where do you find out what configuration bits Swordfish sets?

Assuming you have a normal Swordfish installation, look in the directory ..\Swordfish\Includes\ for the file with the same name as the DEVICE you set at the outset of the program, with the extension .BAS. In this case, the file will be 18F4620.BAS. (If you were using an 18F2580, the file would be 18F2580.BAS.)

If you open this file with Swordfish's IDE, or with a text editor such as Notepad, you will find a list of all the possible configuration fuses that you can access via Swordfish. Here's the first few lines of the public configuration fuses for the 18F4620:

// configuration fuses...

Public Config

OSC(OSC) = [LP, XT, HS, RC, EC, ECIO6, HSPLL, RCIO6, INTIO67, INTIO7],

FCMEN(FCMEN) = [OFF, ON],

IESO(IESO) = [OFF, ON],

Then, towards the bottom of the file you will find the configuration fuse settings Swordfish will use, by default:

// default fuses...

Config

OSC = HS,

FCMEN = OFF,

IESO = OFF,

PWRT = ON,

BOREN = ON,

WDT = OFF,

WDTPS = 128,

PBADEN = OFF,

STVREN = ON,

LVP = OFF,

XINST = OFF,

DEBUG = OFF

 

If you do not explicitly define a setting for each of these configuration fuses, Swordfish will apply these default values.

By in large, the default values are good choices. In our case, the default oscillator parameter would be wrong, but the rest of the default settings match our manual overrides.

So, then, why did we go on a long detour to look at configuration fuses that do nothing but duplicate existing settings? There are two reasons:

  • Default configuration fuses are a relative recent addition to Swordfish, and the earlier versions I used had no Swordfish-defined defaults. Either you manually entered the desired configuration fuse setting or you lived with whatever Microchip established as the power-on configuration.
  • Although Swordfish does a very good job at defining default configuration fuses, your program may need something different. Hence, it's a good habit to set the configuration fuses the way you want them to be, and not depend upon the default. And, it's possible that a later release file might change the default settings, without you being aware of it.

You might be tempted to modify the 18F4620.BAS file, for example, to make the default oscillator mode HSPLL. That's not a good idea for several reasons. One is that a Swordfish update might replace your modified 18F4620.BAS file without you being aware of it. Another is that you might wish to provide a copy of your program source code to a friend or colleague for testing or other purpose. If your code depends upon a specially modified 18F4620.BAS file, you must also include it, and that file must be copied into the other system, displacing the standard file. All in all, it not a good idea to modify the files in the INCLUDE directory unless you have an excellent reason.

 

Next time, we'll look at a bit of real code.
 
OK, here we go with some "real" code, not this setup stuff.

 

Const

SinTable(16) As Byte = (8,10,13,14,15,14,13,10,8,5,2,1,0,1,2,5)

 
Swordfish allows you to define array of constants that may be used just like a standard array.

The syntax is to name the constant [SInTable], dimension the constant [16], define the type of data to be stored in the constant array [byte] and, finally, enter the data, with each value separated by a comma and with the data enclosed in parentheses.

 

Values the constant array SinTable are accessed just like any other array; a = SinTable(3) assigns the value of 14 to the variable a. Remember that the first value in the constant array has an index of 0.
 
So much for the mechanics, now what the heck is SinTable? I'll try to keep the explanation simple, but a bit of math is involved.

The values in SinTable are the values of the sine function, taken every 22.5 degrees, with an offset of 8 and scaled for a maximum peak-to-peak value of 0...15.

In pseudo-code, the values of SinTable are computed as:

For i = 0 to 15
   SinTable(i) = Integer(8 + 8 * Sin(i*22.5) )
Next i

For clarity, this assumes the Sin function operates with the argument in degrees, not radians as is normally the case. There's also a bit of a discontinuity in that the offset should be 7.5, not 8, but since our byte variable is integer, we use 8. Then we force the positive peak to be 15, not 16 as would be expected from the equation.

Now, since SinTable is a constant, we can't directly compute it as part of the normal program code. We could let Swordfish compute some constants, but for SinTable, we use an electronic calculator or a spreadsheet, such as Excel.

Here's the result in Excel:

 

Step

Sin(22.5*Step)

Integer(8+ 8*Sin(22.5*Step))

0 0.00000 8
1 0.38268 11
2 0.70711 13
3 0.92388 15
4 1.00000 16
5 0.92388 15
6 0.70711 13
7 0.38268 11
8 0.00000 8
9 -0.38268 4
10 -0.70711 2
11 -0.92388 0
12 -1.00000 0
13 -0.92388 0
14 -0.70711 2
15 -0.38268 4
 
Hence, the Excel spreadsheet says that SinTable(3), for example, should be 15. I've taken some liberties with these computed values to better fit the real (in this sense, I mean a real number, i.e., not an integer) sin values into our limited range of 0...15. Hence, the values in SinTable do not quite match these computed quantities.
 
So, you are probably thinking, I know how you computed the values in SinTable, but why did you fill it with a crude approximation of the sine function?

Here's a hint to think about until the next installment. Our 4-bit DAC can convert values from 0...15 into voltages. Perhaps there's some relationship between that fact and converting our sine wave data into a range of numbers between 0 and 15.

 

More tomorrow, work permitting.

 
Let's think about how we determine the frequency of our output sine wave. We know that by sending the correct 4-bits to the DAC, we can create a waveform that approximates a sine wave. But, how do we make the output at a specific frequency? If we want a 1000 Hz output, how do do it? Here's the output from both 4-bit DACs, one generating a 733 Hz sine waveform and the second at 2.56 KHz. But why does the program generate these frequencies, not, for example, 1234 Hz, or 4321 Hz?
 

We start with the basic concept that all DDS programs, including ours, are sampled systems, in that at certain intervals the program outputs a new DAC value. In some cases, the new DAC value may be identical with the previous value, so don't understand this to mean that the output voltage always goes up or down with each new step interval.

In our sample Swordfish program, the interval between successive sample outputs is 5.6 microseconds. How do we know that? I'll analyze it in detail later, but our code sets an output pin (Port C, bit 0) to logic high and clears it to logic low once every sample interval, so we can measure the interval. (Or, we could look at Swordfish's output assembler code and count the instructions and calculate the interval. It's much easier to throw the oscilloscope onto the waveform and measure it.) Here's the output of this "sentinel" pin:

 

We see that our sample interval is 5.576 microseconds, which we will round to 5.6 microseconds. If you would rather think in terms of frequency, it's 179.3 KHz, but we should focus on time duration here, not frequency.

So, we know that once every 5.6 microseconds, we must calculate and output a DAC value that corresponds to the instantaneous value of our desired sine wave output.

I should add that for a high quality output waveform, the step interval must not vary. This can be a problem with an optimizing compiler such as Swordfish, as it may "short circuit" some mathematical operations depending on the particular values. Hence the time to execute a certain program loop might vary depending on the calculations made within the loop. If there's enough interest, we can look at ways to force the output steps to always be at the same interval.

Let's consider a simple example. The illustration below shows our objective-- a target sinusoidal waveform, at 2.77 Hz. The red vertical lines represent the output step interval, in our simple case, once every 79 milliseconds. The black squares show our desired output voltage at each sample interval, assuming, of course, that our DAC has infinite resolution and can perfectly assume any output voltage between -1 and +1 volts.

If this illustration looks familiar--performing an analog-to-digital sample of 2.77 Hz input waveform with a A/D sample interval of 79 milliseconds--you are perfectly correct. In fact, the DAC with DDS program is the inverse of a sampled ADC system.

I'll leave you to ponder these similarities, and how we generate our desired output frequency until a later installment.

 
Let's start by noting that our 2.77 Hz waveform goes through 2.77 X 360 = 997.2 degrees every second. (The example is actually based on 1,000 degrees / sec, but rounding error gives us 997.2.)

If, in one second, our waveform goes through 997.2 degrees, and if an output sample occurs once every 79 milliseconds, then our waveform must advance by 977.2 degrees/sec x 0.079 sec/sample = 78.8 degrees/sample between every sample point.

Our waveform generator program must then output the following every sample interval: Output(n) = Sin(78.8 degrees * n). The first 13 values of the output can be computed as:

 

Sample  Time (ms) Degrees Sin (Degrees)
0 0 0 0
1 78 78.8 0.980955
2 156 157.6 0.38107
3 234 236.4 -0.83292
4 312 315.2 -0.70463
5 390 394 0.559193
6 468 472.8 0.921863
7 546 551.6 -0.20108
8 624 630.4 -0.99998
9 702 709.2 -0.18738
10 780 788 0.927184
11 858 866.8 0.547563
12 936 945.6 -0.71447
 
Suppose we wanted to generate a slower waveform, say one half cycle per second, or 0.5 Hz. What is the increment angle between successive samples?

0.5 Hz is 180 degrees per second. Hence, every sample interval the desired waveform advances 180 degrees/sec x 0.079 sec/sample = 14.22 degrees per sample.

Here are the first 10 output values needed to generate our hypothetical 0.5 Hz sinewave with a 0.079 second sample interval:

Sample Time (ms) Degrees Sin (Degrees)
0 0 0 0
1 79 14.22 0.245646
2 158 28.44 0.476238
3 237 42.66 0.677646
4 316 56.88 0.837528
5 395 71.1 0.946085
6 474 85.32 0.996666
7 553 99.54 0.98617
8 632 113.76 0.915241
9 711 127.98 0.788226

 

Let's plot these data points and other data points for the first 3 seconds or so of our synthesized 0.5 Hz waveform.
Looks a lot like a 0.5 Hz sine wave, doesn't it? Of course, we assume in this simple example that our DAC has infinite resolution (we'll get to the 4-bit DAC shortly).

Usually, we will run the DAC output through a low pass filter (also known as an anti-alias filter, for reasons we may touch upon in a later installment, time and interest permitting). We can simulate a low pass filter with our graph program by connecting the discrete output points with an interpolated waveform, using a cubic spline algorithm. Don't worry if this does not mean much to you -- consider it a way of smoothing the waveform similar to how a draftsman would have done it before the days of CAD. He would have taken out a flexible bit of thin wood or plastic and bent it so that it touched three or more adjacent data points and then drawn a fair curve between the data points using the natural curve of the wood or plastic under tension.

Here is the cubic spline interpolated output.

Sure looks like a nice, smooth pure sinewave doesn't it.

Oh, you don't believe it's the same stepped waveform run through a mathematical equivalent of a low pass filter? Here is the original stepped response and the spline response.

Next time, we'll get back to our program and determine many degrees we must advance each sample interval to obtain 733 Hz output with a 5.6 microsecond sample. 

Why don't you compute it and compare your results with my answer in the next installment.

 
We use the same approach as in our 0.5 Hz example. We'll start our calculations with degrees and then consider the 4-bit DAC and the length 16 sine table.

In one second, a 733 Hz sine outputs 733 /second x 360 degrees = 263880 degrees/second. Therefore in 5.6 microseconds, the output advances by 2.6388 x 105 degrees/second x 5.6 x 10-6 seconds/sample = 1.4777 degrees/sample.

Compared with the relatively coarse step interval in our examples, we would expect (DAC resolution permitting) a relatively clean output, with the need for exotic low pass filter designs.

To understand how this translates to our limited sine table and 4-bit DAC, let's go back to our Swordfish code:

 

Dim

aStep, bStep As Word,

PhaseAccumA As Word, 'use these as a 4.12 (15 bit total)

PhaseAccumB As Word, 'phase accumulators

Temp As Byte

 
 

For simplicity, I'll concentrate on the part of the code generating the 733 Hz signal and temporarily ignore the 2.56 KHz related code.

The word length (two bytes, or 16 bits) variable PhaseAccumA holds the total phase information that we use to index into the constant SinTable. This variable is usually called the "phase accumulator" in DDS terminology. Many high end DDS chips have a 32-bit or more phase accumulator. (The Z90 uses an Analog Devices AD9851 DDS integrated circuit with a 32-bit phase accumulator and a 10-bit DAC. Its clock rate is 180 MHz, corresponding to a 5.5 ns sample interval .) Ours is 16 bits, more than enough to demonstrate the concept.

Let's look in more detail at how we use the phase accumulator variable. We will consider the 16 bits of PhaseAccumA as 4-bit and 12-bit parts, separated by an imaginary "binary point."

  Integer Index into SinTable Implicit
"Binary"
Point
Fractional Part
  MSB

LSB

 

4-bits

.

12 bits

The upper four bits (this is usually called a nibble, but Swordfish does not support nibble variables) is our index into the constant array SinTable. The lower 12 bits are the "fractional" parts of the phase accumulator.

If we look at a traditional decimal number, such as 1.234, we can regard the digits to the right of the decimal point as the "fractional" part of the number, and the digits to the left of the decimal point the "integer" part of the number. Our phase accumulator has exactly the same structure, except that being binary, we'll call the separator between the integer and fractional parts the "binary point," instead of the "decimal point."

If we convert PhaseAccumA's structure into decimal, the integer value runs from 0 to 15. The fractional part runs from 0 to 0.999755... in steps of 0.000244... (1/212 or 1/4096)

 

Next installment will delve into PhaseAccumA's structure in more detail and show how we use it to index into the sine table.