Issue for launching of third cog
JChris
Posts: 58
Hi,
I use a propeller C3, and I would like to launch 3 programs in 3 cogs (1 program for one cog)
When I launch 2 cogs, there is no problem.
Why there is a problem when I launch the third?
If anyone has a hint I would appreciate
I use a propeller C3, and I would like to launch 3 programs in 3 cogs (1 program for one cog)
When I launch 2 cogs, there is no problem.
Why there is a problem when I launch the third?
CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 GPS_PIN_TX = 0 GPS_PIN_RX = 1 GPS_PIN_RST = 2 GSM_Rx = 3 GSM_Tx = 4 GSM_power = 5 GSM_Baud = 9600 Pol_Rx = 6 Pol_Baud = 9600 CLS = 16 CR = 13 QUOTE = 34 OBJ pst : "Parallax Serial Terminal" GSM : "FullDuplexSerial" GPS : "gps_basic" Pol : "FullDuplexSerial" Pub main | lat, lon, ok pst.start(115200) Pol.start(Pol_Rx, -1, 0, Pol_Baud) GSM.start(GSM_Rx, GSM_Tx, 0, GSM_Baud) GPS.startx(GPS_PIN_RX, 2, 9600, 2500) cognew(GPS_Start,@stack) cognew(Pol_Start,@stack2) cognew(GSM_Start, @stack1)
If anyone has a hint I would appreciate
Comments
Are you running out of cogs? PST and each of your two FDSs take up a cog each. If gps_basic also takes up a cog, and there's another cog being used somewhere I don't know about because you didn't post all of your code, then you're be out of cogs.
You should look into Tracy Allen's FullDuplexSerial4port.spin or one of its derivatives. It can do up to four serial ports in a single cog, instead of hogging up a whole cog for each one. That will save you two or three cogs, depending on whether or not you can get gps_basic to also use FullDuplexSerial4port.spin.
Each of the serial drivers runs in its own cog.
In general it's a good idea to start any objects being used by a particular cog from the cog which will be calling the object. I don't think the objects you're using will be a problem but other objects need to be called from the same cog as the one which started the object so it's a good idea to make it a general rule to always start an object from the cog which will access the object.
I don't see an obvious problem with code posted. The problem is likely elsewhere or as Electrodude suggests, there's not enough stack space for a given cog.
It would be a good idea to post your code as an archive which includes all the objects you're using.
I presume that the xxx.start routines each start a cog. I know that FullDuplexSerial at least does this, so there is no need for the cognew.
Is this really what you are intending to do???
I have modified my code for cogs :
In my code, I use one cog for the main (first cog), one cog for pst (second cog), one cog for pol (third cog), two cogs for GPS (fourth and fifth cogs) and one cog for GSM (sixth cog). So I have 6 cogs which are used, Am I right?
About COGINIT, I launch my spin methods in the associated cog.
But I have an issue with the GPS because it has two cogs, so when I run my program, I get NO GPS all the time.
My spin method for GPS_Start is the following :
One more thing, When I try to retrieve the cog ID that GPS.startx return, I get (-1), I think it's because its return two number of cog so it doesn't know which cog it has to return? see the following picture
Electrodude, I don't think the issue it's the stack space but I have maybe wrong.
The Propeller Tool will save all the files used as a .zip file. If you upload this archive we can take a look at the program ourselves.
Why are you using COGINIT? You are starting three objects, getting their cog IDs, and then restarting those cogs with your own cogs. You're effectively stopping those objects by reloading their cogs with your own code. Let the Propeller pick cog IDs for you by using COGNEW instead of COGINIT. There are very few cases - such as purposely restarting cogs, which is not what you want to do - where COGINIT is the right command to use.
Once you get your cogs running, I'd definitely recommend making your stacks smaller. I've never seen any Spin program that needed 1000 longs of stack.
The various objects you use start cogs on their own. Your "COGINIT" calls end up wiping out drivers you previously started.
Many objects return the cog ID plus one.
The GPS object uses two cogs. The start method adds one to each of the cog IDs and then returns the value of these two numbers combined with a logical "and".
I haven't checked, but I believe the object will return either negative one (true) or zero (false). A return value of negative one indicates the object was successfully started.
I'm not certain, but I believe this return value is one of the things which had confused you.
I removed all the "COGINIT" statements. You really don't want to use "coginit" unless you know what you're doing. In general "cognew" is a safer way to start a cog since it will automatically select an unused cog.
In general you only want a single cog calling a serial driver. Your original code had several cogs calling the same serial driver.
I changed your debug serial driver to fullduplexSerial since you were using this object several times already. Using Parallax Serial Terminal.spin does pretty much the same thing as fullduplexSerial.
One failing of fullduplexSerial is it doesn't catch framing errors. If a port isn't connected, fullduplexSerial will receive nul characters. I added several checks for these nul characters to the code. If any of your serial devices send raw data (I don't think they do), you'll want to change the way nul characters are treated.
You used the same global variable "char" as the incoming character in several cogs. The value of "char" would have been very unpredictable. I deleted this global variable and added local variables. Though the way the works now, a global variable would have been okay.
Rather than multiple loops in the main program, the main program now only has a single main loop. This will keep the debug statements from being corrupted.
I've attached the code to this post and I'll also embed the code in the next reply.
Edit: I deleted the code which had been attached to this reply. There were several bugs in the posted code. A more recent version of the program may be found in a later reply.
Edit: The code I originally embedded here had several bugs. I've replaced the code with the latest version. This latest version is also attached to a later reply.
Yes I know about making my stacks smaller but it was for see if the issue came from that.
I already tried to code with COGNEW but it doesn't work, the cog for GSM_start doesn't run.
The code in "GSM_start" doesn't need to be launched in a new cog. It appears to only run once. The main cog should call this method as part of the start up procedure. The example I posted shows one way of doing this.
I just noticed I didn't handle the timeout option correct in the "Rx_GSM" method.
The program requires a terminal interface in order to operate correct. I believe the original code also required a terminal interface.
The program starts by requesting the user to press any key. Once this initial key press has been received the program runs through its initialization process.
Here's the output of this initialization process without either a GPS module or a GSM module connected to the Propeller.
The output will likely be different with GPS and GSM units attached to the Propeller.
As you can see from the above output, the program currently uses 6 of the Propeller's 8 cogs.
The code I previously posted had an error in the "Poso_Rx" method. I treated all input data containing a zero as an error. I just noticed one of the expected input values "LP1" is zero.
I've attached the top object (the only one I changed) to this post.
Once the program has completed the initialization process it calls the method "MainLoop" which runs continuously in cog #0.
Here's the output from the program without any modules connected.
I modified the method "SafeTx" to display international characters.
The method "SafeTx" will display non-ASCII data as hexadecimal values. The output "<$00>" seen above is an example of the output from this method.
As I mentioned in my previous reply, the modified program I posted uses six of the Propeller's eight cogs. I'll attempt to give more detail on how these cogs are used.
When the Propeller is started only one cog is initially used. This first cog (cog #0) is loaded with the Spin interpreter. The Spin interpreter is an assembly program which loaded from ROM. The Spin interpreter may be loaded to more than one cog. Two of the six cogs used by the program I attached above are running Spin interpreters. The other four cogs in use are running code written in assembly (PASM). Technically the Spin interpreter is also running PASM code but since this code is reading and executing Spin code it can also be said to be running Spin code.
So when the program begins, it starts out running Spin code in cog #0. The first line of Spin code is:
Since the object "pst" had been defined as "FullDuplexSerial", the "start" method of "FullDuplexSerial" is called.
Inside the "start" method of "FullDuplexSerial" is the line of code:
The above code tells the Propeller to load the PASM code starting at "entry" into the next available cog. The address of "rx_head" is passed to the newly started cog so it knows where in hub RAM to read and write data.
If you look down towards the bottom of "FullDuplexSerial" you can find the PASM code starting at "entry" which will be loaded into the new cog.
You don't need to worry about the code above, but keep in mind the above code and the code after the above line is loaded into this new cog.
Since only cog #0 had been in use, the "cognew" statement will cause the PASM code to be loaded into cog #1. If a cog isn't available, the cognew statement will return -1. Many people would rather an object return zero if a cog isn't available so many objects add one to the value returned by the cognew statement. "FullDuplexSerial" follows this "add one" convention so the first call of FullDuplexSerial's start method will return 2 rather than 1. As you can see this return value in the output I posted earlier.
At this point in the program two cogs are active. Cogs number zero and one are these active cogs.
The second call of FullDuplexSerial's start method returns 3 indicating cog #2 has been started.
At this point we have one cog (#0) running an instance of the Spin interpreter and two cogs (#1 and #2) running PASM code loaded by FullDuplexSerial's start method.
The next cogs started are started by the GPS object.
The "startx" method of the object "gps_basic" contains the following code.
The first "cognew" statement loads the PASM code beginning at "rxserial" and also passes the address of "rxpin" to this newly started cog. This cog will be #3. Here again we see the "plus one" convention. Since one is added to the return value of the newcog statement, the value of "rxcog" will be 4. If for some reason no cogs were available, the cognew statement would return -1 which would result in "rxcog" being set to zero. In Spin zero is also "false". If "rxcog" had been set to zero the second cognew statement wouldn't be executed. Since "rxcog" is a value other than zero the "if rxcog" condition is met and the second cognew statement is executed.
This second cognew statement uses a name of a method as the first parameter which indicates another instance of the Spin interpreter should be launched in the next available cog. This next cog will be #4 and "parsecog" will be set to 5.
The Spin interpreter requires stack space. The second parameter in this last cognew statement contains the address of the hub RAM which should be used as stack space.
The line "return (rxcog) and (parsecog)" returns the value of "(rxcog) and (parsecog)". If you substitute the values of these variables we get "4 and 5". A number must be zero to evaluate as false and neither 4 nor 5 are zero so the expression "(rxcog) and (parsecog)" evaluates as "true". "true" in Spin is negative one. A 32-bit number with all the bits set to one is -1 using the encoding system used by the Propeller (two's compliment). So the "startx" method of the object "gps_basic" returns -1 when the cogs are successfully launched.
After the call of GPS.startx we are now up to five cogs in use (#0 through #4).
The "GSM.start" call adds one more cog (#5).
In your original code you had multiple "coginit" statements which would have overwritten several of the previously started cogs with Spin interpreters. You do not what to do this.
At this point in the program we finish initializing devices and then start the main program loop. While the main program loop runs in this top object in cog #0 we have four PASM loops running in cogs #1, 2, 3 and 5. The Spin interpreter running in cog #0 runs executes the code in this main loop. Cog #4 is also running a Spin interpreter which is executing code in the "GPS" object.
We have all the cogs we need for now and there's no need to use either cognew or coginit.
Keeping all this stuff, about cogs and objects, straight takes a while but once you understand how these things work programming the Propeller becomes a lot of fun.
I have 3 peripherals that I want to manage in parallel for a real-time application. These peripherals are:
- a GPS module
- a GSM module
- a sensor module
All items are working using UART communication.
This is why I am launching 3 cogs to start communication with the 3 peripherals, using FullDuplexSerial libraries.
The problem is that once I have initiated the Start procedure (an instance of FDSerial) in a COG, say for starting the GPS, I don’t know how to use the same COG to carry on with the rest of my program.
Indeed, the Start procedure in the FullDuplexSerial library launches a COG using the newcog instruction and returns the COG number in my main program.
I’d like to use this same COG number to run the rest of the program in charge of managing the GPS device.
The above occurs 3 times since I am launching in sequence 3 Starts in FDSerial in order to manage 3 peripherals in parallel.
If you want to run spin code to deal with each serial object you would have to launch a spin object to do that, and it would then launch the FDS object. That would take 6 cogs and is probably not necessary unless there is a lot of processing to be done for each input device. The serial objects do buffer the inputs, so in most cases each input can be processed in sequence without loosing any data.
But be aware this is half duplex and receiving and transmitting blocks until the data is shifted in/out. If you really want full duplex and data processing in its own cog then you need 2 cogs per device. Or you use the 4 port serial driver and 3 additional cogs for the data processing.
Andy
You can't use any of the cog which have been loaded with FullDuplexSerial for anything else. The cog is busy listening to the serial port and monitoring for commands to send data. The commands given to FullDuplexSerial must come from a different cog.
The GPS object uses two cogs, one cog is used to run PASM code which monitors incoming serial messages and the other cog parses the received messages. The PASM portion of the GPS object is similar to FullDuplexSerial but only receives data and doesn't send data.
The code I attached to an early post shows how to use multiple instances of FullDuplexSerial. Cog #0 sends data to the various devices using the "str" , "tx", "dec" and "hex" methods of FullDuplexSerial. This same cog also monitors incoming data using "rxTime". I changed all your calls to the method "rx" to "rxTime" so the program wouldn't stop when there wasn't any data available.
The object name is used to identify which instance of FullDuplexSerial should be used. For example when "pst" is used, the "str" and "dec" methods called know to place the data in the tx buffer associated with the FullDuplexSerial instance used for terminal communication.
In the above code the same cog (cog #0) which calls the "str" and "Dec" methods executes these methods. So cog #0 runs the following Spin code:
Since the "Str" method calls the "Tx" method, cog #0 also runs the Spin code found in the "Tx" method.
The "Tx" method places the data in the appropriate buffer which will then be read by the cog running the FullDuplexSerial PASM code. Since the "pst" instance of FullDuplexSerial had been loaded into cog #1, cog #1 will process the outgoing data. Cog #1 has to receive instructions from other cogs in order for it to transmit data. Cog #1 is continuously monitoring a variable in hub RAM to see if there is data to transmit or not. Cog #1 will change the value of a variable in hub RAM so other cogs (cog #0 in this example) will know if it's ready for more data or not. IIRC the "tx_tail" and "tx_head" are the variables used by FullDuplex to communicate with other cogs. You don't have to worry about these details, FullDuplexSerial takes care of this for you.
The following line found in the "GSM_Start" method uses the instance of FullDuplexSerial associated with the "gsm" object name:
Again it's cog #0 which calls the "str" method. The Spin portion of FullDuplexSerial is executed by cog #0. The "str" call places data in the buffer associated with the "gsm" instance of FullDuplexSerial. Cog #5 which had previously been launched running FullDuplexSerial PASM code will take the data from the tx buffer and transmit it on the appropriate I/O pin.
I've listed which cog does what but in general you don't need to worry about knowing the cog ID numbers associated with the various objects.
The program I attached earlier is an example of a commonly used strategy for using multiple serial drivers. A single cog (cog #0 in this case) is used to monitor multiple serial drivers.
Since most of the program outputs data to the terminal, it's a good idea to keep the main loop activities in a single cog. If two cogs attempt to send data to the same serial driver at once, the data will be corrupted and garbled.
You do have two remaining cogs. These could be used to monitor individual peripherals but I don't see a need to take this approach. The GPS object shows a good example of how to use an additional cog to monitor incoming data from a serial line. You could do something similar with your GSM module but if you try to send data to the terminal from more than one cog, you'll need to use a serial driver which uses locks.
I think the code I posted earlier does a lot of what you want. The method "MainLoop" checks two of the peripherals. I didn't see any GSM interaction other than the initialization process but if you wanted to send data with your GSM peripheral, you could add a method to do so and call this method from "MainLoop".
Edit: I hadn't seen kwinn's or Ariba's replies when I wrote this last reply.