PDA

View Full Version : Error with FullDuplexSerial.Spin object



SamMishal
07-10-2009, 03:55 AM
HELP http://forums.parallax.com/images/smilies/freaked.gif

I think I have found a problem with the FullDuplexSerial.Spin library object.

Look at this code:


Con
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
OBJ
Debug[2] : "FullDuplexSerial"

PUB Main | x
Debug[0].Start(31,30,0,115200) ' Start the Debug Terminal Cog
'Debug[1].Start(31,30,0,115200) ' Start the second Debug Terminal Cog
waitcnt(clkfreq*3+cnt)
repeat x from 1 to 3
Debug[0].Dec(x)
waitcnt(clkfreq+cnt)
repeat x from 4 to 8
Debug[1].Dec(x)


If you run this code you will get 123 on the PST......HOWEVER.....if you uncomment the line Debug[1].Start(....)
the program stops working and may even spew out garbage some times.


The program is just to demo the problem. The real problem arises if multiple objects need to have access to
FullDuplexSerial and declare their own instances then the same problem occurs.....the multiple instances seem
to interfere with each other.......I am thinking that the various cogs are driving the TX and RX pins continously
and therefore are stopping the other cogs from doing their own transmissions.......I am not very up to par with
PASM and maybe someone with more knowledge than mine can help...e.g. Chip Gracey, Jeff Martin..... PLEASE.

But even if we manage to solve the problem......the sample programs below illustrate another problem.

The program does not work as given due to the problem mentioned above. BUT....Also please read on for ANOTHER problem....



{
Top_Object.Spin
}
Con
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000

OBJ
Debug : "FullDuplexSerial"
ObjB : "B_Object"


PUB Main|NumSongs,i,x
Debug.Start(31,30,0,115200) ' Start the Debug Terminal Cog
' if you comment this out then the
' ObjB.Print works
ObjB.Init ' Start Object B
waitcnt(clkfreq*2+cnt)
Debug.Tx(Debug#Cls) ' Clear SerialTerminal screen
Debug.Dec(12)
Debug.Tx(13)
ObjB.Print(34)



{
B_Object.Spin
}
OBJ
Debug : "FullDuplexSerial"

PUB Init
Debug.Start(31,30,0,115200) ' Start the Debug Terminal Cog
' if you comment this line out then the
' Top object Debug works
Pub Print(val)
Debug.Dec(val)
Debug.Tx(13)


The other problem is in the scenario of the top_object program, there would be two cogs (even if they did not interfer with each other) running
the serial I/O. This is wasteful.

So how would one provide Serial I/O to multiple objects without having multiple cogs (which actually does not work any way
as shown above)????????????

I hope someone whith DEEP knowledge of all this stuff can help


Samuel


Post Edited (SamMishal) : 7/9/2009 8:12:39 PM GMT

rokicki
07-10-2009, 04:03 AM
This is straightforward. You need a singleton version of FullDuplexSerial, and if you want to drive them
from distinct cogs, you also need to add locking.

This is a well-known and well-understood problem.

Mike Green
07-10-2009, 04:07 AM
You can't use FullDuplexSerial this way. Each Serial.start call starts up a cog to do the actual I/O and, since you've asked them to both use the same I/O pins, they interfere with each other. You have to very specifically design an object to be called from several objects. Look at the "SerialMirror" object in the Object Exchange for a serial driver designed to be called from multiple objects.

You will have to add your own locks to "Serial Mirror" if you plan to call it from several different cogs. It's not hard to do.

There's not very much call for the sort of serial driver you're asking for. Most of the time FullDuplexSerial or Simple_Serial will do the trick. That's why no one has bothered to contribute one (other than Mirror and that one doesn't have locking).

Post Edited (Mike Green) : 7/9/2009 8:19:36 PM GMT

SamMishal
07-10-2009, 04:08 AM
rokicki said...
This is a well-known and well-understood problem.
OK.....so has anyone solved it???? is there a singleton version of the FullDuplexSerial.Spin......that also implements
locking and that also gets around the waste of having multiple cogs running it but still enabling multiple objects to have
access to serial I/O...??


I would like to see Chip Gracey's and·Jeff Martin's input on this.....PLEASE....


Sam
·

