View Full Version : Changes to FullDuplexSerial

09-19-2007, 03:15 AM
Hi all,

I've tried adding a small feature to FullDuplexSerial, but it doesn't appear to work all too well, and I can't figure out why. The original FullDuplexSerial features, among others, the methods 'tx', 'rx', and 'rxcheck'. I wanted to add a 'txcheck' (to mirror 'rxcheck', which always returns without delay, and returns the read byte if there was one, or -1 otherwise).

The 'txcheck' method is supposed to write a byte to the output buffer, but only if the buffer is not full. In other words, it always returns without delay, and returns true if the byte was written, or false if the buffer was full. Here is the relevant portion of the original code (only 'tx'):

PUB tx(txbyte)

'' Send byte (may wait for room in buffer)

repeat until (tx_tail <> (tx_head + 1) & $F)
tx_buffer[tx_head] := txbyte
tx_head := (tx_head + 1) & $F

if rxtx_mode & %1000

And this is what I replaced it with in my version:

PUB txcheck(txbyte)

if (tx_tail <> (tx_head + 1) & $F)
tx_buffer[tx_head] := txbyte
tx_head := (tx_head + 1) & $F
if rxtx_mode & %1000
return true
return false

PUB tx(txbyte)

'' Send byte (may wait for room in buffer)

repeat until txcheck(txbyte)

Aside from the lack of documentation in 'txcheck', it seemed pretty harmless to me. The exact same condition is evaluated, and the exact same code is executed to write the byte if the condition evaluates to true. The new 'tx' should work exactly the same as the original 'tx'.

I tested both versions of 'tx', and while the original version (i.e. from the propeller tool's library) obviously works as expected, my version doesn't work reliably. Note that the only difference between these two tests is as indicated in the code above. Everything else remains the same. It seems as though the new 'tx' sometimes works, and other times it skips bytes. This becomes apparent when outputting long streams (which in my test are displayed visually as a moving image on the pc, and the image appears "torn" in some places when using my version of 'tx').

Is there something in FullDuplexSerial that causes it to stop working correctly if the code size changes, or anything like that? Because, for the life of me I can't see what the problem could be, unless it is somehow caused by other parts of the FullDuplexSerial object that are not obviously related. Any insights would be appreciated.

And for now, I won't even attempt another change I've been considering, namely to increase the size of the buffers (am not yet proficient enough in propeller assembly to even touch that part of FullDuplexSerial)...

P.S. It appears I'm stuck on another (unrelated) issue, to which I haven't had a reply in a while. Please take a look at http://forums.parallax.com/showthread.php?p=674720

09-19-2007, 03:35 AM
No, "tx" is not harmless http://forums.parallax.com/images/smilies/smile.gif
It calls "txcheck" and under certain condition "tcheck" can call "tx" again.

This is generally not forbidden as such, but I get the feeling that this is not what you have in mind.
Please check this situation!

Mike Green
09-19-2007, 03:42 AM
I don't see anything wrong with what you've done. Can you try experimenting with different Bauds? If you slow things down, the transmit buffer will fill up faster and more easily and the PC will be less likely to drop characters if that's what's happening. You might also try outputting sequential bytes and have the PC program detect missing values and display what the first missed byte value was and the average, minimum, and maximum number of correct characters before a missing one. That might give you some clues.

Increasing the size of the buffers is easy. There's one constant in the assembly initialization routine that needs to be changed (#16) to whatever power of two you want (like 32 or 64). You also need to change the $F to $1F or $3F for 32 or 64 bytes (and, of course, change the buffer sizes). These bitmasks are in several of the Spin routines and occur twice (I believe) in the assembly routines.

You could substitute named constants for the bit masks and buffer sizes if you want to experiment with several buffer sizes. You could even make the transmit and receive buffers different sizes as long as they're powers of two in size.

09-19-2007, 07:31 AM
I don't see 'tx' being called anywhere from 'txcheck' (unless I'm totally not seeing something, which I must admit, after all my failures, I'm starting to wonder about). Perhaps you misread the call to 'rx', immediately below the second 'if'? In my case, it never passes the test of that 'if', which is correct. Even so, 'rx' does not call 'tx' either. As far as I can tell, 'tx' is not being re-entered under any condition. And if it would, hypothetically, it would be under the same conditions as in the original version. There is no essential difference in the code.

I was testing this thing with a CMUcam (v1) which is connected to the propeller, which basically just acts as a "serial repeater" between the CMUcam and the PC. The PC runs CMUcamGUI, a testing tool that comes with the cam. This tool only works at 115,200 baud and so does my CMUcam ***(see notes at the bottom of this post). I could try another way to test my changes to FullDuplexSerial, of course. Would have to get back to you on that.

From visual inspection of the output, I would guess that the problem occurs only when the buffer gets full. Although I don't know if/how the buffer fills up, since both the PC and the CMUcam transmit/receive at the same rate. Short transmissions, or large ones over a longer period of time (i.e. that don't require full bandwidth) appear to work fine. Transmissions that require more bandwidth (in my case, color tracking with line mode enabled, if you know the CMUcam) cause the "image" (of tracked pixels) to "tear up" horizontally. The portions below each tear is misaligned, to the left, suggesting that bytes are missing. Oddly, it does not confuse the CMUcamGUI tool (which gets confused quite easily), and the effect appears pretty consistent. But since it only happens with my version of 'tx', I have to assume the problem lies there.

