;*************************************************************************** ; ; File Name :'SERIAL.asm" ; Title :Serial I/O ; Date :98.07.21 ; Version :0.01 ; Support telephone :765 287 1987 David B. VanHorn ; Support fax :765 287 1989 ; Support Email :dvanhorn@cedar.net ; Target MCU :AT90S8515 ; ; DESCRIPTION ; ;*************************************************************************** ; M O D I F I C A T I O N H I S T O R Y ; ; rev. date who why ; ---- -------- --- ------------------------------------------ ; 0.01 98.07.29 dvh Creation ; 0,02 98.08.29 dvh Added break and "wait for char" ; 0.03 98.09.01 dvh Working on making it more interrupt driven ; ;*************************************************************************** ;I'm not really satisfied with this yet, I'm not having a lot of luck modeling ;a fully interrupt-driven comm scheme along with things like the "wait for char" ;feature. Also, there's a conflict if the output buffer fills up. There is just ;no good way to handle that as far as I can see. I always end up with a construct ;that loops forever and dosen't return to the main system. ; ;Your input is most welcome here :) ;*************************************************************************************** ; Set_Baud_2400: ldi TEMP,207 ; rcall SET_BAUD ; ret Set_Baud_4800: ldi TEMP,103 ; rcall SET_BAUD ; ret Set_Baud_9600: ldi TEMP,51 ; rcall SET_BAUD ; ret Set_Baud_14400: ldi TEMP,34 ; rcall SET_BAUD ; ret Set_Baud_19200: ldi TEMP,25 ; rcall SET_BAUD ; ret Set_Baud_28800: ldi TEMP,16 ;Error is 2.1% rcall SET_BAUD ; ret Set_Baud_57600: ldi TEMP,8 ;Error is 3.7% rcall SET_BAUD ; ret Set_Baud_115200: ldi TEMP,3 ;Error is 7.8% rcall SET_BAUD ; ret Set_Baud: mov LOOP,TEMP ;Save it for a moment rcall WT4TXMT ;Wait till all prev sent data is gone out UBRR,LOOP ;Set the baud rate ret ; ; ;************************************************************ ; ;This one waits for TX buffer empty ; WT4TXMT: rcall Timed_Smack ;Reset the watchdog if needed rcall SEROUT_Check ;returns # of bytes stored, in TEMP brne WT4TXMT ; ret ; ;This one waits till the current char is done transmitting ; WT4XCHR: rcall Timed_Smack ;Reset watchdog if needed in TEMP,USR ; andi TEMP,$40 ;Get the TX Complete bit breq WT4XCHR ; ret ; ;************************************************************ ; Check_Serial_IN: rcall SERIN_Check ;returns bytes stored, in TEMP breq Check_Serial_IN_Done ; ldi ZL,low(SERIAL_IN_BUF) ;Absolute pointer to head ldi ZH,high(SERIAl_IN_BUF) ; ld TEMP,Z+ ;The head byte is junk ld TEMP,Z ;If non-empty, then take a byte out of ;serial_in_buf, and use it somehow and TEMP,TEMP ;If non zero brne Check_Serial_in_Full ;then let the serial in buffer fill ;If zero returned, then the byte was stored, so kill it from ;the serial in buf. ldi ZL,low(SERIAL_IN_BUF) ldi ZH,high(SERIAL_IN_BUF) lds YL,(SERIAL_IN_TAIL) lds YH,(SERIAL_IN_TAIL+1) rcall Kill_Head ;Kill the sent byte sts (SERIAL_IN_TAIL),YL ; sts (SERIAL_IN_TAIL+1),YH ; rcall HS_XON ;Since we ate a char, set HS to true if it was false ldi TEMP,0 ; ret Check_Serial_In_Full: ldi TEMP,$FF ;Failed exit Check_Serial_IN_Done: ret ; ;************************************************************ ; ;Set output handshaking. ;Toggled based on current status ;Jam XON or XOFF into output buffer ; HS_XOFF: lds TEMP,HS_FLAG ; and TEMP,TEMP ; breq Ser_HS_Done ; ldi TEMP,XOFF ;Jam XOFF into serial out buffer rcall Store_Serout ;Enable transmit (if not already) in TEMP,UCR ; andi TEMP,$7F ; out UCR,TEMP ;Make sure no more data in TEMP,UCR ; ori TEMP,$20 ;Turn on UDRIE out UCR,TEMP ;in case it's off, so xoff and xon get sent ldi TEMP,$00 ; sts HS_FLAG,TEMP ; rjmp SER_HS_Done ; ; ;************************************************************ ; HS_XON: lds TEMP,HS_FLAG ;Get current status and TEMP,TEMP ; brne Ser_HS_Done ;If non-zero, then it's already cleared ldi TEMP,XON ;Jam XON into serial out buffer rcall Store_Serout ;Enable transmit if not already in TEMP,UCR ; ori TEMP,$80 ; out UCR,TEMP ; in TEMP,UCR ; ori TEMP,$20 ;Turn on UDRIE out UCR,TEMP ;in case it's off, so xoff and xon get sent ldi TEMP,$FF ; sts HS_FLAG,TEMP ; Ser_HS_Done: ret ; ;************************************************************ ; SEND_BREAK: ;Assumption, that you want anything placed in the serial buffer ;to be sent before the break. rcall WT4TXMT ;Wait for serial buffer empty rcall WT4XCHR ;Wait for the last char to get out ;Shift the baud rate slightly lower. in TEMP,UBRR ;Get the current baud rate (51 @ 9600) push TEMP ;Save it mov LOOP,TEMP ;Make a copy clc ; lsr TEMP ;divide it by 8 lsr TEMP ; lsr TEMP ; add LOOP,TEMP ;Add that to the old value out UBRR,LOOP ;Set the new baudrate, two "bits" slower ldi TEMP,NUL ;Load a null, 8 bits of zero out UDR,TEMP ;Start Breaking now. rcall WT4XCHR ;Wait till the char is sent pop TEMP ;Get the old baud rate out UBRR,TEMP ;Restore the original baud rate ret ; ;************************************************************ ; ;Input: Char to get in temp, Delay in TEMP2 ;Output: TEMP2=0 for ok, or FF for timeout or wrong char ; WT4CHAR: ;This starts the Serial_Wait engine to look for an incoming char. sts Char_Time,TEMP2 ;System will decrement on 1mS system ticks sts Watch_Char,TEMP ;save temp for compare ;Turn off inbound serial ints ret ; ;Char_Time = 0, Not waiting, or timed out. ;Char_Time <> 0, actively waiting. ;Watch_Char = byte value waiting on. ; Serial_Wait: lds TEMP,Char_Time ;Are we waiting on a char (0=no) and TEMP,TEMP ;Is Char_Time=0? brne WT4CHAR_ACTIVE ;If not, then we're waiting ret ;Else return ;If we're waiting, then see if it's happened WT4CHAR_ACTIVE: in TEMP,USR ;Did we get a char yet? andi TEMP,$80 ;Check bit 7 brne WT4CHAR_GOTIT ; ret ;Nope, just keep waiting WT4CHAR_GOTIT: in TEMP2,UDR ;Get the Char andi TEMP2,$7F ;Mask off parity(?) lds TEMP,Watch_Char ;What were we looking for? cp TEMP,TEMP2 ;Is this it? breq WT4CHAR_SUCCESS ; WT4CHAR_SUCCESS: ret ; ;*************************************************************************************** ;