PDA

View Full Version : Actually, how WOULD I generate a 100 ns pulse from Spin?



Dennis Ferron
02-12-2007, 01:29 PM
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?

Mike Green
02-12-2007, 01:45 PM
Dennis,
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

T Chap
02-12-2007, 01:57 PM
25 seconds? That is puzzling

Mike Green
02-12-2007, 02:04 PM
Once PHSx rolls over (to zero), it will continue to accumulate FRQx values (of 1) every clock cycle. It will take 2^30 clocks until the sign bit becomes a one again (setting the output to one). At 80MHz, that's about 25 seconds.

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

T Chap
02-12-2007, 02:05 PM
Ah ok I am reading the app note now to understand it. I was looking for some similar ideas to optimize the stepper control in Spin.

rokicki
02-13-2007, 02:27 AM
Right, I use this trick in my SD driver routines (even the assembly ones). Very useful to be able to set two edges using
one instruction.

T Chap
02-13-2007, 04:25 AM
Here is what I concluded from Mike's idea, but I haven't hooked up a scope yet to see the results:





outa[0] := 0
dira[0] := 1
ctra := %00100 << 26 + 0
frqa := 1
phsa := -7
repeat ' unless this is nested already in a repeat




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

Mike Green
02-13-2007, 05:17 AM
originator,
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

