PULSIN_uS(PIN, State) / timeout
Hi, i often use the BS2_functions. One that is very easy for RC projects is the PULSIN_uS and used PULSIN_Clk. But when my transmitter is still off (no puls), the program who reads the puls, is not running any more. The function waitpeq(|< Pin, |< Pin, 0) in PULSIN_Clk is waiting ....... for a RC puls.
Is it possible the build a time out there, so when there is no puls after timeout of say 2msec, then the puls width is 0msec. I'm not an expert in the special functions like waitpeq.
what means (in PULSIN_Clk): Note: Absence of pulse can cause cog lockup if watchdog is not used - See distributed example
** here the functions *******************************************************
PUB PULSIN_uS (Pin, State) : Duration | ClkStart, clkStop, timeout
{{
Reads duration of Pulse on pin defined for state, returns duration in 1uS resolution
Note: Absence of pulse can cause cog lockup if watchdog is not used - See distributed example
x := BS2.Pulsin_uS(5,1)
BS2.Debug_Dec(x)
}}
Duration := PULSIN_Clk(Pin, State) / us + 1 ' Use PulsinClk and calc for 1uS increments
PUB PULSIN_Clk(Pin, State) : Duration
{{ special versi from Travis
Reads duration of Pulse on pin defined for state, returns duration in 1/clkFreq increments - 12.5nS at 80MHz
Note: Absence of pulse can cause cog lockup if watchdog is not used - See distributed example
x := BS2.Pulsin_Clk(5,1)
BS2.Debug_Dec(x)
}}
DIRA[pin]~
ctra := 0
if state == 1
ctra := (%11010 << 26 ) | (%001 << 23) | (0 << 9) | (PIN) ' set up counter, A level count
else
ctra := (%10101 << 26 ) | (%001 << 23) | (0 << 9) | (PIN) ' set up counter, !A level count
frqa := 1
waitpne(|< Pin, |< Pin, 0) ' Wait for opposite state ready
phsa:=0 ' Clear count
waitpeq(|< Pin, |< Pin, 0) ' wait for pulse
waitpne(|< Pin, |< Pin, 0) ' Wait for pulse to end
Duration := phsa ' Return duration as counts
ctra :=0
Comments
The problem doing it with conditional statements in Spin is that you'd loose a lot of accuracy. The waitxxx statements prevent high-level code loops that are slow.
If this is just a matter of waiting for the RC receiver to be active, before proceeding, you can do this:
I don't like to post untested code so you can see that this does work by running the attached demo -- no connections to the outside world are required. The code has a little servo pulse generator that can be turn on and off with the USE_SIM constant. Be sure to set this to NO before connecting your RC receiver to the test pin.
I connected a logic analyzer to verify the pulse generator cog was behaving as expected. Here's a capture:
My code matches:
You might want to connect a 50K pull-down to the servo input pin(s). This will keep the pins in a known state if the receiver is disconnected.
Hi Jon,
Thank you for your quick reaction. I have use the scope to see the signals. It works very well if there is a pull down resistor (50K) to the ground. If you have a pullup to the 3V3volt it comes with the "- - Present" messages but not with the Pulse_in value. I have with my RC receiver the same problem. By power on, the output of the receiver goes high, and when there is no transmitter on, the output still goes high. When you switch the transmitter on, the output of the receiver goes low with pulses to high, and you get the Pulse_in values. So by power on (receiver) the detection from low to high, will be interpreted as one long (high) pulse I think?
A workaround for me, is to detect the transition from high to low of the receiver output. This happens when the transmitter switch on. And then measure the PulseIn.
Your PULSE_IN (px, state) looks a lot more complex than the PULSIN_uS (Pin, State) from the BS2_Functions. Is it the accuracy?
That's interesting -- and good information.
Good idea. You can use POSEDGE mode (%01010) which will tell you that there was a 0->1 transition that indicates pulse(s) from the receiver.
My version is functionally identical except that it will use CTRB if CTRA is busy doing something else. That said, I reviewed it, did some timing tests, and made changes (library is attached).
Libraries are generic; for something specific like in your project you might just write a specific method for capturing a servo pulse (once you know it's safe to do so). Perhaps like this.
I updated my little demo and have it attached (includes update to jm_io.spin). It uses another constant to switch between the IO library (generic pulse_in())and the servo_pulse() method.
Jon,
Here is the referenced BS2_Functions.spin file that was written by Martin Hebel.
Jon, i have tested the rcx_active(pin), the io.pulse_in(RCX, 1) and the servo_pulse(pin), in my RC project.
Everything works as intended GREAT! Thanks.
If i want the servo_pulse(pin) routine in a separate IO object, where can / must i declare the US_001?
Normally you do this only in the main program
pub servo_pulse(pin) : result | mask
'' Measure servo pulse (0-1-0) on pin
'' -- return value in usecs
'' -- uses CTRA
mask := |<pin ' create mask for pin
dira[pin] := 0
ctra := (%01000 << 26) | pin
frqa := 1
waitpne(mask, mask, 0) ' wait for low
phsa := 0 ' reset measurement
waitpeq(mask, mask, 0) ' wait for high
waitpne(mask, mask, 0) ' wait for low
return (phsa + constant(US_001 >> 1)) / US_001 ' convert to us (round up)
Since timing is controlled by the top object, you could just pass it as a parameter in the start() method of your object.
Is your separate object going to block, or use a another cog so that it can free-run w/o blocking the main app? If the latter, you have more choices. Since each cog has two counters, you could watch two pins at the same time and never block your foreground program. I did this for a TV show prop element (belt buckle for the character Black Thunder). The brightness and animation control came from a 4-channel wireless DMX receiver. I used two cogs (2 counters each) to measure the PWM on each channel and make programming decisions the pixels in the buckle PCB from there.
Jon , impressive your project! it's amazing what you can do with the Propeller 1/2
I have built a RC submarine, 1.65m/30kg, started years ago with several basic stamps. Years later i was able to replace the basic stamps by one Java stamp.
Now i use the Propeller1 for almost 10 years. And also for other projects. I have the I2C bus system, air compressor, main ballast tank, pistons, static depth control etc.
A technical challenge for years!
Thanks for sharing. Despite the fact that i have been working with it for many years, the possibilities are enormous.
The most of the time my software is high level. The special (hardware) functions are very powerful.
_Since timing is controlled by the top object, you could just pass it as a parameter in the start() method of your object. _
**************** MAIN PORGRAM)**********************
con { timing }
_xinfreq = 5_000_000 ' use 5MHz crystal
MS_001 = CLK_FREQ / 1_000 ' ticks in 1ms
US_001 = CLK_FREQ / 1_000_000
Can I do this in the jm_io.start (CLK_FREQ), the servo_pulse(pin) is located in jm_io object
In my opinion you should only set timing (constants) in the topmost object. You could do this if you want to use the standard IO library.
That said, if you want to keep your top file clear of low- and mid-level methods, my inclination would be to write a clean servo input object since it doesn't need all of the flexibility of pulse_in(). It took just a minute. Have a look at what I attached.
Your sub project sounds neat. The guy who did the mechanical
Hi Jon,
It's clear now and it works very well. I learned a lot!. Your terminal application is also very useful, i will use it as default.
Thanks again!
Excellent. Good luck with your project.