DDS (generating sine waves) with onboard DAC using latest PIC 16F chips?


Results 1 to 40 of 77

Threaded View

  1. #37
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,621


    Did you find this post helpful? Yes | No

    Default Re: DDS (generating sine waves) with onboard DAC using latest PIC 16F chips?

    Hi,
    I continued to play a bit with the DDS code and I think you're actually correct Hank - doing it in "pure" PBP is to slow, even at 32Mhz. Using DT-Ints with a PBP handler I was unable to get a high enough interrupt frequency due to the overhead added by the saving and restoring of the system variables.

    So, what I did was what I told you not to... I'm using a handler written in PBP but treating it as a "real" ASM interrupt handler. But what I did was to look thru the generated ASM-listing trying to figure out which system variables that was actually used by the ISR and then added save/restore code for those only. This allowed me to get 40kHz (and higher if needed) interrupt frequency which proved to be sufficient for a 5kHz output.

    I then added a simple serial receive routine as the main program which allows you to send the desired frequency in units of .1Hz, 10000=1000.0Hz. I don't have a real frequency counter/meter but my FLKUKE87 shows the frequency beeing absolutely spot on, like 1000.0Hz and 150.00Hz.

    The amplitude still suffers as the frequency goes up and I don't know if it's due to the filter or just the way DDS works. You could possibly rescale the output values proportional to frequency or something like that making the amplitude lower but constant over the whole range.

    Anyway, here's the code:
    Code:
    '****************************************************************
    '*  Name    : DDS.pbp                                           *
    '*  Author  : Henrik Olsson                                     *
    '*  Notice  : Copyright (c) 2010 Henrik Olsson 2010             *
    '*          : All Rights Reserved                               *
    '*  Date    : 2010-12-18                                        *
    '*  Version : 1.0                                               *
    '*  Notes   : Originally tested on 18F4520 with 8Mhz x-tal and  *
    '*          : X4 PLL. 8-bit value output to PortB, R2R DAC and  *
    '*          : 2nd order lowpass filter. Sampling frequency set  *
    '*          : to 40kHz to get a nice looking sinewave at 5kHz   *
    '*          : output. Could probably work with a lower sampling-*
    '*          : frequency if a better filter was used.            *
    '*                                                              *
    '****************************************************************
     
    ' Tweak this value to get output frequency to within spec (or as good as possible)
    ' I'm not sure why the theoretical value doesn't match exactly but in my case it
    ' was off by ~0.4%.
    FACTOR CON 10845                ' 10737=40kHz sampling, 21475=20kHz sampling.
     
    #CONFIG
        CONFIG OSC = HSPLL
        CONFIG FCMEN = OFF
        CONFIG IESO = OFF
        CONFIG PWRT = ON
        CONFIG BOREN = OFF
        CONFIG WDT = OFF
        CONFIG WDTPS = 512
        CONFIG CCP2MX = PORTC
        CONFIG PBADEN = OFF
        CONFIG LPT1OSC = OFF
        CONFIG STVREN = OFF
        CONFIG MCLRE = ON
        CONFIG LVP = OFF
        CONFIG XINST = OFF
        CONFIG DEBUG = OFF
    #ENDCONFIG
     
    DEFINE OSC 32                   ' 8MHz * 4 = 32Mhz
    DEFINE INTHAND High_INT         ' We're treating the interrupt as an ASM routine.
    DEFINE NO_CLRWDT 1              ' Dog is not home, no need to kick it.
    DEFINE HSER_RCSTA 90h           ' Enable serial port & continuous receive
    DEFINE HSER_TXSTA 20h           ' Enable transmit, BRGH = 0
    DEFINE HSER_CLROERR 1           ' Clear overflow automatically
    DEFINE HSER_SPBRG 51            ' 38400 Baud @ 32MHz, 0,16%
     
    SPBRGH = 0
    BAUDCON.3 = 1                   ' Enable 16 bit baudrate generator
     
    ' These variables are used in the ISR to save the PBP system variables that the code in the ISR uses.
    R0_SHADOW VAR WORD BANK0
    R1_SHADOW VAR WORD BANK0
    R4_SHADOW VAR WORD BANK0
     
    LSW     VAR WORD                ' Two words forming the 32 bit accumulator
    MSW     VAR WORD
    ADDL    VAR WORD                ' Two words forming the value getting added to the accumulator
    ADDH    VAR WORD
    OVF     VAR WORD                ' Used in the add routine to determine overflow.
    Frequency VAR WORD              ' The output frequency in 0.1 units (12345=1234.5Hz)
    Temp    VAR WORD
    Buffer  VAR BYTE[10]
    BufPtr  VAR BYTE
    i       var byte
     
    RCIF    VAR PIR1.5              ' EUSART Receive Interrupt Flag
     
    PortB = 0                       ' We have the 8-bir R2R-ladder on PortB.
    TrisB = 0                       ' Make it an output
     
    TRISC.1 = 0
    TRISC.3 = 1
    TRISC.6 = 0                     ' TxPin is output
    TRISC.7 = 1                     ' RxPin is input
     
    ' 200 cycles between interrupts, 200*125ns = 25us, 1/25us = 40kHz interrupt frequency.
    PR2 = 200
     
    ADDL = 0
    ADDH = 5000
     
    IPR1.1   = 1        ' TMR2 high priority
    PIE1.1   = 1        ' TMR2 IE
    INTCON.6 = 1        ' PIE
    INTCON.7 = 1        ' GIE
     
    Temp = RCREG        ' Dummy read
     
    BufPtr = 0          ' Point to beginning of buffer
     
    Frequency = 25000                   ' Startup with 2500Hz output frquency.
    Temp = Frequency * FACTOR
    AddH = R0                           ' Get high word from R0
    AddL = R2
     
    HSEROUT ["Program start",13]
     
    T2CON.2  = 1                        ' Start TMR2
    '----------------------- Main program starts here -------------------------
    Main:
      If RCIF THEN                      ' Do we have a byte in the receive buffer?
        Buffer[BufPtr] = RCREG          ' Grab it
     
        ' Check if it's either a CR or a LF. If it is we've got all we need.
        ' Otherwise prepare to receive next byte.
        If (Buffer[BufPtr] = 13) OR (Buffer[BufPtr]) = 10 THEN
          GOSUB HandleIt
        ELSE
          BufPtr = BufPtr + 1
        ENDIF
     
      ENDIF
    Goto Main  
    '---------------------------------------------------------------------------
     
    '---------------------------------------------------------------------------  
    HandleIt:
      Frequency = Buffer[0] - 48        ' Get first byte and convert to ASCII
     
      For i = 1 to BufPtr - 1           ' Iterate thru the rest of the buffer.
        Frequency = Frequency * 10 + (Buffer[i] - 48)
      NEXT
     
      HSEROUT ["F: ", DEC Frequency / 10, ".", DEC Frequency // 10, "Hz",13]
     
      ' A 16*16bit multiplication results in a 32bit result which aren't available
      ' to us normaly. Fortunately we can "extract" the high and low word of the
      ' multiplication from the system variables R0 and R2. This only works if the
      ' value is extracted immeidiately after doing the multiplication otherwise the
      ' next PBP could overwrite it.
      Temp = Frequency * FACTOR
      AddH = R0                           ' Get high word from R0
      AddL = R2                           ' Get low word from R2
      BufPtr = 0
     
      ' Flush the buffer.
      While RCIF
        Temp = RCREG
      WEND
     
    RETURN
    '--------------------------------------------------------------------------
     
    '--------------------------- Interrupt code here ---------------------------------------
    ' ISR takes roughly 12us at 32Mhz. This code is written in PBP but we are treating it as an
    ' ASM interrupt routine. This is possible becasue I've investigated which PBP system variables
    ' the code uses and I'm saving those on ISR entry and resore them on ISR exit. Be very careful
    ' if adding more code to this ISR. This is experimental and it's possible that I've missed a
    ' PBP system variable. If unexpected things starts happening with the main routine then I'm
    ' probably f-ing up the system variables with this ISR...
    @ High_INT:
    PortC.1 = 1                               ' Diagnostics remove later to save some time :-)
     
    @ MOVE?WW R0, _R0_SHADOW                  ; Save PBP system variable R0     (2+2)
    @ MOVE?WW R1, _R1_SHADOW                  ; Save PBP system variable R0     (2+2)
    @ MOVE?WW R4, _R4_SHADOW                  ; Save PBP system variable R0     (2+2)
     
        Lookup MSW.HIGHBYTE, [$80,$83,$86,$89,$8C,$8F,$92,$95,$98,$9C,$9F,$A2,$A5,$A8,$AB,$AE,$B0,$B3,$B6,$B9,$BC,$BF,$C1,$C4,_
        $C7,$C9,$CC,$CE,$D1,$D3,$D5,$D8,$DA,$DC,$DE,$E0,$E2,$E4,$E6,$E8,$EA,$EC,$ED,$EF,$F0,$F2,$F3,$F5,$F6,$F7,$F8,$F9,$FA,$FB,$FC,$FC, _
        $FD,$FE,$FE,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FE,$FE,$FD,$FC,$FC,$FB,$FA,$F9,$F8,$F7,$F6,$F5,$F3,$F2,$F0,$EF,$ED,$EC, _
        $EA,$E8,$E6,$E4,$E2,$E0,$DE,$DC,$DA,$D8,$D5,$D3,$D1,$CE,$CC,$C9,$C7,$C4,$C1,$BF,$BC,$B9,$B6,$B3,$B0,$AE,$AB,$A8,$A5,$A2,$9F,$9C, _
        $98,$95,$92,$8F,$8C,$89,$86,$83,$7F,$7C,$79,$76,$73,$70,$6D,$6A,$67,$63,$60,$5D,$5A,$57,$54,$51,$4F,$4C,$49,$46,$43,$40,$3E,$3B, _
        $38,$36,$33,$31,$2E,$2C,$2A,$27,$25,$23,$21,$1F,$1D,$1B,$19,$17,$15,$13,$12,$10,$0F,$0D,$0C,$0A,$09,$08,$07,$06,$05,$04,$03,$03, _
        $02,$01,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$01,$01,$02,$03,$03,$04,$05,$06,$07,$08,$09,$0A,$0C,$0D,$0F,$10,$12,$13, _
        $15,$17,$19,$1B,$1D,$1F,$21,$23,$25,$27,$2A,$2C,$2E,$31,$33,$36,$38,$3B,$3E,$40,$43,$46,$49,$4C,$4F,$51,$54,$57,$5A,$5D,$60,$63, _
        $67,$6A,$6D,$70,$73,$76,$79,$7C],PortB
     
    Add:
        OVF = LSW                               ' Remember least significant word
        LSW = LSW + ADDL                        ' Add low word 
     
        If LSW < OVF Then                       ' Did we wrap around/overflow?
            MSW = MSW + 1                       ' Increment high word
        ENDIF
        MSW = MSW + ADDH                        ' Add high word 
     
        PIR1.1 = 0                              ' Clear TMR2 Interrupt flag
     
    @ MOVE?WW _R0_SHADOW, R0                    ; Restore PBP system variable R0     (2+2)
    @ MOVE?WW _R1_SHADOW, R1                    ; Restore PBP system variable R0     (2+2)
    @ MOVE?WW _R4_SHADOW, R4                    ; Restore PBP system variable R0     (2+2)
     
        PortC.1 = 0                             ' Diagnostics, remove later to save some time... :-)
     
    @ retfie FAST
    '----------------------------------------------------------------------------------------------------------
    It would be interesting to see what it does with your PWM aproach insted on the R2R DAC, just be careful if you change the ISR. Let me know if give it a try.

    /Henrik.
    Last edited by HenrikOlsson; - 10th September 2011 at 10:34.

Members who have read this thread : 1

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts