Actually, how WOULD I generate a 100 ns pulse from Spin?
Dennis Ferron
Posts: 480
In my earlier question about the ! and ~ operators, I was just looking for a shorthand and said I didn't have a time constraint on the pulse. Well I don't: on the reset pulse. But I do on a different line, the Single Step line. It turns out that in my circuit, if the Single-Step pulse I generate is too long, I will miss some of the 6502 instructions. I have to bring my Single Step line high to enable the processor to run again, but it has to go low immediately or the circuit will let the processor execute 2 or 3 instructions at a row before it stops.
Spin isn't fast enough to toggle the line without missing two or three 6502 cycles in between. I could do this in assembly but it seems silly to launch another cog just for the purpose of toggling a pin one time.
I see that the Synth object generates a signal using the counters without needing to launch a separate cogs. Is there a way to use on of the counters as a one-shot timer?
Spin isn't fast enough to toggle the line without missing two or three 6502 cycles in between. I could do this in assembly but it seems silly to launch another cog just for the purpose of toggling a pin one time.
I see that the Synth object generates a signal using the counters without needing to launch a separate cogs. Is there a way to use on of the counters as a one-shot timer?
Comments
I think you could use mode 4 or 5 (NCO/PWM), set your OUTA bit to zero, DIRA to output, load PHSx with -7 and FRQx with 1, then set CTRx. I think you'll get 8 clock cycles of one (8 * 12.5ns = 100ns), then PHSx will roll over to zero. At that point, you'll have maybe 25 seconds to check PHSx for a positive value and clear CTRx to zero to stop the whole thing, all very doable in Spin.
Mike
The trick with this scheme (and I'm not sure) is that the pin will stay at zero until one system clock after you set CTRx, then go high, then there'll be 7 more clocks where the pin stays at one, then it goes low as the PHSx counter rolls over (as -7 is incremented every system clock).
Post Edited (Mike Green) : 2/12/2007 6:08:47 AM GMT
one instruction.
I was thinking this might offer some optimization for the stepper code that could use some speed.
Post Edited (originator) : 2/12/2007 11:53:42 PM GMT
To make this work right, you have to set CTRx as the last thing you do. 1) You don't know what FRQx and PHSx are at this point and the counter will start adding FRQx to PHSx on the next clock (after setting CTRx). 2) The output pin will be enabled from the counter when you set CTRx. You want this to happen after everything else is set up for the pulse width to work properly.
Since you're running in Spin and an operation will take several microseconds anyway, you might as well follow the "CTRA := %00100 << 26 + 0" with an immediate "CTRA := 0". The timed pulse will be over already.
Mike
but it's just adding zero). This makes it really easy to reason about.
So what I'd do is at initialization, set frqa and phsa to 0, then set up ctrx. After that to generate a pulse, set phsa to your pulse width
and frqa to 1. To generate another pulse within a few seconds, just set phsa. After you are done, clear frqa.
You can of course also do active-low pulses in the same way, and for these, clearing ctrx is a problem. This is another reason to use
phsa and frqa to control the wire, and just let ctrx maintain its value.
Interpreting Mike's latest notes:
Interpreting rokiki's method:
Is anything being gained by the counter ideas versus outa[noparse][[/noparse]0] := 1 followed by outa[noparse][[/noparse]0] := 0?
outa[noparse][[/noparse]0] := 1
outa[noparse][[/noparse]0] := 0
gives you a pulse of about 3 microseconds or something.
frqa := 1
and then later
phsa := -1
gives you a 12.5ns pulse.
phsa := -10
gives you a 125ns pulse.
phsa := -8
gives you a 100ns pulse.
(These numbers may be slightly off and should be verified with a
high-quality oscilliscope; setting phsa will override accumulation for
that cycle, but it would be really good to verify the hardware works
the way we think it does).
In addition, if you have a loop where you are pulsing it
frequently, you get both edges of the pulse for a single phsa
assignment. For instance, let's say we needed 100 100ns pulses.
would do this. (The pulses would be separated by some number of
microseconds.)
I've wanted to be able to cascade counters in interesting ways
(to, for instance, use one output to gate another counter that
is generating pulses on another output) but none of the modes
seem to permit both output generation *and* conditional
accumulation. Too bad.
If we set frqa to 0 and phsa to 0, the output signal is low.
When we set frqa to 1, phsa starts incrementing, once per cycle.
But it will take 2B cycles (roughly) for it to go high, and that's a long time (some number of seconds), so it's not a big deal that it's free-running
while we (briefly) do other things.
Eventually we want a pulse. So we assign phsa to -8. This overrides the accumulation for the current
cycle, and phsa just gets smashed to -8. The sign bit of -8 is high, so the output goes high as soon as
the assignment is done.
Every cycle, the -8 is incremented, so it goes -7, -6, -5, -4, etc. When it gets to 0, the sign bit of phsa
is cleared, and at that instant, the output signal goes low.
So the output signal stayed high for eight clock cycles.
Again, after phsa gets to 0, it will continue to increment, 1, 2, 3, 4, etc., but until it gets past 2B, the
sign bit will stay low, so we have plenty of time to do other stuff and eventually clear frqa to zero to
stop the accumulation before it hits 2B.
Perhaps this diagram will help.
The length of the phase accumulator is 32 bits, and at 80 megahertz it takes 53687091200 nanoseconds to go through one complete cycle. Think of that as +/- 26.843545600 seconds. Mike rounded that off to 25 seconds. I should add that it takes that long to get through it if frqa = 1 or frqa = -1. Of course, when frqa=0, it just stops whereever it is. If frqa=+1, the phase advances toward the right, and if frqa=-1 is regresses toward the left. We won't mention higher + and - values of frqa, except to say that they make the phase advance or regress faster.
That is what lets you generate the pulse you want. Phase starts at zero, pa0 is low output as phase starts advancing. Suddenly the program shifts the phase back to -8, the pin pa0 goes high and stays there for 8 clock cycles until it again hits phase zero. At that point in time your program has some 25 seconds to quench the counter before it will set that pin high again on you. But this program acts right away. It is a spin program, so it will take many more than 8 cycles to interpret the frqa:=0 command, and on the diagram I estimated 50 clock cycles. That is where it resets the counter. If you wanted to generate a longer pulse, your program would either have to wait, or come back and quench it sometime before that 26.84... seconds expired. There are lots of effects that can be had by plaing with the phase and frequency.
I wish the counter had a couple more modes, too, agreeing with rokicki. For example, if this NCO mode could have feedback in the same manner as POSDET and NEGDET, then the advance of the counter would depend on the counter output phs(31) from one cycle back. The pulseout counter could self-quench.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Tracy Allen
www.emesystems.com
I know this doesn't add up, except that now there are 4 Spin instructions versus the previous 2
***Ok I placed the ctra, frqa, and the dira in the init(and non-running state code), so there is now only instruction per running cycle, I hear maybe a 5 % increase in speed. I use frqa after the motor is done, which would not usually exceed 25 seconds, so I'll have to put a frqa := 0 somewhere, although it is already placed in the non-running state.
Post Edited (originator) : 2/13/2007 2:45:02 AM GMT
Remember that the number of spin statements executed per pulse is still about the same (we don't know exactly without analyzing the byte codes). What you get is a precisely determined pulse width that can be as short as one clock cycle. The number of pulses per second is about the same.
Mike
The only instruction in the running loop is phsa := -20, everthing else lives in the init, and parked states.
I find this topic quite profound. The three of you (Tom, Mike, Tracy) have done a fantastic job of explaining this specific mode of the counters. I have recently been commenting out Tom's SD code, in an effort to apply it to my stuff. I basically got stumped and moved on when I got to the following line of code:
neg FRQA, #1
I simply could not understand the significance. I recognized that it was placing -1 (ALL 1's) into FRQA, but it never "clicked". Now I understnad why!
This is a VERY clever way of using the PHSA and FRQA registers. Setting PHSA to -18 (for instance) is quite handy, ESPECIALLY when used in bit-banging code. I like the option to use the 26 seconds as a time-out, if needed.
Maybe Paul and update the counter document and place a link to this post in the appendix or reference section???
-Parsko
A few things to clarify.
Does PHSx accept upper and lower limits, similar to limting a variable. PHSx #> 2B
If an input pin is used to count, the Frqx is not used as the inc/dec input. What is the method to assign the pin to PHSx? If this is simply covered in the app note, I will check it this afternoon.
FRQx is always used as the increment/decrement value. If you're using an input to trigger counting, the input triggers adding FRQx to PHSx. The CTRx value assigns pin(s). The Propeller Manual and the app note both discuss this.
I figure, if I get the CTRA config correct, then what I'll do is move all the setup outside the time dependent code and just call a modified version:
Do I have the right idea / method here?
Fred
PS: Thanks for ALL of the help everyone!
You could set it up to have a single "on" bit followed by "off" bits, and set the timing for the bits to be the duration you need for the one bit (100 ns?), or you could issue 16 "on" in a row followed by 16 "off" and set the timing to be 1/16th of what you need. The second way will finish sooner in case you need to issue another one immediately.
So I'll call it twice...
Instead of the WAITCNT(400+CNT) command which would be overkill in waiting, could I use something like WAITPNE(|<APin,|<APin,0)?
I gathered from the docs, should cause the cog to not execute instructions but let the clock continue until the pin changes state.
But, I would guess that if the pause is too short, the condition would just fall through... I've testing to do..
See Marko's improvement, below.