Shop Learn P1 Docs P2 Docs
Definitions p2 — Parallax Forums

Definitions p2

I have been struggling to read through all of the documentation about the P2.
First is there a more defined "dictionary" of the commands and what do they do in a more simple explanation of what they really mean and what they do?
What is the X and the Y? I know that they are acronyms but what are the long version of the acronyms mean.
Thank you in advance.


  • RaymanRayman Posts: 12,818

    The smartpins are very flexible but also a bit complex...

    Maybe the Hardware Manual describes them best?

    Anyway, the main command is "WRPIN" for Write Pin, which sets the main mode.
    X and Y are parameters of the main mode, set by WXPIN and WYPIN.

    SETDACS is to set the Digital to Analog Converters (DACS).
    This is a normally unrelated thing, but probably connected in some cases...

  • evanhevanh Posts: 13,628

    The three instructions load the smartpin's three write-only registers:

    • WRPIN for setting the mode word. The mode register isn't named.
    • WXPIN and WYPIN for X and Y data/parameter registers. X and Y are just short names, no particular meaning.

    There is also one read-only data register in each smartpin. RDPIN is used to get its data. This one is named Z.

    SETDACS is a little oddball. Thrown in as a bonus because it was easy to add I suspect. SETDACS writes to the streamer's four DAC data pipes when the streamer isn't doing so. But it's generally easier to just use a WRPIN instead for simple setting of a DAC.

  • evanhevanh Posts: 13,628
    edited 2022-09-10 01:22

    There can be naming confusion in the docs also with the low level pin A/B verses smartpin A/B inputs. I've personally been separating the naming with SmartA/SmartB and PinA/PinB (or APin/BPin). This helps me keep them distinct in my source code.

    Rayman uses my naming here -
    He updated the chart again on last page -

  • evanhevanh Posts: 13,628
    edited 2022-09-10 03:07

    WRPIN's mode register performs many jobs:

    • Selecting a smartpin mode with %SSSSS bit-field. %00000 = OFF (Not a smartpin)
    • Modifying pin control with %TT bit-field. Affected by %SSSSS and DAC output mode below.
    • Selecting SmartA/B input sources with %AAAA/%BBBB bit-fields. SmartA is passed to IN when not used by smartpin.
    • Debounce/logic of SmartA/B with %FFF bit-field.
    • Set low-level pin control mode with %MMMMMMMMMMMMM bit-field. This is further subdivided in an encoded form:
    %0MMM_CIOHHHLLL for Logic I/O modes.  Includes pinA vs pinB comparator.
    %100MMM_OHHHLLL for ADC input modes.  Includes Logic output modes.
    %101MM_DDDDDDDD for DAC output modes.  Includes one preset ADC mode.
    %11MM_CDDDDDDDD for CompDAC input modes.

    The low-level %DDDDDDDD bit-field acts as a DAC databus and is optionally piped from any streamer/cog without using the smartpin.

    PS: There is a number of possible combinations that are not available due to limit of 32 mode bits in WRPIN.

  • pilot0315pilot0315 Posts: 766
    edited 2022-09-10 12:50


    Thanks I will study these and try to tease out the examples. I am presently trying to understand the pwm routines.

  • JonnyMacJonnyMac Posts: 8,238
    edited 2022-09-10 20:45

    Keep in mind that the PWM smart pins sub-divide the X register into two elements. I think it's easier to start with the sawtooth version. Sawtooth and Triangle have to do with the behavior of an internal counter, and nothing to do with the shape of the output waveform. Both will give a variable duty-cycle, fixed-frequency square wave. This is from the docs

    %01001 = PWM sawtooth

    This mode overrides OUT to control the pin output state.

    X[15:0] establishes a base period in clock cycles which forms the empirical high-time and low-time units.

    X[31:16] establishes a PWM frame period in terms of base periods.

    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.

    A counter, updating at each base period, counts from one up to the frame period. Then, Y[15:0] is captured, IN is raised, and the process repeats.

    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.

    During reset (DIR=0), IN is low, the output is low, and Y[15:0] is captured.

    The X register setup is tricky at first. X.word[1] (high 16 bits) is the number of units in your PWM period. I do a lot of LED control and use 255 here because DMX lighting control uses 0 to 255 for 0 to 100%. You could make things easy and have 1% resolution by setting X.word[1] to 100. The lower 16 bits of X tell the P2 how many system ticks are in one of your units at your desired PWM frequency. This is calculated by dividing your system clock frequency by your desired PWM frequency (e.g., 20kHz), and then dividing that by the units in 100%. Once you have that sorted and the smart pin is running, changing the Y register will change the PWM duty cycle. Keep in mind that your ticks/unit value (X.word[1]) must fit into 16 bits, so this can have an affect on your low side of your PWM frequency. For very low frequencies you'll want to use high ticks/units and high units values.

    In Spin, I do this:

      x.word[0] := 1 #> ((clkfreq / hz) / units) <# $FFFF           ' ticks per unit @ target hz
      x.word[1] := units                                            ' units in 100%
      pinstart(pin, P_OE | P_PWM_SAWTOOTH, x, duty)                 ' start pwm smart pin

    ...where hz is my target PWM frequency, units is the 100% value, and duty is the initial PWM setting. The PWM smart pin has an internal counter that goes from 1 to your 100% setting. If your duty value is at or above this counter, the output is high. As soon as the counter value goes past your duty setting, the output goes low. Graphically, it looks like this:

    In Triangle mode, the counter counts down and then back up, which will cut your PWM frequency in half with the same X register settings. In my Spin driver when Triangle mode is specified, I make this adjustment to keep the PWM frequency matched to the value passed to the start method:

      x.word[0] >>= 1                               
      pinstart(pin, P_OE | P_PWM_TRIANGLE, x, duty) 

    Dividing the ticks/unit value by 2 would double the frequency in Sawtooth mode; in Triangle mode (witch doubles the output) this adjustment keeps the target PWM frequency.

  • JonnyMacJonnyMac Posts: 8,238
    edited 2022-09-10 18:05

    Here's a dirt-simple example of making a slow (0.5Hz) LED blinker with a duty cycle of 0 (off) to 100%. Note how I had to scale the units and duty (x 100) to get the ticks/unit value to fit into 16 bits while running a 200MHz.

    pub slow_blink(pin, duty) | x
    '' Blink LED slowly (0.5Hz) at duty cycle (0% to 100%)
      x.word[0] := 1 #> (clkfreq << 1) / 100_00 <# $FFFF
      x.word[1] := 100_00
      duty := (0 #> duty <# 100) * 100
      pinstart(pin, P_OE | P_PWM_SAWTOOTH, x, duty)

    Remember, if you want to use wypin() to set the duty cycle of this blinker, that value must be scaled into the range of 0..100_00 to get the expected results.

    This exercise got me thinking about how slow one could go. The highest ticks/unit setting is $FFFF, and the highest units setting is $FFFF. For me, the PWM frequency works out to be 200_000_000 / ($FFFF * $FFFF) which is 0.04656Hz, which is a period of 21.474 seconds. I knocked together this bit of code to verify:

      wrpin(55, P_PLUS1_A)                                          ' use P55 to watch P56 
      state := pinread(55)                                          ' get initial state
      pinstart(56, P_OE | P_PWM_SAWTOOTH, $FFFF_FFFF, $7FFF)        ' very slow pwm on P56
      t0 := getms()                                                 ' start timing
      repeat                                                        ' report ms between states
        if (pinread(55) <> state)
          t1 := getms()
          term.fstr1(@"State change at: %dms\r", t1-t0)
          t0 := t1
          state := state ^ 1

    Confirmed. Time between state changes is 10.737 seconds.

    A few minutes later...

    Changing the PWM mode to triangle doubles the PWM period.

  • @JonTitus

    Rayman sent me to this document.
    This is a line from it:
    You may download all software examples in this section from the URL here...
    On page one. :) Where is "here". The link does not work for me. Maybe it is me.
    Thanks in advance.

Sign In or Register to comment.