Shop OBEX P1 Docs P2 Docs Learn Events
smartpin questions (was: how to capture time on pin change) — Parallax Forums

smartpin questions (was: how to capture time on pin change)

ManAtWorkManAtWork Posts: 2,176
edited 2020-01-31 08:50 in Propeller 2
Let's say I want to count position of a step/dir signal pair. That's easy, smart pin mode %01101 does "accumulate A-input positive edges with B-input supplying increment (B=1) or decrement (B=0)".

For a servo it would be useful to also measure velocity. The control loop samples the position counter at a rate of serveral 1000 times a second. At low speeds the velocity (position delta) dithers between 0 and 1 for positive or 0 and -1 for negative velocity. This is very coarse and results in a stepper motor like noise.

If I additionally recorded a time-stamp of the last step signal edge I could calculate velocity as (delta position)/(delta time) which gives a very high resolution even for speeds below 1 step per loop period.

I haven't found a smart pin mode for capturing (absolute) time. There are modes for measuring time between edges but I think they are not ideal for this purpose because I don't know in advance how many edges are to be counted/measured in the next period. Maybe the best method would be to trigger an interrupt on each positive edge of the step signal and record the current time with a GETCT instruction. Does anybody have a better idea?

Maximum step frequency is 1MHz or a bit higher. If the interrupt needs only one single instruction it won't load the cog too much. An incremental encoder works the same way just with a quadrature signal instead of a step/dir pair (mode %01011 instead of %01101). Input filtering can be used to avoid interrupt overload caused by a high frequency oscillation when the optical sensor of the encoder is close to the edge of a codewheel line. For capturing time-stamps of a quadrature signal change the interrupt has to be triggered on both edges of both A and B inputs.

«1

