18F2550 Software Uart in Assembly Problem

Discussion in 'Embedded Systems and Microcontrollers' started by ozirock, Oct 25, 2012.

  1. ozirock

    Thread Starter Member

    Jan 13, 2011
    44
    0
    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

    Code ( (Unknown Language)):
    1.  
    2. baud                            ; AT 2400 BAUD THE PERIOD IS 416.6 US
    3.                                    ; CLK = 4MHz
    4.          movlw    D'16'       ; 1 US (BAUD RATE CONSTANT)
    5.          movwf   COUNT     ; 1 US
    6. baud1
    7.          decfsz   COUNT,F  ; 1 US (+ 1 US MORE IF SKIP)
    8.          goto      baud1     ; 2 US
    9.                                   ; FALL THRU...AFTER 1+1+3x68+1 = 207 US
    10. half_baud
    11.          movlw    D'16'       ; 1 US
    12.          movwf    COUNT    ; 1 US
    13. hbaud1
    14.          decfsz    COUNT,F ; 1 US (+ 1 US MORE IF SKIP)
    15.           goto      hbaud1   ; 2 US
    16.           retlw     0            ; ...AFTER 1+1+3x68+1 = 207 US (X2=414 US)
    17.  
    18.  
    19. outch_n                        ; THIS ROUTINE USES 8 DATA BITS
    20.          movwf    SERBUF    ; SERBUF CONTAINS CHARACTER TO XMT
    21.          movlw    8             ; THE CHARACTER HAS 8 BITS
    22.          movwf    TEMP
    23.          bcf         PORTB,7   ; SET START-BIT TO A "SPACE"
    24.          call         baud        ; WAIT ONE BAUD TIME
    25. outch_n1
    26.          rrf          SERBUF,F  ; ROTATE THE FIRST BIT INTO CARRY
    27.          btfss      STATUS,0  ; TEST THE CARRY BIT
    28.          bcf         PORTB,7   ; IF BIT IS 0 SET OUTPUT PIN TO A "0" (SPACE)
    29.          btfsc      STATUS,0 ; TEST THE CARRY BIT AGAIN
    30.          bsf         PORTB,7   ; IF BIT IS 1 SET OUTPUT PIN TO A "1" (MARK)
    31.          call         baud       ; ONE BAUD-BIT DELAY
    32.          decfsz     TEMP,F    ; IF COUNT IS ZERO THEN XMIT A STOP BIT
    33.          goto        outch_n1 ; ...ELSE XMIT NEXT BIT
    34.          rrf           SERBUF,F ; ROTATE CARRY, GET THE MSB BACK INTO BIT 7
    35.          bsf          PORTB,7   ; SET PIN TO A 1 (A "MARK") FOR THE STOP BIT
    36.          call         baud        ; FIRST BAUD-BIT DELAY
    37.          call         baud        ; SECOND BAUD-BIT DELAY
    38.          retlw       0             ; RETURN WITH THE CHARACTER IN SERBUF
    39.  
    40.  
    41.  
    Is there something I'm missing, something thats different in the 18F series compared to the 16F which is causing the problem?
     
  2. JohnInTX

    Moderator

    Jun 26, 2012
    2,347
    1,029
    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.
     
  3. ozirock

    Thread Starter Member

    Jan 13, 2011
    44
    0
    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!!
     
  4. takao21203

    Distinguished Member

    Apr 28, 2012
    3,577
    463
    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.
     
  5. JohnInTX

    Moderator

    Jun 26, 2012
    2,347
    1,029
    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: Oct 25, 2012
  6. MMcLaren

    Well-Known Member

    Feb 14, 2010
    759
    116
    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;

    Code ( (Unknown Language)):
    1.  
    2. #define setc    bsf   STATUS,C
    3. #define clrc    bcf   STATUS,C
    4. #define skpc    btfss STATUS,C
    5. #define skpnc   btfsc STATUS,C
    6.  
    Code ( (Unknown Language)):
    1.  
    2.         radix   dec
    3. Put232                          ; 9600 baud Tx subroutine
    4.         movwf   txbyte          ; save Tx data byte
    5.         movlw   10              ; 1 start + 8 data + 1 stop bit
    6.         movwf   BitCtr          ; setup bit counter
    7.         clrc                    ; C = 0 (start bit)
    8.         bra     SendBit         ; send start bit
    9. NextBit
    10.         DelayCy(104*usecs-10)   ; 104 usecs -10 cycle loop time
    11.         setc                    ; always shift in a 'stop' bit
    12.         rrcf    txbyte,F        ; put data bit in Carry
    13. SendBit
    14.         movf    PORTB,W         ; read port
    15.         iorlw   1<<TxPin        ; set TxPin bit to 1
    16.         skpc                    ; if data bit = 1 skip, else
    17.         xorlw   1<<TxPin        ; set TxPin bit to 0
    18.         movwf   PORTB           ; precise "bit time" intervals
    19.         decfsz  BitCtr,F        ; done? yes, skip, else
    20.         bra     NextBit         ; send next bit
    21.         return                  ;
    22.  
    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;

    Code ( (Unknown Language)):
    1.  
    2. ;******************************************************************
    3. ;  K8LH DelayCy() subsystem macro generates four instructions     *
    4. ;******************************************************************
    5.  
    6.         radix   dec
    7. clock   equ     4               ; 4, 8, 12, 16, 20 (MHz), etc.
    8. usecs   equ     clock/4         ; cycles/microsecond multiplier
    9. msecs   equ     clock/4*1000    ; cycles/millisecond multiplier
    10.  
    11. DelayCy macro   delay           ; 11..327690 cycle range
    12.         movlw   high((delay-11)/5)+1
    13.         movwf   delayhi
    14.         movlw   low ((delay-11)/5)
    15.         call    uDelay-(((delay-11)%5)*2)
    16.         endm
    17.  
    18.  
    Code ( (Unknown Language)):
    1.  
    2. ;******************************************************************
    3. ;  K8LH DelayCy() subsystem 16-bit uDelay subroutine              *
    4. ;******************************************************************
    5.         nop                     ; entry for (delay-11)%5 == 4
    6.         nop                     ; entry for (delay-11)%5 == 3
    7.         nop                     ; entry for (delay-11)%5 == 2
    8.         nop                     ; entry for (delay-11)%5 == 1
    9. uDelay  addlw   -1              ; subtract 5 cycle loop time
    10.         skpc                    ; borrow? no, skip, else
    11.         decfsz  delayhi,F       ; done?  yes, skip, else
    12.         bra     uDelay          ; do another loop
    13.         return                  ;
    14.  
     
    Last edited: Oct 25, 2012
Loading...