; ; SERIAL COMMUNICATIONS INTERFACE USING TIMER INTERFACE MODULE Version 0.1 ; ; (C)1999 by Mario Becroft ; ; This is a serial communications interface implemented with software and the Timer Interface ; Module (TIM) of the 68HC08. ; Unlike a purely software implementation, this code is interrupt-driven and can operate in ; the background. (Of course it uses some processor time and has other limitations compared ; to a real SCI.) ; ; The transmitter interrupt executes in a maximum of ~83 cycles, and occurs once per bit. ; With the high speed mode enabled it is a maximum of ~75 cycles. ; Therefore with a 8 MHz bus clock, the maximum theoretical transmit data rate would be ; 96KBPS or 106KBPS (with high speed mode). ; ; The receiver interrupt executes in a maximum of ~96 cycles and occurs once per bit. ; With the high speed mode enabled it is a maximum of ~90 cycles. ; Therefore with a 8 MHz bus clock, the maximum theoretical receive data rate woul be ; 83KBPS or 88KBPS (with high speed mode). ; ; During full-duplex operation the above figures would be lower. ; Interrupt response latency must be considered. The most critical section is the receiver, ; if the interrupt occurs more than half a bit-time minus receiver interrupt execution time ; late, bits will be mis-read. Otherwise the interrupt latency must never be more than 1 bit ; time minus the total transmit or receive interrupt execution time. Full-duplex operation ; is more complicated. ; ; To use this code follow these steps: ; 1. Decide whether half- or full-duplex operation is required. For full-duplex operation ; the transmitter and receiver need separate timer channels; for half-duplex operation the ; transmitter and receiver can share one timer channel. ; 2. Change the Links to TIM registers to reference the timer channel(s) to be used. ; 3. Set the interrupt vectors for said timer channels to Txrx_timer_isr. ; 4. Ensure that the variables below are placed into RAM. ; 5. Call Txrx_init during program initialisation. ; 6. Set the parameters below for baud rate etc. ; ; Then use Tx_byte and Rx_byte to send and receive data. ; In half-duplex mode Rx_enable must be used to re-enable the receiver after transmitting. ; DO NOT use Rx_enable WHILE transmission is in progress (TX_BUSY is set)! ; The TX_BUSY flag in the tx_ctl variable indicates that the transmitter is running. ; TX_NEWBYTE in tx_ctl indicates that the transmit buffer contains untransmitted data. ; RX_NEWBYTE in rx_ctl indicates that new data has been received. ; RX_EFRAME in rx_ctl indicates a framing error occured on received data. ; ; Description of receiver operation: ; RX_* timer channel does IC to get the falling edge of the start bit. ; RX_2* timer channel is then used to do an OC timed to occur in the middle of each bit, ; at which time the bit is sampled. ; If both timer channels are the same then the OC will output a logic high level during ; reception so current limiting on the input should be used. $INCLUDE 'gp32regs.inc' ; SERIAL COMMUNICATIONS INTERFACE VARIABLES TO BE LOCATED IN RAM ; org RAM tx_ctl ds 1 ; Transmitter control register tx_data ds 1 ; Transmitter data buffer tx_shift ds 1 ; Transmitter shift register rx_ctl ds 1 ; Receiver control register rx_data ds 1 ; Receive data buffer rx_shift ds 1 ; Receiver shift register ; USER-CONFIGURABLE PARAMETERS $SETNOT tx_small_bit_time ; Set if tx bit time is less than 128 for fast code $SETNOT rx_small_bit_time ; Set if rx bit time is less than 128 for fast code $SETNOT rx_tiny_bit_time ; Set if rx 1.5 bits times is < 128 for fast code $SETNOT txrx_small_bit_time ; MUST BE SET if any of above 3 are set (saves H reg) $SETNOT tx_inverted ; Set for inverted transmitter output $SETNOT rx_inverted ; Set for inverted receiver input ; Baud rate timing information specified as a number of timer ticks. ; 9600 BPS with timer freq = 4.9152 MHz = 512 TX_BTIMEH equ $02 TX_BTIMEL equ $00 RX_BTIMEH equ $02 RX_BTIMEL equ $00 RX_BTIMESH equ $03 ; Bit time * 1.5 RX_BTIMESL equ $00 ; Links to TIM registers TX_TCNTH equ T1CNTH ; Timer counter register high to use for TX TX_TCNTL equ T1CNTL ; Timer counter register low to use for TX TX_TSC equ T1SC0 ; Channel timer status and control register to use for TX TX_TCHH equ T1CH0H ; Channel register high to use for TX TX_TCHL equ T1CH0L ; Channel register low to use for TX TX_PORT equ PORTD ; Port on which transmitter pin is situated TX_PIN equ 4 ; Which pin is the transmitter RX_TCNTH equ T1CNTH ; Timer counter register high for RX IC RX_TCNTL equ T1CNTL ; Timer counter register low for RX IC RX_TSC equ T1SC0 ; Channel timer status and control register for RX IC RX_TCHH equ T1CH0H ; Channel register high for RX IC RX_TCHL equ T1CH0L ; Channel register low for RX IC RX_2TSC equ T1SC0 ; Channel timer status and control register for RX OC RX_2TCHH equ T1CH0H ; Channel register high for RX OC RX_2TCHL equ T1CH0L ; Channel register low for RX OC RX_PORT equ PORTD ; Port on which transmitter pin is situated RX_PIN equ 4 ; Which pin is the receiver ; The following macros must be manually commented or uncommented ; for positive or negative transmission. This is because the P&E assembler apparently ; does not support conditional assembly of macros. ; USE FOR POSITIVE TRANSMISSION $MACRO Tx_set bset TX_PIN,TX_PORT bset TX_PIN,TX_PORT+4 ; Make pin an output clr TX_TSC $MACROEND $MACRO Tx_clear bclr TX_PIN,TX_PORT bset TX_PIN,TX_PORT+4 ; Make pin an output mov #MSA.,TX_TSC $MACROEND ; USE FOR NEGATIVE TRANSMISSION ;$MACRO Tx_clear ; bset TX_PORT,TX_PIN ; bset TX_PIN,TX_PORT+4 ; Make pin an output ; clr TX_TSC ;$MACROEND ;$MACRO Tx_set ; bclr TX_PIN,TX_PORT ; bset TX_PIN,TX_PORT+4 ; Make pin an output ; clr TX_TSC ;$MACROEND ; USE FOR POSITIVE RECEPTION $MACRO Rx_oc_set clr RX_TSC ; Stop IC clr RX_2TSC ; Stop OC and set default output high mov #RX_OC_SET_VAL,RX_2TSC ; Enable OC to set output $MACROEND ; USE FOR NEGATIVE RECEPTION ;$MACRO Rx_oc_set ; clr RX_TSC ; Stop IC ; mov #MSA.,RX_2TSC ; Stop OC and set default output low ; mov #RX_OC_SET_VAL,RX_2TSC ; Enable OC to clear output ;$MACROEND $IF tx_inverted TX_CLEAR_VAL equ {CHIE.+MSA.+ELSB.+ELSA.} TX_SET_VAL equ {CHIE.+MSA.+ELSB.} $ELSEIF TX_CLEAR_VAL equ {CHIE.+MSA.+ELSB.} TX_SET_VAL equ {CHIE.+MSA.+ELSB.+ELSA.} $ENDIF $MACRO Tx_oc_clear mov #TX_CLEAR_VAL,TX_TSC $MACROEND $MACRO Tx_oc_set mov #TX_SET_VAL,TX_TSC $MACROEND $MACRO Tx_nextbit brset 0,tx_shift,bit_is_1 ; Is next bit 0 or 1? mov #TX_CLEAR_VAL,TX_TSC ; Clear output on compare bra bit_done bit_is_1 mov #TX_SET_VAL,TX_TSC ; Set output on compare bit_done lsr tx_shift $MACROEND $IF rx_inverted RX_OC_SET_VAL equ {CHIE.+MSA.+ELSB.} RX_FALLING_VAL equ {CHIE.+ELSA.} $ELSEIF RX_OC_SET_VAL equ {CHIE.+MSA.+ELSB.+ELSA.} RX_FALLING_VAL equ {CHIE.+ELSB.} $ENDIF $MACRO Rx_ic_falling clr RX_2TSC ; Stop OC mov #RX_FALLING_VAL,RX_TSC ; Enable IC on rising edge $MACROEND $MACRO Rx_input bclr RX_PIN,RX_PORT+4 ; Make pin an input clr RX_TSC ; Disable timer on this pin $MACROEND ; Flags in the transmitter and receiver control bytes TX_NEWDATA equ 4 ; tx_ctl new data flag TX_BUSY equ 5 ; tx_ctl transmitter busy flag RX_NEWDATA equ 4 ; rx_ctl new data flag RX_EFRAME equ 5 ; rx_ctl new data flag ; ; CODE STARTS HERE ; ; org ROM ; Initialise the transmitter and receiver. To be called once during system initialisation. ; Modifies registers: NONE Txrx_init clr tx_ctl clr rx_ctl rts ; Reads a received byte into A and clears the new-data flag. If no new data had arrived ; then A = 0. ; Modifies registers: A Rx_byte clra brclr RX_NEWDATA,rx_ctl,rx_nodata lda rx_data bclr RX_NEWDATA,rx_ctl rx_nodata rts ; Enables the receiver so that bytes may be received. ; In a single-timer, half-duplex configuration, Tx_enable and Rx_enable are mutually exclusive. ; Modifies registers: NONE Rx_enable Rx_ic_falling rts ; Transmits the byte in A by writing it to the transmit register, setting the new-data flag ; and calling Tx_enable. NOTE: If a byte was already buffered but not transmitted, it will ; be replaced by the new one. ; Modifies registers: A, X Tx_byte sta tx_data bset TX_NEWDATA,tx_ctl bsr Tx_enable rts ; Enables the transmitter so that bytes may be transmitted. Called automatically ; by Tx_byte. Use if transmitter was disabled after Tx_byte was called but before byte ; was transmitted. ; In a single-timer, half-duplex configuration, Tx_enable and Rx_enable are mutually exclusive. ; Modifies registers: A, X, H (if small_bit_time is set) Tx_enable brclr TX_NEWDATA,tx_ctl,tx_toobusy ; Abort if no new data brset TX_BUSY,tx_ctl,tx_toobusy ; Abort if already busy bclr TX_NEWDATA,tx_ctl ; Cancel new data flag sei ; Get time + start bit must be atomic Tx_clear ; Output 0 (start bit) ; Set OC for current time + 1 bit time $IF tx_small_bit_time ; If baud rate timing is less than 128 then use faster code ldhx TX_TCNTH cli aix #TX_BTIMEL sthx TX_TCHH $ELSEIF ldx TX_TCNTH cli lda TX_TCNTL add #TX_BTIMEL sta tx_shift ; temp storage txa adc #TX_BTIMEH sta TX_TCHH lda tx_shift ; temp storage sta TX_TCHL $ENDIF mov tx_data,tx_shift ; Move data into output shift register Tx_nextbit ; Set the tx sequencer to 0 lda tx_ctl and #$F0 sta tx_ctl bset TX_BUSY,tx_ctl ; Signify transmitter busy tx_toobusy rts ; Interrupt service routine for transmitter and receiver. The interrupt vector for the ; timer channel(s) used for the SCI should point to this routine. Txrx_timer_isr $IF txrx_small_bit_time pshh $ENDIF brset TX_BUSY,tx_ctl,txrx_tx1 ; Transmitting? bra txrx_rx ; No, skip tx txrx_tx1 brset CHF,TX_TSC,txrx_tx2 ; OC occured? bra txrx_rx ; No, skip tx txrx_tx2 ; Handle a transmit OC ; Set OC for old time + 1 bit time $IF tx_small_bit_time ldhx TX_TCHH aix #TX_BTIMEL sthx TX_TCHH $ELSEIF lda TX_TCHL add #TX_BTIMEL tax lda TX_TCHH adc #TX_BTIMEH sta TX_TCHH stx TX_TCHL lda TX_TCHL ; Unlock IC - IS THIS NECESSARY (FOR HALF-DUPLEX)?? $ENDIF ; Increment transmitter sequencer and take appropriate action inc tx_ctl lda tx_ctl and #$0F cmp #8 blo tx_next_bit cbeqa #10,tx_eot cbeqa #9,tx_wait_eot ; Transmit the stop bit Tx_oc_set bra tx_oc_done tx_eot ; End of transmission Tx_set ; Make output idle bclr TX_BUSY,tx_ctl bsr Tx_enable ; Transmit the next byte bra tx_oc_done tx_wait_eot ; Wait for end of transmission Tx_oc_set bra tx_oc_done tx_next_bit Tx_nextbit tx_oc_done txrx_rx ; Handle receive input capture or output compare brset CHF,RX_TSC,txrx_rx_icoc ; IC occured? brset CHF,RX_2TSC,txrx_rx_icoc ; OC occured? txrx_done bra txrx_end txrx_rx_icoc lda RX_2TSC ; Read the RX OC channel control reg cbeqa #{RX_OC_SET_VAL+$80},txrx_rx_oc ; Check if this is a OC interrupt ; Set OC for IC time + 1.5 bit times $IF rx_tiny_bit_time ldhx RX_TCHH aix #RX_BTIMESL sthx RX_2TCHH $ELSEIF lda RX_TCHL add #RX_BTIMESL tax lda RX_TCHH adc #RX_BTIMESH sta RX_2TCHH stx RX_2TCHL lda RX_TCHL ; Unlock IC $ENDIF Rx_oc_set ; Clear receiver sequencer lda rx_ctl and #$F0 sta rx_ctl txrx_end $IF txrx_small_bit_time pulh $ENDIF rti txrx_rx_oc Rx_input ; Increment receiver sequencer and take appropriate action inc rx_ctl lda rx_ctl and #$0F cbeqa #9,rx_get_stop_bit rx_get_bit brset RX_PIN,RX_PORT,rx_sample_bit ; Get bit into carry rx_sample_bit ror rx_shift $IF rx_inverted lda rx_shift eor #$80 sta rx_shift $ENDIF ; Set OC to old time + 1 bit time $IF rx_small_bit_time ldhx RX_2TCHH aix #RX_BTIMEL sthx RX_2TCHH $ELSEIF lda RX_2TCHL add #RX_BTIMEL tax lda RX_2TCHH adc #RX_BTIMEH sta RX_2TCHH stx RX_2TCHL lda RX_2TCHL ; Unlock IC $ENDIF Rx_oc_set bra txrx_end rx_get_stop_bit $IF rx_inverted brclr RX_PIN,RX_PORT,rx_stop_ok $ELSEIF brset RX_PIN,RX_PORT,rx_stop_ok $ENDIF bset RX_EFRAME,rx_ctl rx_stop_ok bset RX_NEWDATA,rx_ctl mov rx_shift,rx_data Rx_ic_falling ; Wait for next byte bra txrx_end