Shop OBEX P1 Docs P2 Docs Learn Events
Actually, how WOULD I generate a 100 ns pulse from Spin? — Parallax Forums

Actually, how WOULD I generate a 100 ns pulse from Spin?

Dennis FerronDennis Ferron Posts: 480
edited 2013-05-22 20:24 in Propeller 1
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?

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2007-02-12 05:45
    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 ChapT Chap Posts: 4,223
    edited 2007-02-12 05:57
    25 seconds? That is puzzling
  • Mike GreenMike Green Posts: 23,101
    edited 2007-02-12 06:04
    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 ChapT Chap Posts: 4,223
    edited 2007-02-12 06:05
    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.
  • rokickirokicki Posts: 1,000
    edited 2007-02-12 18:27
    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 ChapT Chap Posts: 4,223
    edited 2007-02-12 20:25
    Here is what I concluded from Mike's idea, but I haven't hooked up a scope yet to see the results:

    
    outa[noparse][[/noparse]0] := 0
    dira[noparse][[/noparse]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 GreenMike Green Posts: 23,101
    edited 2007-02-12 21:17
    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
  • rokickirokicki Posts: 1,000
    edited 2007-02-12 21:50
    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 GreenMike Green Posts: 23,101
    edited 2007-02-12 21:58
    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 ChapT Chap Posts: 4,223
    edited 2007-02-13 00:05
    Ttrying to implement the concepts.

    Interpreting Mike's latest notes:

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



    Interpreting rokiki's method:

    
    ' init the counter
    outa[noparse][[/noparse]0] := 0
    dira[noparse][[/noparse]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[noparse][[/noparse]0] := 1 followed by outa[noparse][[/noparse]0] := 0?
  • rokickirokicki Posts: 1,000
    edited 2007-02-13 00:19
    Only if you need brief pulses.

    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.

    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 ChapT Chap Posts: 4,223
    edited 2007-02-13 00:32
    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?
  • rokickirokicki Posts: 1,000
    edited 2007-02-13 00:47
    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 ChapT Chap Posts: 4,223
    edited 2007-02-13 00:56
    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 AllenTracy Allen Posts: 6,664
    edited 2007-02-13 01:17
    The timing of the pulse produced by the counter will be much more precise than that produced by the Spin outa[noparse][[/noparse]0]:=1 : outa[noparse][[/noparse]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[noparse][[/noparse]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.

    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
    432 x 106 - 3K
  • T ChapT Chap Posts: 4,223
    edited 2007-02-13 01:42
    Wow thanks Tracy, only just now do I "see" why it is called Phsx. This is very helpful.
  • T ChapT Chap Posts: 4,223
    edited 2007-02-13 02:30
    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[noparse][[/noparse]Mstep] := 1 outa[noparse][[/noparse]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[noparse][[/noparse]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 GreenMike Green Posts: 23,101
    edited 2007-02-13 02:46
    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 ChapT Chap Posts: 4,223
    edited 2007-02-13 02:50
    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.
  • parskoparsko Posts: 501
    edited 2007-02-13 08:20
    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 ChapT Chap Posts: 4,223
    edited 2007-02-13 19:37
    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 GreenMike Green Posts: 23,101
    edited 2007-02-13 20:08
    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.
  • KaosKiddKaosKidd Posts: 296
    edited 2013-05-22 11:25
    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!
  • JasonDorieJasonDorie Posts: 1,930
    edited 2013-05-22 12:52
    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 GreenMike Green Posts: 23,101
    edited 2013-05-22 13:00
    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.
  • KaosKiddKaosKidd Posts: 296
    edited 2013-05-22 13:17
    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
    
  • JonnyMacJonnyMac Posts: 9,107
    edited 2013-05-22 19:32
    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.
  • kuronekokuroneko Posts: 3,623
    edited 2013-05-22 20:11
    JonnyMac wrote: »
    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 := [COLOR="#FF0000"]phsa :=[/COLOR] 0                                     ' prevent pre-arming
      ctra := (%00100 << 26) | pin                          ' setup counter (NCO-SE)
    [COLOR="#FF0000"]  frqa := 1                                             ' enable pulse
      phsa := -ticks                                        ' load pulse length
    [/COLOR]
      waitpne(|< pin, |< pin, 0)                            ' ensure finished
      frqa := 0                                             ' prevent auto-output
      ctra := 0
    
  • JonnyMacJonnyMac Posts: 9,107
    edited 2013-05-22 20:24
    Sorry, you're right -- when phsa is negative the output goes high. Thanks for the reminder.
Sign In or Register to comment.