Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

software UART timeout

Status
Not open for further replies.

qtommer

Member
hi , I'm receiving serial UART on an 876A using the following code. The reception is okay.
Code:
SerialRcv:	
		BANK0
		btfss	PIR1,RCIF
		goto	SerialRcv

		MOVF	RCREG,w
		RETURN


I tried to implement a software timeout whereby if no data is received after X us, retransmit occurs. If timeout occurs a second time, program goes to error loop and stays there.

The code is as follows:

Code:
;;;;;;;;;SERIAL RECEIVE;;;;;;;;;;;;;;;;;;;;
SerialRcv:	

keepwaiting:
		btfsc	PIR1,RCIF
		goto	bytercvd
bytenotrcvd:
		decfsz	timecommdelay,f         ;timecommdelay set arbitrarily as 1000us
		goto	keepwaiting
		btfss	flags,0
		goto	onemoretime
		goto	error_occured
		
		
onemoretime:
		bsf		flags,0
		goto	ATagain

bytercvd:
		movf	RCREG,w
goback:	RETURN

however, the code always jumps to error_occured routine regardless of whether bytes are received in time or not and I can't simulate UART in MPLAB to debug=(
So I need to ask, Is my implementation correct?

Thanks:)
 
More information needed. What is the purpose of the "flags" register?
 
It's hard to say without seeing the rest of your code, but I'm willing to bet that it receives one character, sets "FLAG,0", and then when it gets to "BTFSS FLAG,0" again, it skips next and goes into your error loop.
 
Last edited:
thanks for the replies,
sorry for the lack of clarity. the commented code flow goes like that:

Code:
;;;;;;;;;SERIAL RECEIVE;;;;;;;;;;;;;;;;;;;;
SerialRcv:	

keepwaiting:
		btfsc	PIR1,RCIF                  ; if byte not received, decrease timecommdelay
		goto	bytercvd                    ;else go to bytercvd routine