Here is the spin code I use as "serial repeater":


_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000

CMUcamTX = 14
CMUcamRX = 15
CMUcamBaud = 115200

CAM = 0
PC = 1


ser[ 2 ] : "FullDuplexSerialNew" ' The forum did not show this correctly without the extra spaces around '2'


long mStack[100]

PUB Main | lByte

if cogid == 0
ser[CAM].Start(CMUcamRX, CMUcamTX, 0, CMUcamBaud)
ser[PC].Start(31, 30, 0, CMUcamBaud)
cognew(Main, @mStack)

' Tried to keep it as simple and obvious as possible,
' hoping to avoid more unexpected side effects...
lByte := ser[CAM].rxcheck
if lByte <> -1

lByte := ser[PC].rxcheck
if lByte <> -1

One cog blurts out everything it gets from the cam, to the pc. The other cog does the same but in the opposite direction. The "FullDuplexSerialNew" is my version of the object, the only difference with the original being what I showed in my previous post. If I change the above code to use "FullDuplexSerial" instead (i.e. the original one), it works.

Although... I've tried it again just now, and now, regardless of which version of FullDuplexSerial I use, everything hangs as soon as I start the high-bandwidth test... Don't know what to say... I'm baffled.

By the way, I'd just like to say, for no particular purpose whatsoever, that it is terribly easy to confuse => and >= especially when one is accustomed to using the latter for performing the former... http://forums.parallax.com/images/smilies/tongue.gif (e.g. compare "if lByte => 0" to "if lByte >= 0"... both compile without a problem, but have a rather different effect).

About the buffer size, I had looked at the asm portion, and noticed the $0F's and the #16 where it made sense to me. I just wasn't sure if I might have missed anything, though. It's not easy for me to read and I'm not yet sure about how everything works, when it comes to prop asm. And considering the unexpected issues I keep encountering, I thought it best to leave those buffers alone until I was sure. Thanks!

Anyway, I'm starting to wonder. While playing around with my propeller, I've been puzzled to the point of frustration many times by little (or not so little) things not working as expected. And I'm not new to programming. About two of these instances, I have posted on this forum, and in both cases so far, the mystery remains. Could it be that my propeller, or perhaps my board, is somehow broken?


On a side note, the CMUcam should actually have jumpers to configure its baudrate, but they are missing from mine, along with all other jumper headers and pins except for the servo connector. Much of the "glue logic" for these pins, as well as some other things like the regulator, max232, power switch, and an IC socket, are missing too...

It does sport two pairs of solid-core wires (for connecting power and serial) which are attached directly to the board with some solder and hot glue (the power wires being placed where the regulator should have been, and the hot glue on the serial wires nicely covering neighbouring thru-holes to make them unusable).

It's interesting to note that I bought these (two CMUcam's, as well as two GP2D02's and a bunch of other items) as they are, from Antratek, the dutch distributor for Parallax. This was in 2003, the GP2D02 is no longer available from them, and the CMUcam1 only as the Boe-Bot appmod. Anyway, I can only guess that Antratek wanted to save a few coins by slapping together their own version of the CMUcam. I now regret not having filed any complaints at the time.

... my posts are getting longer and longer...

09-19-2007, 07:37 AM
By the way, I think I discovered a bug in FullDuplexSerial, which might be biting me as well (although I would have expected worse symptoms). Who can spot it:

PUB rxcheck : rxbyte

'' Check if byte received (never waits)
'' returns -1 if no byte received, $00..$FF if byte

if rx_tail <> rx_head
rxbyte := rx_buffer[rx_tail]
rx_tail := (rx_tail + 1) & $F

Mike Green
09-19-2007, 08:01 AM
If you're referring to the "rxbyte--", it's not obvious, but the result value is always initialized to zero and this changes it to -1 if no bytes are available to read.

09-19-2007, 09:02 AM
Ok, I see, thanks. I found it hard to believe that stack variables are initialized to 0 at run-time, every time a function is called. I actually did a test to make sure before I posted about it. Of course my test did not involve the "result" slot, just a regular local variable. My mistake.

I imagine then, that it was done for efficiency, if indeed "rxbyte--" requires less interaction with hub memory (in terms of fetching spin bytecode, and any stack operations) than a plain and simple "rxbyte := 0". Seems backwards, but it could be I guess.

I was kind of hoping that this "bug" was the cause of all my problems in life http://forums.parallax.com/images/smilies/tongue.gif because then it meant I could fix it. I actually had tried it, and a bunch of other things, moving stuff around, reorganizing code, restructuring statements etc. but everytime I do, it generally serves only to make things worse. As illustrated by the aforementioned fact that I now have no more luck with the original FullDuplexSerial than with my modified one, regarding the CMUcam.

Post Edited (_Mark) : 9/19/2007 2:12:05 AM GMT

09-19-2007, 12:43 PM
(1) Yes, sorry; I misread "rx" for "tx" - it was very late in the night http://forums.parallax.com/images/smilies/smile.gif
(2) Indeed all local varaibles are undefined at entry of a routine, EXCEPT the return value.
(3) Still no idea what your problem could be....

09-19-2007, 07:41 PM
Same here. Thanks in any case, to you both.