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?
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.
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).
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.
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.
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.
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
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
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.
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
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
Comments
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.
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).
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
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 := 0Post Edited (DavidGreg) : 6/25/2008 2:38:05 AM GMT
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 Baker
Propeller Applications Engineer
Parallax, Inc.
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 := 0Post Edited (DavidGreg) : 6/26/2008 1:33:24 AM GMT
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 := 0http://forums.parallax.com/showthread.php?p=734261