bytenotrcvd:
		decfsz timecommdelay,f  ;decrease timer.If time not equal zero,
		goto	keepwaiting            ;keep waiting for byte.
		btfss	flags,0                    ;Timer has elapsed. Test if time out has happened once or twice.     
		goto	onemoretime         ;if only once goto one moretime 
		goto	error_occured        ;if timer has timed out the second time(indicated by flags,0, goto error loop.
		
		
onemoretime:
		bsf		flags,0       ;timer has timed out once. Set flag to indicate      
                                                 ;one timeout has occured.
		goto	ATagain             ;Retransmit again and wait for reply.

bytercvd:
		movf	RCREG,w            ;byte is received in time, store in w reg.
        	RETURN

The purpose of the flags is to indicate that a timeout has occured once. Hence the LSB of flags is set when one timeout occurs. The second time the program times out, it checks the flags and if it finds out that it has been set, it will go straight to error_routine.

This is the whole code (without the initializations and declarations)

Code:
		CALL	InitSerial

;		call	LED_Delay
ATagain:
		BANK0
		movlw	d'1000'              ;initialize delay time
		movwf	timecommdelay

		movlw	'A'                        ;send AT
		call	SerialSend
		movlw	'T'
		call	SerialSend
		movlw	0x0D
		call	SerialSend

		
		call	rcvOK                      ;call receive routine
		bsf		PORTB,1         ;set OK LED. (3 bytes received!)
		goto	$+0                         ;halt



error_occured
		bsf		PORTB,4       ;set error LED (timeout occured twice)
		goto	$+0
;;;;;;;;;SERIAL RECEIVE;;;;;;;;;;;;;;;;;;;;
SerialRcv:	

keepwaiting:
		btfsc	PIR1,RCIF
		goto	bytercvd
bytenotrcvd:
		decfsz	timecommdelay,f
		goto	keepwaiting
		btfss	flags,0
		goto	onemoretime
		goto	error_occured
		
		
onemoretime:
		bsf		flags,0
		goto	ATagain

bytercvd:
		movf	RCREG,w
goback:	RETURN

;;;;;;;;;;RECEIVE OK ROUTINE;;;;;;;;;
rcvOK:
		call	SerialRcv
		xorlw	'K'
		btfss	STATUS,Z
		goto	rcvOK
		call	SerialRcv
		xorlw	0x0D
		btfss	STATUS,Z
		goto	rcvOK
		call	SerialRcv	
		xorlw	0x0A
		btfss	STATUS,Z
		goto	rcvOK
		RETURN
	


;;;;;;SERIAL SETUP;;;;;;;;;;;;;;;;;;;;;;;
InitSerial:
		BANK1
		movlw	b'11000000'
		iorwf	TRISC,f
		movlw	D'25'
		movwf	SPBRG
		movlw	0x24
		movwf	TXSTA
		BANK0
		movlw	0x90
		movwf	RCSTA

		Return

;;;;;;SERIAL SEND;;;;;;;;;;;;;;;;;;;;;;;;;;
SerialSend:
		BANK0
busywait:
		btfss	PIR1,TXIF
		goto	busywait
		movwf	TXREG
		return
 
Last edited:
I see your issue.

First off...ATAgain needs to be a subroutine and your "goto ATAgain" needs to be "call ATAgain". This way you can place a return instruction after that subroutine, and have your code return right back to the next instruction after the "call ATAgain" instruction.

Second off...there are only 8 bits in each RAM register and the maximum decimal value you can get with 8 bits is 255. This means that the maximum decimal number that can be loaded into a register on an 8 bit PIC is 255. You cannot move the value d'1000' into the time delay register. If you need a longer delay loop you'll have to set up what is called a "nested delay loop". If you have the register instead loaded with the value of 250, then have a second register that is decremented once each time the first register is decrimented from 250 to zero from the value of 4 to zero, that will decrement your register value of 250 four times within the same loop, which would be the equivalent of decrementing it from 1000 to zero.

Here..try this corrected code -

Code:
		CALL	InitSerial

;		call	LED_Delay



;;;;;;;;;SERIAL RECEIVE;;;;;;;;;;;;;;;;;;;;
SerialRcv:	

keepwaiting:
		btfsc		PIR1,RCIF		;branch if receive flag is set
		goto		bytercvd		;else continue
bytenotrcvd:
		decfsz		timecommdelay1,F	;decriment timecommdelay1 register once, branch if value in register = 0
		goto		keepwaiting		;loop back
		decfsz		timecommdelay2,F	;decriment timecommdelay2 register once for each time that timecommdelay1 register is decrimented to zero
		goto		keepwaiting		;branch if timecommdelay2 = zero. else loop back
		btfsc		flags,0			;branch if LSB of "flags" register is set
		goto		error_occured		;else continue
		
		
onemoretime:
		bsf		flags,0			;set LSB of "flags" register
		call		ATagain			;call AT Again subroutine to retransmit "AT"
		goto		keepwaiting		;recheck for received data

bytercvd:
		movfw		RCREG
goback:		RETURN

;;;;;;;;;;RECEIVE OK ROUTINE;;;;;;;;;
rcvOK:
		call		SerialRcv
		xorlw		a'K'
		btfss		STATUS,Z
		goto		rcvOK
		call		SerialRcv
		xorlw		0x0D
		btfss		STATUS,Z
		goto		rcvOK
		call		SerialRcv	
		xorlw		0x0A
		btfss		STATUS,Z
		goto		rcvOK
		RETURN
	


;;;;;;SERIAL SETUP;;;;;;;;;;;;;;;;;;;;;;;
InitSerial:
		BANK1
		movlw		b'11000000'
		iorwf		TRISC,f
		movlw		D'25'
		movwf		SPBRG
		movlw		0x24
		movwf		TXSTA
		BANK0
		movlw		0x90
		movwf		RCSTA

		Return

;;;;;;SERIAL SEND;;;;;;;;;;;;;;;;;;;;;;;;;;
SerialSend:
		BANK0
busywait:
		btfss		PIR1,TXIF
		goto		busywait
		movwf		TXREG
		return
keepwaiting:
		btfsc		PIR1,RCIF                  ; if byte not received, decrease timecommdelay
		goto		bytercvd                    ;else go to bytercvd routine
bytenotrcvd:
		decfsz	 	timecommdelay1,F	 ;decrease timer.If time not equal zero,
		goto		keepwaiting           	 ;keep waiting for byte.
		decfsz		timecommdelay2,F	
		goto		keepwaiting


ATagain:
		BANK0
		movlw		d'250'              ;initialize delay time
		movwf		timecommdelay1
		movlw		d'4'
		movwf		timecommdelay2

		movlw		a'A'                        ;send AT
		call		SerialSend
		movlw		a'T'
		call		SerialSend
		movlw		0x0D
		call		SerialSend

		
		call		rcvOK                      ;call receive routine
		bsf		PORTB,1        		 ;set OK LED. (3 bytes received!)
		return


error_occured
		bsf		PORTB,4    		   ;set error LED (timeout occured twice)
		goto		$

Make sure you create another register named timecommdelay2 and rename timecommdelay to timecommdelay1. The loop I put in is a nested loop that will load timecommdelay1 with the value of 250, and timecommdelay2 is loaded with the value of 4 and ensures that timecommdelay1's value of 250 is decrimented to zero 4 times which will equal a value of 1000.
 
Last edited:
got you loud and clear. Many thanks for the reply and the correction. I'll be going back to the lab to try it soon(it's a weekend in my part of the world and the lab is closed) :D

Thanks alot again! Appreciate it :) will get back to ya soon!:)
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top