; ***************************************************************************************** ; Virtual Peripheral Universal Full Duplex UART with Buffering ; ; Length: xxx bytes (total) ; Authors: Chip Gracey, President, Parallax Inc. 10/03/97 to 07/09/98 ; Craig Webb, Consultant to Scenix Semiconductor Inc. ; Christopher Waters, Celsius Research Ltd. ; Stephen Holland, Scenix Semiconductor Inc. ; Modifications: ; 11/18/98, Craig Webb ; - documented source and cleaned up code ; 01/07/99, Christopher Waters ; - added TX and RX FIFO buffering for full-duplex ; 02/15/99, Craig Webb ; - expanded for 2 channel operation ; - minimal clock frequency at 19.2Kbps ; 03/06/99, Stephen Holland ; - modified for 50MHz and up to 115.2Kbps operation ; 03/11/99, Stephen Holland ; - added autobaud capability for both channels ; 03/19/99, Stephen Holland ; - extended autobaud capability for operation from 110 - 115.2Kbps ; 03/29/99, Stephen Holland ; - added IFDEF to allow using same code for SX28 or SX48/52 targets ; 03/30/99, Stephen Holland ; - modified second channel for fixed 9600bps operation ; 04/05/99-04/10/99, Stephen Holland ; - added 166552-like bus interface and debugged ; 04/12/99, Stephen Holland ; - debugged additional handshaking lines DTR & DSR ; 11/01/2004, Jim Brain ; - made autobaud detection conditional code ; - added high speed configurable UART2 back in ; - cleaned up code for prod SX52 ; - added conditional to inline uart vp code, (saves 7 cycles per uart) ; - moved autobaud code to subroutines, to save space. ; ;***************************************************************************************** ; Assembler directives ; ; uses: SX28AC, 2 pages of program memory, 8 banks of RAM, high speed osc. ; operating in turbo mode, with 8-level stack & extended option reg. ; ;***************************************************************************************** LIST Q=37 IRC_CAL IRC_FAST ; if you change this, change xtal as well FREQ 50000000 ;oscillator frequency ;***************************************************************************************** ; Conditional compilation defines ; ; AUTO_BAUD. Define if you want to use the auto baud rate selection mechanism. (not tested) ; INLINE_VP. Use if you want to inline the UART VP routines. Saves 7 cycles per UART ; ;***************************************************************************************** ;SX28 SX48_52 ;AUTO_BAUD INLINE_VP include "stdinc.src" ID 'DUART' ;program ID label RESET reset_entry ;set reset/boot address ;***************************************************************************************** ; Program constants ;***************************************************************************************** ; Ring buffer sizes ; Note: if RX+TX values>16 then ring buffer bank definition will need to be adjusted rx_ring_size_1 equ 16 ;size (in bytes) of the rx ring buffer tx_ring_size_1 equ 16 ;size of the tx ring buffer rx_ring_size_2 equ 16 ;size (in bytes) of the rx ring buffer tx_ring_size_2 equ 16 ;size of the tx ring buffer int_period = 217 ;cycles between interrupt passes xtal = 50000000 RTCC_prescalar = 1 ;int_period = 163 ;cycles between interrupt passes ;xtal = 75000000 ;RTCC_prescalar = 2 ;***************************************************************************************** ; Port Assignment: Bit variables ;***************************************************************************************** IF SX_TYPE = 0 ;SX28 port definitions RA_init equ %1011 ;initialize port RA RA_IO equ %0100 ;Set RA in/out directions; RB_init equ %00000000 ;initialize port RB RB_IO equ %11111111 ;Sets RB in/out directions RC_init equ %10001010 ;initialize port RC RC_IO equ %00100101 ;Sets RC in/out directions rx_pin_1 equ rc.0 ;UART channel 1 receive input tx_pin_1 equ rc.1 ;UART channel 1 transmit output RTS_1 equ rc.2 ;RTS input CTS_1 equ rc.3 ;CTS output DSR_1 equ rc.4 ;DSR output DTR_1 equ rc.5 ;DTR input RI_1 equ rc.6 ;RI output CD_1 equ rc.7 ;CD output ELSE ;SX52 port definitions RA_init equ %00000000 ;initialize port RA RA_IO equ %11111111 ;Set RA in/out directions; RB_init equ %00000000 ;initialize port RB(NS16552-like data bus interface) RB_IO equ %11111111 ;Sets RB in/out directions RC_init equ %11011010 ;initialize port RC(UART channel 1 handshaking) RC_IO equ %00100101 ;Sets RC in/out directions RD_init equ %11011010 ;initialize port RD(UART channel 2 handshaking) RD_IO equ %00100101 ;Sets RD in/out directions RE_init equ %00000000 ;initialize port RE(NS16552-like address bus interface) RE_IO equ %11111111 ;Sets RE in/out directions a0 equ re.0 ;UART address bit 0 input a1 equ re.1 ;UART address bit 1 input a2 equ re.2 ;UART address bit 2 input chsl equ re.3 ;UART channel select input cs equ re.4 ;UART chip select input read equ re.5 ;UART !read input write equ re.6 ;UART !write input data_bus equ rb ;UART 8-bit data bus ; UART (channel 1) rx_pin_1 equ rc.0 ;RXD input tx_pin_1 equ rc.1 ;TXD output RTS_1 equ rc.2 ;RTS input CTS_1 equ rc.3 ;CTS output DSR_1 equ rc.4 ;DSR output DTR_1 equ rc.5 ;DTR input RI_1 equ rc.6 ;RI output CD_1 equ rc.7 ;CD output ; UART (channel 2) rx_pin_2 equ rd.0 ;RXD input tx_pin_2 equ rd.1 ;TXD output RTS_2 equ rd.2 ;RTS input CTS_2 equ rd.3 ;CTS output DSR_2 equ rd.4 ;DSR output DTR_2 equ rd.5 ;DTR input RI_2 equ rd.6 ;RI output CD_2 equ rd.7 ;CD output ENDIF B75 equ 0 B110 equ 1 B134 equ 2 B150 equ 3 B300 equ 4 B600 equ 5 B1200 equ 6 B2400 equ 7 B4800 equ 8 B9600 equ 9 B19200 equ 10 B38400 equ 11 B57600 equ 12 B115200 equ 13 ;***************************************************************************************** ; Global Register definitions ;***************************************************************************************** org $0a ;start of program registers temp ds 1 ;temporary storage isr_temp ds 1 ;used by isr - must be global flags ds 1 uart1_en equ flags.0 uart2_en equ flags.1 rx_flag_1 equ flags.2 rx_flag_2 equ flags.3 speed_1 equ flags.4 speed_2 equ flags.5 IFDEF AUTO_BAUD lo_detected_1 equ flags.6 lo_detected_2 equ flags.7 ENDIF line_status_1 ds 1 rx_dr_1 equ line_status_1.0 ;Receiver Data Ready(DR) indicator buffer_oe_1 equ line_status_1.1 ;Buffer Overrun Error(OE) indicator parity_e_1 equ line_status_1.2 ;Parity Error(PE) indicator framing_e_1 equ line_status_1.3 ;Framing Error(FE) indicator break_int_1 equ line_status_1.4 ;Break Interrupt(BI) indicator thre_1 equ line_status_1.5 ;Transmitter Holding Register Empty(THRE) indicator temt_1 equ line_status_1.6 ;Transmitter Empty(TEMT) indicator lsr7_1 equ line_status_1.7 ;LSR7 line_status_2 ds 1 rx_dr_2 equ line_status_2.0 ;Receiver Data Ready(DR) indicator buffer_oe_2 equ line_status_2.1 ;Buffer Overrun Error(OE) indicator parity_e_2 equ line_status_2.2 ;Parity Error(PE) indicator framing_e_2 equ line_status_2.3 ;Framing Error(FE) indicator break_int_2 equ line_status_2.4 ;Break Interrupt(BI) indicator thre_2 equ line_status_2.5 ;Transmitter Holding Register Empty(THRE) indicator temt_2 equ line_status_2.6 ;Transmitter Empty(TEMT) indicator lsr7_2 equ line_status_2.7 ;LSR7 rtcc_period ds 1 ;***************************************************************************************** ; RAM Bank Register definitions ;***************************************************************************************** ;********************************************************************************* ; Bank 1 ;********************************************************************************* org bank1 uart_1_tx = $ ;UART 1 TX bank divisor_1 ds 2 tx_high_1 ds 1 ;hi byte to transmit tx_low_1 ds 1 ;low byte to transmit tx_count_1 ds 1 ;number of bits sent tx_divide_1 ds 2 ;xmit timing (/16) counter2 tx_ring_ip_1 ds 1 ;transmit ring in pointer tx_ring_op_1 ds 1 ;transmit ring out pointer tx_ring_cnt_1 ds 1 ;transmit ring contents count string1 ds 1 ;indirect ptr to output string ;********************************************************************************* ; Bank 2 ;********************************************************************************* org bank2 uart_2_tx = $ ;UART 2 TX bank divisor_2 ds 2 tx_high_2 ds 1 ;hi byte to transmit tx_low_2 ds 1 ;low byte to transmit tx_count_2 ds 1 ;number of bits sent tx_divide_2 ds 2 ;xmit timing (/16) counter tx_ring_ip_2 ds 1 ;transmit ring in pointer tx_ring_op_2 ds 1 ;transmit ring out pointer tx_ring_cnt_2 ds 1 ;transmit ring contents count string2 ds 1 ;indirect ptr to output string ;********************************************************************************* ; Bank 3 ;********************************************************************************* org bank3 uart_rx = $ ;UART RX bank rx_count_1 ds 1 ;number of bits received rx_divide_1 ds 2 ;receive timing counter rx_byte_1 ds 1 ;buffer for incoming byte rx_ring_ip_1 ds 1 ;receive ring in pointer rx_ring_op_1 ds 1 ;receive ring out pointer rx_ring_cnt_1 ds 1 ;receive ring contents count rx_count_2 ds 1 ;number of bits received rx_divide_2 ds 2 ;receive timing counter rx_byte_2 ds 1 ;buffer for incoming byte rx_ring_ip_2 ds 1 ;receive ring in pointer rx_ring_op_2 ds 1 ;receive ring out pointer rx_ring_cnt_2 ds 1 ;receive ring contents count ;********************************************************************************* ; Bank 4 ;********************************************************************************* org bank4 uart_tx_ring_1 = $ ;UART channel 1 TX ring buffers tx_ring_1 ds tx_ring_size_1 ;********************************************************************************* ; Bank 5 ;********************************************************************************* org bank5 uart_rx_ring_1 = $ ;UART channel 1 RX ring buffers rx_ring_1 ds rx_ring_size_1 ;********************************************************************************* ; Bank 6 ;********************************************************************************* org bank6 uart_tx_ring_2 = $ ;UART channel 2 TX ring buffers tx_ring_2 ds tx_ring_size_2 ;********************************************************************************* ; Bank 7 ;********************************************************************************* org bank7 uart_rx_ring_2 = $ ;UART channel 2 RX ring buffers rx_ring_2 ds rx_ring_size_2 ;***************************************************************************************** ; Watches ;***************************************************************************************** watch rx_ring_ip_1,8,uhex watch rx_ring_cnt_1,8,udec watch rx_ring_1,16,fstr watch tx_ring_ip_1,8,uhex watch tx_ring_cnt_1,8,udec watch tx_ring_1,16,fstr watch rx_ring_ip_2,8,uhex watch rx_ring_cnt_2,8,udec watch rx_ring_2,16,fstr watch tx_ring_ip_2,8,uhex watch tx_ring_cnt_2,8,udec watch tx_ring_2,16,fstr watch divisor_1,16,udec ;***************************************************************************************** ; Macros ;***************************************************************************************** ; UART ring macro ; Advance the pointer through the ring, wrapping around if necessary ; This could be more efficient for aligned and power of 2 ring sizes. ;********************************************************************************* ringadv macro 3 ; Arguments ptr,base,size inc \1 ; Increment the pointer ; Check for wrap around mov w,\1 ; Load the pointer xor w,#(\2+\3) ; Check if ptr = base+size mov w,#\2 snz mov \1,w ; Equal, set ptr to base endm ;********************************************************************************* ; VP: uart_1_transmit ; 110 - 57.6Kbaud UART with RTS/CTS handshake ; ** Part of the Interrupt Service Routine ** ;********************************************************************************* UART1_TRANSMIT macro bank uart_1_tx ;switch to serial register bank inc tx_divide_1 ;ready to transmit bit? snz inc tx_divide_1+1 sz ;if tx_divide_1+1=0 then skip jmp uart_1_receive ;else, exit :continue mov w,divisor_1 ;reload baud period divisor_1(16-bit) mov tx_divide_1,w not tx_divide_1 ;compliment tx_divide_1 mov w,divisor_1+1 mov tx_divide_1+1,w not tx_divide_1+1 ;compliment tx_divide_1+1 test tx_count_1 ;are we sending? sz jmp :txbit ;yes, send next bit mov w,tx_ring_cnt_1 ;is tx ring empty? snz jmp uart_1_receive ;yes, go to :transmit_out ; jnb DTR_1,uart_1_receive ; jnb RTS_1,uart_1_receive :txring mov w,tx_ring_op_1 ;move one character from the ring to the mov fsr,w ; transmitter using indirect addressing mov w,indf bank uart_1_tx ;switch back to the uart bank not w ;ready bits (inverse logic) mov tx_high_1,w ; store data byte setb tx_low_1.7 ; set up start bit mov tx_count_1,#10 ;1 start + 8 data + 1 stop bit dec tx_ring_cnt_1 ;decrement tx ring byte count ringadv tx_ring_op_1,tx_ring_1,tx_ring_size_1 ;advance ring pointer :txbit clc ;ready stop bit rr tx_high_1 ; and shift to next bit rr tx_low_1 ; dec tx_count_1 ;decrement bit counter movb tx_pin_1,/tx_low_1.6 ;output next bit uart_1_receive bank uart_rx movb c,rx_pin_1 ;get current rx bit test rx_count_1 ;currently receiving byte? sz jmp :rxbit ;if so, jump ahead mov w,#9 ;in case start, ready 9 bits sc ;skip ahead if not start bit mov rx_count_1,w ;it is, so renew bit count clc bank uart_1_tx mov w,divisor_1 ;reload 1.5 bit period divisor_1(16-bit) bank uart_rx mov rx_divide_1,w bank uart_1_tx mov w,divisor_1+1 bank uart_rx mov rx_divide_1+1,w mov w,>>rx_divide_1+1 ;w=divisor_1/2 snc inc rx_divide_1+1 ;16-bit add mov w,>>rx_divide_1 add rx_divide_1,w snc inc rx_divide_1+1 not rx_divide_1 ;compliment rx_divide_1 not rx_divide_1+1 ;compliment rx_divide_1+1 :rxbit inc rx_divide_1 ;ready to transmit bit? snz inc rx_divide_1+1 sz ;if rx_divide_1+1=0 then skip IFDEF INLINE_VP jmp :done ELSE retp ; Return to the interrupt handler ENDIF bank uart_1_tx mov w,divisor_1 ;reload 1 bit period divisor_1(16-bit) bank uart_rx mov rx_divide_1,w bank uart_1_tx mov w,divisor_1+1 bank uart_rx mov rx_divide_1+1,w not rx_divide_1 ;compliment rx_divide_1 not rx_divide_1+1 ;compliment rx_divide_1+1 :get_bit movb c,rx_pin_1 ;get current rx bit dec rx_count_1 ;last bit? sz ;if not rr rx_byte_1 ; then save bit sz ;and skip to end IFDEF INLINE_VP jmp :done ELSE retp ; Return to the interrupt handler ENDIF mov w,rx_ring_cnt_1 ; Will this byte make the receive buffer full? inc wreg xor w,#rx_ring_size_1 ; Compare with the buffer size sz jmp :rx_ok ; Not full clrb CTS_1 ; CTS = false - not ready to receive data clrb DSR_1 ; DSR = false - not ready to receive data setb buffer_oe_1 ; Signal receive buffer overflow IFDEF INLINE_VP jmp :done ELSE retp ; Return to the interrupt handler ENDIF :rx_ok setb CTS_1 ; CTS = true - ready to receive data setb DSR_1 ; DSR = true - ready to receive data mov w,rx_byte_1 ; Save the received byte in global mov isr_temp,w mov w,rx_ring_ip_1 ; Store character in receive buffer mov fsr,w ; Set indirect address mov w,isr_temp ; temp must be global mov indf,w ; Store the received byte bank uart_rx ; Restore bank ringadv rx_ring_ip_1,rx_ring_1,rx_ring_size_1 inc rx_ring_cnt_1 ; Increment the ring buffer count :done ENDM ;********************************************************************************* ; VP: uart_2_transmit ; 110 - 57.6Kbaud UART with RTS/CTS handshake ; ** Part of the Interrupt Service Routine ** ;********************************************************************************* UART2_TRANSMIT macro bank uart_2_tx ;switch to serial register bank inc tx_divide_2 ;ready to transmit bit? snz inc tx_divide_2+1 sz ;if tx_divide_1+1=0 then skip jmp uart_2_receive ;else, exit :continue mov w,divisor_2 ;reload baud period divisor_1(16-bit) mov tx_divide_2,w not tx_divide_2 ;compliment tx_divide_1 mov w,divisor_2+1 mov tx_divide_2+1,w not tx_divide_2+1 ;compliment tx_divide_1+1 test tx_count_2 ;are we sending? sz jmp :txbit ;yes, send next bit mov w,tx_ring_cnt_2 ;is tx ring empty? snz jmp uart_2_receive ;yes, go to :transmit_out ; jnb DTR_2,uart_2_receive ; jnb RTS_2,uart_2_receive :txring mov w,tx_ring_op_2 ;move one character from the ring to the mov fsr,w ; transmitter using indirect addressing mov w,indf bank uart_2_tx ;switch back to the uart bank not w ;ready bits (inverse logic) mov tx_high_2,w ; store data byte setb tx_low_2.7 ; set up start bit mov tx_count_2,#10 ;1 start + 8 data + 1 stop bit dec tx_ring_cnt_2 ;decrement tx ring byte count ringadv tx_ring_op_2,tx_ring_2,tx_ring_size_2 ;advance ring pointer :txbit clc ;ready stop bit rr tx_high_2 ; and shift to next bit rr tx_low_2 ; dec tx_count_2 ;decrement bit counter movb tx_pin_2,/tx_low_2.6 ;output next bit uart_2_receive bank uart_rx movb c,rx_pin_2 ;get current rx bit test rx_count_2 ;currently receiving byte? sz jmp :rxbit ;if so, jump ahead mov w,#9 ;in case start, ready 9 bits sc ;skip ahead if not start bit mov rx_count_2,w ;it is, so renew bit count clc mov w,divisor_2 ;reload 1 bit period divisor_1(16-bit) bank uart_rx mov rx_divide_2,w bank uart_2_tx mov w,divisor_2+1 bank uart_rx mov rx_divide_2+1,w not rx_divide_2 ;compliment rx_divide_1 not rx_divide_2+1 ;compliment rx_divide_1+1 :rxbit inc rx_divide_2 ;ready to transmit bit? snz inc rx_divide_2+1 sz ;if rx_divide_1+1=0 then skip IFDEF INLINE_VP jmp :done ELSE retp ; Return to the interrupt handler ENDIF bank uart_2_tx mov w,divisor_2 ;reload 1 bit period divisor_1(16-bit) bank uart_rx mov rx_divide_2,w bank uart_2_tx mov w,divisor_2+1 bank uart_rx mov rx_divide_2+1,w not rx_divide_2 ;compliment rx_divide_1 not rx_divide_2+1 ;compliment rx_divide_1+1 :get_bit movb c,rx_pin_2 ;get current rx bit dec rx_count_2 ;last bit? sz ;if not rr rx_byte_2 ; then save bit sz ;and skip to end IFDEF INLINE_VP jmp :done ELSE retp ; Return to the interrupt handler ENDIF mov w,rx_ring_cnt_2 ; Is the receive buffer already full? inc wreg xor w,#rx_ring_size_2 ; Compare with the buffer size sz jmp :rx_ok ; Not full clrb CTS_2 ; CTS = false - not ready to receive data clrb DSR_2 ; DSR = false - not ready to receive data setb buffer_oe_2 ; Signal receive buffer overflow IFDEF INLINE_VP jmp :done ELSE retp ; Return to the interrupt handler ENDIF :rx_ok setb CTS_2 ; CTS = true - ready to receive data setb DSR_2 ; DSR = true - ready to receive data mov w,rx_byte_2 ; Save the received byte in global mov isr_temp,w mov w,rx_ring_ip_2 ; Store character in receive buffer mov fsr,w ; Set indirect address mov w,isr_temp ; temp must be global mov indf,w ; Store the received byte bank uart_rx ; Restore bank ringadv rx_ring_ip_2,rx_ring_2,rx_ring_size_2 inc rx_ring_cnt_2 ; Increment the ring buffer count :done endm ;********************************************************************************* ; VP: uart_1_transmit_fast ; 57.6K - 115.2Kbaud UART with RTS/CTS handshake ; ** Part of the Interrupt Service Routine ** ;********************************************************************************* UART1_TRANSMIT_FAST macro bank uart_1_tx ;switch to serial register bank decsz tx_divide_1 ;ready to transmit bit? jmp uart_1_rx_fast mov tx_divide_1,divisor_1 ;reload baud period test tx_count_1 ;are we sending? sz jmp :txbit ;yes, send next bit mov w,tx_ring_cnt_1 ;is tx ring empty? snz jmp uart_1_rx_fast ;yes, go to :transmit_out ; jnb DTR_1,uart_1_rx_fast ; jnb RTS_1,uart_1_rx_fast :txring mov w,tx_ring_op_1 ;move one character from the ring to the mov fsr,w ; transmitter using indirect addressing mov w,indf bank uart_1_tx ;switch back to the uart bank not w ;ready bits (inverse logic) mov tx_high_1,w ; store data byte setb tx_low_1.7 ; set up start bit mov tx_count_1,#10 ;1 start + 8 data + 1 stop bit dec tx_ring_cnt_1 ;decrement tx ring byte count ringadv tx_ring_op_1,tx_ring_1,tx_ring_size_1 ;advance ring pointer :txbit clc ;ready stop bit rr tx_high_1 ; and shift to next bit rr tx_low_1 ; dec tx_count_1 ;decrement bit counter movb tx_pin_1,/tx_low_1.6 ;output next bit uart_1_rx_fast bank uart_rx ;switch to serial register bank movb c,rx_pin_1 ;get current rx bit test rx_count_1 ;currently receiving byte? sz jmp :rxbit ;if so, jump ahead mov w,#9 ;in case start, ready 9 bits sc ;skip ahead if not start bit mov rx_count_1,w ;it is, so renew bit count clc bank uart_1_tx mov w,>>divisor_1 add w,divisor_1 bank uart_rx mov rx_divide_1,w ;ready 1.5 bit periods :rxbit decsz rx_divide_1 ;middle of next bit? IFDEF INLINE_VP jmp :done ELSE retp ; Return to the interrupt handler ENDIF bank uart_1_tx mov w,divisor_1 ;yes, reload 1 bit period bank uart_rx mov rx_divide_1,w dec rx_count_1 ;last bit? sz ;if not rr rx_byte_1 ; then save bit sz ;and skip to end IFDEF INLINE_VP jmp :done ELSE retp ; Return to the interrupt handler ENDIF mov w,rx_ring_cnt_1 ; Is the receive buffer already full? inc wreg xor w,#rx_ring_size_1 ; Compare with the buffer size sz jmp :rx_ok ; Not full clrb CTS_1 ; CTS = false - not ready to receive data clrb DSR_1 ; DSR = false - not ready to receive data setb buffer_oe_1 ; Signal receive buffer overflow IFDEF INLINE_VP jmp :done ELSE retp ; Return to the interrupt handler ENDIF :rx_ok setb CTS_1 ; CTS = true - ready to receive data setb DSR_1 ; DSR = true - ready to receive data mov w,rx_byte_1 ; Save the received byte in global mov isr_temp,w mov w,rx_ring_ip_1 ; Store character in receive buffer mov fsr,w ; Set indirect address mov w,isr_temp ; temp must be global mov indf,w ; Store the received byte bank uart_rx ; Restore the bank ringadv rx_ring_ip_1,rx_ring_1,rx_ring_size_1 inc rx_ring_cnt_1 ; Increment the ring buffer count :done endm ;********************************************************************************* ; VP: uart_2_transmit_fast ; 57.6K - 115.2Kbaud UART with RTS/CTS handshake ; ** Part of the Interrupt Service Routine ** ;********************************************************************************* UART2_TRANSMIT_FAST macro bank uart_2_tx ;switch to serial register bank decsz tx_divide_2 ;ready to transmit bit? jmp uart_2_rx_fast mov tx_divide_2,divisor_2 ;reload baud period test tx_count_2 ;are we sending? sz jmp :txbit ;yes, send next bit mov w,tx_ring_cnt_2 ;is tx ring empty? snz jmp uart_2_rx_fast ;yes, go to :transmit_out ; jnb DTR_2,uart_2_rx_fast ; jnb RTS_2,uart_2_rx_fast :txring mov w,tx_ring_op_2 ;move one character from the ring to the mov fsr,w ; transmitter using indirect addressing mov w,indf bank uart_2_tx ;switch back to the uart bank not w ;ready bits (inverse logic) mov tx_high_2,w ; store data byte setb tx_low_2.7 ; set up start bit mov tx_count_2,#10 ;1 start + 8 data + 1 stop bit dec tx_ring_cnt_2 ;decrement tx ring byte count ringadv tx_ring_op_2,tx_ring_2,tx_ring_size_2 ;advance ring pointer :txbit clc ;ready stop bit rr tx_high_2 ; and shift to next bit rr tx_low_2 ; dec tx_count_2 ;decrement bit counter movb tx_pin_2,/tx_low_2.6 ;output next bit uart_2_rx_fast bank uart_rx ;switch to serial register bank movb c,rx_pin_2 ;get current rx bit test rx_count_2 ;currently receiving byte? sz jmp :rxbit ;if so, jump ahead mov w,#9 ;in case start, ready 9 bits sc ;skip ahead if not start bit mov rx_count_2,w ;it is, so renew bit count clc bank uart_2_tx mov w,>>divisor_2 add w,divisor_2 bank uart_rx mov rx_divide_2,w ;ready 1.5 bit periods :rxbit decsz rx_divide_2 ;middle of next bit? IFDEF INLINE_VP jmp :done ELSE retp ; Return to the interrupt handler ENDIF bank uart_2_tx mov w,divisor_2 ;yes, reload 1 bit period bank uart_rx mov rx_divide_2,w dec rx_count_2 ;last bit? sz ;if not rr rx_byte_2 ; then save bit sz ;and skip to end IFDEF INLINE_VP jmp :done ELSE retp ; Return to the interrupt handler ENDIF mov w,rx_ring_cnt_2 ; Is the receive buffer already full? inc wreg xor w,#rx_ring_size_2 ; Compare with the buffer size sz jmp :rx_ok ; Not full clrb CTS_2 ; CTS = false - not ready to receive data clrb DSR_2 ; DSR = false - not ready to receive data setb buffer_oe_2 ; Signal receive buffer overflow IFDEF INLINE_VP jmp :done ELSE retp ; Return to the interrupt handler ENDIF :rx_ok setb CTS_2 ; CTS = true - ready to receive data setb DSR_2 ; DSR = true - ready to receive data mov w,rx_byte_2 ; Save the received byte in global mov isr_temp,w mov w,rx_ring_ip_2 ; Store character in receive buffer mov fsr,w ; Set indirect address mov w,isr_temp ; temp must be global mov indf,w ; Store the received byte bank uart_rx ; Restore the bank ringadv rx_ring_ip_2,rx_ring_2,rx_ring_size_2 inc rx_ring_cnt_2 ; Increment the ring buffer count :done endm ;***************************************************************************************** ; Interrupt Service Routine ;***************************************************************************************** ORG 0 ;interrupt starts at 0h interrupt IFDEF AUTO_BAUD call @uart_1_autobaud IF SX_TYPE=1 call @uart_2_autobaud ENDIF ENDIF ;********************************************************************************* ; Virtual Peripheral: Universal Asynchronous Receiver Transmitter (UART) ; ; This routine sends and receives 2 channels of RS232C serial data, ; currently configured (though modifications can be made) for the popular ; "No parity-checking, 8 data bit, 1 stop bit" (N,8,1) data format. ; ; RECEIVING: ; Whenever a valid byte of data has been received, it is put into ; the rx_ring FIFO buffer. The address pointer rx_ring_ip and FIFO byte count ; rx_ring_cnt are also incremented with every new byte. A receive buffer overflow ; is signaled by the uart_rx_oflow flag. ; ; TRANSMITTING: ; The transmit routine requires the data to be inverted and loaded (tx_high+tx_low) ; register pair (with the inverted 8 data bits stored in tx_high and tx_low bit 7 set ; high to act as a start bit). Then the number of bits ready for transmission ; (10=1 start + 8 data + 1 stop) must be loaded into the tx_count register. ; As soon as this latter is done, the transmit routine immediately begins sending the data. ; The handshaking signals RTS and CTS have also been implemented. ; The CTS output is used to indicate that the DCE (SX) is ready to receive data. ; The RTS input is used to indicate that the DTE (terminal) is ready for receiving. ; Data is only sent if the RTS line is active. ; ; This routine has a varying execution rate and therefore should always be ; placed after any timing-critical virtual peripherals such as timers, ; adcs, pwms, etc. ; Note: The transmit and receive routines are independent and either may be ; removed, if not needed, to reduce execution time and memory usage, ; as long as the initial "BANK serial" (common) instruction is kept. ; ; Input variable(s) : tx_low (only high bit used), tx_high, tx_count ; Output variable(s) : rx_flag, rx_byte ; Variable(s) affected : tx_divide, rx_divide, rx_count ; Flag(s) affected : rx_flag ; Size : Transmit - 34 bytes + 1 byte shared with receive code ; Receive - 41 bytes + 1 byte shared with transmit code ; Timing (turbo) : ; Transmit - (a) [multiplexing] 2 cycles ; (b) [not sending] 5 cycles ; (c) [sending] 18 cycles ; (d) [buffer empty] 13 cycles ; (e) [start sending] 34 cycles ; + 1 cycle shared with RX code ("bank" instr.) ; Receive - (a) [multiplexing] 2 cycles ; (b) [not receiving] 9 cycles ; (c) [start receiving] 15 cycles ; (d) [receiving, awaiting bit] 13 cycles ; (e) [receiving, bit ready] 20 cycles ; (f) [receiving, last bit] 37 cycles ; (g) [receiving, buffer full] 25 cycles ; ; ;********************************************************************************* IFDEF INLINE_VP ; we are inlining the VP functions, to save 7 cycles per UART UART1 jnb uart1_en,UART2 ;execute UART channel 1 only when enabled jb speed_1,uart_1_transmit_fast UART1_TRANSMIT jmp UART2 uart_1_transmit_fast UART1_TRANSMIT_FAST UART2 IF SX_TYPE = 1 ;only compile if target is SX52 jnb uart2_en,uarts_done ;execute UART channel 2 only when enabled jb speed_2,uart_2_transmit_fast UART2_TRANSMIT jmp uarts_done uart_2_transmit_fast UART2_TRANSMIT_FAST ENDIF ELSE ; we are calling the VP functions, not inlining UART1 jnb uart1_en,UART2 ;execute UART channel 1 only when enabled jb speed_1,:fast call @uart_1_transmit jmp UART2 :fast call @uart_1_transmit_fast UART2 IF SX_TYPE = 1 ;only compile if target is SX52 jnb uart2_en,uarts_done ;execute UART channel 2 only when enabled jb speed_2,:fast call @uart_2_transmit jmp uarts_done :fast call @uart_2_transmit_fast ENDIF ENDIF uarts_done ;***************************************************************************************** ; End of Interrupt Service Routine ;***************************************************************************************** isr_end ;********************************************************************************* ; Set Interrupt Rate ;********************************************************************************* mov w,#-int_period ;interrupt every 'int_period' clocks retiw ;exit interrupt ;***************************************************************************************** ; RESET VECTOR ;***************************************************************************************** ;********************************************************************************* ; Program execution begins here on power-up or after a reset ;********************************************************************************* reset_entry IF SX_TYPE = 0 ;SX28 mov m,#$0f ;point mode to port I/O's mov ra,#RA_init ;initialize port RA mov !ra,#RA_IO ;Set RA in/out directions mov rb,#RB_init ;initialize port RB mov !rb,#RB_IO ;Set RB in/out directions mov rc,#RC_init ;initialize port RC mov !rc,#RC_IO ;Set RC in/out directions ELSE ;SX52 mov m,#$1f ;point mode to port I/O's mov ra,#RA_init ;initialize port RA mov !ra,#RA_IO ;Set RA in/out directions mov rb,#RB_init ;initialize port RB mov !rb,#RB_IO ;Set RB in/out directions mov rc,#RC_init ;initialize port RC mov !rc,#RC_IO ;Set RC in/out directions mov rd,#RD_init ;initialize port RD mov !rd,#RD_IO ;Set RD in/out directions mov re,#RE_init ;initialize port RE mov !re,#RE_IO ;Set RE in/out directions ENDIF ;********************************************************************************* ; Clear all Data RAM locations ;********************************************************************************* IF SX_TYPE = 0 ;SX28 clr fsr ;reset all ram banks :zero_ram sb fsr.4 ;are we on low half of bank? setb fsr.3 ;If so, don't touch regs 0-7 clr ind ;clear using indirect addressing incsz fsr ;repeat until done jmp :zero_ram ELSE ;SX52 mov w,#$0a ;reset all ram starting at 08h mov fsr,w :zero_ram clr ind ;clear using indirect addressing incsz fsr ;repeat until done jmp :zero_ram clrb fsr.7 ENDIF call @uarts_init ;initialise the uarts IF RTCC_prescalar = 1 mov !option,#%10011111 ;enable rtcc interrupt ENDIF IF RTCC_prescalar = 2 mov !option,#%10010000 ;enable rtcc interrupt ENDIF IFNDEF AUTO_BAUD mov w,#B115200 call @set_bps_rate_1 IF SX_TYPE = 1 ;only compile if target is SX48/52 mov w,#B115200 call @set_bps_rate_2 ENDIF ENDIF jmp @main ;***************************************************************************************** ; MAIN PROGRAM CODE ;***************************************************************************************** org $200 main ;********************************************************************************* ; Main Program Loop ;********************************************************************************* IF SX_TYPE = 0 ;only compile if target is SX28 mov w,#_hello ;send hello string to channel 1 call @send_string_1 :prompt mov w,#_prompt ;send prompt to channel 1 call @send_string_1 main_loop call @get_byte_1 ;get byte via UART channel 1 sz call @send_byte_1 ;echo byte back to sender on channel 1 jmp main_loop ;return ELSE mov w,#_hello ;send hello string to channel 2 call @send_string_2 mov w,#_ch2 ;send channel string to channel 2 call @send_string_2 mov w,#_prompt ;send prompt to channel 2 call @send_string_2 mov w,#_hello ;send hello string to channel 1 call @send_string_1 mov w,#_ch1 ;send channel string to channel 1 call @send_string_1 mov w,#_prompt ;send prompt to channel 1 call @send_string_1 main_loop call @get_byte_1 ;get byte via UART channel 1 sz call @send_byte_1 ;echo byte back to sender on channel 1 call @get_byte_2 ;get byte via UART channel 2 sz call @send_byte_2 ;echo byte back to sender on channel 2 jmp main_loop ;return ; this stuff was left in, to show how to create a 16550 device cs_loop snb cs ;wait for chip select active(low) jmp cs_loop ;return sb read ;check for read active(low) jmp :cont ; it's low, must be a request sb write ;check for write active(low) jmp :cont ; it's low, must be a request jmp main_loop ;return :cont mov temp,re ;keep only the lower 3 address bits and temp,#$07 sb chsl ;if chsl = 0, jmp channel_2 ; then channel 2 is being addressed channel_1 snz ;if lower 3 address bits = 0, jmp tx_rx_buffer_1 ; then tx/rx buffer is being addressed mov w,#1 ;is current address = $01? mov w,temp-w snz jmp int_enable_1 mov w,#2 ;is current address = $02? mov w,temp-w snz jmp int_id_1 mov w,#3 ;is current address = $03? mov w,temp-w snz jmp fifo_control_1 mov w,#4 ;is current address = $04? mov w,temp-w snz jmp modem_control_1 mov w,#5 ;is current address = $05? mov w,temp-w snz jmp line_status_1 mov w,#6 ;is current address = $06? mov w,temp-w snz jmp modem_status_1 jmp main_loop ;return tx_rx_buffer_1 ;$00 -> Receive Buffer/Transmit Holding Register sb read ;if read bit = 0, jmp :read ; then receive buffer is being addressed sb write ;if write bit = 0, jmp :write ; then transmit holding buffer is being addressed jmp main_loop ;return :read mov w,#$00 ;change data bus to output mov !rb,w call @get_byte_1 ;get byte mov rb,w ;load byte in w on data bus jnb read,$ mov w,#$ff ;change data bus to input(hi-Z) mov !rb,w jmp main_loop ;return :write mov w,rb ;load byte on data bus in w call @send_byte_1 ;send byte jnb write,$ jmp main_loop ;return int_enable_1 ;$01 -> Interrupt Enable jmp main_loop ;return int_id_1 ;$02 -> Interrupt ID jmp main_loop ;return fifo_control_1 ;$03 -> FIFO Control jmp main_loop ;return modem_control_1 ;$04 -> MODEM Control jmp main_loop ;return line_stat_1 ;$05 -> Line Status snb read ;if read bit = 1(not active), jmp :write ; then return(not meant to be written to) :read ; else line status register is being read mov w,#$00 ;change data bus to output mov !data_bus,w mov w,line_status_1 ;get line_status mov data_bus,w ;load byte in w on data bus mov w,#$ff ;change data bus to input(hi-Z) mov !data_bus,w :write jmp main_loop ;return modem_status_1 ;$06 -> MODEM Status jmp main_loop ;return scratch_1 ;$07 -> Scratch jmp main_loop ;return channel_2 snz ;if lower 3 address bits = 0, jmp tx_rx_buffer_2 ; then tx/rx buffer is being addressed mov w,#1 ;is current address = $01? mov w,temp-w snz jmp int_enable_2 mov w,#2 ;is current address = $02? mov w,temp-w snz jmp int_id_2 mov w,#3 ;is current address = $03? mov w,temp-w snz jmp fifo_control_2 mov w,#4 ;is current address = $04? mov w,temp-w snz jmp modem_control_2 mov w,#5 ;is current address = $05? mov w,temp-w snz jmp line_status_2 mov w,#6 ;is current address = $06? mov w,temp-w snz jmp modem_status_2 jmp main_loop ;return tx_rx_buffer_2 ;$00 -> Receive Buffer/Transmit Holding Register sb read ;if read bit = 0, jmp :read ; then receive buffer is being addressed sb write ;if write bit = 0, jmp :write ; then transmit holding buffer is being addressed jmp main_loop ;return :read mov w,#$00 ;change data bus to output mov !rb,w call @get_byte_2 ;get byte mov rb,w ;load byte in w on data bus jnb read,$ mov w,#$ff ;change data bus to input(hi-Z) mov !rb,w jmp main_loop ;return :write mov w,rb ;load byte on data bus in w call @send_byte_2 ;send byte jnb write,$ jmp main_loop ;return int_enable_2 ;$01 -> Interrupt Enable jmp main_loop ;return int_id_2 ;$02 -> Interrupt ID jmp main_loop ;return fifo_control_2 ;$03 -> FIFO Control jmp main_loop ;return modem_control_2 ;$04 -> MODEM Control jmp main_loop ;return line_stat_2 ;$05 -> Line Status snb read ;if read bit = 1(not active), jmp :write ; then return(not meant to be written to) :read ; else line status register is being read mov w,#$00 ;change data bus to output mov !data_bus,w mov w,line_status_2 ;get line_status mov data_bus,w ;load byte in w on data bus mov w,#$ff ;change data bus to input(hi-Z) mov !data_bus,w :write jmp main_loop ;return modem_status_2 ;$06 -> MODEM Status jmp main_loop ;return scratch_2 ;$07 -> Scratch jmp main_loop ;return ENDIF ;***************************************************************************************** ; PROGRAM DATA ;***************************************************************************************** org $400 ;must be lower half of page (for calls) datapage ; String data for user interface (must be in lower half of memory page) ; _hello dw 13,10,13,10,'High speed SX DUART Demo 11/01/2004',0 _ch1 dw 13,10,13,10,'Channel 1',0 _ch2 dw 13,10,13,10,'Channel 2',0 _prompt dw 13,10,'>',0 ; 75 110 134 150 300 600 1200 2400 4800 9600 19200 38400 57600 115200 230400 IF xtal=50000000 ;50MHz @ 217 _divisors dw 3072,2095,1719,1536, 768, 384, 192, 96, 48, 24, 12, 6, 4, 2, 0 ENDIF IF xtal=75000000 IF RTCC_prescalar=2 ;75MHz, half speed @ 163 _divisors dw 3067,2091,1717,1534, 767, 383, 192, 96, 48, 24, 12, 6, 4, 2, 0 ELSE ;75Mhz, @ 217 _divisors dw 0,3142,2579,2304,1152, 576, 288, 144, 72, 36, 18, 9, 6 3, 0 ENDIF ENDIF ;_divisors dw 0,2304,1536,1048,856,768,384,192,96,94,48,32,24,16,12,6,4,2 ;***************************************************************************************** ; SUBROUTINES ;***************************************************************************************** ;********************************************************************************* ; Function: set_bps_rate_1 ; Set the baud rate, based on an index ; 0 = 75bps ; 1 = 110bps ; 2 = 134bps ; 3 = 150bps ; 4 = 300bps ; 5 = 600bps ; 6 = 1200bps ; 7 = 2400bps ; 8 = 4800bps ; 9 = 9600bps ; 10 = 19200bps ; 11 = 38400bps ; 12 = 57600bps ; 13 = 115200bps ; ;********************************************************************************* set_bps_rate_1 mov m,#4;datapage/100h ; with indirect addressing* mov temp,w mov w,#_divisors add w,temp ;store divisors address iread ; using the mode register snz ; if W is zero, invalid bps rate retp bank uart_1_tx setb uart1_en mov divisor_1,w mov w,m and w, #$0f jnz :lo_speed setb speed_1 retp :lo_speed mov divisor_1+1,w clrb speed_1 retp IF SX_TYPE = 1 ;only compile if target is SX52 ;********************************************************************************* ; Function: set_bps_rate_2 ; Set the baud rate, based on an index ;********************************************************************************* set_bps_rate_2 mov m,#4;datapage/100h ; with indirect addressing* mov temp,w mov w,#_divisors add w,temp ;store divisors address iread ; using the mode register snz ; if W is zero, invalid bps rate retp bank uart_2_tx setb uart2_en mov divisor_2,w mov w,m and w, #$0f jnz :lo_speed setb speed_2 retp :lo_speed mov divisor_2+1,w clrb speed_2 retp ENDIF ;********************************************************************************* ; Function: uarts_init ; Initialise the UARTs ;********************************************************************************* uarts_init jmp _uarts_init ;********************************************************************************* ; Function: get_byte_1 ; Get byte via serial port ; Byte received is returned in W ;********************************************************************************* get_byte_1 bank uart_rx ; Select the bank :wait mov w,rx_ring_cnt_1 ; Get the number of bytes in the rx ring snz ; Is the receive ring empty? jmp :return ; Yes, block until not empty mov w,rx_ring_op_1 ; Load the ring out pointer mov fsr,w mov w,indf ; Get character from buffer mov temp,w ; Save character in temp bank uart_rx ; Restore the bank ringadv rx_ring_op_1,rx_ring_1,rx_ring_size_1 ; Advance ring pointer dec rx_ring_cnt_1 ; Decrement rx char count sb CTS_1 ; If CTS is set, clear it setb CTS_1 ; CTS = true - ready to receive data sb DSR_1 ; If DSR is set, clear it setb DSR_1 ; DSR = true - ready to receive data mov w,temp ; Return byte in W setb rx_flag_1 retp :return ;clr w ; Return 0 in W retp ;********************************************************************************* ; Function: get_byte_2 ; Get byte via serial port ; Byte received is returned in W ;********************************************************************************* IF SX_TYPE = 1 ;only compile if target is SX52 get_byte_2 bank uart_rx ; Select the bank :wait mov w,rx_ring_cnt_2 ; Get the number of bytes in the rx ring snz ; Is the receive ring empty? jmp :return ; Yes, block until not empty mov w,rx_ring_op_2 ; Load the ring out pointer mov fsr,w mov w,indf ; Get character from buffer mov temp,w ; Save character in temp bank uart_rx ; Restore the bank ringadv rx_ring_op_2,rx_ring_2,rx_ring_size_2 ; Advance ring pointer dec rx_ring_cnt_2 ; Decrement rx char count sb CTS_2 ; If CTS is set, clear it setb CTS_2 ; CTS = true - ready to receive data sb DSR_2 ; If DSR is set, clear it setb DSR_2 ; DSR = true - ready to receive data mov w,temp ; Return byte in W setb rx_flag_2 retp :return ;clr w ; Return 0 in W retp ENDIF ;********************************************************************************* ; Function: send_byte_1 ; Send byte via serial port ; Byte to be sent should be in W ;********************************************************************************* send_byte_1 bank uart_1_tx ; Select the bank mov temp,w :wait mov w,tx_ring_cnt_1 ; Load the current ring contents xor w,#tx_ring_size_1 ; Compare to the ring size snb status.2 ; Is there room for a character? jmp :wait ; No, block until there is room mov w,tx_ring_ip_1 ; Get buffer pointer mov fsr,w mov w,temp mov indf,w ; Save temp in the ring bank uart_1_tx ; Restore the bank ringadv tx_ring_ip_1,tx_ring_1,tx_ring_size_1 ; Advance ring pointer inc tx_ring_cnt_1 ; Increment tx char count retp ;leave and fix page bits ;********************************************************************************* ; Function: send_byte_2 ; Send byte via serial port ; Byte to be sent should be in W ;********************************************************************************* IF SX_TYPE = 1 ;only compile if target is SX52 send_byte_2 bank uart_2_tx ; Select the bank mov temp,w :wait mov w,tx_ring_cnt_2 ; Load the current ring contents xor w,#tx_ring_size_2 ; Compare to the ring size snb status.2 ; Is there room for a character? jmp :wait ; No, block until there is room mov w,tx_ring_ip_2 ; Get buffer pointer mov fsr,w mov w,temp mov indf,w ; Save temp in the ring bank uart_2_tx ; Restore the bank ringadv tx_ring_ip_2,tx_ring_2,tx_ring_size_2 ; Advance ring pointer inc tx_ring_cnt_2 ; Increment tx char count :return retp ;leave and fix page bits ENDIF ;********************************************************************************* ; Function: bytes_avail_1 ; Get number of bytes waiting in receive buffer ; Number of bytes in buffer will be returned in W ;********************************************************************************* bytes_avail_1 bank uart_rx ; Ensure ring variables mov w,rx_ring_cnt_1 ; Get receive ring count retp ;********************************************************************************* ; Function: bytes_avail_2 ; Get number of bytes waiting in receive buffer ; Number of bytes in buffer will be returned in W ;********************************************************************************* IF SX_TYPE = 1 ;only compile if target is SX52 bytes_avail_2 bank uart_rx ; Ensure ring variables mov w,rx_ring_cnt_2 ; Get receive ring count retp ENDIF ;********************************************************************************* ; Function: send_string_1 ; Send string pointed to by address in W register ; *Note: High nibble into mode register denotes page address of data ;********************************************************************************* send_string_1 bank uart_1_tx mov string1,w ;store string address ; jnb CTS_1,$ ;wait for CTS_1 = true - ready to receive data :loop mov w,string1 ;read next string character mov m,#4;datapage/100h ; with indirect addressing* iread ; using the mode register IF SX_TYPE = 0 ;SX28 mov m,#$0f ;point mode to port I/O's ELSE ;SX52 mov m,#$1f ;point mode to port I/O's ENDIF test w ;are we at the last char? snz ;if not=0, skip ahead retp ;yes, leave & fix page bits ;not 0, so send character mov temp,w :wait mov w,tx_ring_cnt_1 ; Load the current ring contents xor w,#tx_ring_size_1 ; Compare to the ring size snb status.2 ; Is there room for a character? jmp :wait ; No, block until there is room mov w,tx_ring_ip_1 ; Get buffer pointer mov fsr,w mov w,temp mov indf,w ; Save temp in the ring bank uart_1_tx ; Restore the bank ringadv tx_ring_ip_1,tx_ring_1,tx_ring_size_1 ; Advance ring pointer inc tx_ring_cnt_1 ; Increment tx char count inc string1 ;point to next character jmp :loop ;loop until done ;********************************************************************************* ; Function: send_string_2 ; Send string pointed to by address in W register ; *Note: High nibble into mode register denotes page address of data ;********************************************************************************* IF SX_TYPE = 1 ;only compile if target is SX52 send_string_2 bank uart_2_tx mov string2,w ;store string address ; jnb CTS_2,$ ;wait for CTS_2 = true - ready to receive data :loop mov w,string2 ;read next string character mov m,#4;datapage/100h ; with indirect addressing* iread ; using the mode register mov m,#$1f ;point mode to port I/O's test w ;are we at the last char? snz ;if not=0, skip ahead retp ;yes, leave & fix page bits ;not 0, so send character mov temp,w :wait mov w,tx_ring_cnt_2 ; Load the current ring contents xor w,#tx_ring_size_2 ; Compare to the ring size snb status.2 ; Is there room for a character? jmp :wait ; No, block until there is room mov w,tx_ring_ip_2 ; Get buffer pointer mov fsr,w mov w,temp mov indf,w ; Save temp in the ring bank uart_2_tx ; Restore the bank ringadv tx_ring_ip_2,tx_ring_2,tx_ring_size_2 ; Advance ring pointer inc tx_ring_cnt_2 ; Increment tx char count inc string2 ;point to next character jmp :loop ;loop until done ENDIF _uarts_init bank uart_rx ; Select the bank clr rx_ring_cnt_1 ; The receive ring is empty mov w,#rx_ring_1 mov rx_ring_ip_1,w ; Set the in and out pointers to the start of mov rx_ring_op_1,w ; the receive ring IF SX_TYPE = 1 ;only compile if target is SX52 clr rx_ring_cnt_2 ; The receive ring is empty mov w,#rx_ring_2 mov rx_ring_ip_2,w ; Set the in and out pointers to the start of mov rx_ring_op_2,w ; the receive ring ENDIF bank uart_1_tx clr tx_ring_cnt_1 ; The transmit ring is empty mov w,#tx_ring_1 mov tx_ring_ip_1,w ; Set the in and out pointers to the start of mov tx_ring_op_1,w ; the transmit ring IF SX_TYPE = 1 ;only compile if target is SX52 bank uart_2_tx clr tx_ring_cnt_2 ; The transmit ring is empty mov w,#tx_ring_2 mov tx_ring_ip_2,w ; Set the in and out pointers to the start of mov tx_ring_op_2,w ; the transmit ring setb uart2_en ;enable UART channel 2 ENDIF retp ;***************************************************************************************** ; Jump Table for Page 2 ;***************************************************************************************** org $600 IFDEF AUTO_BAUD ;********************************************************************************* ; Virtual Peripheral: UART Autobaud (channel 1) ; ; This routine adds Autobaud capability to the UARTs by incrementing an 8-bit count ; while the rx line is low. If a known character (the 'a' or 'A', $61 or $41 in this case) ; is input, the count will be proportional to the bit rate of the received datastream. ;********************************************************************************* uart_1_autobaud snb uart1_en ;Don't run if UART channel 1 is already running jmp :autobaud1_done ; jnb RTS_1,:autobaud1_done ; if RTS = false, so DTE has nothing to send ; jnb DTR_1,:autobaud1_done ; if DTR = false, so DTE is not ready to receive setb CTS_1 ; CTS = true - ready to receive data setb DSR_1 ; DSR = true - ready to receive data bank uart_1_tx snb lo_detected_1 ;low detected on previous pass, start count jmp :start_count snb rx_pin_1 ;wait for rx line go low jmp :autobaud1_done ;rx line is high, exit setb lo_detected_1 ;start counting during low pulse :start_count snb rx_pin_1 ;increment count only when rx line is low jmp :test_result ;rising edge after low pulse, test result :inc_low inc divisor_1 ;increment count while low snz inc divisor_1+1 jmp :autobaud1_done :test_result mov w,divisor_1 ;divisor_1(16-bit) contains baudrate divisor mov tx_divide_1,w mov w,divisor_1+1 mov tx_divide_1+1,w setb uart1_en ;enable uarts mov w,#2^$ff ;Check to see if baud > 57.6Kbps add w,tx_divide_1 snc ;skip if less than 2 jmp :comp ;jump if greater than 2 :no_comp setb speed_1 ; then set speed_1 to indicate fast UART rate jmp :setup_rx_115 :comp dec divisor_1 ;trim divisor_1 dec tx_divide_1 ;trim tx_divide_1 not tx_divide_1 ;compliment tx_divide_1 not tx_divide_1+1 ;compliment tx_divide_1+1 :setup_rx bank uart_1_tx clc mov w,>>divisor_1+1 ;reload 1/2 bit period divisor_1(16-bit) bank uart_rx mov rx_divide_1+1,w bank uart_1_tx mov w,>>divisor_1 bank uart_rx mov rx_divide_1,w mov w,#9 ;ready 9 bits mov rx_count_1,w ;renew bit count not rx_divide_1 ;compliment rx_divide_1 not rx_divide_1+1 ;compliment rx_divide_1+1 jmp :autobaud1_done :setup_rx_115 bank uart_rx mov w,#9 ;ready 9 bits mov rx_count_1,w ;renew bit count mov rx_divide_1,#1 :autobaud1_done retp ;********************************************************************************* ; Virtual Peripheral: UART Autobaud (channel 2) ; ; This routine adds Autobaud capability to the UARTs by incrementing an 8-bit count ; while the rx line is low. If a known character (the 'a' or 'A', $61 or $41 in this case) ; is input, the count will be proportional to the bit rate of the received datastream. ;********************************************************************************* IF SX_TYPE=1 uart_2_autobaud snb uart2_en ;Don't run if UART channel 1 is already running jmp :autobaud2_done ; jnb RTS_2,:autobaud2_done ; if RTS = false, so DTE has nothing to send ; jnb DTR_2,:autobaud2_done ; if DTR = false, so DTE is not ready to receive setb CTS_2 ; CTS = true - ready to receive data setb DSR_2 ; DSR = true - ready to receive data bank uart_2_tx snb lo_detected_2 ;low detected on previous pass, start count jmp :start_count snb rx_pin_2 ;wait for rx line go low jmp :autobaud2_done ;rx line is high, exit setb lo_detected_2 ;start counting during low pulse :start_count snb rx_pin_2 ;increment count only when rx line is low jmp :test_result ;rising edge after low pulse, test result :inc_low inc divisor_2 ;increment count while low snz inc divisor_2+1 jmp :autobaud2_done :test_result mov w,divisor_2 ;divisor_1(16-bit) contains baudrate divisor mov tx_divide_2,w mov w,divisor_2+1 mov tx_divide_2+1,w setb uart2_en ;enable uarts mov w,#2^$ff ;Check to see if baud > 57.6Kbps add w,tx_divide_2 snc ;skip if less than 2 jmp :comp ;jump if greater than 2 :no_comp setb speed_2 ; then set speed_1 to indicate fast UART rate jmp :setup_rx_115 :comp dec divisor_2 ;trim divisor_1 dec tx_divide_2 ;trim tx_divide_1 not tx_divide_2 ;compliment tx_divide_1 not tx_divide_2+1 ;compliment tx_divide_1+1 :setup_rx bank uart_2_tx clc mov w,>>divisor_2+1 ;reload 1/2 bit period divisor_1(16-bit) bank uart_rx mov rx_divide_2+1,w bank uart_2_tx mov w,>>divisor_2 bank uart_rx mov rx_divide_2,w mov w,#9 ;ready 9 bits mov rx_count_2,w ;renew bit count not rx_divide_2 ;compliment rx_divide_1 not rx_divide_2+1 ;compliment rx_divide_1+1 jmp :autobaud2_done :setup_rx_115 bank uart_rx mov w,#9 ;ready 9 bits mov rx_count_2,w ;renew bit count mov rx_divide_2,#1 :autobaud2_done retp ENDIF ENDIF IFNDEF INLINE_VP uart_1_transmit_fast jmp _uart_1_transmit_fast IF SX_TYPE = 1 uart_2_transmit_fast jmp _uart_2_transmit_fast ENDIF ;********************************************************************************* ; VP: uart_1_transmit ; 110 - 57.6Kbaud UART with RTS/CTS handshake ; ** Part of the Interrupt Service Routine ** ;********************************************************************************* uart_1_transmit UART1_TRANSMIT retp ; Return to the interrupt handler ;********************************************************************************* ; VP: uart_2_transmit ; 110 - 57.6Kbaud UART with RTS/CTS handshake ; ** Part of the Interrupt Service Routine ** ;********************************************************************************* IF SX_TYPE = 1 ;only compile if target is SX52 uart_2_transmit UART2_TRANSMIT retp ; Return to the interrupt handler ENDIF ;********************************************************************************* ; VP: uart_1_transmit_fast ; 57.6K - 115.2Kbaud UART with RTS/CTS handshake ; ** Part of the Interrupt Service Routine ** ;********************************************************************************* _uart_1_transmit_fast UART1_TRANSMIT_FAST retp ; Return to the interrupt handler IF SX_TYPE=1 ;********************************************************************************* ; VP: uart_2_transmit_fast ; 57.6K - 115.2Kbaud UART with RTS/CTS handshake ; ** Part of the Interrupt Service Routine ** ;********************************************************************************* _uart_2_transmit_fast UART2_TRANSMIT_FAST retp ; Return to the interrupt handler ENDIF ENDIF ;***************************************************************************************** END ;End of program code ;*****************************************************************************************
file: /Techref/scenix/lib/io/osi2/serial/DUART_0412_JB.SRC, 60KB, , updated: 2004/11/19 16:12, local time: 2024/10/8 15:35,
44.220.184.63:LOG IN ©2024 PLEASE DON'T RIP! THIS SITE CLOSES OCT 28, 2024 SO LONG AND THANKS FOR ALL THE FISH!
|
©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions? <A HREF="http://www.piclist.com/Techref/scenix/lib/io/osi2/serial/DUART_0412_JB.SRC"> scenix lib io osi2 serial DUART_0412_JB</A> |
Did you find what you needed? |
PICList 2024 contributors:
o List host: MIT, Site host massmind.org, Top posters @none found - Page Editors: James Newton, David Cary, and YOU! * Roman Black of Black Robotics donates from sales of Linistep stepper controller kits. * Ashley Roll of Digital Nemesis donates from sales of RCL-1 RS232 to TTL converters. * Monthly Subscribers: Gregg Rew. on-going support is MOST appreciated! * Contributors: Richard Seriani, Sr. |
Welcome to www.piclist.com! |
.