DDS Sinewave Generator
using the
5-bit DAC Peripheral

I was experimenting last night with the PIC16F1823 device, and came up with the program below that uses its on-board 5-bit DAC peripheral to generate a sine wave output. The results were actually remarkable for such a small sine table and a low resolution DAC. The code listing for the Positron8 DDS Sinewave Generator can be viewed below:

Click here to view the Positron8 DDS SineWave Generator Code Listing:

' /\\\\\\\\\
' /\\\///////\\\
' \/\\\ \/\\\ /\\\ /\\\
' \/\\\\\\\\\\\/ /\\\\\ /\\\\\\\\\\ /\\\\\\\\ /\\\\\\\\\\\ /\\\\\\\\\\\ /\\\\\\\\\
' \/\\\//////\\\ /\\\///\\\ \/\\\////// /\\\/////\\\ \////\\\//// \////\\\//// \////////\\\
' \/\\\ \//\\\ /\\\ \//\\\ \/\\\\\\\\\\ /\\\\\\\\\\\ \/\\\ \/\\\ /\\\\\\\\\\
' \/\\\ \//\\\ \//\\\ /\\\ \////////\\\ \//\\/////// \/\\\ /\\ \/\\\ /\\ /\\\/////\\\
' \/\\\ \//\\\ \///\\\\\/ /\\\\\\\\\\ \//\\\\\\\\\\ \//\\\\\ \//\\\\\ \//\\\\\\\\/\\
' \/// \/// \///// \////////// \////////// \///// \///// \////////\//
' Let's find out together what makes a PIC Tick!
'
' DDS algorithm for generating a sinewave from the 5-bit DAC on a PIC16F1823 device.
' Written for the Positron8 compiler by Les Johnson.
'
' A simple low pass filter circuit, as shown below, can be used to get a sine waveform out.
' The filter's components will need to be changed for different frequency sine waves.
'
' | 1K
' PORTA.0 |----/\/\/\-----o-----> Output
' | |
' | __|__
' PORTA.1 |--- +5V _____ 10nF
' | |
' | |
' | -----
' --- Gnd
'

Device = 16F1823 ' Tell the compiler what device it will compile for
Declare Xtal = 16 ' Tell the compiler what frequency the device will be operating at

'-----------------------------------------------------------------------------------------
' The main program starts here
'

Main:
Setup() ' Setup the program
DDS_SineWave(1000, 10000) ' Output a 1 KHz sinewave for approx 10 seconds

'-----------------------------------------------------------------------------------------
' Generate a sinewave signal from the 5-bit DAC pin
' Input : pFreq holds the DDS value for the frequency to generate
' : pDuration holds the approximate time (in ms) for the waveform to be outputted
' Output : None
' Notes : None
'

Proc DDS_SineWave(pFreq As Dword, pDuration As Dword)
Dim lAccum As Long ' Accumulator for the DDS
Symbol cDivisor = (((_xtal / 4) * 1000000) / 2240.5) ' Create a divisor for the device's frequency and the loop speed
Symbol cMult = (65536.0 / cDivisor) ' Create a floating point multiplier constant
'
' Sine wave data for the 5-bit DAC
'

Dim SineTable As Flash8 = {16, 17, 19, 20, 21, 23, 24, 25,
26, 27, 28, 29, 30, 30, 31, 31,
31, 31, 31, 30, 30, 29, 28, 27,
26, 25, 24, 23, 21, 20, 19, 17,
16, 14, 12, 11, 10, 08, 07, 06,
05, 04, 03, 02, 01, 01, 00, 00,
00, 00, 00, 01, 01, 02, 03, 04,
05, 06, 07, 08, 10, 11, 12, 14}

pDuration = pDuration * 114
' Calculate the duration in approx milliseconds
pFreq = pFreq * cMult
' Calculate the value for the DDS based upon the frequency required
lAccum = 0
' Reset the accumulator
DACCON0bits_DACEN = 1
' Enable the DAC
Do ' Create a loop
lAccum = lAccum + pFreq.Long
' Accumulation of the frequency
DACCON1 =
CRead8 SineTable[lAccum.Byte2 & $3F] ' Divide output by 65536 and extract the first 6 bits
Dec pDuration ' Decrement the duration counter
If pDuration = 0 Then ' Has the duration reached 0?
DACCON0bits_DACEN = 0
' Yes. So disable the DAC
ExitProc ' Exit the procedure
EndIf
Loop
EndProc

'-----------------------------------------------------------------------------------------
' Initialise the 5-bit DAC peripheral on a PIC16F1823 device
' Input : None
' Output : None
' Notes : Disables the DAC for now
'

Proc DAC_Init()
DACCON0 = 0b00100100
DACCON1 = 0
ANSELA.0 = 1
' Make the DAC pin analogue
ANSELA.1 = 1
' Make the +Vref pin analogue
PinInput PORTA.0 ' Make the DAC pin an input
PinInput PORTA.1 ' Make the +Vref pin an input
EndProc

'-----------------------------------------------------------------------------------------
' Setup the program
' Input : None
' Output : None
' Notes : None
'

Proc Setup()
Osc_16MHz() ' Setup the device to operate at 16Mhz with its internal oscillator
DAC_Init() ' Initialise the DAC
EndProc

'-----------------------------------------------------------------------------------------
' Setup the PIC16F1823 to use the internal oscillator at 16 MHz
' Input : None
' Output : None
' Notes : None
'

Proc Osc_16MHz()
OSCCON = 0b01111010
OSCTUNE = $00
BORCON = $00
EndProc

'-----------------------------------------------------------------------------------------
' Setup the config fuses for the internal oscillator on a PIC16F1823 device
'

Config1 FOSC_INTOSC, WDTE_OFF, MCLRE_ON, CP_OFF, BOREN_OFF
Config2 PLLEN_OFF, LVP_OFF, WRT_OFF, STVREN_OFF

In the above code listing, the line of code:

Symbol cDivisor = (((_xtal / 4) * 1000000) / 2240.5) ' Create a divisor for the device's operating frequency and loop speed

may need to be tweaked a bit to get the frequency as exact as the parameter's value, because the frequency of the DDS relies on the loop iteration timing, and that can change slightly if it is in a program where the variables are not all in the same RAM space etc... The frequency calculation could also be changed to use integer only values, or a specific value placed in the DDS procedure for a particular frequency required, if the frequency is not to be passed to the procedure.

And the line of code:

pDuration = pDuration * 114 ' Calculate the duration in approx milliseconds

will need to be tweaked if there are changes made within the loop for the time the waveform will be outputted for.

Here is a good site for generating the sine table data that can be adapted for programs:

Sine Table Data Generator

Below is a screenshot of the above program being simulated and generating a very clean 1 KHz sine waveform:

The Positron8 source code for the DDS SineWave generator and the Proteus simulation project can be downloaded from here: DDS_SineWave - 5-bit DAC