{{
*****************************************
* *
* Author: Ian Mitchell *
*****************************************
}}
{
Revision History:
Version 1.0 - original file created
}
con
_CLKMODE = XTAL1 + PLL16X
_XINFREQ = 5_000_000
var
long frequency
obj
' DDS : "DDS1_0.1"
' DDS : "DDS4_0.1"
DDS : "DDS7_0.2"
ss : "Simple_Serial"
' pst : "Parallax Serial Terminal" ' Serial communication object
pub main
'dira[16..23]~~ 'Set pins to outputs
'outa[16] := 1
' pst.start(9600) '' Initialize serial communication to the PC through the USB connector
' repeat 100
' pst.Str(String("Testing one two three, one two three..."))
' pst.NewLine
' waitcnt(clkfreq*5+cnt)
' repeat 2
' ss.init(31,30,9600)
' ss.str(String("Testing one two three, one two three..."))
' ss.tx($0d)
' ss.dec(clkfreq)
' ss.tx($0d)
DDS.start(3_999_000)
' DDS.start(3_123_000)
waitcnt(clkfreq*3+cnt)
repeat
repeat
waitcnt(clkfreq*3+cnt)
DDS.setfrequency(4_123_000)
waitcnt(clkfreq*3+cnt)
DDS.setfrequency(2_123_000)
waitcnt(clkfreq*3+cnt)
DDS.setfrequency(2_000_100)
waitcnt(clkfreq*3+cnt)
DDS.setfrequency(1_123_000)
repeat 'loop forever to keep cog alive
dat
{{
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ TERMS OF USE: MIT License │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │
│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │
│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│
│is furnished to do so, subject to the following conditions: │
│ │
│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│
│ │
│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │
│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │
│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │
│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
}}
DDS7_0.2.spin
{{
1 0 (init to base_phase*0)
2 1000 (init to base_phase*1)
3 2000 (init to base_phase*2)
4 3000 (init to base_phase*3)
5 4000 (init to base_phase*4)
6 5000 (init to base_phase*5)
7 6000 (init to base_phase*6)
1 7000 (add base_phase*7)
2 8000 (add base_phase*7)
3 9000 (add base_phase*7)
4 10000 (add base_phase*7)
5 11000 (add base_phase*7)
6 12000 (add base_phase*7)
7 13000 (add base_phase*7)
1 14000 (add base_phase*7)
2 15000 (add base_phase*7)
3 16000 (add base_phase*7)
4 17000 (add base_phase*7)
5
6
7
1
2
3
4
5
6
7
}}
con
dds_clock = 20_000_000
var
long phase_init_1 'initial phase value
long phase_chan_1 'new phase change value
long phase_sync_1 'clock sync value
long phase_accu_1 'phase accumulator value (passed back)
long phase_init_2 'initial phase value
long phase_chan_2 'new phase value
long phase_sync_2 'clock sync value
long phase_accu_2 'phase accumulator value (passed back)
long phase_init_3 'initial phase value
long phase_chan_3 'new phase value
long phase_sync_3 'clock sync value
long phase_accu_3 'phase accumulator value (passed back)
long phase_init_4 'initial phase value
long phase_chan_4 'new phase value
long phase_sync_4 'clock sync value
long phase_accu_4 'phase accumulator value (passed back)
long phase_init_5 'initial phase value
long phase_chan_5 'new phase value
long phase_sync_5 'clock sync value
long phase_accu_5 'phase accumulator value (passed back)
long phase_init_6 'initial phase value
long phase_chan_6 'new phase value
long phase_sync_6 'clock sync value
long phase_accu_6 'phase accumulator value (passed back)
long phase_init_7 'initial phase value
long phase_chan_7 'new phase value
long phase_sync_7 'clock sync value
long phase_accu_7 'phase accumulator value (passed back)
obj
ss : "Simple_Serial"
pub start(frequency) | phase,syncval
dira[24]~~
outa[24] := 0
phase := fraction(frequency,dds_clock)
{{
ss.init(31,30,9600)
ss.dec(phase)
ss.tx($0d)
}}
phase_init_1 := phase*1
phase_init_2 := phase*2
phase_init_3 := phase*3
phase_init_4 := phase*4
phase_init_5 := phase*5
phase_init_6 := phase*6
phase_init_7 := phase*7
phase *= 7
phase_chan_1 := phase
phase_chan_2 := phase
phase_chan_3 := phase
phase_chan_4 := phase
phase_chan_5 := phase
phase_chan_6 := phase
phase_chan_7 := phase
{{
ss.str(String("phase_init_1: "))
ss.dec(phase_init_1)
ss.tx($0d)
ss.str(String("phase_init_2: "))
ss.dec(phase_init_2)
ss.tx($0d)
ss.str(String("phase_init_3: "))
ss.dec(phase_init_3)
ss.tx($0d)
ss.str(String("phase_init_4: "))
ss.dec(phase_init_4)
ss.tx($0d)
ss.str(String("phase_init_5: "))
ss.dec(phase_init_5)
ss.tx($0d)
ss.str(String("phase_init_6: "))
ss.dec(phase_init_6)
ss.tx($0d)
ss.str(String("phase_init_7: "))
ss.dec(phase_init_7)
ss.tx($0d)
}}
syncval := 800_000+cnt '10ms into the future
phase_sync_1 := syncval+4*0
phase_sync_2 := syncval+4*1
phase_sync_3 := syncval+4*2
phase_sync_4 := syncval+4*3
phase_sync_5 := syncval+4*4
phase_sync_6 := syncval+4*5
phase_sync_7 := syncval+4*6
cognew(@ddsload,@phase_init_1)
cognew(@ddsload,@phase_init_2)
cognew(@ddsload,@phase_init_3)
cognew(@ddsload,@phase_init_4)
cognew(@ddsload,@phase_init_5)
cognew(@ddsload,@phase_init_6)
cognew(@ddsload,@phase_init_7)
pub setfrequency(frequency) | newphase,syncval,t1,t2
newphase := fraction(frequency,dds_clock)
'flag new frequency
outa[24] := 1
'wait for notification from each cog
repeat until phase_chan_1==0
repeat until phase_chan_2==0
repeat until phase_chan_3==0
repeat until phase_chan_4==0
repeat until phase_chan_5==0
repeat until phase_chan_6==0
repeat until phase_chan_7==0
'reset the flag
outa[24] := 0
'find the largest accumulator value
'set initial phase
phase_init_1 := newphase*1
phase_init_2 := newphase*2
phase_init_3 := newphase*3
phase_init_4 := newphase*4
phase_init_5 := newphase*5
phase_init_6 := newphase*6
phase_init_7 := newphase*7
t1 := cnt
'sync the cogs
syncval := 80_000+cnt '1ms into the future
phase_sync_1 := syncval+4*0
phase_sync_2 := syncval+4*1
phase_sync_3 := syncval+4*2
phase_sync_4 := syncval+4*3
phase_sync_5 := syncval+4*4
phase_sync_6 := syncval+4*5
phase_sync_7 := syncval+4*6
'set the new phase change
newphase *= 7
phase_chan_1 := newphase
phase_chan_2 := newphase
phase_chan_3 := newphase
phase_chan_4 := newphase
phase_chan_5 := newphase
phase_chan_6 := newphase
phase_chan_7 := newphase
t2 := cnt
ss.dec(t2-t1)
ss.tx($0d)
pri fraction(a,b) : f
'division of (a/b)*2^32
a <<= 1
repeat 32
f <<= 1
if a => b
a -= b
f++
a <<= 1
dat
org 0
ddsload
jmp #ddsstart
' mov dira,testdir
' mov outa,testout
'stop jmp #stop
'long $00_ff_00_80
long $00_ff_00_83
long $00_ff_00_86
long $00_ff_00_89
long $00_ff_00_8c
long $00_fe_00_8f
long $00_fe_00_92
long $00_fd_00_95
long $00_fd_00_99
long $00_fc_00_9c
long $00_fb_00_9f
long $00_fb_00_a2
long $00_fa_00_a5
long $00_f9_00_a8
long $00_f8_00_ab
long $00_f7_00_ae
long $00_f5_00_b1
long $00_f4_00_b4
long $00_f3_00_b6
long $00_f1_00_b9
long $00_f0_00_bc
long $00_ee_00_bf
long $00_ed_00_c2
long $00_eb_00_c4
long $00_e9_00_c7
long $00_e8_00_c9
long $00_e6_00_cc
long $00_e4_00_cf
long $00_e2_00_d1
long $00_e0_00_d3
long $00_de_00_d6
long $00_db_00_d8
long $00_d9_00_da
long $00_d7_00_dc
long $00_d5_00_df
long $00_d2_00_e1
long $00_d0_00_e3
long $00_cd_00_e5
long $00_cb_00_e7
long $00_c8_00_e8
long $00_c6_00_ea
long $00_c3_00_ec
long $00_c0_00_ee
long $00_bd_00_ef
long $00_bb_00_f1
long $00_b8_00_f2
long $00_b5_00_f3
long $00_b2_00_f5
long $00_af_00_f6
long $00_ac_00_f7
long $00_a9_00_f8
long $00_a6_00_f9
long $00_a3_00_fa
long $00_a0_00_fb
long $00_9d_00_fc
long $00_9a_00_fd
long $00_97_00_fd
long $00_94_00_fe
long $00_91_00_fe
long $00_8e_00_ff
long $00_8b_00_ff
long $00_87_00_ff
long $00_84_00_ff
long $00_81_00_ff
long $00_7e_00_ff
long $00_7b_00_ff
long $00_78_00_ff
long $00_74_00_ff
long $00_71_00_ff
long $00_6e_00_fe
long $00_6b_00_fe
long $00_68_00_fd
long $00_65_00_fd
long $00_62_00_fc
long $00_5f_00_fb
long $00_5c_00_fb
long $00_59_00_fa
long $00_56_00_f9
long $00_53_00_f8
long $00_50_00_f7
long $00_4d_00_f5
long $00_4a_00_f4
long $00_47_00_f3
long $00_44_00_f1
long $00_42_00_f0
long $00_3f_00_ee
long $00_3c_00_ed
long $00_39_00_eb
long $00_37_00_e9
long $00_34_00_e8
long $00_32_00_e6
long $00_2f_00_e4
long $00_2d_00_e2
long $00_2a_00_e0
long $00_28_00_de
long $00_26_00_db
long $00_24_00_d9
long $00_21_00_d7
long $00_1f_00_d5
long $00_1d_00_d2
long $00_1b_00_d0
long $00_19_00_cd
long $00_17_00_cb
long $00_16_00_c8
long $00_14_00_c6
long $00_12_00_c3
long $00_11_00_c0
long $00_0f_00_bd
long $00_0e_00_bb
long $00_0c_00_b8
long $00_0b_00_b5
long $00_0a_00_b2
long $00_08_00_af
long $00_07_00_ac
long $00_06_00_a9
long $00_05_00_a6
long $00_04_00_a3
long $00_04_00_a0
long $00_03_00_9d
long $00_02_00_9a
long $00_02_00_97
long $00_01_00_94
long $00_01_00_91
long $00_00_00_8e
long $00_00_00_8b
long $00_00_00_87
long $00_00_00_84
long $00_00_00_81
long $00_00_00_7e
long $00_00_00_7b
long $00_00_00_78
long $00_00_00_74
long $00_00_00_71
long $00_01_00_6e
long $00_01_00_6b
long $00_02_00_68
long $00_02_00_65
long $00_03_00_62
long $00_04_00_5f
long $00_05_00_5c
long $00_06_00_59
long $00_07_00_56
long $00_08_00_53
long $00_09_00_50
long $00_0a_00_4d
long $00_0c_00_4a
long $00_0d_00_47
long $00_0e_00_44
long $00_10_00_42
long $00_11_00_3f
long $00_13_00_3c
long $00_15_00_39
long $00_17_00_37
long $00_18_00_34
long $00_1a_00_32
long $00_1c_00_2f
long $00_1e_00_2d
long $00_20_00_2a
long $00_23_00_28
long $00_25_00_26
long $00_27_00_24
long $00_29_00_21
long $00_2c_00_1f
long $00_2e_00_1d
long $00_30_00_1b
long $00_33_00_19
long $00_36_00_17
long $00_38_00_16
long $00_3b_00_14
long $00_3d_00_12
long $00_40_00_11
long $00_43_00_0f
long $00_46_00_0e
long $00_49_00_0c
long $00_4b_00_0b
long $00_4e_00_0a
long $00_51_00_08
long $00_54_00_07
long $00_57_00_06
long $00_5a_00_05
long $00_5d_00_04
long $00_60_00_04
long $00_63_00_03
long $00_66_00_02
long $00_6a_00_02
long $00_6d_00_01
long $00_70_00_01
long $00_73_00_00
long $00_76_00_00
long $00_79_00_00
long $00_7c_00_00
long $00_7f_00_00
long $00_80_00_00
long $00_83_00_00
long $00_86_00_00
long $00_89_00_00
long $00_8c_00_00
long $00_8f_00_01
long $00_92_00_01
long $00_95_00_02
long $00_99_00_02
long $00_9c_00_03
long $00_9f_00_04
long $00_a2_00_05
long $00_a5_00_06
long $00_a8_00_07
long $00_ab_00_08
long $00_ae_00_09
long $00_b1_00_0a
long $00_b4_00_0c
long $00_b6_00_0d
long $00_b9_00_0e
long $00_bc_00_10
long $00_bf_00_11
long $00_c2_00_13
long $00_c4_00_15
long $00_c7_00_17
long $00_c9_00_18
long $00_cc_00_1a
long $00_cf_00_1c
long $00_d1_00_1e
long $00_d3_00_20
long $00_d6_00_23
long $00_d8_00_25
long $00_da_00_27
long $00_dc_00_29
long $00_df_00_2c
long $00_e1_00_2e
long $00_e3_00_30
long $00_e5_00_33
long $00_e7_00_36
long $00_e8_00_38
long $00_ea_00_3b
long $00_ec_00_3d
long $00_ee_00_40
long $00_ef_00_43
long $00_f1_00_46
long $00_f2_00_49
long $00_f3_00_4b
long $00_f5_00_4e
long $00_f6_00_51
long $00_f7_00_54
long $00_f8_00_57
long $00_f9_00_5a
long $00_fa_00_5d
long $00_fb_00_60
long $00_fc_00_63
long $00_fd_00_66
long $00_fd_00_6a
long $00_fe_00_6d
long $00_fe_00_70
long $00_ff_00_73
long $00_ff_00_76
long $00_ff_00_79
long $00_ff_00_7c
long $00_ff_00_7f
ddsstart or dira,dirmask 'set group of 8 pins to output
mov 0,sin0 'restore first element of sine table
mov p_phase_init,par 'set pointer to phase init parameter
mov p_phase_chan,par 'set pointer to phase chan parameter -4
mov p_phase_sync,par 'set pointer to phase sync parameter -8
mov p_phase_accu,par 'set pointer to phase accu parameter -12
add p_phase_chan,#4 'adjust to correct location
add p_phase_sync,#8 'adjust to correct location
add p_phase_accu,#12 'adjust to correct location
sync rdlong acc,p_phase_init 'set accumulator to initial phase value
rdlong pha,p_phase_chan 'set the phase value
rdlong syn,p_phase_sync 'set the sync value
waitcnt syn,#0 'sync with other cogs
loop mov tmp,acc 'need to change acc so copy it
shr tmp,#24 'get the high order byte
movs sin1,tmp 'index into sine table
add acc,pha 'add phase to accumulator
sin1 mov outa,0-0 'output sine value
mov outa,#0 'clear for next cog
testn exitmask,ina wz 'test for new frequency
mov tmp,acc 'need to change acc so copy it
shr tmp,#24 'get the high order byte
movs sin2,tmp 'index into sine table
add acc,pha 'add phase to accumulator
sin2 mov outa,0-0 'output sine value
mov outa,#0 'clear for next cog
if_nz jmp #loop 'carry on, unless frequency changed
wrlong zero,p_phase_chan 'indicated stopped
wait rdlong pha,p_phase_chan wz 'get the new phase value
if_z jmp #wait 'wait for a valid phase value
jmp #sync 'sync with other cogs
p_phase_init long $0 'pointer to initial phase value parameter
p_phase_chan long $0 'pointer to new phase change value parameter
p_phase_sync long $0 'pointer to clock sync value parameter
p_phase_accu long $0 'pointer to phase accumulator value (passed back) parameter
zero long $0
dirmask long $00_ff_00_ff
sin0 long $00_ff_00_80
exitmask long $01_00_00_00
syn long $0
pha long $0
acc long $0
tmp long $0
teststopped long $00_aa_00_00
testpha long 30006
testout long $00_72_00_00
Note: this code is experimental. I've only done a bit of testing but it seems to work!
The -con- part of the op code of all the table entrys in 0000 the whole table is a bunch of nop's.
Correct. That was the intended message
As for your rdlong aversion, your loop will lock nicely to a 3 hub window grid (48 cycles, 8+7*4 aligned to 16n). Only the first rdlong will suffer from jitter depending on where you've been. That said, if you change your code like this (table omitted):
mov dira, PinMsk 'LSB to PA0, LSB to PA7
:loop rdlong M_,par 'Get Freq Control Value
add Acc, M_ 'M $1FF max if literal with #$xxx
ror Acc, #16 'Postion index
movs :inline, Acc 'Store 9-bit index
andn :inline, #%1_00000000 'Clamp to 8-bits
rol Acc, #16 'Restore accumulator
:inline mov outa, 0-0 'Get long value from table
jmp #:loop 'Loop again
even the first rdlong is properly aligned. The first instruction of a cog is -4 cycles away from its hub-window, the table consists of 256 nops which can be ignored in terms of hub-window lock (256*4 = 16*64). Which means that the mov dira, PinMsk is also at -4 placing the rdlong at +0. So no jitter involved here. FWIW, it's 8..23 (8 + 0..15) cycles.
@Tom: FWIW, here is a 2 hub window version (table omitted). The original code just missed it by 4 cycles which demanded closer inspection. As the accumulator update is placed in the blind slot it requires a separate setup sequence before the loop (similar to my [post=896415]counter version[/post], phsx sampling).
mov dira, mask ' -4
rdlong tuning, par ' +0 = initial read
[COLOR="blue"]add accu, tuning[/COLOR] ' +8 prime accumulator
:loop mov temp, accu ' -4 get private copy
rdlong tuning, par ' +0 = update tuning word
shr temp, #24 ' +8 extract index
movs :inline, temp ' -4 implant index
[COLOR="blue"]add accu, tuning[/COLOR] ' +0 = update accumulator
:inline mov outa, 0-0 ' +4
jmp #:loop ' +8
mask long $FF
accu long 0
tuning res 1
temp res 1
Cool DDS. It is amazing to me the way things on this forum always improve themselves just by existing. Are we back in the 80s (figuring out the best way to do everything, and pushing the limits)? It sure feels like it and I really enjoy this.
I haven't actually built the circuit yet but am just drawing the schematic.
Wow, I have always figured out the theory, done the math, wrote a connection+components list, built the test circuit, and then (once it is working) drawn the schematic for my circuits.
@ Kuroneko
Thank's for the ideas. I was busy this weekend with Taxes but I tried your 3-hub version and
had good results. With and without testing for pin 8 as a sync signal.
When I tried your latest 2 hub version today my output frequency dropped from in the 10Khz range all the way down to 100Hz range.
I also was contemplating how you figure out what and where things are happening in the hub timing.
Still thinking on that , a good learning exercise.
I attached both 3hub and 2hub versions , the main spin code calls ChangeFreq in a loop I atached
the 3hub version for reference. The filename is incrementing as I experiment.
@David
Glad you liked the thread, this thread and the one on the AD9833 DDS chip were going good last year around this time.
When I tried your latest 2 hub version today my output frequency dropped from in the 10Khz range all the way down to 100Hz range.
Note that the tuning word is affected by loop length and accumulator size. So for the 3 hub window version you need $18937 (10kHz). The 2 hub window version uses a 32bit accumulator, i.e. tuning value for 10kHz is $10624DD. If you're happy with a 24bit accumulator then multiply the (24bit) tuning word by 256 before passing it to the DDS cog. HTH
Also, the start method should pass M instead of a hard-wired constant to the DDS cog (affects both implementations).
@ Kuroneko
I should have answered back sooner, sorry.
Your values work, I missed the switch to a 32 bit accumulator in your last version. I just copyed the code in and tested it.
My hard coded constants are just me playing around with values as I try things.
Other than watching it sweep back and forth I have been working on a Delphi App to send it values and do the tuning value
calculation. Not on the fly for sweep values but start, stop freq and fixed freq etc.
Comments
dds.spin:
DDS7_0.2.spin
Note: this code is experimental. I've only done a bit of testing but it seems to work!
:-)
As for your rdlong aversion, your loop will lock nicely to a 3 hub window grid (48 cycles, 8+7*4 aligned to 16n). Only the first rdlong will suffer from jitter depending on where you've been. That said, if you change your code like this (table omitted): even the first rdlong is properly aligned. The first instruction of a cog is -4 cycles away from its hub-window, the table consists of 256 nops which can be ignored in terms of hub-window lock (256*4 = 16*64). Which means that the mov dira, PinMsk is also at -4 placing the rdlong at +0. So no jitter involved here. FWIW, it's 8..23 (8 + 0..15) cycles.
Thank's for the info, I will give it a try.
A learning lesson here also
Thank's
Tom
Wow, I have always figured out the theory, done the math, wrote a connection+components list, built the test circuit, and then (once it is working) drawn the schematic for my circuits.
Thank's for the ideas. I was busy this weekend with Taxes but I tried your 3-hub version and
had good results. With and without testing for pin 8 as a sync signal.
When I tried your latest 2 hub version today my output frequency dropped from in the 10Khz range all the way down to 100Hz range.
I also was contemplating how you figure out what and where things are happening in the hub timing.
Still thinking on that , a good learning exercise.
I attached both 3hub and 2hub versions , the main spin code calls ChangeFreq in a loop I atached
the 3hub version for reference. The filename is incrementing as I experiment.
@David
Glad you liked the thread, this thread and the one on the AD9833 DDS chip were going good last year around this time.
Tom
Also, the start method should pass M instead of a hard-wired constant to the DDS cog (affects both implementations).
I should have answered back sooner, sorry.
Your values work, I missed the switch to a 32 bit accumulator in your last version. I just copyed the code in and tested it.
My hard coded constants are just me playing around with values as I try things.
Other than watching it sweep back and forth I have been working on a Delphi App to send it values and do the tuning value
calculation. Not on the fly for sweep values but start, stop freq and fixed freq etc.
Thank's
Tom