Shop OBEX P1 Docs P2 Docs Learn Events
Wait for Pin State or Count - attempts to make fastest ASM code — Parallax Forums

Wait for Pin State or Count - attempts to make fastest ASM code

Timothy D. SwieterTimothy D. Swieter Posts: 1,613
edited 2014-11-18 02:50 in Propeller 1
In a couple of my projects recently, I've come across situations where I need to wait for a pin change while also watching a counter for timeouts.

In the first case, it is for protocol processing. The pin is an input and wiggling with data coming in. Part of receiving the protocol is to allow for some variability in receiving, but if nothing is received in a certain amount of time the packet should be processed and then go back to waiting for the start signature of the next packet. So, I'm watching a pin changing, but also want to keep an eye on a counter. Note this isn't checking a counter every time a pin changes, because the pin could be held low (or high) for a long duration, causing a timeout.

In the second case, every so often (determined by the counter), a set of processing is occurring. But, if a pin changes to a specified state (at any time) then a specific processing should take place. Note this isn't if the pin is at the specified state when the timer exceeds, but that the special processing should occur 'immediately' when the pin changes state.

So, below is my first pass at an ASM Snippet that may work for the above situations. It gets the current state of the pins and the counter. Checks both of them, and if either condition exists it goes to the proper section of code. The priority of either condition is determined by which is checked first. The code doesn't include a check yet for CurrentCnt or TargetCnt rollover. I've not tried running this yet, because of lack of equipment while I am traveling. I thought it would be good to bounce it off the community and see if this could be made tighter (faster) or if someone has already tackled this problem. Another avenue I may explore is employing Counter A or Counter B, if it helps to get this as fast as possible.

This is just a snippet, assume I/O and Targets are set up prior to entering this stage of the code.
              'Get the current state of the inputs and counter
:Loop         mov       CurrentPins, ina
              mov       CurrrentCnt, cnt


              'Check for time to be exceeded
              cmp       CurrrentCnt, TargetCnt  wz, wc
if_nz_and_nc  jmp       #:TimeExceeded


              'Use only one of the following low or high checks
              'Use if checking for a pin to be low
              and       CurrentPins, TargetPinMask wz
        if_z  jmp       #:PinChanged


              'Use if checking for a pin to be high
'             and       CurrentPins, TargetPinMask wc
'       if_c  jmp       #:PinChange


              jmp       #:Loop




:PinChanged   nop




:TimeExceeded nop

