; ***************************************************************************************** ; 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 ; ;***************************************************************************************** ; 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. ; ;***************************************************************************************** ; SX28 Definitions ; DEVICE pins28,pages4,banks8,oschs ; DEVICE turbo,stackx,optionx ; ID '28DUART' ;program ID label SX_TYPE = 1 ;0=SX28, 1=SX48/52 ; SX52 Definitions DEVICE sx52,oschs,turbo,stackx,optionx,sync ID '52DUART' ;program ID label RESET reset_entry ;set reset/boot address ; FREQ 50000000 ;oscillator frequency ;***************************************************************************************** ; 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 ;***************************************************************************************** ; 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 RTCC_prescaler = 1 ;to change: see OPTION command later on xtal = 50000000 ;***************************************************************************************** ; 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 ;***************************************************************************************** ; 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 lo_detected_1 equ flags.4 speed_1 equ flags.5 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 ;***************************************************************************************** ; Odd RAM Bank Register definitions ;***************************************************************************************** ;********************************************************************************* ; Bank 1 ;********************************************************************************* org $10 ;********************************************************************************* ; Bank 3 ;********************************************************************************* org $30 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 5 ;********************************************************************************* org $50 uart_2_tx = $ ;UART 2 TX bank ;divisor_2 ds 1 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 1 ;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 7 ;********************************************************************************* org $70 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 1 ;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 9 ;********************************************************************************* org $90 uart_tx_ring_1 = $ ;UART channel 1 TX ring buffers tx_ring_1 ds tx_ring_size_1 ;********************************************************************************* ; Bank B ;********************************************************************************* org $b0 uart_rx_ring_1 = $ ;UART channel 1 RX ring buffers rx_ring_1 ds rx_ring_size_1 ;********************************************************************************* ; Bank D ;********************************************************************************* org $d0 uart_tx_ring_2 = $ ;UART channel 2 TX ring buffers tx_ring_2 ds tx_ring_size_2 ;********************************************************************************* ; Bank F ;********************************************************************************* org $f0 uart_rx_ring_2 = $ ;UART channel 2 RX ring buffers rx_ring_2 ds rx_ring_size_2 ;***************************************************************************************** ; Even RAM Bank Register definitions ;***************************************************************************************** ;********************************************************************************* ; Bank 2 ;********************************************************************************* org $20 ;********************************************************************************* ; Bank 4 ;********************************************************************************* org $40 ;********************************************************************************* ; Bank 6 ;********************************************************************************* org $60 ;********************************************************************************* ; Bank 8 ;********************************************************************************* org $80 ;********************************************************************************* ; Bank A ;********************************************************************************* org $a0 ;********************************************************************************* ; Bank C ;********************************************************************************* org $c0 ;********************************************************************************* ; Bank E ;********************************************************************************* org $e0 ;***************************************************************************************** ; 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 ;***************************************************************************************** ; Interrupt Service Routine ;***************************************************************************************** ORG 0 ;interrupt starts at 0h interrupt ;********************************************************************************* ; 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. ;********************************************************************************* autobaud1 snb uart1_en ;Don't run if UART channel 1 is already running jmp :autobaud_done ; jnb RTS_1,:autobaud_done ; if RTS = false, so DTE has nothing to send ; jnb DTR_1,:autobaud_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 :autobaud_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 :autobaud_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 :autobaud_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 :autobaud_done ;********************************************************************************* ; 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 ; ; ;********************************************************************************* UART1 jnb uart1_en,UART2 ;execute UART channel 1 only when enabled jb speed_1,:fast_1 call @uart_1_transmit skip :fast_1 call @uart_1_transmit_115 UART2 IF SX_TYPE = 1 ;only compile if target is SX52 jnb uart2_en,uarts_done ;execute UART channel 2 only when enabled call @uart_2_transmit 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 mov w,#%00010000 ;point FSR to odd banks mov fsr,w ENDIF call @uarts_init ;initialise the uarts mov !option,#%10011111 ;enable rtcc interrupt jmp @main ;***************************************************************************************** ; MAIN PROGRAM CODE ;***************************************************************************************** org $200 main IF SX_TYPE = 0 ;SX28 jmp main28 ELSE ;SX52 jmp main52 ENDIF ;********************************************************************************* ; Main Program Loop ;********************************************************************************* IF SX_TYPE = 0 ;only compile if target is SX28 main28 jnb uart1_en,$ 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 call @send_byte_1 ;echo byte back to sender on channel 1 jmp main_loop ;return ELSE main52 jnb uart1_en,$ ;wait for autobaud on UART channel 1 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 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 main_loop snb cs ;wait for chip select active(low) jmp main_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,'SX DUART Demo 04/12/99',0 _ch1 dw 13,10,13,10,'Channel 1',0 _ch2 dw 13,10,13,10,'Channel 2',0 _cr dw 13,10,0 _prompt dw 13,10,'>',0 _hi dw 13,10,'Hi!',0 _space dw ' ',0 ;***************************************************************************************** ; SUBROUTINES ;***************************************************************************************** ; Function: uarts_init ; Initialise the UARTs ;********************************************************************************* 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 ;********************************************************************************* ; 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 ;***************************************************************************************** ; Jump Table for Page 2 ;***************************************************************************************** org $600 uart_1_transmit_115 jmp uart_1_transmit_115_ ;********************************************************************************* ; VP: uart_1_transmit ; 110 - 57.6Kbaud UART with RTS/CTS handshake ; ** Part of the Interrupt Service Routine ** ;********************************************************************************* uart_1_transmit 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 retp ; Return to the interrupt handler 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 retp ; Return to the interrupt handler 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 retp ; Return to the interrupt handler :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 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 bank uart_2_tx ;switch to serial register bank decsz tx_divide_2 ;ready to transmit bit? jmp uart_2_receive ;else, exit mov w,#24 ;reload 9600bps period constant mov tx_divide_2,w 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,#36 mov rx_divide_2,w :rxbit decsz rx_divide_2 ;ready to transmit bit? retp ; Return to the interrupt handler mov w,#24 ;reload 1 bit period constant mov rx_divide_2,w :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 retp ; Return to the interrupt handler 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 retp ; Return to the interrupt handler :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 retp ; Return to the interrupt handler ENDIF ;********************************************************************************* ; VP: uart_1_transmit_115 ; 57.6K - 115.2Kbaud UART with RTS/CTS handshake ; ** Part of the Interrupt Service Routine ** ;********************************************************************************* uart_1_transmit_115_ bank uart_1_tx ;switch to serial register bank decsz tx_divide_1 ;ready to transmit bit? jmp uart_1_rx_115 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_115 ;yes, go to :transmit_out ; jnb DTR_1,uart_1_rx_115 ; jnb RTS_1,uart_1_rx_115 :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_115 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? retp ; Return to the interrupt handler 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 retp ; Return to the interrupt handler 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 retp ; Return to the interrupt handler :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 retp ; Return to the interrupt handler ;***************************************************************************************** END ;End of program code ;*****************************************************************************************
file: /Techref/scenix/lib/io/osi2/serial/Duart_0412.SRC, 48KB, , updated: 2002/1/15 15:43, local time: 2024/12/8 19:42,
18.97.14.82:LOG IN
|
©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.SRC"> scenix lib io osi2 serial Duart_0412</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! |
.