Comments

  • cgraceycgracey Posts: 14,150
    You could always use TWO smart pins - one to track the step/dir and another to count cycle or state times. You could use more than two pins, for that matter. Each smart pin can look at its own pin, plus pins within +/-3 of itself.
  • My problem is that I don't understand how the "count time", "count states" or "count periods" modes work. I have the vague feeling that some of the "for periods in X+ cycles, count ..." might provide what I'm looking for.

    The problem is that I don't have a stable, repeating input frequency. The encoder position or step/dir command can stop at any moment or even reverse direction in the middle of a measurement period. Er, period is probably the wrong word. The P2 docs use "period" for the time between A/B input edges.
    A measurement is taken across some number of A-input rise/edge to B-input rise/edge periods, until X clock cycles elapse and then any period in progress completes.

    If the current period doesn't complete (step sequence stops) then no result is stored if I understand correctly. I'll draw a timing diagram to illustrate it...
  • Let's assume a sysclock of 100MHz for easy calculation and my controll loop samples the state of the position counter every millisecond (once per 100_000 syclocks). At the second sample (time=200_000) the timestamp register reads t2=158876 and position is p2=0. The state at the previous sample was t1=56998 and p1=-2. So velocity is calculated as dp/dt = (p2-p1)/(t2-t1) = 1.963e-5 or 1.963 counts per ms.

    For the fourth sample velocity is (2-1)/(336_789-243567)=1.073 counts/ms.

    If the step pulses stop after the fourth sample then the fifth sample would give dp and dt both zero. Division by zero is undefined but as no pulses arrived velocity can be assumed to be zero.
    770 x 118 - 3K
  • evanhevanh Posts: 15,910
    edited 2020-01-28 20:59
    Have a read of the renamed list I made. It should help with categories at least. https://forums.parallax.com/discussion/comment/1482243/#Comment_1482243

    If you are sure you can process each step one by one then you can measure just the duration of each step - mode %10000_0. But most likely you'll be wanting to also smoothly handle multiple steps per update, in that case, as Chip indicated, you'll be wanting to know both the step count and the duration over which they occurred. This requires two smartpins to give the two measurements - modes %10111_0 and %10101_0 respectively.
  • evanhevanh Posts: 15,910
    A dynamic filter on the velocity component of the feedback might be a better solution though. When motion becomes very slow, the filter can be built up to smooth it out.
  • jmgjmg Posts: 15,172
    edited 2020-01-28 21:27
    ManAtWork wrote: »
    The problem is that I don't have a stable, repeating input frequency. The encoder position or step/dir command can stop at any moment or even reverse direction in the middle of a measurement ....

    P2 can capture sysclks on either/both edges, and can do that for single, or over many (X) edges.

    The issue may be around your wide dynamic range :
    Single edges would work fine for lowest speeds (let's say to ~10kHz / 100us report rates), and you could run another pin cell in parallel, that captures every X events.
    That second cell will be an average, and will not precisely catch a stall, but would be used for > 10kHz ( eg X = 1000, would read up to 10MHz at 100us report rate )
    If you know the appx step rates at any ms, you could vary X to dynamically scale.

    Addit: P2 will capture-and-auto-clear, so it will always give an edge-to-(Xth)edge result.
    I'm rusty on how P2 manages overflows : ie if a) the first reading is given to users (later ones ignored), or if b) the most recent reading is given ?
    If you poll it often enough, velocity will not change much
  • ManAtWorkManAtWork Posts: 2,176
    edited 2020-01-29 08:28
    evanh wrote: »
    A dynamic filter on the velocity component of the feedback might be a better solution though. When motion becomes very slow, the filter can be built up to smooth it out.

    Low pass filters have a disadvantage: phase delay. Delays are critical in a motion control application. Delays in the feedback loop limit the gain or can cause overshot. Delays in the command path are no problem as long as they are constant. Variable delay distorts the toolpath trajectory and has to be avoided. My solution has the advantage that delta time is always as close to the sampling period as possible. So phase delay is not perfectly constant but as close as it could.

    Of course, if there are no or very few pulses you have no information about velocity until the next edge arrives. Maybe the best solution is to caclulate a lower and upper limit based on the information available and make a guess that's somewhere in between when there is no exact value (last position info is outdated).
  • As I have to support different feedback devices (incremental encoders, resolvers, absolute encoders...) I have decided to give each position/velocity-measurement device a dedicated cog.

    So the interval measurement modes of the smart pins are not really necessary. I could simply wait for a counter-change event of the quadrature or step/dir decoder and capture time with a GETCT. As the P2 supports waiting with timeout I could handle the stall case conveniently. If there were no pulses during the timeout duration T then I know that velocity must be less than 1/T. So I can adjust the measured velocity to the "best guess" even when no pulses/edges arrive.
  • evanhevanh Posts: 15,910
    Keep it aside for the moment but don't ignore filtering as too hard a problem. Even for multi-axis CNC, if all axes have the same group delay then much of the concern with filter lag goes away. Oscillation is what you're trying to knock on the head here. Well designed filters can help do that.

    High performance resolvers that emulate an encoder, maybe at the drive, have to use interpolation at the very least. And probably extrapolation too. The resolver frequency is only in the kHz whereas encoders have to be good for low MHz at least. In both cases, of interpolation and extrapolation, they should be using filtering techniques to make the emulation more accurate.
  • jmg wrote: »
    If you know the appx step rates at any ms, you could vary X to dynamically scale.

    Yes, that should work and if there were no other possibility I'd have to do it that way. However, I'd like to avoid dynamical adaption if possible. You had to handle special cases as direction changes very carefully. Case-switching or dynamical parameter adaption could cause clicks and jerks or non-linear behaviour if not carefully designed and debugged.

    And yes, my proposal also involves some case-switching but I think it's easy to prove that the "conditional corrections" applied always make the measurement better and not worse.

  • evanh wrote: »
    Have a read of the renamed list I made. It should help with categories at least. https://forums.parallax.com/discussion/comment/1482243/#Comment_1482243

    If you are sure you can process each step one by one then you can measure just the duration of each step - mode %10000_0. But most likely you'll be wanting to also smoothly handle multiple steps per update, in that case, as Chip indicated, you'll be wanting to know both the step count and the duration over which they occurred. This requires two smartpins to give the two measurements - modes %10111_0 and %10101_0 respectively.

    Ah thanks. This is a bit more understandable as the original documentation. I still don't understand every detail. But that's the good thing about the propeller. On other processors it's "eat or die". With the propeller you always have the choice. Peripheral hardware can help to make some things more efficiently but your're not bound to use it. You could always do it in software (bitbang) if the hardware doesn't fit.
  • I still have trouble with the most basic things. I can't even get the quadrature counter running.
    CON
      pinEncA	= 0
      pinEncB	= 1 ' must be pinEncA + 1..3
    
    DAT
    ...
    		fltl	#pinEncA		' reset smartpin
    		wrpin	mode_quad,#pinEncA	' quadrature decoder
    		wxpin	#0,#pinEncA		' no period, continous
    		drvl	#pinEncA		' enable smartpin
    
    		setse1	#%110_000000 + pinEncA ' event smartPin IN=1
    cntLoop
    		'waitse1			' wait for Counter change
    		rdpin	pos,#pinEncA
    		getct	time	
    		wrlong  time,ptrb
    		wrlong  pos,ptra
    		jmprel  #cntLoop
    
    ' 			%AAAA_BBBB_FFF_PPPPPPPPPPPPP_TT_MMMMM_0
    mode_quad	long	%0000_0001_000_0000000000000_00_01011_0 ' + (pinEncB-pinEncA)<<24
    pos		long	0
    time		long	0
    
    I commented out the waitse instruction to force continous reading of the counter. Time is incremented as expected but pos stays zero no matter how I turn the shaft. I checked that P0 and P1 are toggling. I haven't understood the PPP and TT fields of the smart pin mode word. I hope that the default 0 works for this mode.
  • Oh no! :facepalm:

    Suddenly, I had random crashes depending on the number of instructions I inserted or removed. I took a look at the p2asm listing and found out that the compiler took me literally and put the actual address of the cntLoop label as immediate parameter instead of what I expected: #(cntLoop-PC)

    I replaced the jmprel with a jmp and it all works, now. :smiley:
  • evanhevanh Posts: 15,910
    I've forgotten what JMPREL does. Something to do with table lookups I think. It maybe isn't best name for the instruction.

  • Ok, I'm learning step by step...

    Next problem: waitse1 doesn't work, it waits forever. To check if my setse1 is wrong I replaced waitse1 with a testp...wz + if_nz jmp loop. It also waits forever. So I think the problem is that the IN register bit does not become high when the counter is updated.

    In the smartpin docs I read: "The IN bit serves as a flag to indicate to the cog(s) that the smart pin has completed some function or an event has occurred, and acknowledgment is perhaps needed." In lack of detailed information I assumed that the IN bit gets set automatically for the mode "%01011 = A/B-input quadrature encoder". Probably this is not the case. Do I have to set something in the lowlevel pin mode bits so that the event-flag is routed to IN?
  • AribaAriba Posts: 2,690
    I think IN indicates only the end of a measurement period, which you can set with a wxpin.
    If you want to wait for a encoder change, you can always do something like that:
    		...
    		mov	oldpos,pos
    .waitenc	rdpin	pos,#pinEncA
    		cmp	pos,oldpos   wz
    	if_e	jmp	#.waitenc
    		...
    

    In encoder mode the lowlevel pin mode is useful to set a pullup, or a schmitt-triger input, or to enable the glitch filters.

    Andy
  • evanhevanh Posts: 15,910
    edited 2020-01-31 10:08
    The only way IN will be raised by the quadrature counter mode is if X is set to a sampling period. If X==0 then it doesn't latch and has to be polled for active count.

    EDIT: If you're using another timer mechanism then setting X=0 and polling works well.
  • Ok, that makes sense. I hoped that IN=1 meant "new data available in Z" which was a lot more useful than simply "end of measurement period" for periodical measurement (X>0). For X>0 both cases are the same but the former would allow to use interrupts for both cases. Generating an interrupt poeriodically is trivial and could be done without smartpins.
  • Also, using a polling loop (cmp oldpos,pos) cuts resolution by a factor of 6 compared to a waitse which would be precise to one sysclk.

    BTW... from what I understand how the glitch filters work they also have a negative effect on timing resolution. A counter tap feeds the clock input of the flipflops so they get updated only every 2^n sysclks. For example, if I select filt1 the default is 1:32 tap and 3 flipflops. So instead of a constant delay of 96 clocks the signal is only updated every 32nd clock cutting resolution down to 1/32.

    Of course, the way the filters are implemented now is cheaper regarding silicon area. A shift register with hundreds of flipflops would be too expensive so it had to be implemented with a loadable counter which counts down while the input stays the same. This would still require at least a 16 bit counter for each pin.

    I think I quit the whole smartpin stuff and return to manually decoding the quadrature signal as I did with the P1. Apart from reducing code size a bit the smartpins have only disadvantges in this case. Waiting for a pin change (what is the P2 instruction for waitpne?) gives full resolution to one sysclk. The extra time the decoding in software takes even has the side-effect of adding a dead-time in which further pin changes are ignored. This has the same effect as a hardware glitch filter.
  • evanhevanh Posts: 15,910
    edited 2020-01-31 11:45
    Yep, setting X>0 does that too. As well as raising IN, the smartpin metronomically latches the count into Z. IN is then dropped when Z is read with RDPIN.

    PS: The deglitch "filter" hardware is intended for mechanical switches, namely push-buttons. Don't use it for real filtering.

  • WAITPEQ/PNE works with SETPAT and WAITPAT, now, correct?

    > This has the same nearly the same effect as a hardware glitch filter.

    The dead-time prevents the counter from changing faster than the dead-time but a glitch is not fully supressed. We had to check if the pin states are still the same after the dead-time and undo the counter update if not.
  • Is it possible to configure a smartpin so that its IN bit is set to A xor B? I just need the XOR in the input selector without any extra "smart" function.
  • AribaAriba Posts: 2,690
    ManAtWork wrote: »
    Is it possible to configure a smartpin so that its IN bit is set to A xor B? I just need the XOR in the input selector without any extra "smart" function.

    Yes, set the FFF field for wrpin to %011 and no smartpin (mode = %00000)
      wrpin  ##%0000_0001_011_0000000000000_00_00000_0,#PIN
    '       A=PIN B=PIN+1 XOR
    
  • Ah, very good! This way I can get around the setpat/waitpat problem. I could wait for a single change event of the XORed pins.
  • @evanh Thanks for the help.

    @ManAtWork said:
    I still have trouble with the most basic things. I can't even get the quadrature counter running.CON pinEncA = 0 pinEncB = 1 ' must be pinEncA + 1..3 DAT ... fltl #pinEncA ' reset smartpin wrpin mode_quad,#pinEncA ' quadrature decoder wxpin #0,#pinEncA ' no period, continous drvl #pinEncA ' enable smartpin setse1 #%110_000000 + pinEncA ' event smartPin IN=1 cntLoop 'waitse1 ' wait for Counter change rdpin pos,#pinEncA getct time wrlong time,ptrb wrlong pos,ptra jmprel #cntLoop ' %AAAA_BBBB_FFF_PPPPPPPPPPPPP_TT_MMMMM_0 mode_quad long %0000_0001_000_0000000000000_00_01011_0 ' + (pinEncB-pinEncA)<<24 pos long 0 time long 0

    I commented out the waitse instruction to force continous reading of the counter. Time is incremented as expected but pos stays zero no matter how I turn the shaft. I checked that P0 and P1 are toggling. I haven't understood the PPP and TT fields of the smart pin mode word. I hope that the default 0 works for this mode.

    Hello,
    I am attempting to get the gated positive edge counter to work referenced in @JonTitus wrote on pages 24 and 25, see below. Attempting to use the edge counter to see a signal from the x_band sensor.
    Did you ever get the 01011 to work?
    I am uploading my code and the manual.
    Thanks in advance.
    Martin

  • AribaAriba Posts: 2,690

    The examples in the X-BandMotion Detector PDF just counts the frequency from the OUT pin.
    In Spin2 you can do something like that:

    CON
      _clkfreq  = 160_000_000
      XINPIN    = 0                                   'X band input pin
      XENPIN    = 1                                   'X band enable pin
    
    PUB main() | count
      pinstart(XINPIN, P_COUNT_RISES, clkfreq, 0)   'count input pos edeges for 1 second
      pinhigh(XENPIN)
      repeat
        repeat until pinr(XINPIN)                   'wait until measurement period finished
        count := rdpin(XINPIN)                      'read count
        debug("motion ",udec(count)," Hz")          'show result in debug terminal
    

    Andy

  • JonnyMacJonnyMac Posts: 9,102
    edited 2024-10-31 14:13

    I am attempting to get the gated positive edge counter...
    Attempting to use the edge counter to see a signal from the x_band sensor.

    I demonstrated how to do this in a different thread.

      pinstart(CNT_IN, p_count_rises, 0, 0)                         ' set to count pos edges
      waitms(100)
      count := rdpin(CNT_IN)
    

    Once the smart pin is started you can clear its internal count by writing the smart pin DIR bit to 0; in Spin2 I do this with pinfloat(). To re-enable the smart pin use pinlow().

    Since you mentioned the X-Band motion detector, I converted the P1 object I wrote in July to the P2; it's even simpler with the smart pin. See attached object.

    Jon Titus wrote that document very early in the release of the P2 and things have come a long way, especially constant definitions for the P2 (see the online Spin2 docs) which make code easier to read and understand. For example, if you wanted to start the edge counter mode in PASM2, you could do this

                            fltl      pin                           ' reset smart pin
                            wrpin     #p_count_rises, pin           ' set to count rises
                            wxpin     #0, pin                       ' continuous counting
                            wypin     #0, pin                       ' increment only
                            drvl      pin                           ' enable edge counting
    

    These instructions were lifted from the Spin2 interpreters source code for pinstart() -- the values are set specifically for the edge counting feature you want.

  • pilot0315pilot0315 Posts: 910
    edited 2024-11-01 03:49

    @Ariba I found the p count rises command interesting.
    @JonnyMac Can you give me the link to the thread that demos the p count rises?

    Thank you both. I will experiment with both techniques.
    On the pasm side, I am attempting to get a good handle on the asm type of coding.
    On the spin side I am attempting to get a good handle on calling asm from spin as well as improve
    my spin coding.
    Martin

  • JonnyMacJonnyMac Posts: 9,102
    edited 2024-11-01 13:42

    @JonnyMac Can you give me the link to the thread that demos the p count rises?

    It's YOUR thread! :)
    -- https://forums.parallax.com/discussion/176014/starting-smart-pins-from-p2spin#latest

    1. Start with Spin2 to learn the P2.
    2. Use inline assembly to learn PASM2 without having to manually launch a cog.
    3. When you need a full-time cog, do things like in the P1 (though the P2 interface is better and there are a lot more instructions). See jm_fullduplexserial.spin2 as an example.

    The P2 Spin interpreter is much better than the P1 Spin interpreter. My casual testing shows that code runs about 16x faster on the P2 at the same clock speed on both platforms (I tested at 80MHz). My standard P2 code runs at 200MHz, so that's about a 40x speed boost of identical code moving from P1 to P2. Since you're still new to the P2, save the assembly stuff for later.

    With smart pins and inline PASM2 you often don't need to use a separate cog for some processes. For example in a laser tag game I'm writing I need to update 45 WS2812b pixels. To do this takes less than 1.5ms so there's no need to devote a cog to the process when I can use inline assembly to handle the signaling details. I've attached that object in case you want to look at it later. Fair warning: it is advanced and only runs in interpreted Spin2; it will not work in FlexProp (which has its own mechanism for inline assembly).

  • pilot0315pilot0315 Posts: 910
    edited 2024-11-02 10:32

    @JonnyMac

    Don't get old it is a pia can't remember my own name in any of the languages I speak. :D

    I am reading the ws2812b_nc code. Thanks for the good comments in the code. Makes it
    easier to follow than other folks code without comments.

Sign In or Register to comment.