Shop OBEX P1 Docs P2 Docs Learn Events
I Miss The P1 Counters / P2 SIRCS Decoding — Parallax Forums

I Miss The P1 Counters / P2 SIRCS Decoding

JonnyMacJonnyMac Posts: 9,104
edited 2020-07-12 06:07 in Propeller 2
As I'm porting more of my P1 objects to the P2, I'm now noticing how frequently I employed the cog counters. For example, in my P1 SIRCS decoder, I used CTRA in Neg Detect mode to measure a pulse, and CTRB in Pos Detect mode to time the idle spaces between pulses (and detect the end of the stream).

Am I missing something with smart pins, or am I going to have to do grunt timing like this (test code that works)?
pub pulse_in(pin) : result | t0

'' Returns duration of low-going pulse
'' -- value returned in system ticks
'' -- blocks until pulse arrives

  org
                fltl      pin                                   ' make pin an input

.wait           getct     t0                                    ' get system timer (start of pulse)
                testp     pin                           wc      ' check pin state
    if_c        jmp       #.wait                                ' if still high, back to .wait
                
.measure        getct     result                                ' get system timer (end of pulse)
                testp     pin                           wc      ' check pin state
    if_nc       jmp       #.measure                             ' if still low, back to .measure
                
                subs      result, t0
  end