Comments

  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2014-11-15 19:37
    In a couple of my projects recently, I've come across situations where I need to wait for a pin change while also watching a counter for timeouts.

    In the first case, it is for protocol processing. The pin is an input and wiggling with data coming in. Part of receiving the protocol is to allow for some variability in receiving, but if nothing is received in a certain amount of time the packet should be processed and then go back to waiting for the start signature of the next packet. So, I'm watching a pin changing, but also want to keep an eye on a counter. Note this isn't checking a counter every time a pin changes, because the pin could be held low (or high) for a long duration, causing a timeout.

    In the second case, every so often (determined by the counter), a set of processing is occurring. But, if a pin changes to a specified state (at any time) then a specific processing should take place. Note this isn't if the pin is at the specified state when the timer exceeds, but that the special processing should occur 'immediately' when the pin changes state.

    So, below is my first pass at an ASM Snippet that may work for the above situations. It gets the current state of the pins and the counter. Checks both of them, and if either condition exists it goes to the proper section of code. The priority of either condition is determined by which is checked first. The code doesn't include a check yet for CurrentCnt or TargetCnt rollover. I've not tried running this yet, because of lack of equipment while I am traveling. I thought it would be good to bounce it off the community and see if this could be made tighter (faster) or if someone has already tackled this problem. Another avenue I may explore is employing Counter A or Counter B, if it helps to get this as fast as possible.

    This is just a snippet, assume I/O and Targets are set up prior to entering this stage of the code.
                  'Get the current state of the inputs and counter
    :Loop         mov       CurrentPins, ina
                  mov       CurrrentCnt, cnt
    
    
                  'Check for time to be exceeded
                  cmp       CurrrentCnt, TargetCnt  wz, wc
    if_nz_and_nc  jmp       #:TimeExceeded
    
    
                  'Use only one of the following low or high checks
                  'Use if checking for a pin to be low
                  and       CurrentPins, TargetPinMask wz
            if_z  jmp       #:PinChanged
    
    
                  'Use if checking for a pin to be high
    '             and       CurrentPins, TargetPinMask wc
    '       if_c  jmp       #:PinChange
    
    
                  jmp       #:Loop
    
    
    
    
    :PinChanged   nop
    
    
    
    
    :TimeExceeded nop
    

    I've only skimmed your post but wouldn't this bit of code work? (in principal)
                  mov    r1,#500        ' timeout in 500 loops
    wflow        test    mask,ina    wz wc    ' look for a low
        if_nz    djnz    r1,#wflow
        if_c      jmp    #timeout        ' timeout or start bit?
    startb
    
  • Timothy D. SwieterTimothy D. Swieter Posts: 1,613
    edited 2014-11-16 14:44
    Peter -

    Yes, I think that will work, and is more concise than what I posted. Boy I just love getting inspired by the community.

    I'm thinking through your code and it is making sense to me. A timeout (loop count) is calculated based on the known number of clock cycles and frequency of the Prop. You keep looping for the define period (could be defined before runtime or at runtime with a couple calcs). When the new lot bit is detected, the code falls through to the start bit. I'm curious as to why having a "if_c" on the front of the timeout jump and not just have a "if_nz"? Perhaps when testing for one bit, this is the same.

    Just to be sure, with the Assembly Conditions, the instruction only takes 4 clock cycles if the condition doesn't allow execution otherwise it takes the appropriate number of clock cycles for the instruction, true? I hadn't thought much about that until now but it must take some time.
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2014-11-16 15:39
    Peter -

    Yes, I think that will work, and is more concise than what I posted. Boy I just love getting inspired by the community.

    I'm thinking through your code and it is making sense to me. A timeout (loop count) is calculated based on the known number of clock cycles and frequency of the Prop. You keep looping for the define period (could be defined before runtime or at runtime with a couple calcs). When the new lot bit is detected, the code falls through to the start bit. I'm curious as to why having a "if_c" on the front of the timeout jump and not just have a "if_nz"? Perhaps when testing for one bit, this is the same.

    Just to be sure, with the Assembly Conditions, the instruction only takes 4 clock cycles if the condition doesn't allow execution otherwise it takes the appropriate number of clock cycles for the instruction, true? I hadn't thought much about that until now but it must take some time.

    The C bit is set if there are an odd number of 1's following the test instruction. So this is useful to determine whether the bit was high or low as the Z bit will always be zero following a timeout so nz distinguishes a timeout and c/nc the signal. The granularity of the test/djnz combo results in 100ns latency which is fine for almost all but the very high speed signals. If you want a 5ms timeout then load (via a long) 50,000 into the timeout. Fortunately DJNZ is optimized for loops taking only 4 cycles normally but 8 when it exits so if you want to be absolutely precise that's another 50ns for a timeout :)

                  mov    r1,#500        ' timeout in 500 loops
    wflow        test    mask,ina    wz wc    ' look for a low
        if_nz    djnz    r1,#wflow
        if_c      jmp    #timeout        ' timeout or start bit?
    startb
    
  • ErNaErNa Posts: 1,752
    edited 2014-11-17 05:57
    great solution! Thanks, Peter
  • ManAtWorkManAtWork Posts: 2,176
    edited 2014-11-18 02:50
    I also have a bit of code that might be useful. It's not the fastest but it can cancel the timing jitter of the polling loop.
                  neg    FRQA,#1
                  mov    CTRA,modePosCnt1
                  mov    n,#500                  ' timeout counter
                  mov    PHSA,#25
    waitLoop
                  test   maskIn,INA wc              ' wait for pin
            if_nc djnz   n,#waitLoop           
            if_nc jmp    #timeout             
                  mov    time,PHSA                  ' compensate jitter
                  min    time,#9
                  add    time,CNT
                  waitcnt time,#0
    ...
    modePosCnt1   long      %01000<<26 + bitIn      ' counter POS mode, APin=Dat
    maskIn        long      1<<bitIn
    
Sign In or Register to comment.