• 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.

Sending data on 8051 serial port (asynchronously)

Thread starter #1
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.
 

Jon Wilder

Active Member
#2
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:

EE World Online Articles

Loading

 
Top