Shop OBEX P1 Docs P2 Docs Learn Events
Pulse Width Measurement Example? — Parallax Forums

Pulse Width Measurement Example?

DavidGregDavidGreg Posts: 38
edited 2008-06-28 13:05 in Propeller 1
Page 204 of the "Propeller Manual" says that the CTR registers can be used for measuring pulse widths. Is there an example available showing how to do this?

Comments

  • agodwinagodwin Posts: 72
    edited 2008-06-24 11:24
    I've been looking at this myself today: as far as I can tell, the pulse width measurement is simply enabling the counter while the input pin is low (or high). You must then read the accumulator in software before the next pulse starts.

    Since you haven't got any way to synchronise to this edge except to wait for it, this more or less ties up the cog until the measurement is made, i.e. it isn't much better than measuring the pulse width using only software.

    The resolution is better using the counters, but the latency requirements (the time available between the pulse ending and the software acting on it) are just as strict.

    I'd be really pleased if someone would correct me and tell me there's a way to capture sysclk or some other counter value on a pin transition.
  • Mike GreenMike Green Posts: 23,101
    edited 2008-06-24 13:38
    The way you measure pulse widths is pretty much as you've described it. To get the best latency, you do need to allocate a cog to the process. To get the best resolution, it's best to use the counters. You can have the cog wait for the end of the pulse by using a WAITPEQ or WAITPNE first for the leading edge, then again for the trailing edge.

    It's possible to capture CNT in less than 100ns from a pin transition using assembly language. The same thing works in Spin, but slower (WAITPEQ, then X := CNT).
  • PyrotomPyrotom Posts: 84
    edited 2008-06-24 14:12
    Have you downloaded and read the Application Note AN001 which describes the counters in much more depth?? It is a great document, and should help you get a good feel for how to do all sorts of interesting things with the counters.
  • agodwinagodwin Posts: 72
    edited 2008-06-24 15:10
    AN001 is useful, but it doesn't say much about pulse width measurement. You have to guess some of it from the section 'Pin State detection modes of operation', which is mostly about it's use in ADCs.
  • Paul BakerPaul Baker Posts: 6,351
    edited 2008-06-24 23:46
    In psuedo code:
      Establish counter in appropriate logic mode (Table 6 of AN001)
     
    Wait on pin until it is of the opposite state (non counting)
     
    Set FRQA or FRQB to one
     
    Loop: Wait on pin until it is the counting state
     
          Wait on pin until it is in the non counting state
     
          PHSA/PHSB will contain the number of cycles the pin was in the counting state
     
          If performing operation again, reset PHSA/PHSB to zero and repeat loop, else set FRQA/FRQB to 0 (or disable counter by writing 0 to CTRA/CTRB) and end.
    

    Using the counter this way will yeild an exact measurement (even in spin)·so long as the code has enough time to execute in the respective on/off periods.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Paul Baker
    Propeller Applications Engineer

    Parallax, Inc.

    Post Edited (Paul Baker (Parallax)) : 6/24/2008 11:52:28 PM GMT
  • DavidGregDavidGreg Posts: 38
    edited 2008-06-25 02:32
    Thanks for the tip Paul. My application is measuring the width of two servo control pulses in integer milliseconds. I'd like to measure both with a single cog if possible, as I'm rapidly running out of cogs for my application. Below is the code I have so far. I'm stuck on how to use waitpeq (or some other wait). I can't figure out how to use waitpeq without assuming that both servo input pins will be high at the same time. Should I use if statements, a second loop, and INA?

    PUB Measure(Pin1, Pin2)
    {{    }}
      ctra[noparse][[/noparse]30..26] := %11010        ' Set mode to "APIN=1"
      ctra[noparse][[/noparse]5..0] := Pin1            ' Set APIN to Pin1
      frqa := 1                     ' Increment phsa by 1 for each clock tick
    
      ctrb[noparse][[/noparse]30..26] := %11100        ' Set mode to "BPIN=1"  
      ctrb[noparse][[/noparse]5..0] := Pin2            ' Set BPIN to Pin2
      frqb := 1                     ' Increment phsb by 1 for each clock tick 
      
      repeat
        waitpeq()
        waitpeq()
        PulseWidth[noparse][[/noparse]0] := (phsa * 1_000) / clkfreq
        PulseWidth := (phsb * 1_000) / clkfreq
        phsa := 0
        phsb := 0
    

    Post Edited (DavidGreg) : 6/25/2008 2:38:05 AM GMT
  • Ken PetersonKen Peterson Posts: 806
    edited 2008-06-25 15:04
    waitpne(state,mask,port) is your best bet.

    mask := %0110 'pins 1 and 2
    state := INA & mask
    port := 0

    When waitpne returns, XOR (INA & mask)·with state to see which bit(s) changed.

    Given the fact that you're doing a servo pulse width with only millisecond precision, this should be no problem in SPIN.

    [noparse][[/noparse]code]
    repeat
    · state := INA & mask
    · waitpne(state, mask, 0)
    · result := (INA & mask ^ state) & state ' result will have 1's where bit changed and previous state was 1
    · if result & %0010
    ··· pulsewidth[noparse][[/noparse]1] := phsa
    ··· phsa~
    · if result & %0100
    ··· pulsewidth[noparse][[/noparse]2] := phsb
    ··· phsb~
    [noparse][[/noparse]/code]

    somebody hit me over the head if there are errors here, but this is the general idea.
    ·

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔


    Post Edited (Ken Peterson) : 6/25/2008 3:21:45 PM GMT
  • Paul BakerPaul Baker Posts: 6,351
    edited 2008-06-25 16:39
    David, are the servo pulses correlated? IOW do they behave in a predictable way, such as both starting thier pulse at the same time? This will determine the necessary complexity of the routine.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Paul Baker
    Propeller Applications Engineer

    Parallax, Inc.
  • DavidGregDavidGreg Posts: 38
    edited 2008-06-25 20:23
    Hi Paul-

    I misspoke in my above post - I want to calculate the pulse-width in integer micro-seconds, as would be used as an input to the Servo32v3 object.

    I want to read the pulses from a "standard" RC receiver (is there such a thing?). From what I can tell using Google, RC receivers use a sort of round robin schedule for controlling the servos. So maybe I could assume that the individual pins are never on at the same time, in which case it might be possible to determine the pulse width from multiple inputs with a single counter?

    I suppose I could try to scope the output from one of my receivers, but I'm concerned that not all receivers would be the same. Perhaps someone out there would be able to shed light on whether RC receivers tend to be consistent.

    -David

    UPDATE -

    This code works great for a single channel.

    PUB Measure(Pin1)
    {{Continuously acquire pulse width on Pin1 and store as microseconds in var PulseWidth }}
    
      ctra[noparse][[/noparse]30..26] := %11010        ' Set mode to "APIN=1"
      ctra[noparse][[/noparse]5..0] := Pin1            ' Set APIN to Pin1
      frqa := 1                     ' Increment phsa by 1 for each clock tick
      
      repeat
        waitpeq(|< Pin1, |< Pin1, 0)
        waitpne(|< Pin1, |< Pin1, 0)
        PulseWidth := phsa /(clkfreq/1_000_000)
        phsa := 0
    

    Post Edited (DavidGreg) : 6/26/2008 1:33:24 AM GMT
  • DavidGregDavidGreg Posts: 38
    edited 2008-06-27 01:42
    Hello-

    Well, it appears as tho assuming that the pulses are never active at the same time works for at least one RC servo (in this case, a Great Planes Electrifly 4 Channel).

    I've attached my code for reading the pulses. It works for my purposes, but I think with a small amount of work it would make a nice generic object that would be useful in many projects. The part I'm missing is how to pass arrays. I'd like to pass an array of longs containing pin numbers (and its length perhaps?) in to my object and be able to pass out an array of the servo pulse widths in microseconds.

    However, I haven't had much luck with passing arrays. The propeller manual doesn't seem to help and I can't find any info on the forum either. Any suggestions (array passing or otherwise) would be appreciated.

    -David

    {{Servo Inputs}}
    VAR
      long  Stack[noparse][[/noparse]20]                      'Stack space for new cog
      long  PulseWidth                  'Space for storing pulse widths, in miliseconds
      byte  Cog                            'Hold ID of cog in use, if any
     
    PUB Start(Pin1,Pin2,Pin3): Success
    {{Start new servo input process.  Return True if successful.}}
      Stop
      Success := (Cog := cognew(Measure(Pin1,Pin2,Pin3), @Stack) + 1)
    
    PUB Stop
    {{Stop toggling process, if any.}}
      if Cog
        cogstop(Cog~ - 1)
     
    PUB Active: YesNo
    {{Return TRUE if process is active, FALSE otherwise.}}
      YesNo := Cog > 0
    
    PUB GetPulseWidth1
      return PulseWidth[noparse][[/noparse]0]
      
    PUB GetPulseWidth2
      return PulseWidth
    
    PUB GetPulseWidth3
      return PulseWidth
          
    PUB Measure(Pin1,Pin2,Pin3)
    {{Continuously acquire pulse width on Pin1 and store as microseconds in PulseWidth }}
      ctra[noparse][[/noparse]30..26] := %11010        ' Set mode to "APIN=1"
      frqa := 1                     ' Increment phsa by 1 for each clock tick
      
      repeat
      
        ctra[noparse][[/noparse]5..0] := Pin1            ' Set APIN to Pin1  
        waitpeq(|< Pin1, |< Pin1, 0)
        waitpne(|< Pin1, |< Pin1, 0)
        PulseWidth[noparse][[/noparse]0] := phsa /(clkfreq/1_000_000)
        phsa := 0
     
        ctra[noparse][[/noparse]5..0] := Pin2            ' Set APIN to Pin2
        waitpeq(|< Pin2, |< Pin2, 0)
        waitpne(|< Pin2, |< Pin2, 0)
        PulseWidth := phsa /(clkfreq/1_000_000)
        phsa := 0
    
        ctra[noparse][[/noparse]5..0] := Pin3            ' Set APIN to Pin3
        waitpeq(|< Pin3, |< Pin3, 0)
        waitpne(|< Pin3, |< Pin3, 0)
        PulseWidth := phsa /(clkfreq/1_000_000)
        phsa := 0
    
  • DavidGregDavidGreg Posts: 38
    edited 2008-06-28 13:05
Sign In or Register to comment.