SamMishal
07-10-2009, 04:13 AM
Mike Green said...
You can't use FullDuplexSerial this way. Each Serial.start call starts up a cog to do the actual I/O and, since you've asked them to both use the same I/O pins, they interfere with each other. You have to very specifically design an object to be called from several objects. Look at the "SerialMirror" object in the Object Exchange for a serial driver designed to be called from multiple objects.
Thanks Mike......I suspected thusly......

do you have a link for the SerialMirro object....I am trying to find it.....

Regards

Samuel
·

Andy Lindsay (Parallax)
07-10-2009, 04:28 AM
SamMishal said: that also implements locking and that also gets around the waste of having multiple cogs running it but still enabling multiple objects to have access to serial I/O...??

There is an object on obex.parallax.com that takes a step toward this functionality by using a DAT block for variables instead of a VAR block. It's called SerialMirror:

http://obex.parallax.com/objects/189/

Also on obex, you can find MultiCogSerialDebug, which extends the DAT block approach and applies locks.

http://obex.parallax.com/objects/232/

This example puts the onus on the parent object for using locks. An alternate approach might be to build the locks into the object's methods. I started experimenting with methods that shunt strings into holding pens so that the parent object doesn't have to worry about getting locked out of its own task list, but I haven't worked the kinks out yet.

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Andy Lindsay

Education Department
Parallax, Inc.

MagIO2
07-10-2009, 04:29 AM
As you already mentioned it simply makes no sense to have two different FullDuplex objects running for a single pair of serial I/O lines! So, from my point of view this is an error in the usage and not in the object ;o)

It should be possible to modify this object so that it matches with your needs. I don't know the object in detail, but I have the following ideas:
1. If the TX/RX buffer is in the var section, then shift it to the DAT section
2. Have a variable in the DAT section, which shows whether the PASM has already been started
3. Modify the start function to use the right buffers and only start a new COG if not yet started
4. RX and TX functions should be synchronized with locks if you plan to use the object in parallel from different COGs

Then you can use the object in every other object and it does not waste HUB-RAM, because the same object is never copied in HUB-RAM. Only the VAR section is duplicated per instance of the object.

Hope that helps.


PS: Ups ... needed far to long for my reply ;o)

SamMishal
07-10-2009, 04:31 AM
THANKS Guys.....http://forums.parallax.com/images/smilies/yeah.gif

I found the SerialMirror.Spin and IT WORKS.....GREAT.....it is exactly what I needed....





THANKS again



Samuel




Post Edited (SamMishal) : 7/9/2009 8:36:28 PM GMT

SamMishal
07-10-2009, 04:45 AM
Hi Guys,



I looked more closely at the SerialMirror object and it works great as far as allowing multiple

instances from multiple objects to all work together on the same Tx and Rx pins.....BUT.....I think it still

uses multiple cogs if instantiated multiple times.....am I wrong.....if not then is there one that

does not utilize multiple cogs???



Sam

SamMishal
07-10-2009, 05:13 AM
MagIO2 said...

It should be possible to modify this object so that it matches with your needs. I don't know the object in detail, but I have the following ideas:
1. If the TX/RX buffer is in the var section, then shift it to the DAT section
2. Have a variable in the DAT section, which shows whether the PASM has already been started
3. Modify the start function to use the right buffers and only start a new COG if not yet started
4. RX and TX functions should be synchronized with locks if you plan to use the object in parallel from different COGs


I agree....I was thinking the same thing.....the only point I did not think about was number 1......

WHY is this step necessary....it is also what MirrorSpin does but I do not understand why it is
a necessary thing to do???


Samuel
·

MagIO2
07-10-2009, 05:29 AM
If you only run one COG it will only use one set of buffers unless you modify the PASM adding a command that switches the buffer to write to/read from. Having the buffer in the VAR section means that each instance of the object has it's own buffers, but the COG will only use the buffers of the first instance.

So, the read/write functions would simply use the wrong buffers except the ones of the first object.

Kye
07-10-2009, 05:33 AM
Um,

Each object gets its won seperate variables. So, the two versions of the fullDuplex serial have completely different buffers.

Its a problem with spin in general, you have to use the serial mirror hack to get arround it with sperate DAT sections.

Maybe support for parent and child objects sharing the sample driver will be added one day. Maybe not.

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Nyamekye,

Andy Lindsay (Parallax)
07-10-2009, 05:39 AM
Only make one Start method call regardless of how many copies of the object (SerialMirror) you use.

