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 (spamrammerman at ~NOSPAM~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
|file: /Techref/microchip/16F84-rs232-8ch-ba.htm, 12KB, , updated: 2008/3/18 21:48, local time: 2023/12/3 15:54,
|©2023 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.massmind.org/Techref/microchip/16F84-rs232-8ch-ba.htm"> PIC Specific RS232 routine</A>
|Did you find what you needed?|
Welcome to massmind.org!
Welcome to www.massmind.org!