用TMR0实现定时查询。任何带中断的PIC上都可以实现。可用此法扩展多个串口。
;|--------------------------------------------------------------|
;| Implement duplex USART base on normal I/O pin |
;| Using TIMER0 interrupt for bit timing |
;| Tested on PIC16F83 running at 4MHz |
;| Written by Paul Zhang, Microchip Tech Inc |
;| 6 Aug, 2000 |
;| All rights reserved |
;|--------------------------------------------------------------
errorlevel -302 ;no bank warning
errorlevel -301 ;no default file warning
list p=16F83 ;define processor
#include
;
__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC
;code protect = OFF
;watchdog = OFF
;power-up delay timer = ON
;oscillator mode = XT
;===============================
;define RAM variables
cblock 0x0c ;GPR start from 0x0c
w_temp ;W context saving during interrupt
status_temp ;STATUS context saving during interrupt
pclath_temp ;PCLATH context saving during interrupt
USART_F ;containing flags for USART
RX_BUFF ;USART received data buffer
TX_BUFF ;USART transmitting data buffer
RX_SLICE ;RX bit-timing control
TX_SLICE ;TX bit-timing control
RX_bcnt ;RX received bit counting
TX_bcnt ;TX transmitting bit counting
RX_STA ;RX STATE-MACHINE controller
TX_STA ;TX STATE-MACHINE controller
endc
;===============================
;pre-definition for readability
#define RX_PIN PORTA,2 ;assign RX pin
#define TX_PIN PORTA,3 ;assign TX pin
#define TXEN USART_F,0 ;USART transmit enable
#define TXBUSY USART_F,1 ;USRAT transmit is in progress
#define RXBF USART_F,2 ;USART receive buff full
#define RXBUSY USART_F,3 ;USART receive is in progress
#define RX_ERR USART_F,4 ;USART receive error
#define TX_ERR USART_F,5 ;USART transmit error
;===============================
;define constant
#define OSC_FREQ .4000 ;oscillator frequency in KHz
#define BAUDRATE .2400
#define TMR0CONST .118 ;256-OSC_FREQ*1000/4/(BAUDRATE*3) + 2
;===============================
;for my personal style
#define skp0 btfsc
#define skp1 btfss
;**********************************************************************
ORG 0x000
clrwdt
goto MAIN ; go to beginning of program
;=======================================
;Interrupt service routine
ORG 0x004 ; interrupt vector location
movwf w_temp ; save off current W register contents
movf STATUS,w ; move status register into W register
banksel status_temp
movwf status_temp ; save off contents of STATUS register
movf PCLATH,w
movwf pclath_temp ; save off contents of PCLATH
banksel INTCON ;select bank
skp0 INTCON,T0IF ;test for TMR0 interrupt
goto tmr0IntStart ;do TMR0 ISR
;here test for any other interrupt source
goto int_end
tmr0IntStart ;TIMER0 interrupt service
bcf INTCON,T0IF ;clear T0IF
;====== start of RX =======
movlw high($)
movwf PCLATH ;set PCLATH before PCL change
movf RX_STA,w ;get the state value for RX
andlw 0x03 ;for safeguard purpose
addwf PCL,f ;switch to STATE
goto rxStartChk ;check for START bit
goto rxReceiveBit ;receive DATA bit
goto rxIdle ;wait for idle
goto rxEnd ;do nothing
rxStartChk ;check for START bit
skp0 RX_PIN ;test RX pin for START bit
goto rxEnd ;not found
;start bit found. do following
movlw .8
movwf RX_bcnt ;count for 8 bits incoming data
movlw .4
movwf RX_SLICE ;wait 4 time-slice for 1st data bit
movlw .1
movwf RX_STA ;switch to STATE 1 for 1st data bit sampling
goto rxEnd
rxReceiveBit ;receive DATA bit
decfsz RX_SLICE,f ;wait of bit timing
goto rxEnd
;time to sample incoming data bit
rrf RX_BUFF,f ;right shift for new bit space
bcf RX_BUFF,7 ;pre-set to 0
skp0 RX_PIN ;incoming data bit test
bsf RX_BUFF,7 ;set if data bit = 1
movlw .3 ;3 slice for data bit timing
movwf RX_SLICE ;bit timing for next data bit
decfsz RX_bcnt,f ;see if 8-bit completed
goto rxEnd
;bit receive completed, do follwoing
movlw .2
movwf RX_STA ;set to STATE 2 for idle waiting
bsf RXBF ;set receive buffer full
movf RX_BUFF,w ;display data on PORTB
movwf PORTB
goto rxEnd
rxIdle ;wait for idle
skp0 RX_PIN ;try to find STOP bit
clrf RX_STA ;back to STATE 0 for next byte
goto rxEnd
;====== End of RX =========
rxEnd
;====== start of TX =======
;do TX, if transmit is engaged
skp1 TXEN ;skip if TXEN set, do TX
goto tmr0IntEnd ;not in transmit mode
movf TX_SLICE,f ;see if in bit-timing delay
skpnz ;
goto txDo ;bit-timing completed
decfsz TX_SLICE,f ;keep bit-timing delay
goto txEnd
txDo
;Transmit STATE-MACHINE control
movlw high($)
movwf PCLATH ;set PCLATH before PCL change
movf TX_STA,w ;get current state
andlw 0x03 ;make sure in range
addwf PCL,f ;switch to TX STATE
goto txStartBit ;send START bit
goto txDatBit ;send DATA bit
goto txStop ;send STOP bit
goto txIdle ;set transtim IDLE
txStartBit ;TX_STA=0, send START bit here
bsf TXBUSY ;set TX busy flag
movlw .8
movwf TX_bcnt ;count for 8 bit transmitting
bcf TX_PIN ;start bit
movlw .3
movwf TX_SLICE ;set bit timing
movlw .1
movwf TX_STA ;set transmit STATE-MACHINE
goto txEnd
txDatBit ;TX_STA=1, send DATA bit here
;time for next bit sending
rrf TX_BUFF,f ;rotate bit to C
skpnc ;test C
goto