RECEIVING
The normal approach for implementing a software asynchronous receiver is to have a timer tick that runs continuously at 3x or 5x the baud rate (note: odd numbers are better than even numbers). Watch the input to be low on two consecutive ticks. Once that is observed, start sampling the input on every third tick, until you have sampled it nine more times. If the input is high on the ninth time, you've received a properly-framed character. If it's low on the ninth time, you have a framing error.
* * * S - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - S
-----______000000111111222222333333444444555555666666777777----
--______000000111111222222333333444444555555666666777777-------
The above timing diagram shows how things should work. The top line shows what happens on each timer tick (each non-blank is a timer tick; the * is a tick waiting for the start bit, a S is checking to ensure that the line is still low after having received what looked like a start bit. The numbers 0-7 represent the sampling of bits 0-7, and S is the stop bit. The bottom two lines show the incoming data in the cases where one just barely catches the beginning of a start bit (on the third asterisk), or where one just barely misses catching it on one interrupt (so the line's been low for a third of a bit time by the time the start bit is noticed). Note that even with that level of uncertainty, the source is guaranteed to be sending bit 0 when it's sampled, and likewise for the rest of the bits.
One software approach is to sample the line at the indicated interrupts and ignore it on the ones marked with dashes. An alternative is to blindly sample the line on every interrupt into a 32-bit shift register, using something like:
rrf headtail,w
bcf _tail,3
btfsc _INPUTPORT,_INPUTBIT
bsf _STATUS,_tail,3
rrf buff2,w
movff buff1,buff2
movff buff0,buff1
movwf buff0
rrf headtail,f
The last four bits received from the port will be in headtail bits 3..0. The 24 bits before those will be in buff0..buff2, with each one holding every third bit. The bits before that will be in headtail bits 7..4. If one does that, one can check whether headtail holds the bit pattern 00xxxx1x. If it does, copy buff1 to wherever you want the incoming data, copy buff1 to the spot you want the data, OR headtail with 11000011, and full buff0..buff2 with FF. Otherwise do nothing. This approach may be a tiny bit slower than selectively loading or ignoring the data input, but may be better able to recover from framing errors.
TRANSMISSION
Transmission is easier than reception. Arrange for a piece of code to run once per bit time (if your tick is at 3x the baud rate, run the code every third tick). The simplest way to set up the code is to use a couple of bytes to hold the data that should be sent out the port, including start and stop bits. There are a variety of approaches one may use. A rather versatile one would be:
; At start of interrupt
btfss TransmitBuffL,0
bcf OUTPUT
btfsc TransmitBuffL,0
bsf OUTPUT
; Later, after having handled reception:
btfss TransmitBitsLeft,7 ; Assume this counts down to -1 (i.e. 255)
decfsz TransmitTicks,f
goto noTransmit ; Transmit if no bits left, or no ticks
movlw 3
movwf TransmitTicks ; Handle bit transmission every third interrupt
decf TransmitBitsLeft,f
rrf TransmitBuffH,f
rrf TransmitBuffL,f
NoTransmit:
One may prepare a byte for transmission when TransmitTicks bit 7 is set. When that is the case, place the bit sequence to be set into TransmitBuffH and TransmitBuffL, set TransmitTicks to the length of the first bit (typically 3), and TransmitBitsLeft to the total length of the word including start and stop bits. Something like:
TxByte: ; Sends byte in W. Assumes TransmitBitsLeft has high bit set
movwf TransmitBuffL,f
movlw 3
movwf TransmitTicks
movwf TransmitBuffH,f ; LSB (stop bit) is set. Upper bits don't matter.
movlw 9 ; Total frame length (incl. start and stop) minus one
movwf TransmitBitsLeft
bcf _STATUS,_CARRY ; Start bit should be low
rlf TransmitBuffL,f ; Stick start bit in front of what we're sending
rlf TransmitBuffH,f
This approach can send out normal data frames using the style indicated. If one needs more stop bits, one may make sure TransmitBuffH has enough bits set, and increase TransmitBitsLeft appropriately. If one wants to send out a BREAK, one may set TransmitBuffL to 2 and TransmitBitsLeft to 1, set TransmitTicks to the desired length of the break signal (in ticks). To idle the line for a certain length of time, set TransmitBuffL to 1, TransmitBitsLeft to 0, and set TransmitTicks to the desired idle time (in ticks).
Best Answer
I find unions perfectly fine for "sharing" variables. Let me expand on this with code:
... Of course, this could be made much more readable using
typedefs
onset_1
andset_2
.