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.
Bookmarks