LIST P=PIC16F84 INCLUDE "P16F84.INC" RADIX DEC ; RS-232 code. Transmitter runs on timer interrupts and has 16-byte ; queue. receiver uses timer interrupts for timing, but is basically ; polled. the baud rate here is 4800 bps, but easy to change with ; new timing constant for timer register. ; ; wired as follows: ; ; B0 - RS232 receive data ; B1 - RS232 transmit data ; ; Written by Madis Kaal http://www.nomad.ee ; DATAPORT equ PORTB TXBIT equ 1 RXBIT equ 0 counter equ 12 tempw equ 13 temps equ 14 cmd equ 15 tmp1 equ 16 flags equ 17 rxcount equ 18 txstate equ 27 ;transmit machine state txreg equ 28 ;transmitter shift reg txcount equ 29 ;number of bytes in queue txtail equ 30 ;getting out at this offset txhead equ 31 ;stuffing in at this offset txbuf equ 32 ;16-byte queue ;timer0 is programmed to interrupt at every 833/16=52 us ;the timer interrupt occurs when the timer overflows from 255 to 0 ;so we have to write 205 to timer but have to count the overhead ;cycles as well (two cycles are lost at each write to TMR0 and ;there is 3 cycle latency as well, that counts for 5 cycles) ORG 0 goto init ORG 4 ;timer interrupt keeps a time base of 50 us ticks movwf tempw ;save w 1 swapf STATUS,w ; 1 movwf temps ;save status 1 movlw 205+8 ; 1 movwf TMR0 ;reload timer bcf INTCON,T0IF ;clear interrupt flag bcf flags,WAITINT ;indicate interrupt occurred incf counter,f ;bump counter movlw 3 andwf counter,f btfsc STATUS,Z call txsm ;call transmitter machine on ever 4th tick swapf temps,w ; movwf STATUS ; swapf tempw,f ; swapf tempw,w ; retfie ; ;this is a transmitter state machine, it gets called at every fourth tick ;resulting 4800 bps txsm movf txstate,w ;get machine state andlw 15 ;just to be sure addwf PCL,f ;take a jump... goto txs0 goto txs1 goto txs2 goto txs3 goto txs4 goto txs5 goto txs6 goto txs7 goto txs8 goto txs9 goto txs10 goto txs10 goto txs10 goto txs10 goto txs10 goto txs10 ;idle state, if there is data to transmit, move the byte to ;shift register and switch to start bit transmission txs0 movf txcount,f ;test counter btfsc STATUS,Z ;no data, stay in this state return movf txtail,w ;get pointer addlw txbuf ;add buffer base movwf FSR ;make address movf INDF,w ;get the byte movwf txreg ;store to shifter movf txtail,w addlw 1 andlw 15 movwf txtail decf txcount,f incf txstate,f ;switch to start bit state return ;start bit transmission state. this is easy, pull transmitter low ;and switch over to actual transmitter txs1 nop ;adjust to make start bit equal to nop ;following data bits incf txstate,f ;switch to first bit state bcf DATAPORT,TXBIT return ;each of these states will transmit one bit txs2 txs3 txs4 txs5 txs6 txs7 txs8 txs9 rrf txreg,f ;shift next bit to carry ;1 btfsc STATUS,C ;see what we got ;1/2 goto settx ;2 nop ;adjust ;1 bcf DATAPORT,TXBIT incf txstate,f return settx bsf DATAPORT,TXBIT incf txstate,f return ;the actual byte is now transferred, start sending stop bit(s) and ;switch back to idle txs10 bsf DATAPORT,TXBIT ;send stop bit clrf txstate ;back to idle state return ;starts interrupt service startclk movlw 240 movwf TMR0 ;schedule interrupt bcf INTCON,T0IF ;clear int flag bsf INTCON,GIE ;enable general interrupts bsf INTCON,T0IE ;enable timer interrupts return ;puts byte in W to transmitter buffer, locks up until there is ;room ; sendbyte movwf tmp1 ;store the parm sbw movlw 16 subwf txcount,w ;buffer full? btfsc STATUS,Z goto sbw ;wait for room movf txhead,w ;get pointer addlw txbuf ;add buffer base bcf INTCON,GIE ;disable interrupts movwf FSR ;make indirect address movf tmp1,w ;get data back movwf INDF ;store byte bsf INTCON,GIE ;enable general interrupts movf txhead,w ;get pointer addlw 1 ;advance it andlw 15 ;scale back movwf txhead incf txcount,f ;one more byte in fifo return ;this is RS232 receiver part. I can not directly bit-bang because the transmitter ;machine makes cycle counting impossible. so I will wait for start bit, then wait ;for half bit, then 8 full bits followed by bit read, then one more full bit to make ;sure the stop bit passes by. the timing is _very_ crude, with precision about 25% of ;the bit length ; readbyte movlw 8 movwf rxcount clrf tmp1 rdb1 btfsc DATAPORT,RXBIT goto rdb1 call waithbit rdb2 call waitfbit rrf DATAPORT,w rrf tmp1,f decfsz rxcount,f goto rdb2 call waitfbit movf tmp1,w return waitfbit call waithbit waithbit bsf flags,WAITINT whb1 btfsc flags,WAITINT goto whb1 bsf flags,WAITINT whb2 btfsc flags,WAITINT goto whb2 return ; customize this for your port configuration! ; init clrf PORTA ;all output bits to 0 clrf PORTB bsf STATUS,RP0 ;select bank 1 clrf TRISA movlw B'00000001' movwf TRISB bcf OPTION_REG,T0CS ;enable timer mode bcf STATUS,RP0 ;back to bank 0 clrf txstate clrf txcount call startclk bsf DATAPORT,TXBIT ;we now have timer running main call readbyte call sendbyte goto main end