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 (rammerman@adelphia.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
Comments:
See: