# 18F2550 Software Uart in Assembly Problem

#### ozirock

Joined Jan 13, 2011
47
Hello,

I'm playing around with UART on a 18F2550 after using 16F chips previously, I set up the hardware UART as with the 16F without any problem, I send a value it returns the same value.

I then tried to return the value using software uart on PORTB,7 but it sends back different values which makes me think there is something wrong with the clock speed possibly? I am using a 4MHz crystal and the XT_XT configuration.

I'm using the following code to implement the software UART at a baud rate of 9600

Rich (BB code):
baud                            ; AT 2400 BAUD THE PERIOD IS 416.6 US
; CLK = 4MHz
movlw    D'16'       ; 1 US (BAUD RATE CONSTANT)
movwf   COUNT     ; 1 US
baud1
decfsz   COUNT,F  ; 1 US (+ 1 US MORE IF SKIP)
goto      baud1     ; 2 US
; FALL THRU...AFTER 1+1+3x68+1 = 207 US
half_baud
movlw    D'16'       ; 1 US
movwf    COUNT    ; 1 US
hbaud1
decfsz    COUNT,F ; 1 US (+ 1 US MORE IF SKIP)
goto      hbaud1   ; 2 US
retlw     0            ; ...AFTER 1+1+3x68+1 = 207 US (X2=414 US)

outch_n                        ; THIS ROUTINE USES 8 DATA BITS
movwf    SERBUF    ; SERBUF CONTAINS CHARACTER TO XMT
movlw    8             ; THE CHARACTER HAS 8 BITS
movwf    TEMP
bcf         PORTB,7   ; SET START-BIT TO A "SPACE"
call         baud        ; WAIT ONE BAUD TIME
outch_n1
rrf          SERBUF,F  ; ROTATE THE FIRST BIT INTO CARRY
btfss      STATUS,0  ; TEST THE CARRY BIT
bcf         PORTB,7   ; IF BIT IS 0 SET OUTPUT PIN TO A "0" (SPACE)
btfsc      STATUS,0 ; TEST THE CARRY BIT AGAIN
bsf         PORTB,7   ; IF BIT IS 1 SET OUTPUT PIN TO A "1" (MARK)
call         baud       ; ONE BAUD-BIT DELAY
decfsz     TEMP,F    ; IF COUNT IS ZERO THEN XMIT A STOP BIT
goto        outch_n1 ; ...ELSE XMIT NEXT BIT
rrf           SERBUF,F ; ROTATE CARRY, GET THE MSB BACK INTO BIT 7
bsf          PORTB,7   ; SET PIN TO A 1 (A "MARK") FOR THE STOP BIT
call         baud        ; FIRST BAUD-BIT DELAY
call         baud        ; SECOND BAUD-BIT DELAY
retlw       0             ; RETURN WITH THE CHARACTER IN SERBUF
Is there something I'm missing, something thats different in the 18F series compared to the 16F which is causing the problem?

#### JohnInTX

Joined Jun 26, 2012
4,669
When decfsz is followed by goto it takes a total of 3 Tcyc to execute the skip (because goto is a 2 word instruction in the 18F, only 1 in the 16F). Use bra instead. Note that taking the goto takes the same 3Tcyc as before.

I've done many software uarts but sure don't recommend it if you have a hardware one available.

#### ozirock

Joined Jan 13, 2011
47
Hi John,

Thanks for the correction, I'll try that out after work and hopefully that will solve it for me.

Thanks for the advice, from the 16F pics I have found there are a couple of less than ideal limitations to the software uart but as I'm only learning to use the 18F I taught I would try learn by recreating things I've used in the past.

Thanks again!!

#### takao21203

Joined Apr 28, 2012
3,702
Synchronous serial I/O is not too difficult in software, but for asynchronous, you need to maintain the timing, so assembler must be used. That is best avoided, if any possible.

#### JohnInTX

Joined Jun 26, 2012
4,669
Hi John,

Thanks for the correction, I'll try that out after work and hopefully that will solve it for me.

Thanks for the advice, from the 16F pics I have found there are a couple of less than ideal limitations to the software uart but as I'm only learning to use the 18F I taught I would try learn by recreating things I've used in the past.

Thanks again!!
I'm not going to add up your delays but don't forget that call/return each take 2 Tcyc as well. As for the limitations of the 16F vs 18F, there are are many but the problem of managing software uart delays is common to any software uart - not to mention that a simple count-a-bunch delay consumes virtually all of your CPU time. Remember that after you correctly receive a string of chars, you still have to go away and process them. If another char comes in while this is happening you'll miss the char. There are ways to mitigate this problem but IMHO, its not worth the trouble unless you have a very compelling reason to do so.

BTW: I'm not sure what the 207us/416us stuff is all about. The bittime at 9600baud is 104us. Comments are for 2400 baud maybe?

Last edited:

#### MMcLaren

Joined Feb 14, 2010
856
Hi Oisin,

I don't see any obvious problems with your soft usart routine but if you use MPLAB Simulator and Stopwatch you'll see that the bit timing is 110 cycles (usecs) between bits instead of 104 cycles which may be a problem.

May I make a recommendation? Consider using a "cycle accurate" fixed delay subsystem which would allow precise timing using almost any clock (4, 8, 12, 16, 20 MHz, etc.). As an example, here's a cycle accurate 9600 baud Put232 subroutine which should work with almost any clock by utilizing a "cycle accurate" fixed delay subsystem to account for loop cycle overhead;

Rich (BB code):
#define setc    bsf   STATUS,C
#define clrc    bcf   STATUS,C
#define skpc    btfss STATUS,C
#define skpnc   btfsc STATUS,C
Rich (BB code):
        radix   dec
Put232                          ; 9600 baud Tx subroutine
movwf   txbyte          ; save Tx data byte
movlw   10              ; 1 start + 8 data + 1 stop bit
movwf   BitCtr          ; setup bit counter
clrc                    ; C = 0 (start bit)
bra     SendBit         ; send start bit
NextBit
DelayCy(104*usecs-10)   ; 104 usecs -10 cycle loop time
setc                    ; always shift in a 'stop' bit
rrcf    txbyte,F        ; put data bit in Carry
SendBit
iorlw   1<<TxPin        ; set TxPin bit to 1
skpc                    ; if data bit = 1 skip, else
xorlw   1<<TxPin        ; set TxPin bit to 0
movwf   PORTB           ; precise "bit time" intervals
decfsz  BitCtr,F        ; done? yes, skip, else
bra     NextBit         ; send next bit
return                  ;
Please note that you can change the polarity of the RS232 output by replacing the skpc (skip carry) instruction with a skpnc (skip no carry) instruction. Also, if you need two stop bits instead of one, simply change the literal constant '10' value to '11'.

Here's an example of a "cycle accurate" fixed delay subsystem that might be used with the Put232 subroutine listed above;

Rich (BB code):
;******************************************************************
;  K8LH DelayCy() subsystem macro generates four instructions	  *
;******************************************************************

clock   equ     4               ; 4, 8, 12, 16, 20 (MHz), etc.
usecs   equ     clock/4         ; cycles/microsecond multiplier
msecs   equ     clock/4*1000    ; cycles/millisecond multiplier

DelayCy macro   delay           ; 11..327690 cycle range
movlw   high((delay-11)/5)+1
movwf   delayhi
movlw   low ((delay-11)/5)
call    uDelay-(((delay-11)%5)*2)
endm
Rich (BB code):
;******************************************************************
;  K8LH DelayCy() subsystem 16-bit uDelay subroutine              *
;******************************************************************
nop                     ; entry for (delay-11)%5 == 4
nop                     ; entry for (delay-11)%5 == 3
nop                     ; entry for (delay-11)%5 == 2
nop                     ; entry for (delay-11)%5 == 1
uDelay  addlw   -1              ; subtract 5 cycle loop time
skpc                    ; borrow? no, skip, else
decfsz  delayhi,F       ; done?  yes, skip, else
bra     uDelay          ; do another loop
return                  ;

Last edited: