Positron8 - DDS Sinewave Generator with the 5-bit DAC peripheral on a PIC16F1823

Started by top204, Aug 19, 2022, 12:15 PM

Previous topic - Next topic

top204

I was experimenting last night with the PIC16F1823 device, and came up with the program below that uses the 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:

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  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 based upon the device's operating 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 keep 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 based upon the device's operating frequency and the 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 1 KHz sine waveform:

DDS Sinewave Generator with 5-bit DAC.jpg