Comments

  • JonnyMacJonnyMac Posts: 9,104
    edited 2020-07-12 15:10
    One of my favorite features of the P2 is that I can test P2ASM code without having to launch a cog. This SIRCS input code seems to work correctly -- though there is no error checking so I wouldn't deploy it in a commercial application yet. Ultimately, this code will end up in its own cog so that it can receive IR codes at any time.

    For those who have been around P2ASM a while, would you please review and comment? Thanks.
    pub sircs_in(pin) : ircode, irbits | starttix, bit1tix, tfall, trise, tdelta 
    
      starttix := 2400 * US_001 * 9 / 10                            ' 90% of 2.4ms
      bit1tix  := starttix >> 1                                     ' 1 bit is 50% of start bit
    
      org
                    fltl      pin
    
    get_start       getct     tfall                                 ' mark start of pulse
                    testp     pin                           wc
        if_c        jmp       #get_start                            ' wait for 1->0 transition
    
    wait_start      getct     trise                                 ' mark end of pulse
                    testp     pin                           wc
        if_nc       jmp       #wait_start                           ' wait for 0->1 transition
    
    check_start     mov       tdelta, trise                         ' calculate pulse width
                    subs      tdelta, tfall
                    cmps      tdelta, starttix              wcz     ' validate against start pulse width
        if_b        jmp       #wait_start
    
                    mov       ircode, #0                            ' clear results
                    mov       irbits, #0
    
    get_bits        getct     tdelta                                ' wait for start of data bit (1->0)
                    testp     pin                           wc
        if_nc       mov       tfall, tdelta                         ' detected, mark time & continue
        if_nc       jmp       #wait_bit
                    subs      tdelta, trise                         ' time-out (end of data bits)?
                    cmps      tdelta, bit1tix               wcz
        if_a        jmp       #done
                    jmp       #get_bits
    
    wait_bit        getct     trise                                 ' wait for end of data bit (0->1)
                    testp     pin                           wc
        if_c        jmp       #check_bit
                    jmp       #wait_bit
    
    check_bit       mov       tdelta, trise
                    subs      tdelta, tfall
                    cmps      tdelta, bit1tix               wcz
                    shl       ircode, #1                            ' make space for bit
        if_a        or        ircode, #1                            ' write new bit
                    add       irbits, #1                            ' update count      
                    jmp       #get_bits
    
    done            rev       ircode                                ' correct lsbs
                    mov       tdelta, #32
                    sub       tdelta, irbits
                    shr       ircode, tdelta
    
      end
    

  • Jon,
    You can configure a neighbor smartpin to monitor another neighbor. Up to 3 pins away. So for example, you can configure pin 6 to do the neg detect mode, and then configure pin 7 to do pos detect but set it to look at pin 6 for input to the detect.
  • I don't like the idea of using an IO pin for a non-IO function; I did like that I could connect timers to IO (in the P1). In the end, grunt differential timing is going to be best here as it will allow me to detect timeout (as I'm doing in the SIRCS method) and a sensor that has been removed (I add a 500K pull-down to detect a removed sensor).
  • MJBMJB Posts: 1,235
    JonnyMac wrote: »
    I don't like the idea of using an IO pin for a non-IO function;
    you are not using the pin, just the smartpin cell of this pin.
    The pin itself can still be used for digital IO.

  • JonnyMacJonnyMac Posts: 9,104
    edited 2020-07-12 15:05
    you are not using the pin, just the smartpin cell of this pin.
    The pin itself can still be used for digital IO.

    With respect, I'll believe that when I see it presented in practical, real-world code.

    This code works:
    pub pulse0_in(pin) : result | t0
    
      org
    
                    fltl      pin
    
    wait_1_0        getct     t0
                    testp     pin                           wc      ' wait for 1->0
        if_c        jmp       #wait_1_0
    
    wait_0_1        getct     result
                    testp     pin                           wc      ' wait for 0->1
        if_nc       jmp       #wait_0_1
    
                    subs      result, t0                            ' calculate duration of low          
    
      end
    
    While this "smart pin" version returns gibberish.
    pub sp_pulse0_in(pin) : result 
    
      org
    
                    fltl      pin
                    wrpin     ##P_STATE_TICKS, pin
                    wxpin     #0, pin
                    wypin     #0, pin
                    drvl      pin
    
    wait_1_0        testp     pin                           wc      ' wait for 1->0
        if_c        jmp       #wait_1_0
        
    wait_0_1        testp     pin                           wc      ' wait for 0->1
        if_nc       jmp       #wait_0_1
    
                    rdpin     result, pin                           ' read duration of low          
    
      end
    
    It's frustrating that after all of these years of development there is so little documentation, and even more so that the documentation that exists has few practical examples.
  • jmgjmg Posts: 15,173
    edited 2020-07-13 00:59
    JonnyMac wrote: »
    It's frustrating that after all of these years of development there is so little documentation, and even more so that the documentation that exists has few practical examples.

    Yes, the P2 docs are cryptic...

    I think there are other HW supported paths (but firm examples are sparse)

    a) WAITSE1 Wait for the selectable-event-1 event flag
    %001_PPPPPP = INA/INB bit of pin %PPPPPP rises
    %010_PPPPPP = INA/INB bit of pin %PPPPPP falls
    %011_PPPPPP = INA/INB bit of pin %PPPPPP changes


    b) I think the Smart Pins can also capture & clear on either edge - maybe this mode ? %10000 = Time A-input states, and you would check the pin level to see what was just captured.
    For very slow edges, that would allow a single pin to be used.

  • RossHRossH Posts: 5,462
    jmg wrote: »
    Yes, the P2 docs are cryptic...

    Understatement of the year! :)
  • evanhevanh Posts: 15,916
    I treated it as a challenge. Sometimes every word Chip wrote is important. Questions are answered just by rereading the description slower.

    Other times there has been ambiguity in the choice of words and even Chip doesn't remember the definitive meaning reading it back. He does occasionally rewrite a section of the docs.

    More recently, Searth and Jon are quietly fleshing some bits out.

  • evanhevanh Posts: 15,916
    edited 2020-07-13 07:03
    JonnyMac,
    I haven't tried your code but the one extra line below should fix it:
    wait_1_0        testp     pin                           wc      ' wait for 1->0
        if_c        jmp       #wait_1_0
                    rdpin     result, pin                           ' read duration of high
                    nop
    
    wait_0_1        testp     pin                           wc      ' wait for 0->1
        if_nc       jmp       #wait_0_1
                    rdpin     result, pin                           ' read duration of low
    

    EDIT: On second thoughts, TESTP sees stale data when immediately after RDPIN, it'll also need a NOP as well ... edit ...

  • AribaAriba Posts: 2,690
    edited 2020-07-13 12:43
    evanh wrote: »
    JonnyMac,
    I haven't tried your code but the one extra line below should fix it:
    wait_1_0        testp     pin                           wc      ' wait for 1->0
        if_c        jmp       #wait_1_0
                    rdpin     result, pin                           ' read duration of high
                    nop
    
    wait_0_1        testp     pin                           wc      ' wait for 0->1
        if_nc       jmp       #wait_0_1
                    rdpin     result, pin                           ' read duration of low
    

    EDIT: On second thoughts, TESTP sees stale data when immediately after RDPIN, it'll also need a NOP as well ... edit ...

    You can't read the pin state directly when in smart mode. So the wait for 1 -> 0 and 0 -> 1 do not work. A high at the pin indicates that a state change occured, the C flag can show what state it was. So something like that should work:
    wait_1_0        testp     pin                           wc      ' wait for state change
        if_nc       jmp       #wait_1_0
                    rdpin     resultH, pin                  wc      ' read duration of high
        if_nc       jmp       #wait_1_0                             ' was it a high state?
    
    wait_0_1        testp     pin                           wc      ' wait for state chance
        if_nc       jmp       #wait_0_1
                    rdpin     resultL, pin                          ' read duration of low
    
    Andy

    Edit: For the frequencies which IR uses it's not necessary to use PASM, Spin2 would do it also, I think
  • Edit: For the frequencies which IR uses it's not necessary to use PASM, Spin2 would do it also, I think
    You're right, Andy; I'm doing it in P2ASM as a learning exercise. Assembly is not my "go to," so I need to practice. Thanks for the other tips.
  • More recently, Searth and Jon are quietly fleshing some bits out.
    And for my part, I'm trying to provide practical code examples that can actually be deployed without a lot of fuss.
  • Cluso99Cluso99 Posts: 18,069
    edited 2020-07-13 13:24
    JonnyMac wrote: »
    More recently, Searth and Jon are quietly fleshing some bits out.
    And for my part, I'm trying to provide practical code examples that can actually be deployed without a lot of fuss.
    Jon,
    I am pleased you’re giving p2asm a go. The P1 assembler instructions are the best I have seen on any micro and I’ve seen/used a lot. They are simple and regular and therefore easy to remember.
    Of the 32, you’ll mostly use less than half of them.
    On P2, start with the simple regular P1 equivalent instructions first, and you will pick it up in no time. Then you can move to the more exotic P2 instructions as you have need for them. It will fall in to place more easily that way.

    Just remember, when you don’t need speed, spin is great, but when the chips are down, use pasm. Generally tho, I recommend spin and pasm to use separate cogs (cores) even tho P2 can mix them in one cog.
  • SeairthSeairth Posts: 2,474
    edited 2020-07-13 23:43
    JonnyMac wrote: »
    As I'm porting more of my P1 objects to the P2, I'm now noticing how frequently I employed the cog counters. For example, in my P1 SIRCS decoder, I used CTRA in Neg Detect mode to measure a pulse, and CTRB in Pos Detect mode to time the idle spaces between pulses (and detect the end of the stream).

    My first thought is that you can use mode %10001 (Time A-input high states), configured to look at an inverted signal (since yours is actually a low signal). To read the data, use the selectable event mechanism (which I just consolidated to make a little more clear) to detect the completion of the counting, followed by RDPIN/RQPIN to read the count value. Something like:
                wrpin   pin_mode, pin       ' set pin mode
                dirh    pin                 ' enable smart pin
                or      pin, #%001_000000   
                setse1  pin                 ' set selectable event (INx rises)
                waitse1                     ' wait for event (finished counting)
                rdpin   result, pin         ' read count
    
    pin_mode    long    %0001010000000_00_10001_0   ' A input, inverted, mode %10001
    

    Looking at your longer code snippet, you could instead use mode %10000 (Time A-input states) which will give you both high and low durations (with C indicating high or low state). The code would look something like this, I think:
    
    pub sircs_cfg(pin):
    
      org
                  wrpin       ##%0001000000000_00_10000_0, pin      ' configure smart pin: input, time A-input states
                  dirl        pin
                  or          pin, #%001_000000                     ' IN[pin] rises
                  setse1      pin
      end
    
    pub sircs_in(pin) : ircode, irbits | starttix, bit1tix, tdelta 
    
      starttix := 2400 * US_001 * 9 / 10                            ' 90% of 2.4ms
      bit1tix  := starttix >> 1                                     ' 1 bit is 50% of start bit
    
      org
                    dirh      pin                                   ' enable smart pin
    
    get_start       waitse1                                         ' wait for initial transition
        if_c        waitse1                                         ' signal had been high, wait for next low period
                    rdpin     tdelta, pin                           ' read the low duration
                    cmps      tdelta, starttix              wcz     ' validate against start pulse width
        if_b        jmp       #get_start                            ' not valid, start again
    
                    mov       ircode, #0                            ' clear results
                    mov       irbits, #0
    
    get_bits        getct     tdelta
                    addct1    tdelta, bit1tix                       ' set up timeout period
    chk_timeout     pollct1                                 wz      ' check for timeout
        if_z        jmp       #done                                 ' timeout reached, done
                    pollse1                                 wz      ' check for measurement
        if_nz       jmp       #chk_timeout                          ' not yet, loop
                    waitse1                                         ' prior SE was for high state, now wait for low state
                    rdpin     tdelta, pin                           ' read the low duration
                    cmps      tdelta, bit1tix               wcz
                    shl       ircode, #1                            ' make space for bit
        if_a        or        ircode, #1                            ' write new bit
                    add       irbits, #1                            ' update count      
                    jmp       #get_bits
    
    done            dirl      pin                                   ' disable smart pin
                    rev       ircode                                ' correct lsbs
                    mov       tdelta, #32
                    sub       tdelta, irbits
                    shr       ircode, tdelta
    
      end
    

    (mind you, this code is entirely untested, so caveat emptor and all that.)
  • SeairthSeairth Posts: 2,474
    edited 2020-07-13 16:20
    Though not germane to the primary topic, I looked up SIRCS and came across this: SIRCS Protocol. Is your input inverted? If so, you can change the smart pin configuration to invert the input so that your code matches the true(?) signaling levels. Change that first "if_c" to a "if_nc" and the rest probably works as is (some comments should probably be changed as well).
  • JonnyMacJonnyMac Posts: 9,104
    edited 2020-07-13 16:58
    Is your input inverted?
    Yes, the output from the IR sensor is active-low. I have a ton of P1 experience with SIRCS (client projects) -- in fact, I'm finishing an update to one today. I know how to decode SIRCS; my task is how to do it very cleanly in the P2 given the tools for event timing have changed.

    The code that I said works was tested by pointing a Sony remote at an IR sensor and comparing the value returned with what I expected (~2.4ms) and what I could see on my 'scope.
  • Just remember, when you don’t need speed, spin is great, but when the chips are down, use pasm. Generally tho, I recommend spin and pasm to use separate cogs (cores) even tho P2 can mix them in one cog.
    If you're suggesting that one shouldn't use inline P2ASM, I'm going to politely disagree with you, Ray -- it's my favorite thing about the P2. The interpreter is converting byte codes to P2ASM, and I like that the interpreter lets me do the conversion manually. I was able to make my P1 1-Wire object cog-less in the P2 by using inline P2ASM. That made me very happy. I also used it to get precise timing control of I2C in a cog-less object.

    I know that I'll eventually get it (P2ASM), I'm just in the frustrating part of the learning curve.
  • JonnyMac wrote: »
    Is your input inverted?
    Yes, the output from the IR sensor is active-low. I have a ton of P1 experience with SIRCS (client projects) -- in fact, I'm finishing an update to one today. I know how to decode SIRCS; my task is how to do it very cleanly in the P2 given the tools for event timing have changed.

    The code that I said works was tested by pointing a Sony remote at an IR sensor and comparing the value returned with what I expected (~2.4ms) and what I could see on my 'scope.

    Sorry. I definitely wasn't questioning your SIRCS knowledge. It was new to me, so I looked it up and noticed the non-inverted signal. Maybe a better question would have been: am I looking at the right spec? Regardless, the only reason I mentioned is was because the smart pin configuration could be updated to invert the inverted signal so that your code matched the actual signal specification.
  • JonnyMacJonnyMac Posts: 9,104
    edited 2020-07-13 20:11
    Sorry. I definitely wasn't questioning your SIRCS knowledge.
    I knew you weren't -- sorry if I seemed defensive. SIRCS is about timing; signal level depends are where you are in the chain. As I said, the [frequency tuned] sensors we use are active-low, so I am measuring low-going pulses and looking at the idle time between them (to detect the end of the stream).

    This is a source I use for IR protocol information (SIRCS and others)
    -- https://www.sbprojects.net/knowledge/ir/sirc.php

    From a broader view, what I want to learn to do with smart pins is measure a pulse, like the old BS2 PULSIN command (that can be used to receive SIRCS if one is careful). I'm sure it's possible -- I just haven't had my "Aha!" moment with it.

    For grins, I've attached my P1 SIRCS receive object so you can see where I'm coming from.
  • JonnyMac wrote: »
    From a broader view, what I want to learn to do with smart pins is measure a pulse, like the old BS2 PULSIN command (that can be used to receive SIRCS if one is careful). I'm sure it's possible -- I just haven't had my "Aha!" moment with it.

    Take a look at the second block of code in my post above. In that block, I am using a smart pin mode (%10000) that measures the length of each transition. That's primarily because of your timeout test. If not for that, I could have used the measure high mode (%10001) instead (with the input inverted, in your case).

    In a sense, %10000 is PULSIN if you toggle the state parameter each "call", while %10001 is like PULSIN where the state parameter is determined by a bit in the WRPIN d-field.
  • A user-writable, free-running time -- that is not attached to a pin -- would make this all very easy. I proved that I can do SIRCS with delta timing using getct and a little math; I will stick with that.
  • jmgjmg Posts: 15,173
    JonnyMac wrote: »
    A user-writable, free-running time -- that is not attached to a pin -- would make this all very easy.
    Writable timers would need care, and interrupt protection. Read-only timers, and a little math, can be used many times
    JonnyMac wrote: »
    I proved that I can do SIRCS with delta timing using getct and a little math; I will stick with that.

    Yes, tho polling has a coarse granularity, that hardware methods avoid. In your example, that granularity is totally 'don't care' but users may have other tasks where this matters more ?

    You can improve your 3 line granularity down to 2 lines, which is then 4 sysclks.
    Some hardware (event or Smart Pin cell timers) would allow that to come down to 1 sysclk.
Sign In or Register to comment.