Shop OBEX P1 Docs P2 Docs Learn Events
Increment At every button push — Parallax Forums

Increment At every button push

SnyggisSnyggis Posts: 14
edited 2013-06-22 14:22 in Propeller 1
I am struggling to come up with code that will increment a variable with every push of a button (or signal from a sensor).

My sensor can signal at roughly 2000, pulses per second. I cant seem to do it with a push button switch much less at a rate like that.

I would rally appreciate any help here!

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2013-06-22 08:16
    Post your code and a more complete description of what you're trying to do. There's a big difference between a pushbutton and a sensor pulse stream. Pushbutton presses tend to be very noisy and they have contact bounce (look this up in Wikipedia). Counting code has to take this into account. Sensors tend to have clean pulses. What do you have?

    attachment.php?attachmentid=78421&d=1297987572
  • SnyggisSnyggis Posts: 14
    edited 2013-06-22 08:41
    I am trying to make a tachometer for a spindle motor spinning at 60,000 RPM. The photointerupter makes two pulses per revolution. Here is the spec sheet:

    http://www.mpja.com/download/18028op.pdf

    Here is my complete Program:
    CON
       
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
       
    
    OBJ
       
      Debug: "FullDuplexSerialPlus" 
       
       
    Var long stack[50], PPS, Edge, IDX
    
    PUB Main
      Debug.start(31, 30, 0, 57600)  
    
      cognew(PulseOut(21, 55, 150), @stack[10])
      cognew(LEDout(16, 15), @stack[20])
      cognew(LEdge(16), @stack[30])
      cognew(RPMGen(1), @stack[40])
      repeat
        waitcnt(clkfreq + cnt)
          debug.str(String("PPS = "))
          debug.dec(PPS)
          debug.str(string(13))
          debug.dec(IDX)
          debug.str(string(13))
       
    Pub PulseOut(pin, hz, ms)
    
      Dira[pin]:=1
    
      Repeat hz*5
        Outa[Pin]:=1
        waitcnt(clkfreq/100000*200 + cnt)
        outa[Pin] := 0
        waitcnt(clkfreq/hz + cnt) 
    
      Repeat hz*2
        Outa[Pin]:=1
        waitcnt(clkfreq/100000*100 + cnt)
        outa[Pin] := 0
        waitcnt(clkfreq/hz + cnt)
    
      Repeat hz*2
        Outa[Pin]:=1
        waitcnt(clkfreq/100000*ms + cnt)
        outa[Pin] := 0
        waitcnt(clkfreq/hz + cnt)
    
     
    Pub LEDout(InPin, OutPin)
           
      Dira[InPin]:=0
      Dira[OutPin]:=1
      repeat 
        outa[OutPin] := ina[InPin]
    
    Pub LEdge(InPin)| on, off
     Dira[InPin]:=0
     IDX:=0
     
      Repeat 
        if ina[inpin] == 1 and IDX == 0
           IDX := 1
           edge++
             
        if ina[inpin]== 1 and IDX ==1
          IDX:=1
        IDX := 0
      
    
    Pub RPMGen(cycle)
    
      Repeat
        edge~
        waitcnt(clkfreq+cnt)
        PPS := Edge 
    

    Really the only important part for the counter is the LEdge (leading edge) method:
    Pub LEdge(InPin)| on, off
     Dira[InPin]:=0
     IDX:=0
     
      Repeat 
        if ina[inpin] == 1 and IDX == 0
           IDX := 1
           edge++
             
        if ina[inpin]== 1 and IDX ==1
          IDX:=1
        IDX := 0
    

    I was also trying something like this, but cant seem to get either one to work well:
    Pub LEdge(inpin)| Edge
      ctra[30..26]:= %01010
      ctra[5..0]:= 16
      frqa:=1
      Edge~
        
      Repeat 
        phsa~
        waitcnt(clkfreq/1000+cnt)
        if phsa> 1 
              edge++
        RPM := edge
    

    Anyone have any ideas? Really all I want to do is count every time the interupt is tripped. I can count how many times this happens in a minute and calculate the RPM. There has to be an easy way to do that?
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2013-06-22 09:37
    To use the cog counters to do this in the context of your RPMgen method, no need to start another cog. Just initialize the counter, then periodically RPMgen reads the value of phsa into your variable, edge, and then resets phsa back to zero for the next round.
    [SIZE=1][FONT=courier new]PRI SetupEdgeCount
    [/FONT][/SIZE][SIZE=1][FONT=courier new][/FONT][/SIZE][SIZE=1][FONT=courier new]  ctra := %%01010 << 26 + inpin   ' posedge mode on inpin[/FONT][/SIZE][SIZE=1][FONT=courier new]
    [/FONT][/SIZE][SIZE=1][FONT=courier new]   frqa := 1
    [/FONT][/SIZE]
    

    As Mike mentioned, pushbuttons and a sensor like the tachometer are quite different in terms of contact bounce or the lack thereof respectively. The cog counters will count every single bounce of a pushbutton

    For a loop running in another cog, your method sets IDX=0 every time around the loop, so it will continue to count up so long as the pin remains high, rather than just at the 0-->1 transition. You can probably save the logic by working on it a bit more.

    Here are a couple on alternative methods for your consideration. (neither of which have debouncing)
    [FONT=courier new][SIZE=1]PUB Edges(inpin)  ' 
      repeat
        waitpeq(|< inpin, |< inpin, 0)   ' wait for pin high
        edge++     ' count it
        waitpeq(0, |< inpin, 0)   ' wait for pin low
    [/SIZE][/FONT]
    
    [FONT=courier new][SIZE=1]PUB Edges(inpin) | new, old   ' state variable[SIZE=1]s[/SIZE]
     old := ina[inpin]
      repeat
        new := ina[inpin]
        edge += (new ^ old & new)  ' logic detects 0->1 [SIZE=1]edge[/SIZE]
        old := new
    [/SIZE][/FONT]
    
  • Duane DegnDuane Degn Posts: 10,588
    edited 2013-06-22 09:42
    2,000 pulses per second may be difficult for Spin to keep up with.

    You could use the Propeller's built in counters to count the pulses. Ariba wrote some code to count pulses coming from another Propeller. You may be able to adapt his code for your needs.

    There's a section in the Propeller Education Kit (available from the help menu of the Propeller Tool) on counters. If you want to understand Ariba's code you could read up on counters there.

    Another alternative is to use a PASM loop to count the pulses. I bet there are several objects already available to do this but if you wanted to write your own you should read JonnyMac's Spin Zone article "Spinning It Up with Encoders" (or something like that). There's a link to the Spin Zone articles in post #3 of my index (see signature).

    At the top of post #1 of my signature is a link to a Rich's search page to search the Parallax and Savage Circuits forums. You may want to see how others have solved this issue (I'm sure it's been solved many times before).

    Edit: Or better yet, take Tracy's advice.
  • SnyggisSnyggis Posts: 14
    edited 2013-06-22 12:30
    Thanks! It appears to be working. Though the RPM has crazy values. You have given me plenty of reading material!

    Would this code appear to be a good way to debounce?
    PUB Edges(inpin)  ' 
      repeat
        waitpeq(|< inpin, |< inpin, 0)   ' wait for pin high
        edge++     ' count it
        waitcnt(clkfreq/4000+cnt)
        waitpeq(0, |< inpin, 0)   ' wait for pin low
    
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2013-06-22 13:16
    That doesn't debounce. It only adds a delay to the loop. For it to debounce, it has to check the state of the input to verify that it stays the same through two or more sample periods, then act. Here is an example where the first waitpeq is replaced with a loop that increments and auxiliary counter so long as it detects a 1 at the input. That counter has to increase to a threshold value "debounce" before it registers as good. If the input drops back to zero during the count, that resets the whole count back to zero.
    [SIZE=1][FONT=courier new]PUB Edges3(inpin, debounce) | auxcnt
      repeat
        auxcnt~
        repeat until (auxcnt := (auxcnt[/FONT][/SIZE][SIZE=1][FONT=courier new] & -ina[inpin][/FONT][/SIZE][SIZE=1][FONT=courier new] + ina[inpin])) > debounce
        edge++
        waitpeq(0, |< inpin, 0)
    [/FONT][/SIZE]
    
    This snippet debounces the rising edge only, then uses a simple waitpeq for the falling edge. You could add a waitcnt into the inner repeat loop, 100 microseconds say, to make it less sensitive on the Spin timing. That would be for a mechanical pushbutton, and use debounce=20 for a total of about 20 milliseconds of debounce time. However, as Duane noted above, at 2000Hz you don't have time for a lot of Spin instructions. About 0.5 millisecond for the whole loop.

    About the logic...
    auxcnt & -ina[inpin] ' when pin is zero, ANDs 0 with auxcnt, resets auxcnt to zero
    ' when pin is one, ANDs $ffffffff with auxcnt, auxcnt passes through.
  • SnyggisSnyggis Posts: 14
    edited 2013-06-22 14:02
    Awesome, thanks so much for the help. I have been going through the manuals for hours. I still dont fully understand the logic of your code. For the "&" statement to be true both auxcnt & -ina[pin] have to equal 1. With -ina[inpin] being negative I dont see how it will ever allow the "&" statement to be anything other than 0?

    0 & 0 = 0
    0 & -1 = 0

    Am I missing something here? Thanks again!
  • Duane DegnDuane Degn Posts: 10,588
    edited 2013-06-22 14:22
    Snyggis wrote: »
    0 & 0 = 0
    0 & -1 = 0

    Am I missing something here? Thanks again!

    "&" is a bitwise and.

    Page 164 of Propeller Manual (v1.2) does a good job explaining it so I won't repeat what the manual says here.
Sign In or Register to comment.