Cog Confusion
Dave Matthews
Posts: 93
in Propeller 1
I thought I had figured it out, but no...
I have four stepper motors that I want to run as parallel processes. From a cold start everything works. All four motors run according to their parameters, independently. Each motor runs its number of steps and then stops. I can repeat sending parameters to the motors over and over with no problem as long as I keep the same sequence of controlling the motors. But when I restart a motor out of the sequence I used at the cold start, another motor that was running stops. Here is my attempt to post the code; first the main program controlling the launch of the motor sequences (only the pertinent details posted):
==============================================
OBJ
PLCSerial : "FullDuplexSerialPlus"
M1 : "Stepper1"
M2 : "Stepper2"
M3 : "Stepper3"
M4 : "Stepper4"
main
pub main | pos
case motor
1 : M1.Start (Counts[motor], Speed[motor], Direction[motor])
2 : M2.Start (Counts[motor], Speed[motor], Direction[motor])
3 : M3.Start (Counts[motor], Speed[motor], Direction[motor])
4 : M4.Start (Counts[motor], Speed[motor], Direction[motor])
===============================================
And the four stepper controller cogs look like this (only the pertinent details posted):
===================================================
Pub Start (cnts,spd,dir)
Stop
Cog := (cognew(Step (cnts,spd,dir),@Stack) + 1)
Pub Stop
if Cog
cogstop(Cog~ - 1)
================================================
Any words of advice?
I have four stepper motors that I want to run as parallel processes. From a cold start everything works. All four motors run according to their parameters, independently. Each motor runs its number of steps and then stops. I can repeat sending parameters to the motors over and over with no problem as long as I keep the same sequence of controlling the motors. But when I restart a motor out of the sequence I used at the cold start, another motor that was running stops. Here is my attempt to post the code; first the main program controlling the launch of the motor sequences (only the pertinent details posted):
==============================================
OBJ
PLCSerial : "FullDuplexSerialPlus"
M1 : "Stepper1"
M2 : "Stepper2"
M3 : "Stepper3"
M4 : "Stepper4"
main
pub main | pos
case motor
1 : M1.Start (Counts[motor], Speed[motor], Direction[motor])
2 : M2.Start (Counts[motor], Speed[motor], Direction[motor])
3 : M3.Start (Counts[motor], Speed[motor], Direction[motor])
4 : M4.Start (Counts[motor], Speed[motor], Direction[motor])
===============================================
And the four stepper controller cogs look like this (only the pertinent details posted):
===================================================
Pub Start (cnts,spd,dir)
Stop
Cog := (cognew(Step (cnts,spd,dir),@Stack) + 1)
Pub Stop
if Cog
cogstop(Cog~ - 1)
================================================
Any words of advice?
Comments
In any event, there's not enough information provided to answer your question or advise you.
I will try to post better, For what it is worth, the four separate object are identical except for the I/O they address and the values they return. Cog space is set at 128 in each object, and each object has a 'cog' variable declared.
I appreciate your response! You've given me things to think about...
Dave
OBJ
M[4] : "Stepper"
PUB Main
M[motor].Start (ioPin[motor], Counts[motor], Speed[motor], Direction[motor])
Each object in the array will have its own private instance of any variables declared by the object (like Cog and Stack for example). Hence, if the "object" works by itself, in its entirety creating an array of them will all work as well (within hardware limits of course). The best thing about this is you only have one block of code to debug instead of four in your case.
I use this approach frequently for coding convenience, for example to set up simple bidirectional serial communications between a master Prop and two slaves I have the master declare:
OBJ
FDS[2] : "FullDuplexSerial"
and the slaves declare:
OBJ
FDS : "FullDuplexSerial"
then the master simply calls FDS.Start(...) for each of them with appropriate I/O pin parameters and the slave FDS.Start(...)
You can certainly have four independent steppers on four individual cogs, but it is primarily a matter of cleaning up your coding style to allow you to properly evoke them.
You can even have a fifth cog oversee the four, if you so desire.
I've successfully done something similar in Forth, but you really can and should stick with Spin unless you desire interactive control of the motors.
I appreciate all of the comments, and I understand how a single object can contain the code to launch the four cogs I need just by adding the I/O pins to the exchange. That should have been obvious to me!
I will implement that technique soon, but first I want to understand why what I am doing fails.
Is this a proper method to launch a cog from within an object? Would other subsequent cog launches stop this cog?
I feel I have a typo or something I need to learn from!
For some reason, you're setting 16 I/O pins to outputs. This particular cog only should affect a set of 4 I/O pins. Other cogs might setup other I/O pins. That doesn't explain what you're seeing.
You're not resetting Cog in your Stop method. When you call Start again later, it will first call Stop which uses the "old" cog number saved in Cog to stop that cog which now is running something else. You want to use
-Phil
You probably want to have the following as well
Note that you can't call run_steps if nsteps > 0 because you'll change sdir and sdelay while run_stepper is still using the old values (isn't multi-processor coding fun!). You want to put "repeat while running" at the beginning of run_steps as well as step_forward and step_reverse to prevent this.
I understand... Thanks for the reply and for yet again another code example!
Thanks for your help Mike, I'm trying to understand this:
When Stop is called, IF the variable 'cog' has a value greater than 0, COGSTOP is invoked stopping the cog who's number is one less than the value held in the variable 'cog'. Then the variable 'cog' is Post-Set to -1.
When COGNEW is then invoked the variable 'cog' gets set to the the address of the newly started cog +1 so the value of 'cog' is from 1 to 8.
Am I close?
I have modified the stop code to no avail, and I have determined that there is something in the main object that could be the culprit. I have written test code that just sends hard coded parameters to the cogs, over and over, and that works without a failure. Nope, that only works if the sequence of calling the motors in order is not changed!
Before I go further I will find that bug, then I will re-write the stepper code to use one reusable object.
I appreciate the time you all have taken!
-Phil
FWIW, I started two instances of my object and ran them on a QuickStart using the LEDs to show me what the pins were doing. Other than leaving that line out of the stop() method, everything else seems to be working.
Yes, you can, because the background cog uses global variables for steps, direction, and timing. That said, you may have to wait for the current step delay to time out. I ran this bit of code as a test and it behaved as expected.
It may not be perfect, but for simple applications I think the object is workable.
You require a separate stack for each cog.
-Phil
I made some test code to report which cogs were started when each motor was started, each time invoking cognew via the start/stop technique. If I start the 4 motors over and over again in the same sequence , the cogs seem to behave as I think is correct. When the motor routine is begun, each starts the 'next available cog' in this case the sequence of 3 to 6. The next time the 4 motors routine is run, each cog that was running stops its cog, zeros it, then cognew reassigns the same cog to it, since that is the next available I guess. Here is a list of three iterations of the 4 motors being started and the cogs assigned.
M1 C3
M2 C4
M3 C5
M4 C6
M1 C3
M2 C4
M3 C5
M4 C6
M1 C3
M2 C5
M3 C5
M4 C6
If I change the order of the motors starting the cogs do not behave the same way. The first time the routine is run the cogs are assigned as I would expect based upon which one was started first.
M2 C3
M3 C4
M4 C5
M1 C6
The next motor run routine the motors do not seem to get assigned unique cogs. The first two motor routines get the same cog assigned .
M1 C3 (I would expect M1 to stop C6 from above, and perhaps re-issue it)
M2 C3 STOPS M1
M3 C4
M4 C5
The third time is much worse:
M3 C3
M4 C4
M1 C3 STOPS M3
M2 C3 STOPS M1
So why is this? it appears that my stop method is not working correctly, but it works when the motors are called in the same order.
Here's the code I used to test this situation this is the top level object:
And here is the stepper object:
I hope you had a great holiday weekend, thanks again.
Dave
"FakeMotor.spin" an object:
Driven by "FakeMotorTest.spin" the top level object:
Here's the output:
Note that the cog numbers are all consistent.
I will try this and learn....
Dave