I have learned one way to read from a serial port via software UART is via timer interrupts. I have made an implementation of receiving data from one serial port below. I haven't tested it yet because I now also need a way to efficiently transmit data as well without causing bad timings since UART is a timer sensitive protocol. If possible, I want to use one timer to manage both UARTs.

The speed I'm aiming for is 9600bps, and my microcontroller has a crystal speed of 22.1184Mhz. Also, is my code optimized or could I optimize it further so that fewer clock cycles are required for serial processing?

  RX equ P3.0 ;receive bit
  TX equ P3.1 ;xmit bit
  sermode equ 23h ;serial mode.
  SWBUF equ 22h     ;stored data
  SWTBUF equ 24h    ;stored data temporarily
  SWI bit 0h    ;1=received valid data

  org 0000h ;machine start
  ljmp startapp

  org 000Bh ;Timer interrupt
  ljmp timerint

  org 0030h

push PSW
mov C,RX ;get bit asap
push ACC
mov TH0,#0FFh ;reset high timer byte
mov A,sermode
jnz serhalfbit
  ;entered every half bit time if sermode=0
  ;do nothing till bit is zero
  jc nostart
    ;set mode to 1
    inc sermode
    ;set access speed to slow
    mov TL0,#040h
    ;prepare to exit
    sjmp extimerint
jz serfullbit
  ;this is entered every full bit time if sermode > 0
  jnb ACC.0,noend
    jnb ACC.3,noend
      ;if sermode=9, reset it...
      mov sermode,#0h
      ;set usable buffer to temporary buffer value
      mov SWBUF,SWTBUF
      ;set SWI to stop bit value. 1=valid data...
      mov SWI,C
      ;set access speed to fast
      mov TL0,#0A0h
    sjmp extimerint
  ;if sermode=1 to 8, get bit into a buffer and increment mode
  mov A,SWTBUF
  mov SWTBUF,A
  inc sermode
;common exit from interrupt
pop ACC
pop PSW

mov P1,#0FFh        ;reset GPIO
mov P3,#0FFh
mov sermode,#0h ;start at beginning
mov TH0,#0FFh
mov TL0,#0A0h   ;9.6kbps half bit time
mov TMOD,#11h   ;16-bit timers
setb PT0        ;timer 0=high priority
setb TR0        ;timer 0 on
mov IE,#81h         ;en Timer 0 int

; --async function here--
jnb SWI,novalid
  clr SWI
  mov A,SWBUF
  ; A=received serial data
sjmp bigloop

1 Answer 1

  • You can spare a lot of trouble and cycles by configuring P3.0 as an interrupt source. This way start bit detection would not require constant polling.

  • I don't see why do you test ACC.0.

  • sermode is really a baud counter. Consider renaming.

  • I strongly recommend to sample each baud 3 or 5 times, and signal an error if samples disagree.

  • For testing the receiver, use a real RS232 part to transmit data. You may also want to set aside a spare pin, let the timer interrupt to toggle it, and observe the waveform on a scope.

  • \$\begingroup\$ I test ACC.0 and ACC.3 because sermode goes from 0 to 9 where 0 means wait for start bit and 9 means capture stop bit and process byte. 9 in binary is 1001 (LSB and 3rd from LSB set). How can I set P3.0 as an interrupt source? I thought P3.2 and P3.3 (which I used for other purposes anyway) can be the only two GPIO pins that I can configure as interrupt sources. \$\endgroup\$ Aug 28, 2017 at 20:37
  • 1
    \$\begingroup\$ I strongly recommend to sample each baud 3 or 5 times... It's a nice idea but I don't think my chip can afford it. With me trying to put in this UART code, I measured each execution with some 8051 software, and 1/2 the time allowable is spent processing the interrupt. I'm accessing the interrupt handler once every 1/2 bit time. If I spend too much time in the interrupt handler then the rest of my code will not run efficiently. \$\endgroup\$ Aug 28, 2017 at 22:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.