SUBTITLE "RS-232 Definitions"
;
;------------------------------------------------------------
; File Name : RS.inc
;------------------------------------------------------------
; Copyright: Eugene M. Hutorny © 2003, eugene@ksf.kiev.ua
; Revision: V0.01
; Date: Feb 25, 2003d
; Assembler: MPASM version 3.20.07
;------------------------------------------------------------
;
;------------------------------------------------------------
; This file contains macro that expands to RS procedures
; Usage:
; 1. Define CLOCK, CTS', RTS'
; 2. include into your ASM file
; 3. Define RAM locations RS_DATA, RS_ALIGN, RS_COUNT
; 4. Place RS_body macro in your code
; 5. Place RS_delays in your code
; Requires definitions of:
; CLOCK, CTS', RTS'
; Requires visibility of RAM locations:
; RS_DATA, RS_ALIGN", RS_COUNT"
; Note' Only if CTS/RTS control turned on by RS_OPTION_CTSRTS
; Note" For low speed only (bit longer 10 cycles)
;------------------------------------------------------------
; RS_232 options
#define RTS_SPACE_LEVEL 0
#define CTS_SPACE_LEVEL 0
; Hardware flow control is implemented in software on PC
; Doc on 16550 UART (PC16550D.pdf by NS) says:
; CTS, Clear to Send, Pin 36: When low, this indicates that
; the MODEM or data set is ready to exchange data. The CTS
; signal is a MODEM status input whose conditions can be
; tested by the CPU reading bit 4 (CTS) of the MODEM Status
; Register. Bit 4 is the complement of the CTS signal. Bit 0
; (DCTS) of the MODEM Status Register indicates whether
; the CTS input has changed state since the previous reading
; of the MODEM Status Register.
; CTS has no effect on the Transmitter.
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
;------------------------------------------------------------
;
; This macro calculates RS constants for transmitter or reciver
; at specified speed and number of bits
; requires CLOCK to be defined (in hertz)
RS_calcCONST macro speed, bits, rcv
local cycles, align, half
local i, c
cycles = CLOCK / (speed / .25) ; ((CLOCK / 4) / speed ) * 100
half = CLOCK / (.2 * speed / .25)
RS_CYCLESPERBIT set cycles / .100
RS_HALFCYCLE set half / .100
i = 0
c = RS_HALFCYCLE * rcv
align = 0
while( i <= bits ) ; bit alignment for all bits + stop bit
c = c + RS_CYCLESPERBIT
i++
if( (i * cycles + (half - .25) * rcv) - (c * .100) >= .50 )
align = align | (1 << (i-1) )
c++
endif
endw
RS_BITALIGNMENT set align
endm
;------------------------------------------------------------
; This macro generates code for the specified delay (in cycles)
; valid values are 0-1028, gurantied precision - 1 cycle
; delays 0-3 are generated inline, 4-8 - call RS_DELAY(N)IC
; 9 and higher - call RS_DELAY(0-3)WC, WREG is loaded with delay counter
; requires instatiation of RS_delays
RS_delay macro cycles
local c
if( cycles < 0 )
error "Negative delay requested"
else
if( cycles == 0 )
else
if( cycles == .1 )
nop
else
if( cycles == .2)
nope
else
if( cycles == .3)
nope
nop
else
if( cycles <= .8)
call RS_DELAY#v(cycles)IC
RS_DELAYNEEDED set RS_DELAYNEEDED | (1 << cycles)
else
c = (cycles / .4) - .1
if( c > .255 )
error "Too long delay requested"
endif
movlw #v(c)
c = cycles & .3
RS_DELAYNEEDED set RS_DELAYNEEDED | (0x8000 >> c)
call RS_DELAY#v(c)WC
endif
endif
endif
endif
endif
endif
endm
;------------------------------------------------------------
; This macro instantiates delay routines, used in code,
; generated by RS_delay
RS_delays macro
if( RS_DELAYNEEDED & 0xF000 )
if (RS_DELAYNEEDED & 0x1000 )
RS_DELAY3WC ; 0x1000
nop
endif
if (RS_DELAYNEEDED & 0x3000 )
RS_DELAY2WC ; 0x2000
nop
endif
if (RS_DELAYNEEDED & 0x7000 )
RS_DELAY1WC ; 0x4000
nop
endif
RS_DELAY0WC ; 0x8000
addlw -.1 ; 2
skpz ; 3
goto RS_DELAY0WC ; 4
return ;
endif
local i, d
i = .8
d = 0
while( i >= .4 )
if( RS_DELAYNEEDED & (1 << i))
RS_DELAY#v(i)IC
d = .1
endif
d = d && (i > .4 )
i--
if( d )
if( RS_DELAYNEEDED & (1 << i))
nop
else
i--
if( i >= .4)
nope
else
nop
endif
endif
endif
endw
if( RS_DELAYNEEDED & 0x1FF )
return
endif
endm
; This procedure uses port driving method suggested by Regulus Berdin
; http://www.piclist.com/techref/microchip/rs232at500kbps.htm
; this macro generates code for short bit length: 2 - 9 cycles
; Code length:
; maximum = 8 + 4*bits; (RS_CYCLESPERBIT = 5)
; minimum = 5 + 2*bits; (RS_CYCLESPERBIT = 2)
; average = 6 + 3*bits
RS_bodySEND2_9 macro speed, bits, port, pin, spacelvl
TX_SPACE_LEVEL equ spacelvl
local i, d
rrf RS_DATA, W ; prepare data for output:
xorwf RS_DATA, F ; '1' in RS_DATA means toggle port
movlw (1 << pin) ; load port-toggling mask
if( spacelvl )
bsf port, pin ; 0: sending start bit
else
bcf port, pin ; 0: sending start bit
endif
if ( RS_BITALIGNMENT & 1 )
RS_delay (RS_CYCLESPERBIT - .1)
else
RS_delay (RS_CYCLESPERBIT - .2)
endif
i = 0
while( i < bits )
if( i == 0 )
skpnc ; 1: first bit is already in C
else
btfsc RS_DATA,#v(i-.1); 1: test data
endif
xorwf port, F ; 2: send bit
i++
if( i == bits )
d = .1 ; last bit delay
else
d = .0
endif
if ( RS_BITALIGNMENT & (1 << i) )
RS_delay (RS_CYCLESPERBIT - .1 + #v(d))
else
RS_delay (RS_CYCLESPERBIT - .2 + #v(d))
endif
endw
endm
; this macro generates code for long bit length: 10 and more cycles
RS_bodySEND10_H macro speed, bits, port, pin, spacelvl
local looplen, startlen
clrc ; C will be rolled in
if( spacelvl )
bsf port, pin ; 0: sending start bit
else
bcf port, pin ; 0: sending start bit
endif
rlf RS_DATA, W ; 1: prepare data for output:
xorwf RS_DATA, F ; 2: '1' in RS_DATA means toggle port
movlw bits ; 3: load bit count
movwf RS_COUNT ; 4: into counter
if ( (RS_BITALIGNMENT >> .1) == 0 || RS_CYCLESPERBIT > .450 )
looplen = .7 ; alignment not required, loop is 7 cycles long
startlen = .8
else
movlw RS_BITALIGNMENT >> .1 ; 5: load alignment bits, not including start bit
movwf RS_ALIGN ; 6: load to alignment rotation file
looplen = .10 ; alignment required, loop is 10 cycles long
startlen = .10
endif
RS_delay (RS_CYCLESPERBIT - startlen + (RS_BITALIGNMENT & .1)) ; 7: the start bit aligned here
RS_#v(speed)BIT
movlw (1 << pin) ; 7: load port-toggling mask
rrf RS_DATA, F ; 8: put data bit into C
skpnc ; 9: test data bit
xorwf port, F ; 10: send bit
if ( looplen == .10 )
rrf RS_ALIGN, F ; 1: rotate align bits
skpnc ; 2: align if indicated
nope ; 3: this instruction alings the cycle
endif
RS_delay (RS_CYCLESPERBIT - looplen); 4/5: loop is 10 (7) cycle long
decfsz RS_COUNT,F ; 4/5:
goto RS_#v(speed)BIT ; 5/6:
RS_delay .4 ; 6
endm
RS_bodyRECEIVE2_9 macro speed, bits, port, pin, spacelvl, file, expire, delay
local i, c
i = 0
c = RS_CYCLESPERBIT + RS_HALFCYCLE + (RS_BITALIGNMENT & 1) - expire
if( c >= 0 )
RS_delay c
endif
while( i < bits )
if( c >= 0 )
if( spacelvl )
btfss port, pin ; 1: test data on port, pin
else
btfsc port, pin ; 1: test data on port, pin
endif
bsf file, #v(i) ; 2: set data bit
i++
if( i < bits ) ; no delay for the last bit
if ( RS_BITALIGNMENT & (1 << i) )
RS_delay (RS_CYCLESPERBIT - .1)
else
RS_delay (RS_CYCLESPERBIT - .2)
endif
endif
else
messg Too many cycles expired (#v(expire)), can not read bit #v(i) in RS_RECEIVE#v(speed)
i++
c = c + RS_CYCLESPERBIT
if ( RS_BITALIGNMENT & (1 << i) )
c++
endif
if( c >= 0 )
RS_delay c
endif
endif
endw ; exits one cycle after last bit middle
RS_delay RS_HALFCYCLE + delay - .1
endm
RS_bodyRECEIVE12_H macro speed, bits, port, pin, spacelvl, file, expire, delay
local i, c, looplen, startlen
if ( (RS_BITALIGNMENT >> .1) == 0 || RS_CYCLESPERBIT > .450 )
looplen = .9
startlen = .4
else
looplen = .12
startlen = .6
endif
i = 0
c = RS_CYCLESPERBIT + RS_HALFCYCLE + (RS_BITALIGNMENT & 1) - expire - startlen
if( c >= 0 )
RS_delay c ; expiring start bit and half of first bit
c = .0
movlw bits ; 1: load bit count
movwf RS_COUNT ; 2: into counter
else
c = c + .2
messg Too many cycles expired (#v(expire)) for RECIEVE#v(speed). "RS_COUNT" not loaded
endif
if ( looplen == .10 )
if( c >= .0 )
RS_delay c
c = .0
movlw RS_BITALIGNMENT >> 1 ; 3: load alignment bits, not including start bit
movwf RS_ALIGN ; 4: into alignment rotation file
else
messg Too many cycles expired (#v(expire)) for RS_RECIEVE#v(speed). "RS_ALIGN" not loaded
c = c + .2
endif
endif
if( c < 0 )
error Too many cycles expired (#v(expire)) for RS_RECIEVE#v(speed).
else
RS_delay c
endif
RS_#v(speed)_BIT ; data reading loop
clrc ; 1:
if( spacelvl )
btfss port, pin ; 2: test data
else
btfsc port, pin ; 2: test data
endif
setc ; 3: set data bit in C
rrf file, F ; 4: roll-in the databit from C into RS_DATA
decf RS_COUNT,F ; 5:
skpnz ; 6:
goto RS_#v(speed)_STOP ;7
if( looplen == .10 )
rrf RS_ALIGN, F ; 8: rotate align bits
skpnc ; 9: align if indicated
nope ; 10: this instruction alings the cycle
endif
RS_delay (RS_CYCLESPERBIT - looplen); loop is 12 cycles long
goto RS_#v(speed)_BIT; 11:
RS_#v(speed)_STOP ; exits 7 cycles after last bit middle
c=.7
if(bits < .8) ; if there were less than 8 bits,
i=.8
clrc
while(i>bits)
rrf file, F ; fill most significant bits with zeros
i--
c++
endw
endif
RS_delay RS_HALFCYCLE + delay - c
endm
RS_DELAYNEEDED set 0
; RS_bodyRECEIVE generates RS_RECEIVE routine body
; speed - baud rate (57600, 115200, etc.)
; bits - data bits (5..8)
; port - input port (PORTA, PORTB, etc)
; pin - RX pin on the port (0..7)
; spacelvl - Port level (0..1) driven by SPACE (+V) on the interface line
; file - file register where data is placed
; expire - number of IC expired since start bit rasing edge
; delay - number of IC to expire after stop bit is estimated (negative allowed)
RS_bodyRECEIVE macro speed, bits, port, pin, spacelvl, file, expire, delay
RS_calcCONST speed, bits, 1
if(RS_CYCLESPERBIT + RS_HALFCYCLE < .4)
error Baud rate #v(speed) is to high for frequency #v(CLOCK)
endif
if( (RS_CYCLESPERBIT < .12) || (RS_CYCLESPERBIT < .24 && RS_CYCLESPERBIT + RS_HALFCYCLE - expire < .6) )
RS_bodyRECEIVE2_9 speed, bits, port, pin, spacelvl, file, expire, delay
else
RS_bodyRECEIVE12_H speed, bits, port, pin, spacelvl, file, expire, delay
endif
endm
; RS_bodySEND generates RS_SEND routine
; speed - baud rate (57600, 115200, etc.)
; bits - data bits (5..8)
; port - output port (PORTA, PORTB, etc)
; pin - TX pin on the port (0..7)
; spacelvl - Port level (0..1) that drives SPACE (+V) on the interface line
RS_bodySEND macro speed, bits, port, pin, spacelvl
RS_calcCONST speed, bits, 0
movwf RS_DATA ; put data to rotation buffer
if(RS_CYCLESPERBIT < .3)
error Baud rate #v(speed) is to high for frequency #v(CLOCK)
endif
if(RS_CYCLESPERBIT < .10)
RS_bodySEND2_9 speed, bits, port, pin, spacelvl
else
RS_bodySEND10_H speed, bits, port, pin, spacelvl
endif
if( spacelvl )
bcf port, pin ; 2: sending stop bit
else
bsf port, pin ; 2: sending stop bit
endif
endm
;-------------------------------------------------------------
; RS RTS/CTS physical primitives
; CTS, RTS must be defined externally
; CTS_SPACE_LEVEL (0..1) defines logicla SPACE level for CTS
; RTS_SPACE_LEVEL (0..1) defines logicla SPACE level for RTS
#ifdef CTS_SPACE_LEVEL
CTS_setMARK macro
if( CTS_SPACE_LEVEL )
bcf CTS
else
bsf CTS
endif
endm
CTS_setSPACE macro
if( CTS_SPACE_LEVEL )
bsf CTS
else
bcf CTS
endif
endm
#else ; empty macro in CTS is not used
CTS_setMARK macro
endm
CTS_setSPACE macro
endm
#endif
#ifdef RTS_SPACE_LEVEL
RTS_skpMARK macro
if( RTS_SPACE_LEVEL )
btfsc RTS ; skip if RTS is high
else
btfss RTS ; skip if RTS is high
endif
endm
RTS_skpSPACE macro
if( RTS_SPACE_LEVEL )
btfss RTS ; skip if RTS is low
else
btfsc RTS ; skip if RTS is low
endif
endm
#endif