;******************************************************************
; *
; Filename: 12F683 Half-Duplex 9600 Demo.asm *
; Author: Mike McLaren, K8LH *
; Date: 02-Jun-05 (last revision 02-Dec-05) *
; *
; Half Duplex Bit-Banged 9600 Baud Serial I/O Demo *
; (with 16-byte circular receive character buffer) *
; *
; ·Uses 12F683 INTOSC running at 8-MHz *
; ·Bit rate error 0.16% plus or minus 1.0% for INTOSC *
; ·Bit-banged 9600 baud serial I/O *
; ·Half Duplex (should not TX and RX simultaneously) *
; ·TMR2 interrupts at 104-usec intervals (every 208 *
; instruction cycles) and IOC (interrupt on change) *
; for RX start bit leading edge detection on RXPIN *
; ·Circular 16-byte RX character buffer *
; ·Inverted TX and RX signals (MAX232A or similar *
; inverting RS-232 interface required) *
; ·Relatively small - the ISR and the support routines *
; Init232, Put232, and Get232 use 102 words of code *
; space at locations 0x04 through 0x69 *
; ·Worst case 23% ISR 'overhead' (24-usecs) when a *
; complete RX character is added to the circular *
; buffer once every 1.04-msecs while receiving *
; *
; MPLab: 7.21 (tabs=8) *
; MPAsm: 4.02 *
; *
;******************************************************************
;******************************************************************
; *
; Filename: 12F683 Half-Duplex 9600 Demo.asm *
; Author: Mike McLaren, K8LH (k8lh_at_arrl.net) *
; Date: 02-Jun-05 (last revision 02-Dec-05) *
; *
; Half Duplex Bit-Banged 9600 Baud Serial I/O Demo *
; (with 16-byte circular receive character buffer) *
; *
; ·Uses 12F683 INTOSC running at 8-MHz *
; ·Bit rate error 0.16% plus or minus 1.0% for INTOSC *
; ·Bit-banged 9600 baud serial I/O *
; ·Half Duplex (should not TX and RX simultaneously) *
; ·TMR2 interrupts at 104-usec intervals (every 208 *
; instruction cycles) and IOC (interrupt on change) *
; for RX start bit leading edge detection on RXPIN *
; ·Circular 16-byte RX character buffer *
; ·Inverted TX and RX signals (MAX232A or similar *
; inverting RS-232 interface required) *
; ·Relatively small - the ISR and the support routines *
; Init232, Put232, and Get232 use 102 words of code *
; space at locations 0x04 through 0x69 *
; ·Worst case 23% ISR 'overhead' (24-usecs) when a *
; complete RX character is added to the circular *
; buffer once every 1.04-msecs while receiving *
; *
; MPLab: 7.21 (tabs=8) *
; MPAsm: 4.02 *
; *
;******************************************************************
#include <p12f683.inc>
errorlevel -302
__config _FCMEN_OFF & _IESO_OFF & _MCLRE_OFF & _WDT_OFF & _INTRC_OSC_NOCLKOUT
;
; file register variables
;
IPROC equ h'20' ; ISR Process Latch
TXCNT equ h'21' ; TX-232 bit count
TXVAR equ h'22' ; TX-232 data byte
RXCNT equ h'23' ; RX-232 bit count
;
RDPTR equ h'24' ; RX buffer Rd pointer
WRPTR equ h'25' ; RX buffer Wr pointer
;
PTRL equ h'26' ; PutString
PTRH equ h'27' ; PutString
TEMP equ h'28' ; PutByte
RXBUFF equ 0xA0 ; 16 byte RX buffer, A0h-AFh
;
; IPROC flag bit constants
;
RXFLG equ h'00' ; 1=rx in progress
TXFLG equ h'01' ; 1=tx in progress
flag2 equ h'02' ; spare
flag3 equ h'03' ; spare
flag4 equ h'04' ; spare
flag5 equ h'05' ; spare
flag6 equ h'06' ; spare
flag7 equ h'07' ; spare
;
; RXPIN and TXPIN constants
;
RXPIN equ h'01' ; RS-232 RX (GP1)
TXPIN equ h'00' ; RS-232 TX (GP0)
BAUDX equ .208 ; 208 for 9600 baud
;
; file locations used by ISR for saving and restoring the stack
;
W_ISR equ h'70' ; ISR 'W'
S_ISR equ h'71' ; ISR 'STATUS'
P_ISR equ h'72' ; ISR 'PCLATH'
F_ISR equ h'73' ; ISR 'FSR'
;******************************************************************
;
; _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 0x1b,"[2J" ; home cursor, clear screen
dt str,0
Print call PutString ; print string
endm
;}
;******************************************************************
;
; _Print macro - print a string to the RS-232 port
;
_Print macro str ;
local String, Print
movlw low String ;
movwf PTRL ;
movlw high String ;
movwf PTRH ;
goto Print ;
String dt str,0
Print call PutString ; print string
endm
;
; Hardware notes:
;
; <1> INTOSC 8-MHz, 500-nsec instruction cycle
; <2> GP3 (pin 4) > 'bit banged' serial input
; <3> GP0 (pin 7) > 'bit banged' serial output
; <4> RS-232 signals inverted with MAX232 or 2N7000 drivers
; <5> Using 2700 ohm pull-up resistor on GP3
; <6>
; <7>
;
;
; This program simply prints a text string to Hyperterminal
; and echos typed characters back to 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 12F683 and PC...
;
;******************************************************************
; *
; *
; *
; *
; *
;******************************************************************
org 0x0000
Start clrf STATUS ; |B0
goto Main ; |B0
;******************************************************************
; *
; Interrupt Service Routine for Half Duplex Serial I/O *
; *
; Interrupts are generated every 104-us by TMR2 for 9600 *
; baud shift operations and by IOC (interrupt on change) *
; on the RX start bit leading edge on RXPIN GP3... *
; *
; On detecting the RX start bit leading edge, GP3 IOC is *
; turned off, RXFLG is set to indicate rx-in-progress, *
; RXCNT bit count variable is set, and TMR2 is advanced *
; by 1/2 bit (52-usec)... Note - advancing TMR2 could *
; mess up a transmit-in-progress, so you shouldn't send *
; and receive at the same time... *
; *
;******************************************************************
org 0x0004
;
; save MAIN program context
;
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
movf FSR,W ; get FSR |B0
movwf F_ISR ; save FSR |B0
;
; test for GP3 (RXPIN) start bit leading edge IOC interrupt
;
;{
btfss INTCON,GPIF ; IOC "start bit" interrupt? |B0
goto ISR_TX ; no, branch |B0
movf GPIO,W ; yes, read GPIO |B0
bcf INTCON,GPIF ; clear IOC interrupt flag |B0
bsf IPROC,RXFLG ; indicate RX in progress |B0
movlw d'10' ; 10 bits (start, 8 data, stop) |B0
movwf RXCNT ; initialize RX bit counter |B0
movlw BAUDX/2 ; |B0
movwf TMR2 ; inc TMR2 by 1/2 bit (52-usec) |B0
; bsf INTCON,PEIE ; enable peripheral (tmr2) ints |B0
goto ISR_X ; turn off GP3 IOC & exit |B0
;}
; bit banged transmit
;
ISR_TX btfss IPROC,TXFLG ; TX in progress? |B0
goto ISR_RX ; no, branch |B0
movf TXCNT,W ; TX bit count initialized? |B0
bnz ISR_TX0 ; yes, branch, send bit |B0
movlw d'11' ; else, |B0
movwf TXCNT ; initialize TX bit count |B0
goto ISR_TX1 ; send the start bit |B0
ISR_TX0 rrf TXVAR,f ; shift lsb into C |B0
bsf TXVAR,7 ; set bit 7 for stop bits |B0
btfsc STATUS,C ; skip if C=0 |B0
bsf GPIO,TXPIN ; set TX pin |B0
btfss STATUS,C ; skip if C=1 |B0
ISR_TX1 bcf GPIO,TXPIN ; clear TX pin |B0
decfsz TXCNT,f ; last bit? |B0
goto ISR_RX ; no, branch |B0
bcf IPROC,TXFLG ; yes, indicate end of TX |B0
;
; bit banged receive with 16 byte circular RX buffer
;
ISR_RX btfss IPROC,RXFLG ; RX in progress? |B0
goto ISR_XIT ; no, branch |B0
movf WRPTR,W ; RX buffer Wr Pointer |B0
movwf FSR ; RXBUFF[WRPTR] (sort of) |B0
bcf STATUS,C ; assume bit=0 |B0
btfsc GPIO,RXPIN ; is it a 0? |B0
bsf STATUS,C ; no, bit=1 |B0
rrf INDF,f ; shift bit into our char |B0
decfsz RXCNT,f ; all 10 bits? |B0
goto ISR_XIT ; no, branch, else |B0
rlf INDF,f ; get rid of the stop bit |B0
bcf IPROC,RXFLG ; clear RX-in-progress flag |B0
incf WRPTR,W ; W=WRPTR+1 |B0
andlw RXBUFF+h'0F' ; keep in range of A0..AF |B0
xorwf RDPTR,W ; buffer full (WRPTR+1=RDPTR)? |B0
bz ISR_X ; yes, ignore new char, else |B0
xorwf RDPTR,W ; restore WRPTR + 1 value |B0
movwf WRPTR ; update RXBUFF WRPTR |B0
;
; toggle RXPIN (GP3) IOC on or off
;
ISR_X bsf STATUS,RP0 ; select bank 1 |B1
movlw 1<<RXPIN ; mask for RXPIN |B1
xorwf IOC,f ; toggle GP3 IOC |B1
bcf STATUS,RP0 ; select bank 0 |B0
;
; clear TMR2 interrupt flag and restore MAIN program context
;
ISR_XIT bcf PIR1,TMR2IF ; clear TMR2 irq flag first |B0
movf F_ISR,W ; |B0
movwf FSR ; restore FSR |B0
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
;
Put232 btfsc IPROC,TXFLG ; TX in progress? |B0
goto Put232 ; yes, branch and wait |B0
movwf TXVAR ; else, stuff character |B0
bsf IPROC,TXFLG ; initiate TX |B0
return ; |B0
;
; Get232 - exit with received character in W
; - RXBUFF "pull" operation
;
Get232 movf RDPTR,W ; |B0
movwf FSR ; setup for indirect access |B0
xorwf WRPTR,W ; buffer empty (RDPTR=WRPTR)? |B0
bz Get232 ; yes, loop (wait), else |B0
incf RDPTR,W ; increment RDPTR |B0
andlw RXBUFF+h'0F' ; keep it in range of A0..AF |B0
movwf RDPTR ; update RXBUFF RDPTR |B0
movf INDF,W ; pull character |B0
return ; |B0
;******************************************************************
; *
; Companion Init232 subroutine *
; *
;******************************************************************
;
; initialize RS-232 variables before turning on interrupts
;
Init232 clrf IPROC ; clear ISR process latch var |B0
clrf TXCNT ; clear TX bit count var |B0
movlw RXBUFF ; init circular buffer pointers |B0
movwf RDPTR ; set RX buffer Rd pointer |B0
movwf WRPTR ; set RX buffer Wr pointer |B0
;
; put TXPIN latch in the stop condition and setup TRISIO data
; direction for TXPIN output and RXPIN input
;
bsf GPIO,TXPIN ; put TXPIN latch in STOP state |B0
bsf STATUS,RP0 ; select Bank 1 |B1
bcf TRISIO,TXPIN ; set TXPIN as an output |B1
bsf TRISIO,RXPIN ; set RXPIN as an input |B1
;
bsf IOC,RXPIN ; RXPIN 'Interrupt On Change' |B1
;
; configure TIMER2 for 104-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 BAUDX-1 ; number of 500-nsec ticks |B1
movwf PR2 ; 104-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 INTCON,GPIE ; enable IOC interrupts |B0
bsf T2CON,TMR2ON ; start TMR2 |B0
return ; |B0
;******************************************************************
;
; PutString sends a character string through the RS232 port
;
; Entry: setup PTRL and PTRH to string address before entry
; string must be terminated with a 00 byte
;
PutString
call GetTable ; get a table character |B0
andlw b'11111111' ; |B0
skpnz ; 00 byte, last character? |B0
return ; yes, return |B0
call Put232 ; else,output character |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
;******************************************************************
;
; Print byte in W as two ASCII nybbles
;
PutByte movwf TEMP ; save byte |B0
swapf TEMP,W ; swap nybbles in W |B0
call Hex2Asc ; process left nybble |B0
movf TEMP,W ; process right nybble |B0
Hex2Asc andlw b'00001111' ; mask off left nybble |B0
addlw a'6' ; 0-9>36-3F, A-F>40-45 |B0
btfsc STATUS,DC ; A..F? no, skip, else |B0
addlw h'07' ; A-F (40-45) > 47-4C |B0
addlw 0-6 ; '0'..'9' or 'A'..'F' |B0
goto Put232 ; print ASCII nybble |B0
;******************************************************************
; *
; *
; *
; *
; *
;******************************************************************
;
; 12F683 specific peripheral initialization
;
Main movlw b'00000111' ; |B0
movwf CMCON0 ; turn off comparator |B0
bsf STATUS,RP0 ; bank 1 |B1
clrf ANSEL ; turn off A2D, digit I/O |B1
movlw b'00001000' ; |B1
movwf TRISIO ; setup TRISIO as desired |B1
;
; setup 8-MHz INTOSC
;
movlw b'01110000' ; |B1
movwf OSCCON ; 8-MHz INTOSC system clock |B1
Stable btfss OSCCON,HTS ; oscillator stable? |B1
goto Stable ; no, branch |B1
bcf STATUS,RP0 ; bank 0 |B0
;
; initialize RS-232 Serial I/O
;
call Init232 ; |B0
;
; _Title macro - home cursor, clear screen, and print a string
;
_Title "K8LH 12F683 Half Duplex 9600 Demo\r\n\n"
_Print "Test 1\r\n"
_Print "Test 2\r\n\n"
;
; Echo characters coming from Hyperterminal
;
Test call Get232 ; receive character |B0
call Put232 ; echo character |B0
goto Test ; |B0
;
end
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?