 
This progam allows a 16F84A at 20MHz to simultaneously receive 8 full 9600 communication links. The transmit side of the UART is _not_ implemented.
Note that the code has been tested under simulation, but not in actual hardware.
        list    p=16F84a,f=INHX32
        #include <p16F84a.inc>
;=======================================================================================
; Copyright (c) 2000 by Robert V Ammerman (TakeThisOuTrammermanspam at TakeThisOuTadelphia.net as of Jun 2003)
;
; This code may be used for any legal purpose, commerical or not. I only ask that you
; let me know if you use it and that you leave this copyright notice unchanged in 
; your source code.
;
; Also, you understand that I have no liability if this code doesn't work as expected.
;
; Software UAR(no T) code developed by and used with the permission of:
; 
;       Robert V Ammerman
;       RAm Systems
;       (contract development of high performance, high function, low-level software)
;
;======================================================================================
;
; This code is a sample UAR(no T) that receives 8 channels at 9600 baud on a 20Mhz 16F84A
; (or other midrange PIC).
;
; This code has been tested until simulation only, not on real hardware. Use at your own
; risk!
;
;
; The program works by handling timer interrupts at 3x the nominal bit rate and running a 
; state machine for each channel to acquire the data. 
;
; The interrupts are at the rate of:
;
;       20000000/4/174 == 28735.63 Hz.
;
; The 'perfect' rate would be 9600*3 == 28800 Hz.
;
; The bit rate error is thus about 0.22 percent.
;
; The first trick behind this code is the way the state machines are run: instead of
; handling each channel one at a time the program uses 'vertical arithmetic' to 
; process all eight channels together.
;
; The second trick is the way that the program works thru processing the eight
; input bits on the channels, accumulating them into the 'receiver shift register'
; variables, and determining when a byte has been completely received. This is done 
; using only 3 instructions per channel.
;
; Using these two tricks results in code that uses only 76 instructions per interrupt, 
; including context save and restore, but not interrupt latency. Since interrupts 
; are generated every 174 instructions this leaves about 54 percent of the CPU available 
; for 'task level' code.
;
; One important thing to note: since this code does _not_ double buffer the receiver, 
; task level code must process a received byte within 4/3 of a bit time (approximately 
; 390 task level instructions), otherwise the overrun flag will be set for that channel.
    CBLOCK      0x0C
; The state visible to task-level code for the UART receivers
    rsr_chan:8    ; Receiver shift registers for channels 0..7
        rcv_ready         ; Bitmap of channels with bytes ready
        frame_err         ; Bitmap of channels with framing error
        overrun_err       ; Bitmap of channels with overrun error
; The current inputs from the 8 serial ports
        curr_input
; The following bitmapped variable is used to track when a UART shift 
; register is full and thus we have to check for the stop bit at the 
; next sample time.
 
        full 
 
; The following bitmapped variables maintain the state data for each of the 
; channels. Note that each channel will always have exactly 1 one bit set
; in these variables to indicate what state that channel is in.
        wait_start              ; Channel is waiting for start bit
        confirm_start           ; Channel is verifying that it is really in start bit
        ignore1                 ; Channel is waiting for sample time
        ignore2                 ; Channel is waiting for sample time
        sample                  ; Channel is sampling the input state
        stop_exp                ; Channel is expecting the stop bit
; Temps used in computing the new state
        work
        new_wait_start
        new_ignore1
; Save area for interrupts
        int_w_save
        int_status_save
    ENDC
;==============================================================================
; Main entry point
    goto        start
;==============================================================================
; Interrupt handler
    org         0x04
; context save
    movwf       int_w_save       
    swapf       STATUS,W
    movwf       int_status_save
; Clear the timer interrupt and adjust the timer to correct its period
    bcf         INTCON,T0IF
; We want a period of 174 instructions, which is 1/3 of a bit time
; So we have to add 256-174 to the counter. 
; But the counter delays updating by 2 cycles.
; So we really add 256-174+2 to get the
; desired period.
    movlw       D'256'-D'174'+D'2'
    addwf       TMR0,F
