Shop OBEX P1 Docs P2 Docs Learn Events
Smart Pin Mode 4 (pulse out) question — Parallax Forums

Smart Pin Mode 4 (pulse out) question

I'm fiddling with feedback servo and trying to use smart pin mode 4 to generate the control pulses.
CON
            
    oscmode = $010c3f04               'standard stuff
    freq    = 160_000_000
    baud = 115200                     'must configure RUN command to match this
    msec20 = freq/50                  'tics in 20 millisec
    microsec = freq/1_000_000
    thepin = 48
    sync = 49
    pulseMode = %00100_0                'smart pin mode

OBJ
    ser: "PrintfSerial"     'access to Propellor output (output only)

var
    long  param
    byte  cog
    byte  theservo

pub main
    clkset(oscmode, freq)
    ser.start(baud)                  'start up serial terminal
    ser.str(string("hello, world"))
    ser.nl

     param := microsec*1500            '1500 usec
     cog := cognew(@entry, @param)     'send ADDRESS of param
     ser.str(string("cog started: "))
     ser.dec(cog)
     ser.nl

    repeat                              'forever.

DAT
entry     ORG 0
          mov  hubAdrs, ptra          'boss cog passed an address:  save it
          rdlong PWidth, hubAdrs      'fetch the pulse width
          dirh #sync                  'scope sync
          wrpin #pulseMode, #thepin   'set smart pin mode
          wxpin #0, #thepin           'x[31:16] = 0
          getct target
          addct1 target, ##msec20     'set up 20 msec timer
loop      waitct1                     'remainder of 20 msec
          addct1 target, ##msec20     'set up for next pass
          outh #sync                  'scope sync
          waitx ##160
          outl #sync
          wypin pWidth, #thepin       'start a pulse
          jmp  #loop

hubadrs   res  1             'address in hub memory
target    res  1              'target time
pWidth    res  1

I have a 20 msec heartbeat which I can see on sync, but noting happens on ThePin. I think I am setting the smartpin mode correctly, and am setting x[31..16] to zero so it just does a pulse for the duration in y. I would be grateful if some kind soul could help. AdThanksVance