Multiple objects can declare SerialMirror, but their method calls to SerialMirror's other methods (DEC,·STR, etc)·will result in data to/from the same tx and rx buffers because there is only one copy of the code and DAT·block for multiple copies of the object.·

After the one start method call, calls to DEC, STR, etc will work from any object/cog so long as they are not executed close enough together that a data collision occurs. This is why I started experimenting with the lock and multiple buffer scheme.

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Andy Lindsay

Education Department
Parallax, Inc.

Post Edited (Andy Lindsay (Parallax)) : 7/9/2009 9:48:56 PM GMT

SamMishal
07-10-2009, 06:45 AM
Andy Lindsay (Parallax) said...
Only make one Start method call regardless of how many copies of the object (SerialMirror) you use.

Multiple objects can declare SerialMirror, but their method calls to SerialMirror's other methods (DEC,·STR, etc)·will result in data to/from the same tx and rx buffers because there is only one copy of the code and DAT·block for multiple copies of the object.·

After the one start method call, calls to DEC, STR, etc will work from any object/cog so long as they are not executed close enough together that a data collision occurs. This is why I started experimenting with the lock and multiple buffer scheme.

So then if I declare multiple instances in various objects then only the top level needs to call the start method??
This will definitely use only one cog.....and since the DAT section is in hub RAM then the same buffer will be used....
·
Ahaaa.....now I get it.....I will try this soon......
·
The lock issue would be needed if multiple objects are likely to send/receive simultaneously.....but in my application
I am not doing that .....so I will look into the locking stuff later.....Unless Andy Lindsay comes out with a new version
that does all that for us.....Please.....
·
But I have learnt a lot from you guys.........THANKS
·
·
Samuel

SamMishal
07-10-2009, 07:06 AM
Samuel said...
Ahaaa.....now I get it.....I will try this soon......
·


Hurray......all problems are all solved......no multiple cogs and all WORKS PERFECTLY......
only locking is required when I may need it ......but not now......SO THANK YOU GUYS....

Solution as well as understanding and learning new stuff......what a GREAT LOT you guys are
and of course Parallax and the Propeller (and SPIN)·are just the best.....


Samuel

·

Andy Lindsay (Parallax)
07-10-2009, 07:09 AM
Yes, that's the way it should work.· One call to the Start method, and then the calls to any methods in any of the other Serial Mirror object instances should work.· (There is always a chance that data collisions will interleave strings if two cogs make almost simultaneous STR, DEC, etc, method calls without using locks.)·
·
Side notes:
·
The SerialMirror object's start method is actually designed to stop any previously started instances before launching a cog, so you "could" call the start method more than once, and there would still only be one instance running.· This only prevents the same instance of a given object from launching two cogs; you could still launch more than one cog by calling a start in a different instance of the SerialMirror object.· That is why we only want to make one call to a SerialMirror object's Start method.· Then, all the other instances of the object will only communicate through one cog that's running the serial ASM code.

I have a Parallax Serial Terminal debugging application that works with SerialMirror posted in the Propeller Education Kit Labs, Tools & Apps thread, and the next step is to add locks to prevent data collisions.

The existing FullDuplexSerial object can be used with multiple programming tools connected to various pairs of I/O pins so that you could have several simultaneous conversations with one or more PCs or other serial devices.

The Propeller Library also has SimpleSerial, which might be another option for multi-cog debugging. This object uses whatever cog calls its methods to transmit the bytes at up to 19.2 kbps.
·

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Andy Lindsay

Education Department
Parallax, Inc.

Post Edited (Andy Lindsay (Parallax)) : 7/9/2009 11:25:52 PM GMT

electricsmith
07-15-2009, 03:57 AM
Andy Lindsay (Parallax) said...

The existing FullDuplexSerial object can be used with multiple programming tools connected to various pairs of I/O pins so that you could have several simultaneous conversations with one or more PCs or other serial devices.
·


Can anyone point me to some examples of several RS232 devices connected to different tx/rx pins on the propeller and how these are coded?· I have several devices I want to communicate with simulataneously over different serial ports.·

SamMishal
07-15-2009, 04:22 AM
An example would be to use the EB500 and a serial·LCD ......both these need Asynchronous serial



So you would have two objects that use the object FullDuplexSerial and initialize it to the pins.

