用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 <p16F83.inc> ;
- __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 $+3
- bcf TX_PIN ;0 out
- goto $+2
- bsf TX_PIN ;1 out
- movlw .3
- movwf TX_SLICE ;wait 3 time-slices
- decfsz TX_bcnt,f
- goto txEnd ;8 bit serial not end
- movlw .2
- movwf TX_STA ;set transmit STATE-MACHINE
- goto txEnd
- txStop ;TX_STA=2, send STOP bit here
- bsf TX_PIN ;send STOP bit
- movlw .3
- movwf TX_SLICE ;set bit timing
- movlw .3
- movwf TX_STA ;set transmit STATE-MACHINE
- goto txEnd
- txIdle ;TX_STA=3, reset transmission to IDLE
- bcf TXBUSY ;not busy
- bcf TXEN ;not in transmission
- clrf TX_STA ;reset transmit STATE-MACHINE
- goto txEnd
- ;====== End of TX =========
- txEnd
- ;add more TMR0 related code here
- tmr0IntEnd
- movlw TMR0CONST
- addwf TMR0,f
- goto int_end
- int_end
- banksel pclath_temp
- movf pclath_temp,w ; retieve copy of PCLATH register
- movwf PCLATH
- movf status_temp,w ; retrieve copy of STATUS register
- movwf STATUS ; restore pre-isr STATUS register contents
- swapf w_temp,f
- swapf w_temp,w ; restore pre-isr W register contents
- retfie ; return from interrupt
- ;=======================================
- ;Code wriiten for test purpose
- MAIN
- banksel TRISA ;select respective bank
- movlw b'00000100' ;RA2-input, RA3-output
- movwf TRISA
- clrf TRISB
- movlw b'10001000' ;TMR0 in timer mode
- movwf OPTION_REG
- clrf STATUS ;make sure in bank 0
- call USART_INIT
- movlw TMR0CONST
- movwf TMR0
- movlw 0xff
- movwf PORTB
- bsf INTCON,T0IE
- bsf INTCON,GIE
- LOOP ;test code
- skp1 RXBF ;wait for data received
- goto $-1
- bcf RXBF ;clear data flag
- movf RX_BUFF,w
- movwf TX_BUFF ;send back received data
- bsf TXEN
- skp0 TXEN ;wait for transmit completion
- goto $-1
- goto LOOP ;
- ;=======================================
- ;Initializtion of software USART
- USART_INIT
- clrf USART_F ;clear all flag bit
- clrf RX_STA ;reset STATE MACHINE
- clrf TX_STA
- bsf TX_PIN ;TX is in Idle
- return