Extra bit time in FullDuplexSerial?
David Betz
Posts: 14,530
I was just looking through some code that I derived from the FullDuplexSerial driver and noticed the following:
or txdata,#$100 'or in a stop bit
shl txdata,#2
or txdata,#1 'or in a idle line state and a start bit
mov txbits,#11
What's this about "idle line state"? Why is it necessary to add an extra bit time between two adjacent bytes during transmission? Shouldn't it be possible to send to bytes back to back? 
Comments
This is how I do it, but it assumes the TX line is properly setup to begin with.
transmit or txwork, STOP_BITS ' set stop bit(s) shl txwork, #1 ' add start bit mov txcount, #11 ' start + 8 data + 2 stop[Edit] Having thought about it for a minute, Chip probably did this because he couldn't add two stop bits without creating a constant (like I did) -- #$300 would be an illegal constant in PASM as it exceeds 511.
At low baud rates you're right: two stop bits is not required. But at faster baud rates with a software UART, the extra stop bit gives the processor time to handle what just came in (clean-up, buffer management, etc). FDS may be assuming that the other side is software too and is being kind.
It would not be very difficult to write a version of FDS that allowed one to set the number of stop bits. In most apps it's one or two (though I've seen more in a couple instances), and if one is okay, two will work. It seems to be the lowest [practical] common denominator.
Is there a way to set the carry flag with a single instruction?
Is there a way to clear the carry flag with a single instruction?
I'd like to write a function that use the state of the carry to indicate success or failure and it would be nice to be able to load up the value of C with a single instruction.
There's a thread where I discuss my attempt to add even parity to a serial driver. Tracy Allen kindly educated me about start bits and stop bits. There was mention of the two stop bits in this thread. I'm sure I could find it if you're interested in reading it.
Edit: Here's the link:
http://forums.parallax.com/showthread.php/155390-Adding-Even-Parity-to-One-Port-in-Tracy-Allen-s-4-Port-Serial-Object
I think there are ways to do what you want, but I'm not a PASM wizard yet either, just a guy who slogs it out trying to write simple code.
Phil Pilgrim once has a list of instruction tricks that would be helpful -- but they were lost during the forums migration. Perhaps he or on of the other PASM wizards will chime in. I'd like to know, too!
PUB null DAT set_z test $, #0 wz set_nz test $, #1 wz set_c test $, #1 wc set_nc test $, #0 wc set_z_c shr $, #%11111 wz,wc,nr set_z_nc shr $, #%11110 wz,wc,nr set_nz_c shr $, #%01111 wz,wc,nr set_nz_nc shr $, #%01110 wz,wc,nr mov_z_c muxnc $, $ wz,nr mov_z_nc muxc $, $ wz,nr mov_c_z muxz $, $ wc,nr mov_c_nz if_z_or_nc muxnz $, $ wc,nr ' condition flips parity to odd (required) ' this nop's for z = 0 && c = 1 (c == nz) swap_z_c if_z_ne_c sumc $, $ wc,wz,nr not_z muxz $, $ wz,nr not_c muxnc $, $ wc,nr save_z muxnz restore_z, #1 restore_z test $, #1 wz save_c muxc restore_c,#1 restore_c test $, #1 wc save_z_c muxc restore_z_c, #%00001 muxz restore_z_c, #%10000 restore_z_c shr $, #%01110 wz,wc,nr ' %Z---C DATIt can be a good idea to have a second stop bit as optional.(or even more choice, if that is easy to do )
You are right that at the byte-level, and with very fast receivers, there is no strict need, and it could be removed.
There is however a somewhat subtle system level use case where an extra idle bit helps, and that is with Baud skew on long packets.
If you receive 100 bytes, and echo on each one, you can get a TX overflow effect with slight baud differences.
(just 1% here is a full byte of creep)
Adding a second stop bit, allows the Baud to skew and ensures a slightly slower TX, can always pace continual RX.
If you push up the BAUD speed on FTDI/CP210x you will see they add extra stop bits, from the firmware not being able to keep up. They can add 1-2-3-4+ extra idle bit times.
The HS parts of FT232H/FT2232H are able to send higher speeds without added idles/stop bits..
+1
Thanks kuroneko.
-Phil
Keep in mind that 'standard baud rates' are no longer a constraint. (but keep them as a default/fallback)
Most modern USB-UART devices can accept any baud value, and deliver the nearest baud they support, which is mostly from a 12MHz virtual Baud Clock.
http://obex.parallax.com/object/246
If there a 2 COG version of this, that can go (a lot?) faster ? ie one COG for RX and One COG for TX
My dedicated fast serial receive routine works up to 3M baud with only one stop bit between characters so to make sure it can write the received data to the hub and be ready in time it prepares the hub write and index update while it is still receiving data so that all it has to do between characters is to write the data to the hub. btw, the TX part of this is a simple bit-bang without the need for buffering because at these high baud rates it's faster to bang out the bits rather than slow hub buffering and unbuffering.
I sometimes wish that asynch data would always have a start bit (0) followed by a mark (1) and then the data, so that start bits and timing could always be confirmed, that is detect the start bit and/or measure its width, then synchronize on the leading edge of the sync pulse. Then UARTs could automatically adapt their baud rates on the fly. All for the sake of 1 bit we are stuck with fixed baud rates and timing mismatches.
Is that 3.00Mbd with what Crystal / System Clock ? What margin is there for other clocks ?
Suppose a design used a USB related Clock, the two candidates could be
(16*48M/10)/3M = 25.6 - a simply alternating 25/26 would be ~0.4% off ?
(16*48M/8)/3M = 32
or a Std Xtal gives
(16*5M)/3M = 26.6666 => /27/27/26 repeating
I've tested this on a standard 80MHz Prop and checked the this with a scope as well. I know the timing would be off a little but it seems to work fine because I used it at this baudrate for quite some time early in the Tachyon development. 2M baud does not have this timing error at 80Mhz but my newer boards are designed to run from a 6MHz oscillator at 96MHz so there is no timing error at this frequency. btw, my start bit timing is pre-calculated to half a bit time less 4 cycles to compensate for latencies at higher speeds and I always check the stop bit for framing error as part of break detection.
Does it have enough headroom to run 3M at 25.5 cycles per bit average (vs 26 2/3 at 80Mhz )
If it uses an unrolled loop, it may be possible to support fractional baud ideas to get more Xtal choice tolerance.
Check my sig for the Dropbox links where you will find the serial receive right at the end of the Tachyon2.4.spin source or look in the "trouble with serial code" thread where I pasted this code in. It is more than just unrolled, it is interwoven and writes immediate fields to reconstruct the data so that saves a right justification operation as well.
'Receive Routine ======================================= HubRx5Mbs rdbyte Shifter,#0 'sync hub sub HubAddr,#1 'pre-dec address nop 'set hub sweet spot StartSearch jmp #SrchForStart 'entry point OneBit mov SearchCtr,#RxSearchShort 'load number of loops to search for nop FirstBit and RxLine0,ina wc,nr 'parity sample into carry rcr Shifter,#1 'pull sample into shifter djnz BitCtr,#OneBit 'test for 7 bits done and RxLine0,ina wc,nr 'parity sample bit 8 into carry rcr Shifter,#25 'pull sample into shifter and bit0 justify wrbyte Shifter,HubAddr 'save byte in main ram buffer djnz ByteCtr,#SrchForStart 'track received bytes jmp #HubRx5Mbs_ret 'exit on all received ' SrchForStart and RxLine0,ina wc,nr 'test for low start condition if_c djnz SearchCtr,#SrchForStart wz '2x oversample start bit... zero on time-out mov BitCtr,#7 'how many bits... 8th bit is unrolled add HubAddr,#1 'next address if_nz jmp #FirstBit 'look to receive a byte HubRx5Mbs_ret ret 'exit on search timed out 'Transmit Streamer Routine ============================ TxOneBit xor outa,#TxBitTime6 'optional scope bit time indicator.. replace with nop shr Shifter,#1 wc 'get bit muxc outa,#TxLine4 'output it djnz BitCtr,#TxOneBit 'test for 8 bits done sub ByteCtr,#1 wz 'keep track of how many done HubTx5Mbs or outa,#TxLine4 'stop condition rdbyte Shifter,HubAddr 'get value from Hub RAM 'optionally rdbyte Shifter,HubAddr wz 'get value from Hub RAM and exit on zero terminated string add HubAddr,#1 'next byte address OneByte if_nz andn outa,#TxLine4 'start condition mov BitCtr,#8 'how many bits if_nz jmp #TxOneBit +1 'loop for another byte HubTx5Mbs_ret ret 'done RxLine0 long 1 'receive pin mask... Pin0 HubAddr long 0 'location in main RAM ByteCtr long 0 'how many bytes to receive/transmit SearchCtr long 0 'keep track of search time for START condition BitCtr long 0 ' Shifter long 0 'Cheers,
Peter (pjv)
The 1/3 tick error can be centrally positioned, to be -1.5 tick at start and +1.5 tick at Stop bit.
I think there is room to gain another 15% bit timing margin, if you swap the Startbit code to immediately after Stop/save (removes one JMP, and inserts it in a less critical place).
With some low cost Micros having 1.5% Oscillator tolerance, every bit of margin helps.
Of course, if the Micro also provides the 4.8MHz or 6Mhz CLK, then there is no relative drift to allow for.