1. 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.
    Dismiss Notice

Sending data on 8051 serial port (asynchronously)

Discussion in '8051/8951' started by mik3ca, Oct 11, 2017.

  1. mik3ca

    mik3ca Member

    Joined:
    Jun 24, 2017
    Messages:
    376
    Likes:
    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 (text):

    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 (text):

    ;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 (text):

    ;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.
     
  2. Jon Wilder

    Jon Wilder Active Member

    Joined:
    Oct 22, 2010
    Messages:
    868
    Likes:
    83
    Location:
    Fresno, CA
    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 (asm):


    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: Nov 30, 2017

Share This Page