rokicki
02-13-2007, 05:50 AM
Generally, I set up ctrx once, and then just manipulate frqa and phsa. Setting frqa to 0 "stops" the accumulation (it's still happening,
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.

Mike Green
02-13-2007, 05:58 AM
Thanks rokicki, I hadn't thought of that. Also, for active low pulses, you could set PHSx to -1 and FRQx to 0. When you're ready to generate a pulse, set FRQx to -1 (PHSx will start to decrement, but this won't change the I/O pin for about 25 seconds), then set PHSx to the number of clock cycles - 1 that you want for the pulse (since PHSx == 0 will still produce a low). Once PHSx goes negative, you have about 25 seconds to set FRQx to zero to effectively stop the counter.

T Chap
02-13-2007, 08:05 AM
Ttrying to implement the concepts.

Interpreting Mike's latest notes:




outa[0] := 0
dira[0] := 1
frqa := 1
phsa := -7
ctra := %00100 << 26 + 0
ctra := 0





Interpreting rokiki's method:





' init the counter
outa[0] := 0
dira[0] := 1
frqa := 0
phsa := 0
ctra := %00100 << 26 + 0

'to pulse a pin
frqa := 1
phsa := 8 'pulsewidth 100ns example
frqa := 0
phsa := 0





Is anything being gained by the counter ideas versus outa[0] := 1 followed by outa[0] := 0?

rokicki
02-13-2007, 08:19 AM
Only if you need brief pulses.

outa[0] := 1
outa[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.




phsa := 0 # clear phsa
frqa := 1 # start accumulation
repeat 100
phsa := -8 # send a short pulse
frqa := 0 # terminate accumulation




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.

T Chap
02-13-2007, 08:32 AM
I am reading the app note again, and still not clear on how the phsx works in the example above. I know it is the accumulator for frqx, but is the valu3 assigned to phsx a value that overrides any current accumlated value? So that in your example: phsa := -8, is the phsa resetting the high pin to low after 8 pulses?

rokicki
02-13-2007, 08:47 AM
Okay, what happens is the high order bit (sign bit) of phsa is the output signal.

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.

T Chap
02-13-2007, 08:56 AM
Thanks rokicki, now I got it. The nice thing is that the code is free to do other stuff while the outputs are being updated.

Tracy Allen
02-13-2007, 09:17 AM
The timing of the pulse produced by the counter will be much more precise than that produced by the Spin outa[0]:=1 : outa[0]:=0, which is dependent on the sloppy and relatively slow speed of the interpreter. Also, the pulse can be much shorter, down to multiples of 12.5 nanoseconds. There are several ways to do this, in terms of which way it takes the phase.



PUB pulse100
'to pulse a pin pa0 for 8 clock periods, 100 ns at 80 mhz
ctra := %00100 << 26 + 0
dira[0]:=1
frqa := 1 ' pin is stil low
phsa := -8 'pulsewidth 100ns example
frqa := 0 ' <--- in the time it takes Spin to interpret this line, the counter has already generated the 100ns pulse
return



Perhaps this diagram will help.

http://forums.parallax.com/attachment.php?attachmentid=45441

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 (http://www.emesystems.com)

T Chap
02-13-2007, 09:42 AM
Wow thanks Tracy, only just now do I "see" why it is called Phsx. This is very helpful.

T Chap
02-13-2007, 10:30 AM
I substituted the following for the stepper pin on/off, the motors would not run at all below -18, and actually runs noticably slower than the Spin outa[Mstep] := 1 outa[Mstep] := 0.

I know this doesn't add up, except that now there are 4 Spin instructions versus the previous 2





'ctra := %00100 << 26 + Mstep
'dira[0]:=1 pin dir already established in the init, so it is commented out
'frqa := 1
phsa := -18
'frqa := 0






***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

Mike Green
02-13-2007, 10:46 AM
originator,
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

T Chap
02-13-2007, 10:50 AM
Mike, I edited the last post to reflect that I could now use only one instruction to turn on and off the pin, thus some worthwhile preformance increase.


The only instruction in the running loop is phsa := -20, everthing else lives in the init, and parked states.

parsko
02-13-2007, 04:20 PM
Gentlemen,

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

T Chap
02-14-2007, 03:37 AM
Many uses come to mind with the demystification of the counters.

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.

Mike Green
02-14-2007, 04:08 AM
PHSx does not accept upper and lower limits. It's simply a counter. You can set up a program in the cog that just waits for a particular count to arrive and adjust PHSx or FRQx as needed. There are some examples that have been posted on sine wave generation using PWM that do this.

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.

KaosKidd
05-22-2013, 07:25 PM
According to the documents, the CTRA can handle one or two pins, with this being the case, I believe this code will toggle two pins for the duration listed. So what I did was take Tracy Allan's code and modified it so that it can be used for one or two pins. I haven't proven the duration yet (that will come), but the concept of using the counter to toggle two pins in a (shorter then spin can do) period is one of the problems I'm struggling with. And this appears to be a much faster / easier solution then a complete object with PASM in it to simply turn two pins on and off with a duration between 3.5uS and 6.0uS.



PUB Shoot (APin, BPin)
ctra := (($00100 << 26) + (BPin << 14)) + APin 'Set the CTRA CTRMODE to be NCO with the BPin and APin set to incomming vars
DIRA[APin] := 1 'Set APin to output
DIRA[BPin] := 1 'set BPin to output
frqa := 1 'Start the counter running
phsa := -360 'DO IT for right around 4.5uS
waitcnt (400 + cnt) 'ensure the ctr finishes it's job, 400 is about the smallest WAITCNT can go.
frqa := 0 'Stop from happening again...
Return


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!

JasonDorie
05-22-2013, 08:52 PM
Couldn't you also do this with the video generator? Set it up appropriately and the video generator will output any sequence of bits you want. You can use it to output to a single pin, pair of pins, etc. You issue a waitvid command with the 32-bit value containing the bits you want output, and it will issue them. It only waits if it's already busy sending out bits from a previous command.

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.

Mike Green
05-22-2013, 09:00 PM
Look at the chart describing the various counter modes. You'll see that APin and BPin are complements in NCO Differential mode (opposite states). In some modes, BPin is the opposite of APin, just delayed by one clock pulse. I don't think that's what you're expecting.

KaosKidd
05-22-2013, 09:17 PM
Thanks for pointing that out to me... :)

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.. :)



PUB Shoot (APin)
ctra := $00100 << 26 + APin 'Set the CTRA CTRMODE to be NCO with the APin set to incomming vars
DIRA[APin] := 1 'Set APin to output
frqa := 1 'Start the counter running
phsa := -360 'DO IT for right around 4.5uS
waitpne (|< APin, |< APin,0) 'Wait until the pin is low (should happen real fast)
frqa := 0 'Stop from happening again...
Return

JonnyMac
05-23-2013, 03:32 AM
You might want to make your routine a little more generic. I tend to favor "careful" programming, so I might do something like this:

See Marko's improvement, below.

kuroneko
05-23-2013, 04:11 AM
You might want to make your routine a little more generic.
That isn't quite working as expected because the delay between setting up phsa and frqa is added to the pulse width. Better would be:

pub pulse_out(pin, ticks)

'' Generate high-going pulse on pin
'' -- uses CTRA to create pulse

outa[pin] := 0 ' make sure pin is clear
dira[pin] := 1 ' make output

frqa := phsa := 0 ' prevent pre-arming
ctra := (%00100 << 26) | pin ' setup counter (NCO-SE)
frqa := 1 ' enable pulse
phsa := -ticks ' load pulse length

waitpne(|< pin, |< pin, 0) ' ensure finished
frqa := 0 ' prevent auto-output
ctra := 0

JonnyMac
05-23-2013, 04:24 AM
Sorry, you're right -- when phsa is negative the output goes high. Thanks for the reminder.