Comments

  • You're holding the smartpin in reset mode. You need a DIRH #the_pin somewhere.
  • @Mark_T , Thanks for the response. I put in a DIRH after configuring the pin and still no pulse.

    "Smart pins should be configured while their DIR signal is low...Once configured, DIR can be raised high..."
    I also made PWidth a long in case I was somehow not getting it correctly from the hub. I'm sure it is something simple I can't see.
    CON
                
        oscmode = $010c3f04               'standard stuff
        freq    = 160_000_000
        baud = 115200                     'must configure RUN command to match this
        msec20 = freq/50                  'tics in 20 millisec
        microsec = freq/1_000_000
        thepin = 48
        sync = 49
        pulseMode = %00100_0                'smart pin mode
    
    OBJ
        ser: "PrintfSerial"     'access to Propellor output (output only)
    
    var
        long  param
        byte  cog
        byte  theservo
    
    pub main
        clkset(oscmode, freq)
        ser.start(baud)                  'start up serial terminal
        ser.str(string("hello, world"))
        ser.nl
    
         param := microsec*1500            '1500 usec
         cog := cognew(@entry, @param)     'send ADDRESS of param
         ser.str(string("cog started: "))
         ser.dec(cog)
         ser.nl
    
        repeat                              'forever.
    
    DAT
    entry     ORG 0
              mov  hubAdrs, ptra          'boss cog passed an address:  save it
            '  rdlong PWidth, hubAdrs      'fetch the pulse width
              dirh #sync                  'scope sync
              dirl #thepin                'make sure it is in reset
              wrpin #pulseMode, #thepin   'set smart pin mode
              wxpin #0, #thepin           'x[31:16] = 0
              dirh #thePin                'DIR raised high
              getct target
              addct1 target, ##msec20     'set up 20 msec timer
    loop      waitct1                     'remainder of 20 msec
              addct1 target, ##msec20     'set up for next pass
              outh #sync                  'scope sync
              waitx ##160
              outl #sync
              dirh #thepin                '<----shouldn't do anything  
              wypin pWidth, #thepin       'start a pulse
              jmp  #loop
    
    
    pwidth    long 1500 * microsec
    hubadrs   res  1             'address in hub memory
    target    res  1              'target time
    
  • evanhevanh Posts: 15,915
    Tom,
    Looks well written overall. I think you just need to fix one of those little gotcha's with smartpins - The output DIR control has to be overridden because the DIRH instruction is now controlling the smartpin, not the output. Namely pulseMode needs set to %01_00100_0. That's the %TT bits doing their bit.
  • @evanh, thank you for the heads up. I made the change you suggested and now I get an output, but can't make sense of it.

    The attached logic analyzer capture shows what I am seeing. Each of the negative pulses measures 10 nsec, which is the sampling rate.

    Appreciate any help. tc
    CON
                
        oscmode = $010c3f04               'standard stuff
        freq    = 160_000_000
        baud = 115200                     'must configure RUN command to match this
        msec20 = freq/50                  'tics in 20 millisec
        microsec = freq/1_000_000
        thepin = 48
        sync = 49
        pulseMode = %01_00100_0                'smart pin mode
    
    OBJ
        ser: "PrintfSerial"     'access to Propellor output (output only)
    
    var
        long  param
        byte  cog
        byte  theservo
    
    pub main
        clkset(oscmode, freq)
        ser.start(baud)                  'start up serial terminal
        ser.str(string("hello, world"))
        ser.nl
    
         param := microsec            '1500 usec
         cog := cognew(@entry, @param)     'send ADDRESS of param
         ser.str(string("cog started: "))
         ser.dec(cog)
         ser.nl
    
        repeat                              'forever.
    
    DAT
    entry     ORG 0
              mov  hubAdrs, ptra          'boss cog passed an address:  save it
              rdlong PWidth, hubAdrs      'fetch the pulse width
              dirh #sync                  'scope sync
              dirl #thepin                'make sure it is in reset
              wrpin #pulseMode, #thepin   'set smart pin mode
              wxpin #0, #thepin           'x[31:16] = 0
              wypin pwidth, #thepin       'set initial pulse width
              dirh #thePin                'DIR raised high
              getct target
              addct1 target, ##msec20     'set up 20 msec timer
    loop      waitct1                     'remainder of 20 msec
              addct1 target, ##msec20     'set up for next pass
              outh #sync                  'scope sync
              waitx ##160
              outl #sync
              wypin pwidth, #thepin       'start a pulse
              jmp  #loop
    
    
    pwidth    long 1500 * microsec
    hubadrs   res  1             'address in hub memory
    target    res  1              'target time
    
    1202 x 684 - 66K
  • evanhevanh Posts: 15,915
    edited 2019-02-20 01:00
    Ah, reading about the function of that pulse mode: It looks like Y is the number of pulses to generate, not the pulse width. The pulse width looks to be the ratio of X[31:16] over X[15:0], with X[15:0] being the period.

    If you want a continuous level rather than a pulse count, then the NCO or PWM modes on next pages of document would be better. EDIT: I'd recommend %01001 = PWM sawtooth

  • evanhevanh Posts: 15,915
    Just reading about the PWM modes right now and I note all three of them have double buffering on Y, the width parameter. This provides high precision of updating the output level, ie: no glitches.
  • Thanks Evanh, you're prob'ly right. P'raps we read what we want to see rather than what is meant. I believe I can generate at 1500 microsecond pulse using some version of smart pin.
  • evanhevanh Posts: 15,915
    edited 2019-02-20 01:57
    Config of the PWM would be based on desired period. If it's 20 ms then you could go with a microsecond base (sample) resolution, so that would give you a 20,000 working range:
    X[15:0] = sys_clock_freq / 1_000_000
    X[31:16] = 20_000
    Eg:
              wrpin #%01_01001_0, #thepin   'set smart pin mode PWM sawtooth
              wxpin ##(20_000<<16 | microsec), #thepin   '20ms period, 1us resolution 
    

    And set Y = 1500 for your initial level.


    Edit: Added example
  • jmgjmg Posts: 15,173
    I believe I can generate at 1500 microsecond pulse using some version of smart pin.
    P2 could certainly do that.

    The %00100 = pulse/cycle output mode, appears to have no prescaler, instead it has SysLCK/N for the ramp, and compares that, counting pulses each time.
    That may be too quick for 1.5ms time frames.

    PWM mode expects to run forever, so repeats the PWM pulse with 16 bits of prescaler and 16 bits of PWM period/Duty.
    You could reload that on-the fly, for each pulse

    %01001 = PWM sawtooth

    X[31:16] = PWM frame period, you could set this to max at 20ms, then work back from here. 20ms in 160MHz is ~ 22 bits. No separate timer needed.

    X[15:0] = prescaler, here 160M/N, a couple of choices are SysCLK/48 or SysCLK/49
    1/(160M/48/2^16) = 19.6608 ms
    1/(160M/49/2^16) = 20.0704 ms


    Y[15:0] establishes the PWM output value which gets captured at each frame start and used for its duration. It should range from zero to the frame period.
    At each base period, the captured output value is compared to the counter. If it is equal or greater, a high is output. If it is less, a low is output. Therefore, a zero will always output a low and the frame period value will always output a high

    for 1.5ms of Duty Cycle, the load values are
    2^16*(1.5m/19.6608m) = 5000
    2^16*(1.5m/20.0704m) = 4897.959

    Software here only needs to be ready every 20ms to load the following cycles duty cycle, from a small table, or in a small loop.
    Simplest, but modest precision....

    If one part in 5000 is not good enough precision, (it's over 12 bits) , you need to either add more SW. or choose another mode.
    eg you could map 16 bits to 2ms, using a prescaler of 5, and alternate PWM and SW timer.
    This jumps precision to 32000 steps for 1.0 ms span. giving load values of
    1.0m/(5/160M) = 32000 for 1.000 ms
    1.5m/(5/160M) = 48000 for 1.500 ms
    2.0m/(5/160M) = 64000 for 2.000 ms

    or, pushing a little further, the actual time change span is 1.0ms, so prescaler can become a faster 160M/3, and frame period is 53333, for a IN rate of 53333*3/160M = 999.99375 us
    Here, interleave of SW timers and PWM pin control starts to sounds tricky, as you need HI/VAR/LO..LO..LO

    Probably simplest to set a loop that always runs Smart-Pin, to avoid possible glitches..
    L=0, load 53333 (gives 100% hi, for 999.99375 us ) then next load VAR Duty modulation of N=0..53333 for 0..999.99375 us variable portion.
    then load 0 result, 18 times, then repeat for ~20ms total. Each 20ms repeat can choose a next-duty value to load.

    A variant of that could alternate the prescaler as /3, /3, /(3*18) for [999.99375 us, 999.99375 us, 17.9998875 us] time intervals. (code can do something else in that 18ms window)

    As evanh mentioned
    evanh wrote: »
    Just reading about the PWM modes right now and I note all three of them have double buffering on Y, the width parameter. This provides high precision of updating the output level, ie: no glitches.
    so those loads of 53333/Var/0/53333/Var/0.. need to be phased ahead of when they will apply, when doing the prescaler change.
    I think that means when you load 53333, the HW is running 0, so that's when you flip prescaler to slower, then on load Var flip back faster.
    It will be obvious when the phasing is wrong :)

    Is there a smart pin mode that can give SysCLK granularity over 20ms times ?

    %00110 = NCO frequency has 32b adder, but only 16b phase field.

  • Tom
    You were so close, all you needed was to set the base period.
    Here's my test code
    con
    	crystal = 20_000_000
    	dv = 1
    	mlt = 9 '180 Mhz
    	clk = 1 << 24 | (dv-1) << 18 | (mlt-1) << 8
    	sys_clk = crystal / dv * mlt
    
    	pin = 26
    	us = sys_clk / 1_000_000
    
    dat	org
    
    	hubset	#0
    	hubset	##clk | %1111_10_00
    	waitx	##20_000_000/100
    	hubset	##clk | %1111_10_11
    
    	wrpin	#%01_00100_0,#pin	'pulse/cycles
    	wxpin	#1,#pin			'base priod
    	dirh	#pin
    
    	getct	pa
    loop	addct1	pa,_20ms
    	waitct1
    	wypin	pwidth,#pin
    	jmp	#loop
    
    _20mS	long	sys_clk / 50
    pwidth	long	1500 * us
    
    
    
    640 x 480 - 7K
  • jmgjmg Posts: 15,173
    ozpropdev wrote: »
    Tom
    You were so close, all you needed was to set the base period.
    Here's my test code
    Hmm. I missed the hidden mode there, where the usual faster-pwm of
    X[15:0] establishes a base period in clock cycles which forms the empirical high-time and low-time units.

    does not really apply, and is overruled by

    .. base period counter will be compared to on each clock cycle, as it counts from X[15:0] down to 1, before starting over at X[15:0] if decremented Y > 0.
    On each clock, if the base period counter > X[31:16] and Y > 0, the output will be high (else low).

    X[31:16] = 0 bypasses the faster PWM logic, { X[31:16] /X[15:0] } and instead uses only (Y>0) monostable action.

    pwidth above will be 1500*180M/1M = 270000, so the 32b Y field sets the 'one shot' width, to 1 sysclk granularity.
  • Thank you, all. I am now controlling a 360 Feedback Servo. I guess I need to study hubset, but for now will settle for a magic incantation.
  • Cluso99Cluso99 Posts: 18,069
    Excellent news :smiley:

    Perhaps you might like to add the basics to a post in the P2Tricks and Traps thread with a link to this thread for more details?
  • Cluso99 wrote: »
    Perhaps you might like to add the basics to a post in the P2Tricks and Traps thread with a link to this thread for more details?

    I will when I get it cleaned up. It demonstrates a number of techniques.

  • Here is the cleaned up program. It is tuned to run on ES-board, compiled with essmith fastspin 1.38. It contains the following:

    1. Setting the clock to 160 MHz (in spin)
    2. Starting a PASM cog, including passing a parameter via PTRA
    3. Setting up two smart pins, using mode %00100 (pulse output)
    4. Running a 50Hz loop using waitct1/addct1
    5. Using a smart pin to generate a scope sync in one instruction
Sign In or Register to comment.