Mike McLaren - K8LH (Westland, MI) says:
Here is a Serial I/O demo for the 16F819 which uses 3X bit rate interrupts as well as Rx and Tx state machines in the ISR to achieve full-duplex 9600 baud serial I/O with 16-byte circular Tx and Rx buffers...
While this demo could easily be considered a novelty for using nearly 50% of the overall processing time at 9600 baud with an 8-MHz clock, I suspect it could be quite useful with a faster clock and/or at slower baud rates...
list p=16F819, b=8, c= 102, n=71, t=on, st=off, f=inhx32 ;****************************************************************** ;* * ;* Filename: 16F819 Serial 1.asm * ;* Author: Mike McLaren, K8LH (k8lh_at_arrl.net) * ;* Date: 08-Jun-05 (last revision 12-Jun-05) * ;* * ;* Full Duplex Bit-Banged 9600 Baud Serial I/O Demo * ;* (based on a nearly identical 12F683 Demo) * ;* * ;* ·Uses 16F819 INTOSC running at 8-MHz * ;* ·Bit rate error 0.6% plus or minus 1.0% for INTOSC * ;* ·Bit-banged 9600 baud serial I/O * ;* ·Full Duplex (TX and RX simultaneously) * ;* ·Interrupts at approximately 3X bit rate every * ;* 34.5 usecs (every 69 instruction cycles) * ;* ·Circular 16-byte receive ahead buffer * ;* ·Circular 16-byte transmit ahead buffer * ;* ·Inverted TX and RX signals (MAX232A or similar * ;* inverting RS-232 interface required) * ;* ·ISR and Init232, Put232, and Get232 support routines * ;* fit comfortably in the first 192 words of code space * ;* occupying memory from 0004 through 00BF (188 words) * ;* * ;* MPLab: 7.11 (tabs=8) * ;* MPAsm: 4.01 * ;* * ;****************************************************************** #include <p16f819.inc> errorlevel -302 __config _LVP_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_IO ; ; Serial I/O file register variables & buffers ; TX_RPTR equ 0x20 ; TX buffer Rd pointer (isr) TX_WPTR equ 0x21 ; TX buffer Wr pointer (main) TXCHAR equ 0x22 ; TX character (main, Put232) TXWORK equ 0x23 ; TX ISR work byte (isr) ; RX_RPTR equ 0x24 ; RX buffer Rd pointer (main) RX_WPTR equ 0x25 ; RX buffer Wr pointer (isr) RXCHAR equ 0x26 ; RX character (main, Get232) RXWORK equ 0x27 ; RX ISR work byte (isr) ; TX_SM equ 0x28 ; TX state machine var (isr) RX_SM equ 0x29 ; RX state machine var (isr) ; RXBUFF equ 0xA0 ; RX buffer, 16 bytes A0h-AFh TXBUFF equ 0xB0 ; TX buffer, 16 bytes B0h-BFh ; ; 16F819 serial port pin defines ; #define RXPIN PORTB,2 ; RS-232 RX (RB2) #define TXPIN PORTB,5 ; RS-232 TX (RB5) ; ; file locations used by ISR to save and restore context ; W_ISR equ 0x70 ; ISR 'W' S_ISR equ 0x71 ; ISR 'STATUS' P_ISR equ 0x72 ; ISR 'PCLATH' F_ISR equ 0x73 ; ISR 'FSR' ; ; other vars ; FSRTMP equ 0x74 ; Put232 & Get232 (main) PTRL equ 0x75 ; PutStr routine (main) PTRH equ 0x76 ; PutStr routine (main) ;****************************************************************** ; ; _Title macro - home cursor, clear screen, print a string ; _Title macro str ; local String, Print movlw low String ; movwf PTRL ; movlw high String ; movwf PTRH ; goto Print ; String dt h'1B',"[2J" ; <esc>[2J sequence dt str,0 Print call PutString ; print string endm ;****************************************************************** ; ; Hardware notes ; ; <1> INTOSC 8-MHz, 500-nsec instruction cycle time ; <2> RB2 (pin 08) > RXPIN, 'bit banged' serial input ; <3> RB5 (pin 11) > TXPIN, 'bit banged' serial output ; <4> RS-232 signals inverted (use MAX232A or similar) ; <5> ; <6> ; <7> ; ; ; This program simply prints a text string to Hyperterminal ; and echos characters coming from Hyperterminal... ; ; Setup Hyperterminal for 9600, 8, 1, none... Use a MAX232 or ; similar level shifting circuit (I use a pair of 2N7000s) for ; connection between the 16F819 and the PC... ; ;****************************************************************** ;* * ;* * ;* * ;* * ;* * ;****************************************************************** org 0x0000 RESET clrf STATUS ; |B0 goto MAIN ; |B0 ;****************************************************************** ;* * ;* Interrupt Service Routine for a Full Duplex Bit-Banged * ;* 9600 Baud Serial I/O with 16 byte circular receive and * ;* transmit buffers... * ;* * ;* Interrupts are generated at approximately 3 times the * ;* bit rate for 9600 baud at 34.5-usec intervals or every * ;* 69 instruction cycles. * ;* * ;* The transmit and receive processes are executed in the * ;* correct sequence each interrupt cycle by using a state * ;* machine variable and jump table for both RX and TX. * ;* * ;* After detecting a start bit, the receive bit stream is * ;* sampled every third interrupt cycle in the approximate * ;* middle third of each bit (between 33% and 66%). * ;* * ;* The 16 byte circular TXBUFF is located at B0..BF in RAM * ;* and will buffer 15 bytes. The "unload buffer" process * ;* is performed in the ISR after sending a character and * ;* the "load buffer" process is performed outside the ISR * ;* in the Put232 subroutine. * ;* * ;* The 16 byte circular RXBUFF is located at A0..AF in RAM * ;* and will buffer 15 bytes. The "load buffer" process is * ;* performed in the ISR after receiving a character and * ;* the "unload buffer" process is performed outside of the * ;* ISR in the Get232 subroutine. * ;* * ;* Of the 69 instruction cycles between interrupts the ISR * ;* uses between 34 and 35 cycles average each interrupt or * ;* approximately 49.3% to 50.7% of the overall processing * ;* time available. The TX code uses almost the same number * ;* of instruction cycles when transmitting or idle. The RX * ;* code uses 1 more instruction cycle average per interrupt * ;* when receiving than it does when idle. * ;* * ;* 34.0 cycles avg (49.3% mcu overhead) RX idle * ;* 35.0 cycles avg (50.7% mcu overhead) RX in progress * ;* * ;****************************************************************** org 0x0004 ; ; save main program context ; ; 09 cycles every interrupt cycle ; ISR movwf W_ISR ; save W-reg |B? swapf STATUS,W ; doesn't change STATUS bits |B? movwf S_ISR ; save STATUS reg |B? clrf STATUS ; bank 0 |B0 movf PCLATH,W ; get PCLATH |B0 movwf P_ISR ; save PCLATH |B0 bcf PIR1,TMR2IF ; clear TMR2 interrupt flag |B0 ; ; enter the TX state machine ; ; 06 cycles, including TX SM jump, every interrupt cycle ; clrf PCLATH ; |B0 movf TX_SM,W ; get TX state machine |B0 addwf PCL,f ; off we go |B0 ; ; the TX state machine table (cycle times include ISR entry, ; TX state machine jump, and RX state machine jump) ; ; TX idle, 24.0 of 69.0 instructions per interrupt (34.8%) ; TX proc, 24.0 of 69.0 instructions per interrupt (34.8%) ; goto TX_CHK ; TX idle 24 cycles |B0 goto TX_0 ; start bit 24 cycles |B0 goto TX_BUF ; 31 cycles |B0 goto TX_NXT ; 21 cycles |B0 goto TX_BIT ; bit 0 28 cycles |B0 goto TX_NXT ; 21 cycles |B0 goto TX_NXT ; 21 cycles |B0 goto TX_BIT ; bit 1 28 cycles |B0 goto TX_NXT ; 21 cycles |B0 goto TX_NXT ; 21 cycles |B0 goto TX_BIT ; bit 2 28 cycles |B0 goto TX_NXT ; 21 cycles |B0 goto TX_NXT ; 21 cycles |B0 goto TX_BIT ; bit 3 28 cycles |B0 goto TX_NXT ; 21 cycles |B0 goto TX_NXT ; 21 cycles |B0 goto TX_BIT ; bit 4 28 cycles |B0 goto TX_NXT ; 21 cycles |B0 goto TX_NXT ; 21 cycles |B0 goto TX_BIT ; bit 5 28 cycles |B0 goto TX_NXT ; 21 cycles |B0 goto TX_NXT ; 21 cycles |B0 goto TX_BIT ; bit 6 28 cycles |B0 goto TX_NXT ; 21 cycles |B0 goto TX_NXT ; 21 cycles |B0 goto TX_BIT ; bit 7 28 cycles |B0 goto TX_NXT ; 21 cycles |B0 goto TX_NXT ; 21 cycles |B0 goto TX_PTR ; stop bit 28 cycles |B0 goto TX_NXT ; 21 cycles |B0 goto TX_RES ; reset 23 cycles |B0 ; ; perform TX "unload buffer" function and send the stop bit ; ; 13 cycles, including RX SM jump, 01 in 30 TX cycles ; TX_PTR incf TX_RPTR,W ; W = TX_RPTR + 1 |B0 andlw h'0F' ; keep in range 00..0F |B0 addlw TXBUFF ; make it range B0..BF |B0 movwf TX_RPTR ; update TX_RPTR |B0 bsf TXPIN ; send stop bit |B0 goto TX_NXT ; increment TX state |B0 ; ; reset TX state machine during final third of the stop bit ; for 138.5 usecs (1 and 1/3 bit times) between TX characters ; TX_RES clrf TX_SM ; reset TX state machine |B0 goto TX_XIT ; |B0 ; ; copy character from RXBUFF to TXWORK after sending start bit ; ; 17 cycles, including RX SM jump, 01 in 30 TX cycles ; TX_BUF movf FSR,W ; get FSR |B0 movwf F_ISR ; save it |B0 movf TX_RPTR,W ; get TX buffer Rd ptr [B0..BF] |B0 movwf FSR ; setup indirect address |B0 movf INDF,W ; get data |B0 movwf TXWORK ; put it in a work register |B0 movf F_ISR,W ; |B0 movwf FSR ; restore FSR |B0 goto TX_NXT ; increment TX state |B0 ; ; transmit bit ; ; 13 cycles, including RX SM jump, 08 in 30 TX cycles ; TX_BIT rrf TXWORK,f ; |B0 btfsc STATUS,C ; is it a '1'? |B0 TX_1 bsf TXPIN ; yes, send it |B0 btfss STATUS,C ; is it a '0'? |B0 TX_0 bcf TXPIN ; yes, send it |B0 goto TX_NXT ; increment TX state |B0 ; ; if there's a transmit character buffered, bump the TX_SM var ; to initiate the transmit process, else exit leaving TX_SM=00 ; ; 09 cycles, including RX SM jump, every interrupt cycle until ; a TX operation is initiated ; TX_CHK movf TX_RPTR,W ; get TX buffer Rd ptr [B0..BF] |B0 xorwf TX_WPTR,W ; xor TX buffer Wr ptr [B0..BF] |B0 btfss STATUS,Z ; skip empty (TX_RPTR=TX_WPTR) |B0 TX_NXT incf TX_SM,f ; inc TX state machine |B0 ;****************************************************************** ; ; enter the RX state machine (cycle times include ISR_XIT) ; ; RX idle, 10.0 of 69.0 instructions per interrupt (14.5%) ; RX proc, 11.0 of 69.0 instructions per interrupt (15.9%) ; TX_XIT movf RX_SM,W ; get RX state machine |B0 addwf PCL,f ; off we go |B0 ; ; the RX state machine table ; goto RX_CHK ; RX idle 10 cycles |B0 goto RX_NXT ; start bit 09 cycles |B0 goto RX_NXT ; 09 cycles |B0 goto RX_NXT ; 09 cycles |B0 goto RX_BIT ; bit 0 14 cycles |B0 goto RX_NXT ; 09 cycles |B0 goto RX_NXT ; 09 cycles |B0 goto RX_BIT ; bit 1 14 cycles |B0 goto RX_NXT ; 09 cycles |B0 goto RX_NXT ; 09 cycles |B0 goto RX_BIT ; bit 2 14 cycles |B0 goto RX_NXT ; 09 cycles |B0 goto RX_NXT ; 09 cycles |B0 goto RX_BIT ; bit 3 14 cycles |B0 goto RX_NXT ; 09 cycles |B0 goto RX_NXT ; 09 cycles |B0 goto RX_BIT ; bit 4 14 cycles |B0 goto RX_NXT ; 09 cycles |B0 goto RX_NXT ; 09 cycles |B0 goto RX_BIT ; bit 5 14 cycles |B0 goto RX_NXT ; 09 cycles |B0 goto RX_NXT ; 09 cycles |B0 goto RX_BIT ; bit 6 14 cycles |B0 goto RX_NXT ; 09 cycles |B0 goto RX_NXT ; 09 cycles |B0 goto RX_BIT ; bit 7 14 cycles |B0 goto RX_BUF ; buffer 19 cycles |B0 goto RX_NXT ; 09 cycles |B0 goto RX_NXT ; stop bit 09 cycles |B0 goto RX_PTR ; ptr & reset 18 cycles |B0 ; ; copy completed character in RXWORK byte to RXBUFF ; ; 19 cycles, including ISR_XIT, 01 in 30 RX cycles ; RX_BUF movf FSR,W ; |B0 movwf F_ISR ; save FSR |B0 movf RX_WPTR,W ; RX buffer Wr pointer [A0..AF] |B0 movwf FSR ; setup indirect address |B0 movf RXWORK,W ; get completed work byte |B0 movwf INDF ; place it in the RX buffer |B0 movf F_ISR,W ; |B0 movwf FSR ; restore FSR |B0 goto RX_NXT ; increment RX state |B0 ; ; receive bit ; ; 14 cycles, including ISR_XIT, 08 in 30 RX cycles ; RX_BIT btfsc RXPIN ; is it a 0? |B0 bsf STATUS,C ; no, make it a 1 |B0 rrf RXWORK,f ; shift into our work byte |B0 goto RX_NXT ; increment RX state |B0 ; ; perform RX buffer 'load' function after receiving byte ; ; 18 cycles, including ISR_XIT, 01 in 30 RX cycles ; RX_PTR incf RX_WPTR,W ; |B0 andlw RXBUFF+h'0F' ; |B0 xorwf RX_RPTR,W ; buffer full (WPTR+1=RPTR)? |B0 bz RX_RES ; yes, branch |B0 xorwf RX_RPTR,W ; no, restore WPTR+1 value |B0 movwf RX_WPTR ; update WPTR |B0 ; ; reset RX state machine after receiving a complete character ; and during the last 3rd (66%-100%) of the stop bit to allow ; setup time for detecting the next start bit ; RX_RES clrf RX_SM ; reset RX state machine |B0 goto ISR_XIT ; |B0 ; ; test for start bit (low) ; ; 10 cycles, including ISR_XIT, each cycle until RX initiated ; RX_CHK btfss RXPIN ; start bit? |B0 RX_NXT incf RX_SM,f ; inc RX state machine |B0 ; ; 08 cycles each interrupt cycle ; ISR_XIT movf P_ISR,W ; |B0 movwf PCLATH ; restore PCLATH |B0 swapf S_ISR,W ; |B0 movwf STATUS ; restore STATUS |B? swapf W_ISR,f ; don't screw up STATUS |B? swapf W_ISR,W ; restore W-reg |B? retfie ; return from interrupt |B? ;****************************************************************** ;* * ;* Companion Put232 and Get232 subroutines * ;* * ;****************************************************************** ; ; Put232 - enter with character to be sent in W ; - performs TXBUFF 'load buffer' operation ; Put232 movwf TXCHAR ; save character |B0 Pwait incf TX_WPTR,W ; W = WPTR + 1 |B0 andlw h'0F' ; keep it in range 00..0F |B0 addlw TXBUFF ; make it in range B0..BF |B0 movwf RXCHAR ; save here temporarily |B0 xorwf TX_RPTR,W ; buffer full (WPTR+1=RPTR)? |B0 bz Pwait ; yes, branch, wait |B0 movf FSR,W ; get FSR |B0 movwf FSRTMP ; save it |B0 movf TX_WPTR,W ; get TX buffer Wr ptr (B0..BF) |B0 movwf FSR ; setup indirect address |B0 movf TXCHAR,W ; get character |B0 movwf INDF ; place it in TX buffer |B0 movf FSRTMP,W ; |B0 movwf FSR ; restore FSR |B0 movf RXCHAR,W ; get saved TX_WPTR+1 value |B0 movwf TX_WPTR ; update TX_WPTR |B0 movf TXCHAR,W ; restore W entry data |B0 return ; |B0 ; ; Get232 - exit with received character in W & RXCHAR var ; - performs RXBUFF 'unload buffer' operation ; Get232 movf RX_RPTR,W ; |B0 xorwf RX_WPTR,W ; RPTR = WPTR (buff empty)? |B0 bz Get232 ; yes, loop, wait for character |B0 movf FSR,W ; |B0 movwf FSRTMP ; save FSR |B0 movf RX_RPTR,W ; |B0 movwf FSR ; setup indirect address |B0 movf INDF,W ; get RXBUFF[RPTR] character |B0 movwf RXCHAR ; save it for later |B0 movf FSRTMP,W ; |B0 movwf FSR ; restore FSR |B0 incf RX_RPTR,W ; W = RX_RPTR+1 |B0 andlw RXBUFF+h'0F' ; keep it in range of A0..AF |B0 movwf RX_RPTR ; update RX_RPTR |B0 movf RXCHAR,W ; get receive character |B0 return ; |B0 ;****************************************************************** ;* * ;* Companion Init232 subroutine * ;* * ;****************************************************************** ; ; initialize RS-232 variables before turning on interrupts ; Init232 clrf RX_SM ; clr RX state machine var |B0 clrf TX_SM ; clr TX state machine var |B0 movlw RXBUFF ; RX circular buffer address |B0 movwf RX_RPTR ; set RX buffer Rd pointer |B0 movwf RX_WPTR ; set RX buffer Wr pointer |B0 movlw TXBUFF ; TX circular buffer address |B0 movwf TX_RPTR ; set TX buffer Rd pointer |B0 movwf TX_WPTR ; set TX buffer Wr pointer |B0 ; ; put TXPIN latch in the stop condition and setup TRIS data ; direction for TXPIN output and RXPIN input (select bank 1 ; to access the TRIS register instead of the PORT register) ; bsf TXPIN ; put TXPIN latch in stop state |B0 bsf STATUS,RP0 ; select Bank 1 for TRIS access |B1 bcf TXPIN ; set TXPIN as output |B1 bsf RXPIN ; set RXPIN as input |B1 ; ; configure TIMER2 for 34.5-usec interrupts (8-MHz clock) ; ; note: INTCON is 00000000 after any reset ; T2CON is 00000000 after any reset (pre=1, post=1) ; PIE1 is 000-0000 after any reset ; PIR1 is 000-0000 after any reset ; bsf PIE1,TMR2IE ; enable TMR2 interrupts |B1 movlw d'69'-1 ; number of 500-nsec ticks |B1 movwf PR2 ; 34.5-usec interrupts |B1 bcf STATUS,RP0 ; select Bank 0 |B0 bsf INTCON,GIE ; enable global interrupts |B0 bsf INTCON,PEIE ; enable peripheral interrupts |B0 bsf T2CON,TMR2ON ; start TMR2 |B0 return ; |B0 ;****************************************************************** ; ; Print String - enter with string address in PTRL & PTRH ; PutString call GetTable ; get a table character |B0 andlw b'11111111' ; |B0 btfsc STATUS,Z ; last character? |B0 return ; yes, return |B0 call Put232 ; output char |B0 incfsz PTRL,F ; increment pointer |B0 goto PutString ; |B0 incf PTRH,F ; |B0 goto PutString ; |B0 ; GetTable movf PTRH,W ; |B0 movwf PCLATH ; |B0 movf PTRL,W ; |B0 movwf PCL ; |B0 ;****************************************************************** ;* * ;* * ;* * ;* * ;* * ;****************************************************************** MAIN clrf CCP1CON ; capture/compare module off |B0 bsf STATUS,RP0 ; bank 1 |B1 movlw h'06' ; |B1 movwf ADCON1 ; turn ADC module off |B1 movlw b'11111111' ; |B1 movwf TRISA ; set Port A all inputs |B1 clrf TRISB ; set Port B all outputs |B1 ; ; setup INTOSC for 8-MHz and wait for oscillator to stabilize ; movlw b'01110000' ; |B1 movwf OSCCON ; 8-mhz INTOSC system clock |B1 STABLE btfss OSCCON,IOFS ; oscillator stable? |B1 goto STABLE ; no, branch |B1 bcf STATUS,RP0 ; bank 0 |B0 ; ; Initialize RS-232 Serial I/O ; call Init232 ; Initialize RS-232 Serial I/O |B0 ; ; _Title macro - home cursor, clear screen, and print a string ; _Title "K8LH 16F819 Full Duplex Serial I/O Demo\r\n\n" ; ; Echo characters coming from Hyperterminal... ; TEST call Get232 ; receive character |B0 call Put232 ; echo character |B0 goto TEST ; loop forever |B0 ; ;****************************************************************** end
file: /Techref/microchip/16F819-rs232-9600-mm.htm, 27KB, , updated: 2005/6/13 18:39, local time: 2024/12/21 19:22,
18.226.28.97:LOG IN
|
©2024 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://techref.massmind.org/techref/microchip/16F819-rs232-9600-mm.htm"> PIC 16F819 9600 RS232</A> |
Did you find what you needed? |
Welcome to massmind.org! |
Welcome to techref.massmind.org! |
.