; Get the input and prepare to process it
    comf        PORTB,W         ; Get current input state 
    movwf       curr_input      ; Save a copy
    movwf       work
    
; Process all the inputs.
; Note that we only shift in bits for channels that are in the 'sample'
; state.
 
rcv_chan macro n
; The next instruction does two things:
;       1: It puts 'receive shift register full' status, if any, 
;       for the previous channel into the high bit of 'work'.
;       2: It puts the input data bit for the current channel into
;               carry.
 
    rrf         work,F 
; See if this channel is ready to sample
    btfsc       sample,n      ; Do we need a new bit?
; If this channel is ready to sample, the following instruction will be 
; executed to perform two functions:
;       1: It puts the input data bit into the receiver shift register.
;       2: It puts the 'receive shift register full' status into carry.
    rrf         rsr_chan+n,F    ; Yes, move it in
 endm
        rcv_chan 0
        rcv_chan 1
        rcv_chan 2
        rcv_chan 3
        rcv_chan 4
        rcv_chan 5
        rcv_chan 6
        rcv_chan 7
; At this point 'work' contains the 'receive shift register full' bits for
; channels 0..6 (in bits 1..7) and carry has the bit for channel 7.
        rrf     work,W  ; Bring in channel 7's 
                        ;'receive shift register full' bit
; Note that the bits in WREG that correspond to channels that were not
; sampled on this cycle are garbage. Zero them out and then turn on the 
; full bits for the sampled channels that we just filled.
    andwf       sample,W        ; (can only include chans with new bits)
    iorwf       full,F          ; Turn on full bits for sampled channels
; compute the new state variables based on the old state and the 
; 'curr_input' and 'full' vectors
; Note: a stop bit is a 1, a start bit is a 0
; Here are the transitions of the state machine:
; 
;       stop_exp                -> wait_start
;                                   *set rcv_ready bits
;                                   *set frame_err bits
;       wait_start              -> INPUT==1 -> wait_start
;                                  INPUT==0 -> confirm_start
;       confirm_start           -> INPUT==1 -> wait_start 
;                                  INPUT==0 -> ignore1
;       ignore1                 -> ignore2
;       ignore2                 -> FULL==0 -> sample
;                                  FULL==1 -> stop_exp
;                                       *clear full bits
;       sample                  -> ignore1
;                                       *set overrun error bits
;                                       *store data bits
;                                       *set full bits
; Turning this around into math to compute the new values of the state
; bit vectors and 'full', 'rcv_ready', 'overrun_err' and 'frame_err' vectors 
; based on the old state bit vectors and the 'input' and 'full' vectors
; we get: 
;
;   NOTE: These comments are written assuming all the assignments happen
;       simultaneously:
;
;       confirm_start   <- (wait_start & ~input) 
;       ignore1         <- (confirm_start & ~input) | sample
;       ignore2         <- ignore1
;       sample          <- (ignore2 & ~full)
;       stop_exp        <- (ignore2 & full)
;       full            <- full & ~ignore2
;       wait_start      <- stop_exp 
;                        | (wait_start & input)
;                        | (confirm_start & input)
;       rcv_ready       <- rcv_ready | stop_exp
;       frame_err       <- frame_err | (stop_exp & ~input)
;       overrun_err     <- overrun_err | (sample & rcv_ready)
; new_wait_start = ((wait_start | confirm_start) & input) | stop_exp
        movf    wait_start,W
        iorwf   confirm_start,W
        andwf   curr_input,W
        iorwf   stop_exp,W
        movwf   new_wait_start
; new_ignore1 = (confirm_start & ~input) | sample
        comf    curr_input,W
        andwf   confirm_start,W
        iorwf   sample,W
        movwf   new_ignore1
; overrun_err |= sample & rcv_ready
        movf    sample,W
        andwf   rcv_ready,W
        iorwf   overrun_err,F
; sample = ignore2 & ~full
        comf    full,W
        andwf   ignore2,W
        movwf   sample
; rcv_ready |= stop_exp
        movf    stop_exp,W
        iorwf   rcv_ready,F
; frame_err |= stop_exp & ~input
        comf    curr_input,W
        andwf   stop_exp,W
        iorwf   frame_err,F
