;******************************************************************************* ; Duplex UART Routines for the 8xC751 and 8xC752 Microcontrollers ;******************************************************************************* ; This is a demo program showing a way to perform simultaneous RS-232 ; transmit and receive using only one hardware timer. ; The transmit and receive routines divide each bit time into 4 slices to ; allow synchronizing to incoming data that may be out of synch with outgoing ; data. ; The main program loop in this demo processes received data and sends it ; back to the transmitter in hexadecimal format. This insures that we can ; always fill up the receiver buffer (since the returned data is longer than ; the received data) for testing purposes. Example: if the letter "A" is ; received, we will echo "A41 ". ;******************************************************************************* $Title(Duplex UART Routines for the 751/752) $Date(8/20/92) $MOD751 ;******************************************************************************* ; Definitions ;******************************************************************************* ; Miscellaneous TxBitLen EQU -4 + 1 ; Timer slices per serial bit transmit. RxBitLen EQU -4 + 1 ; Timer slices per serial bit receive. RxHalfBit EQU (RxBitLen / 4) + 1 ; Timer slices for a partial bit time. ; Used to adjust the input sampling ; time point. ; Note: TxBitLen and RxBitLen are kept separate in order to facilitate the ; possibility of having different transmit and receive baud rates. The timer ; would be set up to give four slices for the fastest baud rate, and the ; BitLen for the slower channel would be set longer for the slower baud rate. ; BitLen = -4 + 1 gives four timer interrupts per bit. BitLen = -8 + 1 would ; give 8 slices, BitLen = -16 + 1 would give 16 slices, etc. TxPin BIT P1.0 ; RS-232 transmit pin (output). RxPin BIT P1.5 ; RS-232 receive pin (input). RTS BIT P1.3 ; RS-232 request to send pin (output). CTS BIT P1.6 ; RS-232 clear to send pin (input). ; Note: P1.1 and P1.2 are used to input the baud rate selection. ; RAM Locations Flags DATA 20h ; Miscellaneous bit flags (see below). TxOn BIT Flags.0 ; Indicates transmitter is on (busy). RxOn BIT Flags.1 ; Indicates receiver is on (busy). TxFull BIT Flags.2 ; Transmit buffer (1 byte only) is full. RxFull BIT Flags.3 ; Receiver buffer is full. RxAvail BIT Flags.4 ; RX buffer is not empty. OverrunErr BIT Flags.6 ; Overrun error flag. FramingErr BIT Flags.7 ; Framing error flag. BaudHigh DATA 21h ; High byte timer value for baud rate. BaudLow DATA 22h ; Low byte timer value for baud rate. TxCnt DATA 23h ; RS-232 byte transmit bit counter. TxTime DATA 24h ; RS-232 transmit time slice count. TxShift DATA 25h ; Transmitter shift register. TxDat DATA 26h ; Transmitter holding register. RxCnt DATA 27h ; RS-232 byte receive bit counter. RxTime DATA 28h ; RS-232 receive time slice count. RxShift DATA 29h ; Receiver shift register. RxDatCnt DATA 2Ah ; Received byte count. RxBuf DATA 2Bh ; Receive buffer (3 bytes long). Temp DATA 2Fh ; Temporary holding register. ;******************************************************************************* ; Interrupt Vectors ;******************************************************************************* ORG 00h ; Reset vector. AJMP RESET ORG 03h ; External interrupt 0 AJMP Intr0 ; (received RS-232 start bit). ORG 0Bh ; Timer 0 overflow interrupt. AJMP Timer0 ; (4X the RS-232 bit rate). ORG 13h ; External interrupt 1. RETI ; (not used). ORG 1Bh ; Timer I interrupt. RETI ; (not used). ORG 23h ; I2C interrupt. RETI ; (not used). ;******************************************************************************* ; Interrupt Handlers ;******************************************************************************* ; External Interrupt Int0. ; RS-232 start bit transition. Intr0: PUSH ACC ; Save accumulator, PUSH PSW ; and status. CLR IE.0 ; Disable more RX interrupts. SETB RxOn ; Set receive active flag. MOV RxCnt,#11h ; Set bit counter to expect a start. MOV RxTime,#RxHalfBit ; First sample is at a partial bit time. JB TxOn,I0TimerOn ; If TX active then timer is on. MOV RTH,BaudHigh ; Set up timer for selected baud rate. MOV RTL,BaudLow MOV TH,BaudHigh MOV TL,BaudLow SETB TR ; Start timer 0. I0TimerOn: MOV A,RxDatCnt ; Check for buffer about to be full: CJNE A,#2,Int0Ex ; one space left and a byte starting. SETB RTS ; If so, tell whoever is on the ; other end to wait. Int0Ex: POP PSW ; Restore status, POP ACC ; and accumulator. RETI ; Timer 0 Interrupt ; This is used to generate time slices for both serial transmit and receive ; functions. Timer0: PUSH ACC ; Save accumulator, PUSH PSW ; and status. JNB TxTime.7,RS232TX ; Is this an active time slice ; for an RS-232 transmit? JNB TxOn,CheckRx ; If transmit is active, INC TxTime ; increment the time slice count. CheckRx: JNB RxTime.7,RS232RX ; Is this an active time slice ; for an RS-232 receive? JNB RxOn,T0Ex ; If receive is active, increment INC RxTime ; the time slice count. T0Ex: POP PSW ; Restore status, POP ACC ; and accumulator. MOV P3,Flags ; For demo purposes, output status ; on an extra port. RETI ;******************************************************************************* ; RS-232 Transmit Routine ;******************************************************************************* RS232TX: JNB TxCnt.4,TxData ; Go if data bit. JNB TxCnt.0,TxStop ; Go if stop bit. ; Send start bit and do buffer housekeeping. TxStart: JB CTS,TxEx1 ; Is CTS asserted (low) so can we send? ; If not, try again after 1 bit time. CLR TxPin ; Set start bit. MOV TxShift,TxDat ; Get byte to transmit from buffer. CLR TxFull MOV TxCnt,#08h ; Init bit count for 8 bits of data. ; (note: counts UP). TxEx1: MOV TxTime,#TxBitLen ; Reset time slice count. SJMP CheckRx ; Restore state and exit. ; Send Next Data Bit. TxData: MOV A,TxShift ; Get un-transmitted bits. RRC A ; Shift next TX bit to carry. MOV TxPin,C ; Move carry out to the TXD pin. MOV TxShift,A ; Save bits still to be TX'd. INC TxCnt ; Increment TX bit counter MOV TxTime,#TxBitLen ; Reset time slice count. SJMP CheckRx ; Restore state and exit. ; Send Stop Bit and Check for More to Send. TxStop: SETB TxPin ; Send stop bit. JB TxFull,TxEx2 ; More data to transmit? CLR TxOn ; If not, turn off TX active flag, and CLR RTS ; make sure that whoever is on the ; other end knows it's OK to send. JB RxOn,TxEx2 ; If receive active, timer stays on, CLR TR ; otherwise turn off timer. TxEx2: MOV TxCnt,#11h ; Set TX bit counter for a start. MOV TxTime,#TxBitLen-1 ; Reset time slice count, stop bit ; > 1 bit time for synch. SJMP CheckRx ; Restore state and exit. ;******************************************************************************* ; RS-232 Receive Routine ;******************************************************************************* RS232RX: MOV C,RxPin ; Get current serial bit value. JNB RxCnt.4,RxData ; Go if data bit. JNB RxCnt.0,RxStop ; Go if stop bit. ;Verify start bit. RxStart: JC RxErr ; If bit=1, then not a valid start. MOV RxCnt,#08h ; Init counter to expect data. MOV RxTime,#RxBitLen ; Reset time slice count. SJMP T0Ex ; Restore state and exit. ; Get Next Data Bit. RxData: MOV A,RxShift ; Get partial received byte. RRC A ; Shift in new received bit. MOV RxShift,A ; Store partial result in buffer. INC RxCnt ; Increment received bit count. MOV RxTime,#RxBitLen ; Reset time slice count. SJMP T0Ex ; Restore state and exit. ; Store Data Byte, "push"ing it into the FIFO buffer. RxStop: CLR EA ; Don't interrupt the following. MOV A,RxBuf ; "PUSH" the receive buffer. XCH A,RxBuf+1 XCH A,RxBuf+2 MOV RxBuf,RxShift ; Add just completed data to buffer. INC RxDatCnt ; Increment the received byte count. SETB EA ; Re-enable interrupts. SETB RxAvail ; There is data in the RX buffer. PUSH PSW ; Save Carry (received bit)for later. MOV A,RxDatCnt ; Check receiver buffer status. CJNE A,#4,RxChk1 ; Is RX buffer overrun? SETB OverrunErr ; Set status reg overrun error flag. MOV RxDatCnt,#3 ; Re-set buffer counter to "full". RxChk1: CJNE A,#3,RxChk2 ; Is RX buffer full? SETB RxFull ; Set buffer full status. RxChk2: POP PSW ; Retrieve last received bit in Carry. JC RxEx ; If bit=0, then not a valid stop. RxErr: SETB FramingErr ; Remember bad start or stop status. RxEx: JB TxOn,RxTimerOn ; If transmit active, timer stays on, CLR TR ; otherwise turn timer off. RxTimerOn: CLR RxOn ; Turn off receive active. SETB RxTime.7 ; Set bit for no service to ; RX Time Slice Branches. SETB IE.0 ; Re-enable RS-232 receive interrupts. AJMP T0Ex ; Restore state and exit. ;******************************************************************************* ; Subroutines ;******************************************************************************* ; BaudRate - Determine and set the baud rate from switches. ; Note: if the baud rate is altered, the actual change will only occur when ; a transmit or receive is begun while the timer was not already running ; (i.e.: not already busy transmitting or receiving). BaudRate: MOV DPTR,#BaudTable ; Set pointer to baud rate table. ANL A,#03h ; Limit displacement for lookup. RL A ; Double the table index since these ; are 2 byte entries. PUSH ACC ; Save the table index for second byte. MOVC A,@A+DPTR ; Get first byte, and save as the high MOV BaudHigh,A ; byte of the baud rate timer value. POP ACC ; Get back the table index. INC A ; Advance to next table entry. MOVC A,@A+DPTR ; Get second byte, and save as the low MOV BaudLow,A ; byte of the baud rate timer value. RET ; Entries in BaudTable are for a timer setting of 1/4 of a bit time at the given ; baud rate. The two values per entry are the high and low bytes of the value ; respectively. ; Values are calculated as follows: ; Osc Frequency ; 1/4 Bit cell time (in machine cycles) = ----------------- ; Baud Rate * 48 ; Example for 9600 baud with a 16MHz crystal: ; 16,000,000 / 9600 * 48 = 34.7222... machine cycles per quarter bit time. ; Rounded, this is 35. The hexadecimal value for 35 is 23. ; 10000 hex - 23 hex (truncated to 16 bits) = FFDD. Thus, the BaudTable entry ; for 9600 baud is FF, DD hex. BaudTable: DB 0FEh,0EAh ; 1200 baud. DB 0FFh,75h ; 2400 baud. DB 0FFh,0BBh ; 4800 baud. DB 0FFh,0DDh ; 9600 baud. ; TxSend - Initiate RS-232 Transmit. TxSend: JB TxFull,$ ; Make sure TX buffer is free. SETB TxFull ; Reserve the buffer for our use. MOV TxDat,A ; Put character in buffer. JB TxOn,TSTimerOn ; Exit if transmitter already running. SETB TxOn ; Transmit active flag set. MOV TxCnt,#11h ; Init bit counter to expect a start. MOV TxTime,#TxBitLen ; Reset time slice count. JB RxOn,TSTimerOn ; Exit if receiver already active. MOV RTH,BaudHigh ; Set up timer for selected baud rate. MOV RTL,BaudLow MOV TH,BaudHigh MOV TL,BaudLow SETB TR ; Start up the bit timer. TSTimerOn: RET ; PrByte - Output a byte as ASCII hexadecimal format. PrByte: PUSH ACC ; Print ACC contents as ASCII hex. SWAP A ACALL HexAsc ; Print upper nibble. ACALL TxSend POP ACC ACALL HexAsc ; Print lower nibble. ACALL TxSend RET ; HexAsc - Convert a hexadecimal nibble to its ASCII character equivalent. HexAsc: ANL A,#0Fh ; Make sure we're working with only ; one nibble. CJNE A,#0Ah,HA1 ; Test value range. HA1: JC HAVal09 ; Value is 0 to 9. ADD A,#7 ; Value is A to F, needs pre-adjustment. HAVal09: ADD A,#'0' ; Adjust value to ASCII hex. RET ; GetRx - Retrieve a byte from the receive buffer, and return it in A. GetRx: CLR EA ; Make sure this isn't interrupted. DEC RxDatCnt ; Decrement the buffer count. MOV A,RxDatCnt ; Get buffer count. JNZ GRX1 ; Test for empty receive buffer. CLR RxAvail ; If empty, clear data available status. GRX1: ADD A,#RxBuf ; Create a pointer to end of buffer. MOV Temp,R0 ; Save R0. MOV R0,A ; Put pointer where we can indirect. MOV A,@R0 ; Get last buffer data. MOV R0,Temp ; Restore R0. CLR RxFull ; Buffer can't be full anymore. SETB EA ; Re-enable interrupts. RET ;******************************************************************************* ; Reset ;******************************************************************************* Reset: MOV SP,#2Fh ; Initialize stack start. MOV TCON,#0 ; Set timer off, INT0 to level trigger. MOV P3,#0 ; Turn off all status outputs. ; For this demo, we only set up the baud rate once at reset: MOV A, P1 ; Read baudrate bits from P1. RR A ; The switches are on bits 2 and 1. ACALL BaudRate ; Set up the selected baud rate. MOV FLAGS,#0 ; Init all status flags. MOV RxDatCnt,#0 ; Clear buffer count. MOV IE,#93h ; Turn on timer 0 interrupt and ; external interrupt 0. CLR RTS ; Assert RTS so we can receive. ; The main program loop processes received data and sends it back to the ; transmitter in hexadecimal format. This insures that we can always fill ; up the receiver buffer (since the returned data is longer than the ; received data) for testing purposes. Example: if the letter "A" is ; received, we will echo "A41 ". MainLoop: JNB RxAvail,$ ; Make sure an input byte is available. ACALL GetRx ; Get data from the receiver buffer. ACALL TxSend ; Echo original character. ACALL PrByte ; Output the char in hexadecimal format, MOV A,#20h ; followed by a space. ACALL TxSend SJMP MainLoop ; Repeat. END