Synth object less than 1Hz
electricsmith
Posts: 20
Just wondering, I need to generate a broad range of frequencies with some good resolution below 1Hz up to 1KHz.· Has anyone modified the Synth object to work for values less than 1Hz?
Comments
What's the purpose?
Leon
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Amateur radio callsign: G1HSM
frq := fraction(Freq, CLKFREQ*1000, s) 'Compute FRQA/FRQB value
for
frq := fraction(Freq, CLKFREQ, s) 'Compute FRQA/FRQB value
in the Synth code below, wouldn't that give me Hz output that is of the resolution I need? (e.g. I need to generate 1.234Hz on pin 1,
so I send the command synth("A",1,1234))
PUB Synth(CTR_AB, Pin, Freq) | s, d, ctr, frq
Freq := Freq #> 0 <# 128_000_000 'limit frequency range
if Freq < 500_000 'if 0 to 499_999 Hz,
ctr := constant(%00100 << 26) '..set NCO mode
s := 1 '..shift = 1
else 'if 500_000 to 128_000_000 Hz,
ctr := constant(%00010 << 26) '..set PLL mode
d := >|((Freq - 1) / 1_000_000) 'determine PLLDIV
s := 4 - d 'determine shift
ctr |= d << 23 'set PLLDIV
frq := fraction(Freq, CLKFREQ, s) 'Compute FRQA/FRQB value
ctr |= Pin 'set PINA to complete CTRA/CTRB value
if CTR_AB == "A"
CTRA := ctr 'set CTRA
FRQA := frq 'set FRQA
DIRA[noparse][[/noparse]Pin]~~ 'make pin output
if CTR_AB == "B"
CTRB := ctr 'set CTRB
FRQB := frq 'set FRQB
DIRA[noparse][[/noparse]Pin]~~ 'make pin output
PRI fraction(a, b, shift) : f
if shift > 0 'if shift, pre-shift a or b left
a <<= shift 'to maintain significant bits while
if shift < 0 'insuring proper result
b <<= -shift
repeat 32 'perform long division of a/b
f <<= 1
if a => b
a -= b
f++
a <<= 1
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Style and grace : Nil point
The same technique could be used on the Propeller.
Leon
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Amateur radio callsign: G1HSM
@Leon: Talk is cheap -- post code; Propeller code, that is, nobody here gives a rat's behind about the AVR or the XMOS. When I want AVR code, I log in to an AVR forum.
Converting it to PASM should be quite straightforward, it'll run a lot faster with 32-bit arithmetic. If I can find one of my AVR or ARM boards with a DAC chip on it I'll try it on the Propeller.
Leon
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Amateur radio callsign: G1HSM
Post Edited (Leon) : 12/31/2009 9:39:26 PM GMT
let's say 0.007943 Hz. What values do r24..r30 have in this case ?
best regards
Stefan
Leon
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Amateur radio callsign: G1HSM
The reason for using PASM instead of Spin is to get rid of the instruction overhead necessary to get good accuracy at fractional Hz values.
Just discovered the "Ignore User" button and no longer have to put up with [noparse][[/noparse]near-useless] AVR assembly in the Propeller forum
Post Edited (JonnyMac) : 1/1/2010 1:34:09 AM GMT
That's just plain mean. I found the AVR code useful in this thread - it's easy understand as explained, and easy to port to PASM. Thank you Leon!
Regards, David
In some cases I love to rersearch across the internet to find information (even if it is just for somebody else and not for me)
In this case I don't. Leon or Drone can you post a DETAILED example
how to calculate every single value and how the code works
To me as I'm NOT familiar with AVR-assembler I just see
add values, do some kind of output and jump
but I can't see any relation between the
add the out and the lpm (whatever it is)
I would be very happy if you would post an example that shows ALL details of how to calculate it
starting from a low frequency
r24 = ........
r25 = .......
etc.
best regards
Stefan
LPM is Load Program Memory. It loads the memory addressed by R29 and R30 (from the waveform look-up table) into the R0 register. It is then output to the DAC.
I based my hardware and software on this work by Jesper Hansen:
www.myplace.nu/avr/minidds/
The calculation you are after is trivial for a Propeller, as you only have one 32-bit register.
Leon
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Amateur radio callsign: G1HSM
Post Edited (Leon) : 1/1/2010 2:21:36 PM GMT
Happy New Year!
It is run from a timer ISR.
Leon
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Amateur radio callsign: G1HSM
Do you understand why the counter module has the poor resolution? With an 80 mhz clkfreq, the frq parameter is 53. That means that every 12.5 nanoseconds, 53 is added to the phase accumulator, and after 1.013 second the sum passes 2^32 and rolls over back to zero. And repeats at a base frequency of 0.9872 Hz. Not exactly one second. Nearby values bracket the 1 second value:
52 --> 0.9686 Hz
53 --> 0.9872 Hz
54 --> 1.005 Hz
55 --> 1.024 Hz
That is in the nature of direct digital synthesis, which is exactly what the cog counters do. If you can use an external divider chip, the resolution would be much better and also set it and forget it, involving only the cog counter.
53687 --> 999.998 Hz for 1000 Hz entry
53740 --> 1000.986 Hz for 1001 Hz entry
In the digital synthesis of 1000 Hz, the frq value 53687 is added to the phase accumulator 80000 times before it overflows and rolls back to zero, and that is one compete cycle of the most significant bit, high and low. (Actually, it sometimes takes 80001 times for it to roll over, a slightly longer period that makes it 999.998 Hz instead of 1000 Hz, but that bobble and subharmonic content is intrinsic to DDS).
I think the Spin routine that Jon posted about 5 back might well be quite accurate enough for your application at the milliHertz level. There will be a deterministic delay from the waitcnt to the actual pin toggle, which will not affect the frequency. I sense there is some confusion about how to calculate the fractional frequency. To repeat the core snippet,
The constant hctics needs to expressed in number of clock cycles. So if you specify your desired frequency FmHz in milliHertz, then formally,
hctics = clkfreq *1000 / (FmHz * 2) where 1000 is for the milli factor and the 2 is for 1/2 cycle low and 1/2 cycle high.
For example, with clkfreq=80 MHz, and a desired of 1001 milliHertz, then hctics=39_960_039. Check: 12.5 ns period * 39_960_039 = 499.5 ms, half a period at 1.001 Hz.
But as you already know, you can't muliply clkfreq by 1000 within the 2^32 long. One way to manage it is,
hctics := (clkfreq * 50 / FmHz) * 10 ' assuming clkfreq<=80mHz, stays within bounds of 2^32
hctics := (clkfreq * 25 / FmHz) * 20 ' assuming clkfreq<=80mHz, stays within bounds of 2^31
or with a two-step division (slightly more accurate) as
hctics := (clkfreq / FmHz * 500) + (((clkfreq // FmHz) * 500) / FmHz) ' it would be *500 to account for the factor of 2
edit: need to stay within bound of 2^31 to avoid probems with twos complement
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Tracy Allen
www.emesystems.com
Post Edited (Tracy Allen) : 1/2/2010 3:45:13 AM GMT
My intent here is to start a cog that continually checks some variables modified by the main cog and turns on or off on a specific pin
a squarewave that is accurate to the mHz resolution as discussed above.· I understand that the code below takes some time to run so there
will be some inaccuracy.· I was thinking if I could determine what that tick count for those instructions were it could probably be subtracted
from the hctics value to 'calibrate' it.· I may be way off base though.·
************************************************************************************
'Pass this routine the address of the frequency in (cycles/1kSeconds or mHz) (e.g. 1.234Hz would be 1234Hz)
'Pin is the address of the output pin
'Repeat_ptr is the address to a flag that is set when the synth should generate and when it should not
<<<<code to be inserted here to start this routine in its own cog>>>>
PUB Synth_mHz(Pin_ptr, mHz_ptr, Repeat_ptr) | A,B,C,hctics
· A:=LONG[noparse][[/noparse]Pin_ptr][noparse][[/noparse]0]
··DIRA[noparse][[/noparse]A]:=1
·
· REPEAT
··· B:=LONG[noparse][[/noparse]mHz_ptr][noparse][[/noparse]0]
··· C:=LONG[noparse][[/noparse]Repeat_ptr][noparse][[/noparse]0]
··· IF C==0
······NEXT
··· hctics := (clkfreq /·B * 500) + (((clkfreq / * 500) / ··'<<minus some adj for·repeat loop time
·················································································'I changed the clkfreq//B to clkfreq/B, I assumed this was a typo
··· REPEAT
· ··· C:=LONG[noparse][[/noparse]Repeat_ptr][noparse][[/noparse]0]
····· IF C==0
······· EXIT
····· ELSE
······· WAITCNT(hctics + CNT)
······· !OUTA[noparse][[/noparse]A]
DAT
· PIN············· LONG 1
· mHz············ LONG 1234
· SYNTH_ON··· LONG 1
Something like this -- pass the pin (#), the address of the enable flag, and the address of the half-cycle ticks value.
I updated (and attached) the PASM version I wrote yesterday to add an enable flag (your code indcates a desire for that) and uses Tracy's hctics calculation in favor of the fp routines I originally tried.
Post Edited (JonnyMac) : 1/2/2010 1:05:52 AM GMT
Here's a cool tip on controlling an IR LED. You can use a background cog or even a foreground counter module to do the modulation -- just connect the modulation output to the cathode side. That way, you can simply write a "1" to the cathode pin (from any cog) to disable the IR output. Pretty cool.
Post Edited (JonnyMac) : 1/2/2010 2:05:14 AM GMT
Oops, I got caught in the twos complement trap. The result, 80_000_000 * 50 = 4_000_000_000 is indeed less than 2^32. However, it is greater than 2^31, so the subsequent division evaluates it as a twos complement number, -294_967_296. That gives the wrong answer. So the following is safe and is a positive number when clkfreq=80MHz:
hctics := (clkfreq * 25 / FmHz) * 20 ' assuming clkfreq<=80mHz, stays within bounds of 2^31
The other formula I listed is more accurate
@electricsmith,
The // in the formula is not a typo. The first expression is a straight division by B, then the second expression works on the remainder from the first division.
Simple example: 8000/113 = 70.79646017699
The original integer result has been shifted left three decimal places to make room for the contribution from the remainder.
This roundabout method allows making calcs with big numbers so that they don't overflow 2^32, or I should say 2^31 so as not to get into trouble with twos complement. Take this, with clkfreq 80 MHz and B=1234 milliHertz.
hctics := ((clkfreq / B * 1000) + (((clkfreq // * 1000) / ) / 2
' first expression = (80_000_000 / 1234 * 1000) = 64829000 which is less than 2^31 so long as B is greater than about 40 milliHertz
' second expression = (80_000_000 // 1234 * 1000 / 1234) = (1014000 / 1234) = 821
' overall result hctics = 64_829_821 / 2 = 32_414_910
That compares with an "exact" calculated value of (clkfreq * 1000) / (1234 * 2) = 32,414,910.8589951
Working that with the simpler formula,
hctics := (80_000_000 * 25 / 1234) * 20 ' result 32,414,900 pretty close
and better than simply
hctics := 80_000_000 / 1234 * 500 ' result 32,414,500
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Tracy Allen
www.emesystems.com
@Leon:
If you explain your NON-propeller-code like Tracy Allen did explain things, you might win people to take a look at other microcontrollers and even win them to use other microcontrollers too.
With your short and snippy style like above I guess you are just annoying
best regards
Stefan
Post Edited (StefanL38) : 1/2/2010 6:46:47 AM GMT
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Tracy Allen
www.emesystems.com