; stop_exp = ignore2 & full
        movf    ignore2,W
        andwf   full,W
        movwf   stop_exp
; full = full & ~ignore2
 
        comf    ignore2,W
        andwf   full,F
; ignore2 = ignore1
        movf    ignore1,W
        movwf   ignore2
; confirm_start = wait_start & ~input
        comf    curr_input,W
        andwf   wait_start,W
        movwf   confirm_start
; wait_start = new_wait_start
        movf    new_wait_start,W
        movwf   wait_start
; ignore1 = new_ignore1
        movf    new_ignore1,W
        movwf   ignore1
; Context restore
        swapf   int_status_save,W
        movwf   STATUS
        swapf   int_w_save,F
        swapf   int_w_save,W
        retfie
;===============================================================================
; Mainline code
start:
; initialize all the UART status values
        movlw   0x80
        movwf   rsr_chan+0
        movwf   rsr_chan+1
        movwf   rsr_chan+2
        movwf   rsr_chan+3
        movwf   rsr_chan+4
        movwf   rsr_chan+5
        movwf   rsr_chan+6
        movwf   rsr_chan+7
        clrf    rcv_ready
        clrf    frame_err
        clrf    full
        movlw   0xFF
        movwf   wait_start
        clrf    confirm_start
        clrf    ignore1
        clrf    ignore2
        clrf    sample
        clrf    stop_exp
        bsf     STATUS,RP0
        bcf     OPTION_REG-0x80,T0CS
        bcf     STATUS,RP0
        bsf     INTCON,T0IE
        bsf     INTCON,GIE
forever:
    btfsc   rcv_ready,0
    call    rcv_0
    btfsc   rcv_ready,1
    call    rcv_1
    btfsc   rcv_ready,2
    call    rcv_2
    btfsc   rcv_ready,3
    call    rcv_3
    btfsc   rcv_ready,4
    call    rcv_4
    btfsc   rcv_ready,5
    call    rcv_5
    btfsc   rcv_ready,6
    call    rcv_6
    btfsc   rcv_ready,7
    call    rcv_7
    goto    forever
rcvchan macro   chan
rcv_#v(chan):
    btfsc   frame_err,chan
    goto    ferr_#v(chan)
    btfsc   overrun_err,chan
    goto    oerr_#v(chan)
    movf    rsr_chan+chan,W
     ; do something with the byte in W
    bcf     rcv_ready,chan
    movlw   0x80
    movwf   rsr_chan+chan
    return
ferr_#v(chan):
    bcf     frame_err,chan
    bcf     rcv_ready,chan
     ; deal with framing error condition
    movlw   0x80
    movwf   rsr_chan+chan
    return
oerr_#v(chan)
    bcf     overrun_err,chan
    bcf     rcv_ready,chan
     ; deal with overrun condition
    movlw   0x80
    movwf   rsr_chan+chan
    return
 endm
    rcvchan 0
    rcvchan 1
    rcvchan 2
    rcvchan 3
    rcvchan 4
    rcvchan 5
    rcvchan 6
    rcvchan 7
        end
See:
| file: /Techref/microchip/16F84-rs232-8ch-ba.htm, 12KB, , updated: 2008/3/18 22:48, local time: 2025/10/31 10:53, 
owner: RVA-RAm-R00a, 
 
216.73.216.87,10-2-37-96:LOG IN | 
| ©2025 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions? <A HREF="http://www.piclist.com/techref/microchip/16F84-rs232-8ch-ba.htm"> PIC Specific RS232 routine</A> | 
| Did you find what you needed? | 
|  PICList 2025 contributors: o List host: MIT, Site host massmind.org, Top posters @none found - Page Editors: James Newton, David Cary, and YOU! * Roman Black of Black Robotics donates from sales of Linistep stepper controller kits. * Ashley Roll of Digital Nemesis donates from sales of RCL-1 RS232 to TTL converters. * Monthly Subscribers: Gregg Rew. on-going support is MOST appreciated! * Contributors: Richard Seriani, Sr. | 
| Ashley Roll has put together a really nice little unit here. Leave off the MAX232 and keep these handy for the few times you need true RS232! | 
.