So if EB500 is on pins P20 and P21 and the LCD is on P10 and P11 (for Tx and Rx) then you would have
two objects say one is called EB500.Spin and the other called LCD.Spin each of these objects would have its
own OBJ section like this

·· Obj
······· Com : "FullDuplexSerial"



And also it would have methods that will enable the Toplevel object to initialize them say

·· Pub Init(TxPin, RxPin,BaudRate)
······· Com.Start(RxPin, TxPin,0,BaudRate)
······· 'do other initialization stuff

· Pub DoSomething(someParam)
····· Com.Tx(somecalue)
····· 'etc.



So then the top level object would instantiate the LCD.Spin and EB500.Spin objects in its Obj section
and then would call the Init method and then can use the other methods of the object

Con
···· _CLKMODE = XTAL1+PLL16X
···· _XINFREQ· = 5_000_000

Obj
··· LCD··· : "LCD"
··· Eb500: "Eb500"

Pub Main
···· LCD.Init(10,11,9600)
···· EB500.Init(20,21,9600)
···· 'do some more stuff
···· LCD.DoSomething(somevalue)
···· EB500.DoSomething(someothervalue)



etc. etc.



·······

Post Edited (SamMishal) : 7/14/2009 8:38:07 PM GMT

jazzed
07-15-2009, 04:28 AM
Andy, Why don't you guys just release a SingletonFullDuplexSerial.spin that works the way most of us use it?
As Tom said, this usage is widely known and common. I've never had a good reason want to use SerialMirror (ya, I've seen it).
A similar approach can be used with TV, etc... but it's a little harder to describe the "fix."

For those who don't know, all you have to do is make the change below in FullDuplexSerial.spin to be able to use it from any object (but not with multiple instances on different pins as Andy pointed out). Of course every time you update PropTool, you have to readjust :< vomit.




VAR

long cog 'cog flag/id
{
long rx_head '9 contiguous longs
long rx_tail
long tx_head
long tx_tail
long rx_pin
long tx_pin
long rxtx_mode
long bit_ticks
long buffer_ptr

byte rx_buffer[16] 'transmit and receive buffers
byte tx_buffer[16]
}

' add this DAT

DAT

rx_head long 0 '9 contiguous longs
rx_tail long 0
tx_head long 0
tx_tail long 0
rx_pin long 0
tx_pin long 0
rxtx_mode long 0
bit_ticks long 0
buffer_ptr long 0

rx_buffer byte 0 [16] 'transmit and receive buffers
tx_buffer byte 0 [16]



▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
--Steve


Propalyzer: Propeller PC Logic Analyzer (http://www.brouhaha.com/~sdenson/Propalyzer)
http://forums.parallax.com/showthread.php?p=788230 (http://forums.parallax.com/showthread.php?p=788230)

Timmoore
07-15-2009, 08:21 AM
you could try the 4 port serial port object. It is a singleton that supprots up to 4 ports. So you get the singleton behavour and multiple ports. You can include the object in multiple files in your project and they all share the same ports. I have a convention that port 0 is the debug port so any object can report debug info while the other ports are free for other devices to use. It doesn't attempt to solve the secondary probellm of the outputs from different cogs getting mixed up on the debug port, i.e. if 2 cogs ask to output a string to the debug port the strings will be mixed up when sent on the port.

SamMishal
07-15-2009, 08:37 AM
Timmoore said...
·the 4 port serial port object.
What is it called? do you have a link? Also can you not assign the other ports if you do not need them?

Sam
·

Timmoore
07-15-2009, 08:55 AM
pcfullportserial4fc, its by me, so look for the objects I posted in obex. You can specify between 1 and 4 ports, if you dont assign the pins the port doesn't do anything.

SamMishal
07-15-2009, 10:01 AM
Timmoore said...
pcfullportserial4fc, its by me, so look for the objects I posted in obex. You can specify between 1 and 4 ports, if you dont assign the pins the port doesn't do anything.
Hi Tim,

I found pcfullDuplexserial4fc.Spin ....... is it the same?

Regards

Sam
·

Timmoore
07-15-2009, 11:42 AM
yes, sorry got the name wrong.

SamMishal
07-15-2009, 05:01 PM
Hi Tim,



I used it and it is VERY Good.............it is definitely another tool I can

and WILL use.....thanks for making it...............



Regards



Samuel