Simultaneous tasks on a SX28
Matthias09
Posts: 47
Hey guys,
I have a SX 28 that controls a stepper motor and ultrasound sensors. The sensors are measuring the position of a ball on a beam and the stepper motor turns the beam accordingly (Ball and Beam system).
So far I have one big loop in which:
1) Sensors read the ball position
2) angle to turn the beam is calculated
3) and the stepper motor is instructed to turn (cw or ccw) with one step
4) then next loop
The problem I have here is that the calculation and sensor read outs take some time which result in a big time one loop needs to run and hence in a non smooth stepper motor movement. I only run one step per loop as the direction of turning the beam can change with every loop.
So my idea is: instead of letting the motor wait in every loop for the instruction for the next step, I could just run it "in background" in one direction and only give him signals when it's time to change directions. This signal would come from the main loop that runs simultaneously and still keeps on calculating the position of the ball and the direction to turn.
Now I know that the SX only has one processor inside, so would it work, when I make this whole thing with an periodical interrupt (rather than a loop). It would look like that in pseudo code:
INTERRUPT
Every 20ms
1) Sensors read the ball position
2) angle to turn the beam is calculated
if directions <> current direction
give signal to stepper to change direction
endif
MAIN:
DO
DRIVE_STEPPER direction
LOOP
Question I have here:
- during the execution of the periodical interrupt, is the loop in MAIN still working, or is it stopped temporarily until the interrupt finished working? (which meant I would still have the same time problem)
- I already have a non periodical interrupt, that fires whenever the encoder (that measures the angle of the beam) detects a change on one of it's two channels (Quad). Can I have both kinds of interrupts at the same time. Do the interfere somehow? Should I consider anything?
- Any other idea how to delink the stepper motor driver from the calculation (in case the periodical interrupt idea doesn't work)?
Independent of that I have another question. In Debug Mode the motor runs as slow as in normal mode. I use 50MhZ in normal mode, and Debug should only work in 4Mhz (as far as I know). Shouldn't there be a difference in performance? Means the motor should run significantly smoother when not in debug mode? Of course I use the oscillator for the 50mhz.
This are my first lines of code, where I think I define the speed of the processor.
Thanks guys! Thanks! You are great and without your support (on other earlier questions) I wouldn't be where I'm today!
Matthias
Post Edited (Matthias09) : 10/18/2009 2:32:13 AM GMT
I have a SX 28 that controls a stepper motor and ultrasound sensors. The sensors are measuring the position of a ball on a beam and the stepper motor turns the beam accordingly (Ball and Beam system).
So far I have one big loop in which:
1) Sensors read the ball position
2) angle to turn the beam is calculated
3) and the stepper motor is instructed to turn (cw or ccw) with one step
4) then next loop
The problem I have here is that the calculation and sensor read outs take some time which result in a big time one loop needs to run and hence in a non smooth stepper motor movement. I only run one step per loop as the direction of turning the beam can change with every loop.
So my idea is: instead of letting the motor wait in every loop for the instruction for the next step, I could just run it "in background" in one direction and only give him signals when it's time to change directions. This signal would come from the main loop that runs simultaneously and still keeps on calculating the position of the ball and the direction to turn.
Now I know that the SX only has one processor inside, so would it work, when I make this whole thing with an periodical interrupt (rather than a loop). It would look like that in pseudo code:
INTERRUPT
Every 20ms
1) Sensors read the ball position
2) angle to turn the beam is calculated
if directions <> current direction
give signal to stepper to change direction
endif
MAIN:
DO
DRIVE_STEPPER direction
LOOP
Question I have here:
- during the execution of the periodical interrupt, is the loop in MAIN still working, or is it stopped temporarily until the interrupt finished working? (which meant I would still have the same time problem)
- I already have a non periodical interrupt, that fires whenever the encoder (that measures the angle of the beam) detects a change on one of it's two channels (Quad). Can I have both kinds of interrupts at the same time. Do the interfere somehow? Should I consider anything?
- Any other idea how to delink the stepper motor driver from the calculation (in case the periodical interrupt idea doesn't work)?
Independent of that I have another question. In Debug Mode the motor runs as slow as in normal mode. I use 50MhZ in normal mode, and Debug should only work in 4Mhz (as far as I know). Shouldn't there be a difference in performance? Means the motor should run significantly smoother when not in debug mode? Of course I use the oscillator for the 50mhz.
This are my first lines of code, where I think I define the speed of the processor.
DEVICE SX28, OSCHS3, TURBO, STACKX, OPTIONX FREQ 50_000_000
Thanks guys! Thanks! You are great and without your support (on other earlier questions) I wouldn't be where I'm today!
Matthias
Post Edited (Matthias09) : 10/18/2009 2:32:13 AM GMT
Comments
The idea here is your main loop keeps moving through and checking the timers or setting parameters for the interrupt. Even if your main loop "lags", it doesn't matter, the motor will continue to pulse, and the last sensor reading is available, etc.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
1uffakind.com/robots/povBitMapBuilder.php
1uffakind.com/robots/resistorLadder.php
Post Edited (Zoot) : 10/18/2009 2:55:43 AM GMT
The SX can very easily handle multiple threads, one per sensor/action. If you look up the first iteration of SX design contests, I had submitted an entry to handle mutiple threads. Each is independent of the others, hence each times itself. Lookup the RTOS entry, and it will do what you want. It is in assembler, and can not be interfaced to SX/B in those cases where SX/B uses any blocking delays like DELAYUS or serial comms. These you would need to modify into assembler.
Cheers,
Peter (pjv)
The link for your RTOS seems to be broken in the Contest pages. Checked some of the others and they work.
I think you mean this thread:
http://www.parallax.com/tabid/677/Default.aspx
However, as Propability mentioned, I cannot access any code. Unfortunately I'm not able to read/write assembler, hence I'll probably not be able to understand your awarded program.
@ Zoot
Wow! thanks a lot! I'll work on that approach!
Matthias
Post Edited (Matthias09) : 10/19/2009 3:01:49 AM GMT
I tried your little program [noparse]:)[/noparse] and it works great!! Stepper runs faster and much smoother, everything works...except: the Ping.
I have an interrupt running with 100,000 frequency. Is this freq dependent from the freq the SX is running with? Means, does it change in debug mode (4mhz) and running mode (50mhz). Does the debug mode in any way prevent the Ping to work properly?
I always receive 0 as a result of the distance measured by the ping. I also figured out that the interrupt doesn't event count the distance up, after the pulse was sent. (it does not jump in the proper interrupt section, as the change of the variable sensorstate happens too quickly, I figured this out by putting a Pause (counting up tix) after the measurement has ended to delay the change of sensorstate to the next step, start pulse).
I attached my program. I hope you can give me a hint.
Best and have a good weekend!
Matthias
Post Edited (Matthias09) : 11/14/2009 1:02:03 AM GMT
Second, and this may be the cause of the problem -- you don't just want to wait for the ping response, you want to actually check that the ping line goes high THEN goes low, not just check for low. I am sure the ping will not respond in 10us after the trigger line goes low. The ping is programmed to *always* return some kind of high pulse, so you need to check for the edge before closing out the reading.
I know -- my bad -- I didn't think about this in my original example. Probably something like this would suffice:
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
1uffakind.com/robots/povBitMapBuilder.php
1uffakind.com/robots/resistorLadder.php
Post Edited (Zoot) : 11/14/2009 2:16:02 AM GMT
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
1uffakind.com/robots/povBitMapBuilder.php
1uffakind.com/robots/resistorLadder.php
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
1uffakind.com/robots/povBitMapBuilder.php
1uffakind.com/robots/resistorLadder.php
thank you very much! I appreciate your helpful support and the best thing: The Ping runs now!! [noparse]:)[/noparse]
So thanks a lot! I would have never found the error.
However, I got a problem: the values the Ping returns are jumping up and down vastly (e.g. 10 cm distance in rawdata: 19, 59, 9, 36,...). Something must affect the counting of cycles in the interrupt (INC rawdata).
So my question: in DEBUG mode, can I run also with 50mhz, when i defined it with the FREQ command? I know I then use the SX Key clock, and I thought that one can only run with max. 4Mhz (which would be too slow).
I fear, that my interrupt is so long, that it needs sometimes all the clock cycles available between two interrupts just for itself and leaving no time to run the main loop.
I calculated this way: SX runs at 50Mhz (hopefully also in DEBUG mode). One clock cycle then needs 1/50MHz = 20ns = 0.20us to run. My interrupt runs 100,000 per second or every 10us. Between two interrupts I hence can make 10us/20ns = 10000/20 = 500 clock cycles.
Means after every 500 clock cycles, the interrupt occurs, runs, takes away some cycles for itself and leaves some cycles left for the main loop.
Is this correct? Could it be that the interrupt *sometimes* uses all 500 cycles for itself so the main loop cannot go on and the end of the high signal of the PING is not detected immediately (i know that already GOTO needs 3 cycles to process)?
Warmest Wishes,
Matthias
Debug should work OK at 50mhz -- make sure to pull your resonator or crystal.
If you do a simple PULSOUT then PULSIN app as a test for the Ping (like the official Ping demos) do you get the same data bouncing around?
Also, given that most recent version of your app does not process the returned Ping pulse length (by dividing by 2 to remove the return trip and doing a fractional multiply to convert to centimeters) if you are looking at the raw word data returned, a jump of +/- 10 is not highly significant. The Ping can return a maximum pulse of ~18ms = 18000us = 1800 ticks --- so a bounce there of 10 would equal ~0.55 % error or ~1.65 cm when converted and after removing round trip. Not too bad really. What are you using as test surface? Try a nice stiff flat piece of something about 30-40cm away and perpendicular to the Ping and see if you get stable readings.
Of course, if the jumps you are seeing are after correct conversion to cm., that's odd.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
1uffakind.com/robots/povBitMapBuilder.php
1uffakind.com/robots/resistorLadder.php
find the program attached. I've commended some thing out, that are about to return once I finished testing the program.
Thanks for the info about the cycles!
I have made tests like you said, fixed the object in one position and took measurements. it's bascially always like. two types of results: one is right distance, the other significanlty lower, they change randomly with the right results coming more often:
Position one: 83, 82, 79, 33, 82, 34, 83, 82 (right: 80, wrong 30)
Position two: 50, 50, 20, 47, 50, 20, 50, 49, 20 (right: 50, wrong 20)
Matthias
RawToCm CON 2257 ' 1cm / 29.034us. see docu
RawToMm CON 22572 ' 1mm / 2.9034us. see docu
Remember your ticks are 10us -- not 2us or .8us like on some of the Stamps.
So.......hitting the docs:
So you want to divide in the form 1/29.034 where 1 = 1us. But you are counting 10 us increments. So you need to multiply the fractional constant by 10.
That means:
distance = raw ** ((1/2.9034)*$FFFF)
distance = raw ** ( 22571.812357925192 )
distance = raw ** 22572
But you need to divide by two, ideally before scaling, to minimize error:
distance = (raw+1)/2 ' cleaner rounding
distance = distance ** 8887
Picking a random sample of say 9000us (approx halfway through the Ping's useful distance), that should give something like 150 cm:
9000us = 900 ticks
( 900 ticks + 1 ) / 2 = 450 return trip
450 ** 22572 = 154.99198901350423
or 154 cm given that we are not accommodating rounding when using ** above.
Sounds about right. ** is better if the fractional part is less than 1, i.e. multiplying by .7564 or something. */ is required if the fraction part is greater than 1, i.e. multiplying by 1.345. ** is more precise.
The rest looks OK. The anamalous data on the longer measurements is sure odd. Perhaps grasping at straws -- is the ping close to the floor/surface? What did you use for a test object? Are you just watching these values in the debugger? Without a "break" in a known location how do you know you are seeing the most recent full measurement, rather than a value in progress?
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
1uffakind.com/robots/povBitMapBuilder.php
1uffakind.com/robots/resistorLadder.php
I increased the times between to reading to 0.5sec and there were no false (lower readings anymore).
So this smaller values must have been signals from the previous pulse that were bouncing back from long distant other objects and arriving shorlty after I've sent the 2nd pulse and put the pin to input mode and start counting until I recieve something.
However I just tried an older code with classical pulseout, pulsein and with 10ms pause between two reads and that worked just fine. When I use the same 10ms pause for the new algorithm, it gives me most of the time the lower of the two values (in Position two from post above then most of the time 20s).
So is there anything PULSEIN does, what I would have to do manually in the new code as well?
Matthias
I don't convert so far anything. Just use raw data.
I'm gonna read right now your answer! I appreciate this very much!
Matthias
In any case, 500ms between triggers seems awfully long.
Can you post the "classical" program? I am really wondering about the location of a BREAK statement so that the WATCHEed variables are updated at the right time.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
1uffakind.com/robots/povBitMapBuilder.php
1uffakind.com/robots/resistorLadder.php
here is the classical program. It shows the same results like the new one, but without the jumps to smaller values. I use here a 50us pulse rather than a 10us in the new program.
Best,
Matthias
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
1uffakind.com/robots/povBitMapBuilder.php
1uffakind.com/robots/resistorLadder.php
After process is finished, I store the counting variable in a second one (more stable one, like you suggested). I only watch this stable variable, so can be sure I don't see any values in progress.
I think the ping might grasp some other things too, but as I said: with the classical program in same environment, it works just fine.
As you talked about it earlier: when I have a long code that at several positions changes the variable, how can I define I position where I want to see it?
Normally I think with break, means where break stands, there I see all the variables at this time.However in this code with the cyclical interrupt, break doesn't work, as it prevents somehow the ping and encoder to work properly. What would I do here?
I just made another check with the new code and 0.5s distance between two readings. I get still the smaller values. Less often, but still there. Less often probably because I just measure less often.
Your time and efforts are most appreciated.
Matthias
Post Edited (Matthias09) : 11/15/2009 4:24:22 AM GMT
You just need to put the break in state 5 (in the last iteration) after distance is updated.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
1uffakind.com/robots/povBitMapBuilder.php
1uffakind.com/robots/resistorLadder.php