Sending data on 8051 serial port (asynchronously)

Status
Not open for further replies.

mik3ca

Member
I am currently using an AT89C4051 microcontroller with a 22.1184Mhz crystal attached to it.

The following code that someone else posted here is suitable for me if I don't mind synchronous transmitting operation:

Code:
mov scon,#50h
mov tmod,#21h
mov th1,#f4h
mov tl1,#f4h
setb tr1

mov a,#'A'
mov sbuf,a
jnb ti,$

clr ti

Transmission works perfectly, but there is however one problem with it. Every time I send a character, I have to wait as long as it takes for the transmit flag to be set (96 clock cycles I think). The problem is these clock cycles are precious because at any point I could receive data. So at first I wanted to do code like this:

**note: in the code, cc stands for clock cycles, and trigger is clock input.

Code:
;setup serial
mainloop:
    jnb RI,recv
        ;act on received character and ditch invalid characters (uses 19 cc)
    recv:
jb trigger,processdata
ajmp mainloop

processdata:
    ;shift out result and shift in new data while trigger is toggled (uses 54 cc)
    ;format incoming data to prepare byte to serial and set accumlator to serial data (uses 13 cc)
    CLR TI
    mov SBUF,A
    jnb TI,$
ajmp mainloop

Now the above may work wonderfully if I didn't care if data reception speed is ultra slow, but I'd like to have 9600bps for both receive and send. So I tried this method:

Code:
;setup serial
setb TI ;serial port ready to transmit
mainloop:
    jnb RI,recv
        ;act on received character and ditch invalid characters (uses 19 cc)
    recv:
jb trigger,processdata
ajmp mainloop

processdata:
    ;shift out result and shift in new data while trigger is toggled (uses 54 cc)
    ;format incoming data to prepare byte to serial and set accumlator to serial data (uses 13 cc)
    jnb TI,$ ;only stall if data isn't received
    CLR TI ;reset because last byte was received
    mov SBUF,A ;send new data
ajmp mainloop

The problem is the second method does not work. I tried to make all my functionality work within 96 clock cycles so that the serial operations work but with this last idea, all I was able to do was transmit one and only the first character until I resetted the microcontroller, and then after only the first character could be transmitted again.

Is there a good way to make this asynchronous? Now I did try the interrupt route in the past with bad luck.
 
It is best done with a receive interrupt, and when done correctly, the interrupt works beautifully.

I believe our serial interrupt vector address is 0x0023 if I remember correctly -

Code:
RXBUF     EQU    0x60
RXFLAG    EQU    0x61

    org      0x0000    ; reset vector
    ajmp     setup

    org      0x0023    ; serial interrupt vector
    push     ACC    ; save accumulator
    mov      A, PSW    ; save psw
    push     ACC    ;
    acall    serialInterrupt    ; call serial interrupt handler
    pop      ACC    ; restore psw
    mov      PSW, A    ;
    pop      ACC    ; restore accumulator
    reti

serialInterrupt:
    ; serial interrupt handler
    jnb      TI, receive
    clr      TI
    jnb      RI, serialDone   ; exit if not serial interrupt
    clr      RI               ; acknowledge receive interrupt
    mov      RXBUF, SBUF      ; received character to R0
    mov      RXFLAG, #0xFF    ; set software receive flag
serialDone:
    ret    ;done

    org      0x0100
setup:
    mov      RXFLAG, #0x00    ; clear byte received flag
    ; TH1 = 256 - ((Fosc / Baud) / 192)
    ; TH1 = 22,118,400 / 9600 / 192 = 12
    ; 256 - 12 = 244, or 0xF4
    mov      A, PCON    ; set double baud rate bit
    orl      A, #0x80   ;
    mov      PCON, A    ;
    mov      TH1, #0xF4    ; 9600 baud w/22.1184MHz crystal
    mov      TL1, #0xF4    ;
    mov      TMOD, #0x20    ; timer 1 8-bit auto reload
    setb     TR1    ; start timer 1
    mov      SCON, #0x50    ; 8-bit asynchronous UART, enable receive
    setb     ES    ; enable serial interrupts
    setb     EA    ; enable all unmasked interrupts

main:
    mov      A, #0xFF
    cjne     A, RXFLAG, recv
    mov      RXFLAG, #0x00    ; acknowledge received character
    mov      A, RXBUF    ; get received character
                ;act on received character and ditch invalid characters (uses 19 cc)
recv:
    jnb      trigger, main
    ; place what you're sending in the accumulator
    acall    sendSerial
 
sendSerial:
    jnb      TI, sendSerial
    mov      SBUF, A
    ret

    end
 
Last edited:
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…