Shop OBEX P1 Docs P2 Docs Learn Events
Simple encoder pulse count — Parallax Forums

Simple encoder pulse count

joethumphreyjoethumphrey Posts: 25
edited 2011-01-26 13:18 in Propeller 1
Hello all,

I'm a complete newb on this but I have been assigned a project that I'm desperately trying to get started on.

After looking it seems the propeller will do what I need. I'm need to so some inline ISO spec verification on a conveyor.

I cant seem to get the simplest part going though. Which is to read an encoder to derive belt speed.

I have a 5000 pulse encoder that with be running a 1/2" wheel for 10000 pulses an inch @ 56 " per sec. Or 560,000 pulses a sec.

How would I point the pins used to the counter on a cog?
I can see the return from the encoder with the microphone to vga by swapping in the pins connected vs the microphones. I'm using the demo board btw.

If could get that part squared away I think it's just as simple to capture the cnt at those edge points aswell and I can figure out belt speed via the differential .
Kind of a hodge podge trying to get a constant return from the counter.

But all it displays is the pin number on screen.
CON

_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000


OBJ

text : "vga_text"


PUB start | i,a


ctrb := %01010
ctrb := 4
frqb := 1


'start term
text.start(16)

repeat 14

'repeat i from $A1 to $a2
'text.out(i)
text.str(string($A,12,$B,12,$C,2,"cnt"))
text.dec(cnt)
text.str(string($A,14,$B,1,$C,6,"test",$C,2))
text.dec(ctrb)
repeat
text.str(string($A,12,$B,6))
text.hex(i++, 8)

Comments

  • TtailspinTtailspin Posts: 1,326
    edited 2011-01-17 19:03
    The Propeller should make short work of just about any encoder setup..

    Is there a way to have a look at Your encoder setup? I am having trouble visualizing how it reads belt speed.

    I will assume that Your code formatting was thrashed by the forums, and that everything is indented as it should be??

    From what I could see, You get stuck in this repeat loop...
    repeat
        text.str(string($A,12,$B,6))
        text.hex(i++, 8)
    
    It's the last three lines You have written, Your code will never leave this section after it enters..
    and as fast as the Prop is, You might only see one number on screen, maybe try some waitcnt's...

    Also check the sub forum, robotics, and search around for encoder's,
    One of The resident encoder expert's likes to lurk in that forum.
    Just mention dynamic braking, and He will be along shortly...:smile:
  • JonnyMacJonnyMac Posts: 9,208
    edited 2011-01-18 08:29
    I would suggest using a counter in edge detect mode. You can clear the phase counter, wait some period, then collect the phase count which with a bit of math you could convert to belt speed. Once you get used to the Propeller this could run autonomously in its own cog and provide belt speed as a global variable that other Spin cogs could have access to.

    Here's the idea. You would need to call this with cognew passing the input pin (assumes high-going) and the address of your speed variable. This will run in the background and 10x per second update the belt speed which is express in units of 0.1"/second
    pri beltmonitor(encpin, spdpntr) | t, c
    
      ctra := %01010 << 26 | encpin                                 ' set for positive edge
      frqa := 1
      phsa := 0
    
      t := cnt                                                      ' sync timing
      repeat
        waitcnt(t += (clkfreq / 10))                                ' update 10x per second
        c := phsa                                                   ' capture count
        phsa := 0                                                   ' reset for next
        long[spdpntr] := c / 5_600                                  ' write speed to hub
    
  • Ray0665Ray0665 Posts: 231
    edited 2011-01-18 09:10
    Here are two interfaces to encoders
    One is for the Parallax wheel encoder the coding just uses ina and logic
    the other is for a different encoder and uses the propeller counters
    feel free to adopt them to your needs
  • joethumphreyjoethumphrey Posts: 25
    edited 2011-01-18 11:14
    Thank you everyone.
    CON

    _clkmode = xtal1 + pll16x
    _xinfreq = 5_000_000

    VAR

    long stack[10]
    long counter
    OBJ

    text : "vga_text"
    PUb start|freq,Count
    text.start(16)
    cognew(PulseMeasure,@stack)

    dira[4] :=1
    text.str(string($A,14,$B,1,$C,6,"test",$C,2))
    repeat
    text.str(string($A,12,$B,5,"clk "))
    halfperiod :=0
    text.dec(cnt)
    text.str(string($A,12,$B,8,"pul "))

    text.dec(counter)



    PUB PulseMeasure
    ctra := %01000<<26 + 4
    frqa := 1
    phsa := 1
    repeat
    if waitpeq(|<4,|<4,0)
    counter++ ' ^
    if waitpne(|<4,|<4,0)
    counter++

    This does what I wanted but I cant seem to keep it from rebooting every time the counter gets to roughly 235 pulses.

    The "if wait..." seems to have brought on the rebooting but why?

    UPDATE: Seems fixed now after removing the "if"'s . Still accumulates the pulse count without them.

    Strange how it crashes the system though.
  • joethumphreyjoethumphrey Posts: 25
    edited 2011-01-19 08:20
    OK now I have a new problem.

    I have set the accumulator for the read to both positive and negative add. So a pulse in either direction gives an add to the total.
    Which of course isn't right. On my 5000 pulse encoder I get 10000 per rev now.

    So after commenting out the "waitpne" and its adder I'm getting run away numbers. They are alot higher than they should be.
    Adding the check for waitpne and all is back to just double.

    I am missing something simple here but what?
    PUB PulseMeasure
    
      ctra := %01000<<26 + 4
      frqa := 1
    
      repeat
          waitpeq(|<4,|<4,0)
            counter++
          waitpne(|<4,|<4,0)
            counter++
    

    Gives double, counting both trailing and rising edges, but how does that work with the pin being set to only rising?
    PUB PulseMeasure
    
      ctra := %01000<<26 + 4
      frqa := 1
    
      repeat
          waitpeq(|<4,|<4,0)
            counter++
          'waitpne(|<4,|<4,0)
           ' counter++
    

    Gives, very high, run away numbers.
  • Ray0665Ray0665 Posts: 231
    edited 2011-01-19 08:41
    Joe joe joe

    with the code shown you can play with the variable count and the waitxxx till the cows come home
    It just aint doing what you think

    frqa := 1 <---- is the amount to accumulate in phsa for each event set by ctra
    phsa := 0 <---- clears the counter
    ctra := %01000<<26 + 4 <---- sets the mode and pin # - counting begins now
    waitcnt(x+cnt) <---- accumulation time -- counter is now busy counting inphsa
    counter := phsa <---- FETCH THE COUNT (accumulation continues in phsa)

    Post edit ( because a lot of newbees see this stuff )
    Using the waitxxx, count++ you are doing the accumulation by reading the pins directly so when you remove the waitpne the cog waits for pin high and bumps the count then repeats but the prop is so fast that the pin is still high so it bumps again and again and again till it finally goes low and the cycle repeats after the next high and you end up with a meaningless high count, In the case of the two active waitxxx's you get a bump on the positive edge and again on the negative edge, to count pulse by pulse remove one count++ but you still need to have both waitxxx to see the high and then the low so the cog can recognize the pulse. just reverse the order of the waitxxx to see positive pulses or negative pulses.

    using ctra,freq,phsa the cog is doing it for you. You select the type of event and tell it what pin and off it goes. They are different ways of doing the same thing but this method is faster.

    you should check out the counters tutorials.
  • JonnyMacJonnyMac Posts: 9,208
    edited 2011-01-19 09:19
    It has been pointed out that Spin is not fast enough to manually count pulses, you need to use a counter in an edge mode. The attached demo uses the Synth object to generate pulses and a background Spin cog to count them. The background counter updates a global variable (your count) every 0.1s so that you can be fairly responsive.

    Note that the discrepancy between the menu setting (in Hz) and the captured pulses has to do minor math errors in the synth object.
  • ercoerco Posts: 20,260
    edited 2011-01-19 09:22
    JonnyMac, you are da bomb. Love your great input here as usual.
  • Ray0665Ray0665 Posts: 231
    edited 2011-01-19 10:20
    Man you guys respond fast... (this is a good thing)
    I just updated my earlier post you might want to re read it
    Hope I helped too-
  • joethumphreyjoethumphrey Posts: 25
    edited 2011-01-19 11:37
    using ctra,freq,phsa the cog is doing it for you. You select the type of event and tell it what pin and off it goes. They are different ways of doing the same thing but this method is faster.

    Of course, I see it now. I had the right approach to counting but mistakingly added twice. It was so simple. Wait for both pos and neg edges like I have, just don't add for each.
    repeat
          waitpeq(|<4,|<4,0)
            counter++
          waitpne(|<4,|<4,0)
            'counter++ [B]Commenting this one or the other will do. This way I'm guessing adds after pulse but not until another loop passes[/B]
    

    It has been pointed out that Spin is not fast enough to manually count pulses, you need to use a counter in an edge mode.

    Could someone show me where I'm using the spin stuff? I thought the code there was assembly? I can see I've got a ways to go.
    ctra := %01000<<26 + 4
      frqa := 1
    

    It may be better to give a better picture of what I need to do here.

    I'm looking at different methods of collecting data for ISO specing inline for production.
    I make gift cards.

    Bit amplitude, distance , and time are the points I need to collect.

    First thing I need, the pulse accumulator. I know with a 1/2 wheel on a 5k pulse per rev encoder I'll get 10k per inch or 1/10000th " resolution.

    So , with using card detect from a pickup, I'll trigger the read and at each peak of card bits I'll gather the encoder , clock, and amplitude from the peak. With those pieces I can derive and information I need.

    IE.. card speed to run against a voltage gain algorithm for comparison against spec cards. Encoder record separation for bit interval distance.

    I'll have to work into it a bit decoder to verify sentinel locations but after that I think the standard f2f chips will do the full decoding off the same read for actual data returns to correlate data to specimen .

    Thank you everyone, I've got at least a start here on what I need, a few more hundred hours of reading everything and I should be getting data.

    Also for someone else needing something like this or for ridicule.

    data displayed on vga:
    CON
    
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
    VAR
    
      long stack[10]
      long stack1[10]
      long counter
      long card
    OBJ
    
      text        :  "vga_text"
    PUB start|Count
      text.start(16)
      cognew(cardpulse,@stack)
      cognew(PulseMeasure,@stack1)
      dira[4] :=1
      text.str(string($A,14,$B,1,$C,6,"test",$C,2))
      repeat
        text.str(string($A,12,$B,5,"clk "))
    
        text.dec(cnt)
        text.str(string($A,12,$B,8,"encd pul "))
        text.dec(counter)
        text.str(string($A,12,$B,10,"card pul "))
        text.dec(card)
    
    PUB cardpulse
    
    
    
    
      ctrb := %01000<<26 + 1
      frqb := 1
      phsa := 1
      repeat
          waitpeq(|<1,|<1,0)
            card++
          waitpne(|<1,|<1,0)
            'card++
    
    PUB PulseMeasure
    
      ctra := %01000<<26 + 4
      frqa := 1
    
      repeat
          waitpeq(|<4,|<4,0)
            counter++
          waitpne(|<4,|<4,0)
            'counter++
    
    
  • JonnyMacJonnyMac Posts: 9,208
    edited 2011-01-19 15:24
    Could someone show me where I'm using the spin stuff?

    This is Spin code:
    repeat
        waitpeq(|<4,|<4,0)
        waitpne(|<4,|<4,0)
        counter++
    

    This is that code translated to Assembly (by me, the Propeller Tool doesn't compile Spin to native PASM):
    dat
    
                            org     0
    
    entry                   waitpeq ina, SENSE_PIN                  ' wait for high
                            nop
                            waitpne ina, SENSE_PIN                  ' wait for low
                            rdlong  mycount, par                    ' get count from hub
                            add     mycount, #1                     ' increment
                            wrlong  mycount, par                    ' put it back
                            jmp     #entry
    
    
    SENSE_PIN               long    1 << 4                          ' p4
    
    mycount                 res     1
    

    Again... the Spin code is not fast enough for your 560kHz pulse rate. What you can do, however, is put a counter in edge mode like I demoed above and let the counter do all the hard work. That IS possible in Spin because you're using a counter to count, and it does that while other code is running.
  • kuronekokuroneko Posts: 3,623
    edited 2011-01-19 15:41
    @JonnyMac: Shouldn't that be (shadow[ina] defaults to 0):
    dat
    
                            org     0
    
    entry                   waitp[COLOR="red"]ne[/COLOR] ina, SENSE_PIN                  ' wait for high
                            nop
                            waitp[COLOR="red"]eq[/COLOR] ina, SENSE_PIN                  ' wait for low
                            rdlong  mycount, par                    ' get count from hub
                            add     mycount, #1                     ' increment
                            wrlong  mycount, par                    ' put it back
                            jmp     #entry
    
    
    SENSE_PIN               long    1 << 4                          ' p4
    
    mycount                 res     1
    
  • JonnyMacJonnyMac Posts: 9,208
    edited 2011-01-19 17:26
    I'm sure you're right -- was whipping up quick code example to illustrate what Spin is versus Assembly. Thanks, as ever, for the catch.
  • Ray0665Ray0665 Posts: 231
    edited 2011-01-19 20:59
    Joe
    I'm looking at the code you posted most recently and I think you are missing what is being said, if I'm wrong I apologize.

    The point is that there are two independent ways of counting transitions or pulses on i/o pins
    The first is the repeat, waitxxx, count++ and I'm sure you understand that code now.
    This method in spin is the slowest and doing the same thing in assembly is a bit faster,
    either way you do not need to setup anything in the counters for this method to work.

    The second method is to use one of the two independant HARDWARE counters available in each cog
    You activate them by setting values into the ctra,frqa,phsa registers (or b for the second counter) and then
    read the phsa (or b) register whenever you need the count.
    This is the fastest method available on the propeller.
  • joethumphreyjoethumphrey Posts: 25
    edited 2011-01-24 14:51
    Thank you everyone, JonnyMac.

    Got one more issue.

    I can not get the proper response from setting one pin low using the samples you provided.

    org      0
    detecter                waitpne ina, dectectPin                  ' wait for high
                            waitpeq ina, dectectPin                  ' wait for low
                            rdbyte  meddct, par                    ' get count from hub
                            add     meddct, #1                     ' increment
                            wrbyte  meddct, par                    ' put it back
                            jmp     #detecter
    
    
    dectectPin              long    0 << 6                          ' p4
    
    meddct                 res     1
    

    That should set the pin to check for low, right?

    Using a LV-21a laser pickup, the out goes low on detect. If I set it to high it works in reverse. I get counting until I trigger the detect as the pin was set to for high. But on low setting I get nothing.

    I've tried swapping the equal/not equals. Removing each as well with no luck.

    Is there something else to detecting low on a pin that I'm missing? From the examples I can find they look similar.
    Is there some out put that has to be switched on to make the pin high to wait for low or do I need to supply a check voltage on outside?

    Thanks again, Joe.
  • JonnyMacJonnyMac Posts: 9,208
    edited 2011-01-24 15:03
    dectectPin long 0 << 6 ' p4

    Zero shifted right by any value is still zero. If you want to use P4 then it should be 1 << 4

    And... waitpne is used to wait for a pin to go low. If your encoder is using an active-low pulse then this is what you want: look for low, look for high (end of pulse), update count.
  • joethumphreyjoethumphrey Posts: 25
    edited 2011-01-24 15:26
    It needed the pullup resistor on the pin.

    Reset mask to 1 and installed pullup , all is well.

    Thank you again.
  • joethumphreyjoethumphrey Posts: 25
    edited 2011-01-25 11:47
    detecter                waitpeq ina, dectectPin
                            wrbyte  meddct,n1                         
                            waitpne ina, dectectPin                
                            wrbyte  meddct,n2                   
                            jmp     #detecter
    
    
    dectectPin              long    1 << 6                         
    n1                      long    1
    n2                      long    0
    meddct                 res     1
    


    I can't seem to make this work. All I'm looking to do is use that as a global boolean flag , either low, card in and 1 or high = no card and 0.

    What am I missing this time?

    Got It
    detecter                waitpeq ina, dectectPin
                            wrbyte  meddct,n1                         
                            waitpne ina, dectectPin                
                            wrbyte  meddct,n2                   [B]Wrongly called internal variable vs the passed parameter from init[/B]
                            jmp     #detecter
    
    
    dectectPin              long    1 << 6                         
    n1                      long    1
    n2                      long    0
    meddct                 res     1
    

    Corrected:
    detecter                waitpeq ina, dectectPin
                            wrbyte  n1,par                         
                            waitpne ina, dectectPin                
                            wrbyte  n2,par                   
                            jmp     #detecter
    
    
    dectectPin              long    1 << 6                         
    n1                      long    1
    n2                      long    0
    meddct                 res     1
    
  • joethumphreyjoethumphrey Posts: 25
    edited 2011-01-26 13:18
    One more time here.

    I've chopped up the ADC demo script to experiment with amplitude recording but I cant seem to get it to work at the same time as another function.

    They are running on separate cogs with separate variables yet if both are loaded neither work. But they work if one or the other is commented out, noted below in bold.
    CON
    
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
      bits = 16
    
    
    VAR
    
    
      long counter        'encoder accumulator
      long card           'card pulse accu
      byte detect         'card detect
      long val            'amplitude
    OBJ
    
      text        :  "vga_text"
    PUB start|Count
      text.start(16)
      cognew(@entry,@counter)
      cognew(@detecter,@detect)
      cognew(@cardmes,@card)       '[B]Commenting this:[/B]
      cognew(@asm,@val)                '[B]Or this makes the other one work again.[/B]
      text.str(string($A,6,$B,1,$C,6,"Testing",$C,2))
      repeat
        text.str(string($A,12,$B,8,"clk "))
    
        text.hex(cnt,8)
        text.str(string($A,12,$B,9,"encd pul   "))
        text.dec(counter)
        text.str(string($A,12,$B,10,"card pul   "))
        text.dec(card)
        text.str(string($A,12,$B,11,"card detct "))
        text.dec(detect)
        text.str(string($A,12,$B,12,"card amp   "))
        text.dec(val)
    
    DAT
    
                                    org     0
    
    entry                   waitpeq ina, SENSE_PIN                  ' wait for high
                            nop
                            waitpne ina, SENSE_PIN                  ' wait for low
                            rdlong  mycount, par                    ' get count from hub
                            add     mycount, #1                     ' increment
                            wrlong  mycount, par                    ' put it back
                            jmp     #entry
    
    
    SENSE_PIN               long    1 << 4                          ' p4
    
    mycount                 res     1
    
                                    org      0
    detecter                waitpeq ina, dectectPin
                            wrbyte  n1,par                          ' wait for high
                            waitpne ina, dectectPin                  ' wait for low
                            wrbyte  n2,par                    ' put it back
                            jmp     #detecter
    
    
    dectectPin              long    1 << 6                          ' p4
    n1                      long    1
    n2                      long    0
    meddct                 res     1
    
                                    org      0
    cardmes                 waitpeq ina, cardPin                  ' wait for high
                            waitpne ina, cardPin                  ' wait for low
                            rdlong  cardct, par                    ' get count from hub
                            add     cardct, #1                     ' increment
                            wrlong  cardct, par                    ' put it back
                            jmp     #cardmes
    
    
    cardPin              long    1 << 1                          ' p4
    
    cardct                 res     1
    
    
                  org       0
    asm           mov       dira,asm_dira                   'make pins 8 (ADC) and 0 (DAC) outputs
    
                  movs      ctra,#5                         'POS W/FEEDBACK mode for CTRA
                  movd      ctra,#3
                  movi      ctra,#%01001_000
                  mov       frqa,#1
    
                  mov       asm_cnt,cnt                     'prepare for WAITCNT loop
                  add       asm_cnt,asm_cycles
    
    
    :spot         waitcnt   asm_cnt,asm_cycles              'wait for next CNT value (timing is determinant after WAITCNT)
    
                  mov       asm_sample,phsa                 'capture PHSA and get difference
                  sub       asm_sample,asm_old
                  add       asm_old,asm_sample
    
                  wrlong    asm_sample, par
    
                  jmp       #:spot                          'wait for next sample period
    
    asm_cycles    long      |< bits - 1                     'sample time
    asm_dira      long      |< 3                        'output mask
    
    asm_cnt       res       1
    asm_old       res       1
    asm_sample    res       1
    

    I've moved the pins around to get some physical separation but nothing different.

    Anyone see what's causing this?
Sign In or Register to comment.