Shop OBEX P1 Docs P2 Docs Learn Events
Wait for pin to be high for a certain amount of time — Parallax Forums

Wait for pin to be high for a certain amount of time

ryfitzger227ryfitzger227 Posts: 99
edited 2013-09-17 18:20 in Propeller 1
I'm jumping into another part of my timing project and I came across something that I absolutely don't know how to do accurately and by not taking up any extra cogs. What I need to do is pause the system for 500ms while a pin is high. But if the pin doesn't stay high in those 500ms, then it needs to wait for the pin to become high again and then again wait for 500ms. Here's a "step-by-step" diagram of what I want done.
- wait for the pin to become high (for ex: [B]waitpeq(|< 25, |< 25, 0) [/B])
- keep monitoring that pin to make sure it stays high for 500ms
- if the pin did not stay high for 500ms, return to line #1.
- if the pin did stay high for 500ms continue.....

How would I do this in Spin accurately? If you have anymore questions, just ask.

Thanks,
- Ryan

Comments

  • Duane DegnDuane Degn Posts: 10,588
    edited 2013-09-16 15:11
    What does accurately mean?

    If you need a few hundred nanoseconds, You'll either need a second cog to use as a watchdog for a waitpeq, or you'll need to do it PASM.

    If you can get by with something like a few milliseconds, you may be able to use a loop with "if cnt - earlierTime > TIME_LIMIT" type of comparison.

    At least that's all I can think of. I'm curious to see what others come up with.
  • Mark_TMark_T Posts: 1,981
    edited 2013-09-16 15:56
    Duane Degn wrote: »
    What does accurately mean?

    If you need a few hundred nanoseconds, You'll either need a second cog to use as a watchdog for a waitpeq, or you'll need to do it PASM.

    One way to do this is use an extra pin that the watchdog changes, then you use WAITPNE as the instruction, which will respond
    to either pin changing.

    Or the watchdog can just coginit the waiting cog (but this has approx 8k cycle overhead), but no extra pin needed.

    Or you can set a counter going gated on that pin (POS mode), and another that counts negative edges on that pin (NEGEDGE), and just
    poll the counter phase registers? Not perfect as you can read or clear the phase registers in perfect synchrony.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-09-16 19:10
    What are the other possible dynamics of the pin? IOW, what's the maximum amount of time it could stay high? What's the minimum amount of time it could be low between highs?

    -Phil
  • ryfitzger227ryfitzger227 Posts: 99
    edited 2013-09-17 08:58
    I really can't use an extra cog, since there's no more cogs available.

    What I'm trying to do is use a sensor (like an ir sensor) to tell me if something is there. But for example I don't want the timer to start until something has been there for 500ms. This prevents the timer from starting by somebody just walking through the sensor. So the minimum time could be as little as a blink of the eye. A turn around of a ms or so will be okay for a circumstance like that. The only thing I need accurate is the 500ms that the pin needs to be high. I also want to keep away from PASM, since the rest of my project is in Spin.

    There really isn't a maximum time. After the pin has been high for 500ms, I need it to move on to start the timer, etc (so it would no longer even be monitoring that pin).

    Could somebody explain to me a little more of what a watchdog is? (I've never heard of that before with a programming language). If it runs in another cog, I can't use it but it's good to have that knowledge.

    Any more questions, just ask.

    Thanks.
    - Ryan
  • ChrisGaddChrisGadd Posts: 310
    edited 2013-09-17 09:28
    If you just need to pause the routine until a pin stays high for 500ms, then this should work fine:
      target_cnt := clkfreq / 2 + cnt
      repeat while target_cnt - cnt > 0
        if not ina[25]
          target_cnt := clkfreq / 2 + cnt
    
    A watchdog, as implented on the Prop, would need to run in a second cog or external circuitry ... and I don't see how it's really applicable to this situation. Watchdogs are used to detect when a piece of code has hung and send a reset. I also thought about using the counters to count the number of ticks a pin was high, but dunno how you'd make a moving 500ms window.
  • JonnyMacJonnyMac Posts: 9,107
    edited 2013-09-17 10:04
    Here's an easy method that does what you want; call with the trigger pin and the duration (in milliseconds) for an active trigger. Note that this code will block your program until a valid trigger is detected.
    pub debounce(pin, ms) | dbcount, t
    
      dira[pin] := 0                                                ' force to input state
    
      dbcount := 0
    
      t := cnt
      repeat
        waitcnt(t += (clkfreq / 1000))                              ' wait 1ms
        dbcount := ++dbcount * ina[pin]                             ' scan input (clear if 0)
        if (dbcount == ms)                                          ' if at target
          return                                                    '  return to caller
    


    And here's a variation that let's you set the target pin state (1 for high, 0 for low):
    pub debounce(pin, state, ms) | dbcount, t
    
      dira[pin] := 0                                                ' force to input state
    
      dbcount := 0
    
      t := cnt
      repeat
        waitcnt(t += (clkfreq / 1000))                              ' wait 1ms
        if (ina[pin] == state)                                      ' in target state?
          if (++dbcount == ms)                                      ' at debounce timing?
            return
        else
          dbcount := 0
    
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-09-17 12:00
    Here's some code that will get you about as close to exactly 500 ms as one can get in Spin:
    CON
    
      _clkmode      = xtal1 + pll16x
      _xinfreq      = 5_000_000
    
      IPIN          = 0                                     'Input pin from sensor
      OPIN          = 1                                     'Output pin
      IMSK          = 1 << IPIN                             'Mask of input pin
      CTR           = %01000 << 26 | IPIN                   'Ctra init
      PHS           = 80_000_000 / 2                        'Phsa init
      
      TRG           = 80_000_000 / 2 + 20                   'Length of trigger (for testing)
    
    VAR
    
      long stack[30]
      
    PUB  start
    
      cognew(trigger, @stack)                               'Start trigger cog (just for testing). 
    
      frqa := -1                                            'Count down by one.
      ctra := CTR                                           'Start ctra.
    
      repeat
        waitpne(IMSK, IMSK, 0)                              'Wait for IPIN to go low.
        phsa := PHS                                         'Initialize phsa.
        waitpeq(IMSK, IMSK, 0)                              'Wait for IPIN to become high.
        repeat while (phsa > 0 and ina[IPIN])               'Repeat until IPIN has been high for 500 ms or IPIN goes low.
        if (phsa =< 0)                                      'If IPIN was high for 500 ms ...
          do_timer                                          '   ... do timer routine.
    
    PUB do_timer
    
        dira[OPIN]~~                                        'Set OPIN to output.
        outa[OPIN]~~
        waitcnt(cnt + clkfreq >> 6)
        outa[OPIN]~
    
    PUB trigger | t                                         'Simulate pulses on IPIN.
    
      dira[IPIN]~~                                          'Set IPIN to output.
      t := cnt                                              'Initialize t.
      repeat
        waitcnt(t += clkfreq >> 4)                          'Wait 1/16 second.       
        outa[IPIN]~~                                        'Raise IPIN.
        waitcnt(t += TRG)                                   'Wait for TRG time.
        outa[IPIN]~                                         'Lower IPIN.
    

    It uses a counter that counts only when the chosen pin is high. The counter is present with 500 ms and counts down. If the counter rolls under zero while the pin remains high, the timer routine will be called.

    -Phil
  • jmgjmg Posts: 15,173
    edited 2013-09-17 14:23
    If you can tolerate a slightly relaxed spec of taking always 500ms to make the decisions, you could set the pin to drive a counter and then check the counter after 500ms has elapsed. (this does not need an output pin as a time-out)

    Choices are :
    a) IP drives counter by edge :
    First edge loads Counter, Starts 500ms, Read Counter after 500ms. If no change, have no edges inside last 500ms

    b) IP gates the counter
    First edge starts the counter, Starts 500ms, Read counter after 500ms, if Counter = 100% of expected value, IP was never low.

    One method gives how many edges were in the 500ms, and the other gives the % of time the signal was high.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-09-17 14:30
    jmg wrote:
    Choices are :
    a) IP drives counter by edge :
    First edge loads Counter, Starts 500ms, Read Counter after 500ms. If no change, have no edges inside last 500ms

    b) IP gates the counter
    First edge starts the counter, Starts 500ms, Read counter after 500ms, if Counter = 100% of expected value, IP was never low.
    The problem with any method that waits 500 ms is that a second edge may begin a pulse that does last at least 500 ms; but you could miss it.

    -Phil
  • jmgjmg Posts: 15,173
    edited 2013-09-17 15:21
    The problem with any method that waits 500 ms is that a second edge may begin a pulse that does last at least 500 ms; but you could miss it.

    -Phil

    Correct, that is why I said "If you can tolerate a slightly relaxed spec" - the op seems more interested in noise removal, than total minimum decision times, so a relaxed spec may be ok.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-09-17 15:30
    The code I posted does not entail a relaxed spec, nor does it require an output pin. The two outputs are there for simulating a pulse from the sensor and for displaying the pulse acknowledgment on a scope for debugging. Both can be dispensed with in the actual app.

    -Phil
  • tonyp12tonyp12 Posts: 1,951
    edited 2013-09-17 15:38
    > than total minimum decision times

    But if you hard wait 500ms and and a signal was 20ms high + 20ms low + 500ms high
    You would miss the start of the last correct signal as 460ms in to it you stopped, checked the psha and it is 480 (=failed the test)
    And now you start a new test but that will wait for next low to high, so as Phil said you missed it.
  • ryfitzger227ryfitzger227 Posts: 99
    edited 2013-09-17 17:59
    Here's some code that will get you about as close to exactly 500 ms as one can get in Spin:
    CON
    
      _clkmode      = xtal1 + pll16x
      _xinfreq      = 5_000_000
    
      IPIN          = 0                                     'Input pin from sensor
      OPIN          = 1                                     'Output pin
      IMSK          = 1 << IPIN                             'Mask of input pin
      CTR           = 000 << 26 | IPIN                   'Ctra init
      PHS           = 80_000_000 / 2                        'Phsa init
      
      TRG           = 80_000_000 / 2 + 20                   'Length of trigger (for testing)
    
    VAR
    
      long stack[30]
      
    PUB  start
    
      cognew(trigger, @stack)                               'Start trigger cog (just for testing). 
    
      frqa := -1                                            'Count down by one.
      ctra := CTR                                           'Start ctra.
    
      repeat
        waitpne(IMSK, IMSK, 0)                              'Wait for IPIN to go low.
        phsa := PHS                                         'Initialize phsa.
        waitpeq(IMSK, IMSK, 0)                              'Wait for IPIN to become high.
        repeat while (phsa > 0 and ina[IPIN])               'Repeat until IPIN has been high for 500 ms or IPIN goes low.
        if (phsa =< 0)                                      'If IPIN was high for 500 ms ...
          do_timer                                          '   ... do timer routine.
    
    PUB do_timer
    
        dira[OPIN]~~                                        'Set OPIN to output.
        outa[OPIN]~~
        waitcnt(cnt + clkfreq >> 6)
        outa[OPIN]~
    
    PUB trigger | t                                         'Simulate pulses on IPIN.
    
      dira[IPIN]~~                                          'Set IPIN to output.
      t := cnt                                              'Initialize t.
      repeat
        waitcnt(t += clkfreq >> 4)                          'Wait 1/16 second.       
        outa[IPIN]~~                                        'Raise IPIN.
        waitcnt(t += TRG)                                   'Wait for TRG time.
        outa[IPIN]~                                         'Lower IPIN.
    

    It uses a counter that counts only when the chosen pin is high. The counter is present with 500 ms and counts down. If the counter rolls under zero while the pin remains high, the timer routine will be called.

    -Phil

    Phil,

    Thanks for this code. It works great. I added another feature. Is there anyway I can set this to monitor 2 pins? I'm looking at pins 1 and 3. So same concept as before, but now it waits for both pins to be high instead of just 1. I played around with your code, but couldn't get two pins to work.

    Thanks,
    - Ryan
  • jmgjmg Posts: 15,173
    edited 2013-09-17 18:19
    tonyp12 wrote: »
    And now you start a new test but that will wait for next low to high, so as Phil said you missed it.

    After the first edge, subsequent tests do not need to edge re-start.
    From the OPs comments, this sounds like a long de-bounce.
    There is a relaxed solution that exits when the signal is Hi > 500ms, but as you & Phil mention, it can take up to 2x that to deliver the pass tick.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-09-17 18:20
    I believe this will work:
    CON
    
      _clkmode      = xtal1 + pll16x
      _xinfreq      = 5_000_000
    
      IPIN0         = 0                                     'Input pins from sensor(s)
      IPIN1         = 1                                     
      OPIN          = 2                                     'Output pin
      IMSK          = 1 << IPIN0 | 1 << IPIN1               'Mask of input pin
      CTR           = %11000 << 26 | IPIN0 << 9 | IPIN1     'Ctra init
      PHS           = 80_000_000 / 2                        'Phsa init
      
      TRG           = 80_000_000 / 2 + 20                   'Length of trigger (for testing)
    
    VAR
    
      long stack[30]
      
    PUB  start
    
      cognew(trigger, @stack)                               'Start trigger cog (just for testing). 
    
      frqa := -1                                            'Count down by one.
      ctra := CTR                                           'Start ctra.
    
      repeat
        waitpne(IMSK, IMSK, 0)                              'Wait for one or more IPINs to go low.
    '    waitpeq(0, IMSK, 0)                                'Wait for both IPINs to go low.
        phsa := PHS                                         'Initialize phsa.
        waitpeq(IMSK, IMSK, 0)                              'Wait for both IPINs to become high.
        repeat while (phsa > 0 and ina & IMSK == IMSK)      'Repeat until both IPINs have been high for 500 ms or one or more IPINs go low.
        if (phsa =< 0)                                      'If both IPINs were high for 500 ms ...
          do_timer                                          '   ... do timer routine.
    
    PUB do_timer
    
        dira[OPIN]~~                                        'Set OPIN to output.
        outa[OPIN]~~
        waitcnt(cnt + clkfreq >> 6)
        outa[OPIN]~
    
    PUB trigger | t                                         'Simulate pulses on IPIN.
    
      dira := IMSK                                          'Set IPINs to output.   
      t := cnt                                              'Initialize t.
      repeat
        waitcnt(t += clkfreq >> 4)                          'Wait 1/16 second.       
        outa := IMSK                                        'Raise IPINs.
        waitcnt(t += TRG)                                   'Wait for TRG time.
        outa~                                               'Lower IPINs.
    

    It uses the counter's logic function to count only on the AND of both pins.

    BTW, I wasn't sure if you wanted to wait until one pin went low to start looking for coincident highs, or just one of them, so I included options for both.

    -Phil
Sign In or Register to comment.