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