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

[SOLVED for 56K] 8051 SOFTWARE UART with max232

Not open for further replies.


What I'm trying to do is create a tiny circuit that allows me to connect my PC to my special wireless network. I'm using a microcontroller because I want it to convert commands from the PC to the network format which I will define later.

I took a suggestion from somewhere that unused inputs on IC's should be tied to VCC or GND. I tied both of them to VCC and now I learned based on the datasheet that I connected an internal 5K resistor between 5V and ground via the HIN232CP. Perhaps I should just break the unused input pins off the IC to not waste 1mA power? or is that a bad idea?

All items in the schematic should be explanatory in terms of identification. On the top-left is a connection point in which I connect the HM-TRP radio module.

I began coding the microcontroller and testing things out. So far, no heat, and the LED responds correctly, however the software uart does not function correctly.

Ideally, I'd like to receive data at 38400bps but I'd be happy if someone could adjust this code to make it work even at 1200bps so I know how to fix it.

It's either that, or I have max232 wired up wrong. Also, I used 2.2uF capacitors around the max232 because someone suggested I didn't need 10uF and I borrowed a circuit from the internet.

What can I do to solve this?

Here's the code, and the circuit follows.

;Attempted 9.6kbps or 19.2kbps or 38.4kbps channel half duplex

SWUBUF equ 30h ;space for UART functions

;xtal = 22118400hz
;baud = 19200bps
;bit time=((xtal/baud)/12)/2=48
;to timer reload format: 256-48=208=D0h

BITTIM equ 0D0h

SW_INT bit 01h ;Set = Byte processed. Uart functions stall until this bit is cleared again
SW_XMIT bit 0h ;Set = Transmit Data, Clear = Receive Data
S_IN bit P3.2  ;Bit from PC via MAX232
S_OUT bit P3.3 ;Bit to PC via MAX232

SWUCTR equ SWUBUF    ;State Backup variable
SWIDAT equ SWUBUF+1   ;Temporary Data
SWDAT equ SWUBUF+2   ;User Data

LED1 equ P1.7
LED2 equ P1.6
RCFG equ P1.5

org 0h
ljmp main

org 000Bh ;timer 0 interrupt run point
ljmp swruart

org 0030h ;Some spot after interrupt handler addresses

;UART ROUTINE executes every HALF bit time

  push PSW            ;Save user's carry flag
  mov C,S_IN            ;Read bit (just in case)
  xch A,SWUCTR            ;Accumulator=State. (PUSH ACC uses extra cycle)
  jnb ACC.4,swrdat       ;Data mode = State < 16 = Jump
    jb ACC.0,swendb       ;State 16=Stop bit processing.
      jnb SW_XMIT,nxmit2   ;Are we transmitting?
   setb S_OUT       ;Yes, so output stop bit (high)
   clr C           ;Clear carry (to avoid processing receive check)
   setb SW_INT       ;and set Flag. Transmit=DONE
      jnc swreb           ;Is received character stop bit?
   mov SWDAT,SWIDAT   ;Yes, so update data
   setb SW_INT       ;and set Flag. Receive=DONE
      inc A           ;Increment State
      xch A,SWUCTR       ;Backup State and restore Accumulator
      pop PSW           ;and user Carry
reti               ;and exit

;STATE=17 = Wait for new data
  jnb SW_XMIT,nxmit   ;Are we transmitting?
    jb SW_INT,nxmit   ;Yes. Is complete flag set?
      clr S_OUT       ;No, Send out start bit
      mov SWIDAT,SWDAT   ;and load data
      clr A       ;and set state to 0 (data processing mode)
      setb C       ;and set carry (to avoid receive check)
  jc norst       ;Is received character start bit?
    jb SW_XMIT,norst   ;Yes, Are we transmitting?
      clr A       ;No. set state to 0 (data processing mode)
  xch A,SWUCTR       ;Backup state and restore accumulator
  pop PSW       ;and user carry
reti           ;and exit

;State 0-15 - Data processing

  jnb ACC.0,swskn    ;Do we have the right half time?
    xch A,SWIDAT   ;Yes, so switch accumulator with temporary data
    RRC A       ;shift in incoming bit and shift out possible output bit
    xch A,SWIDAT   ;switch data and accumulator back
    jnb SW_XMIT,swskn   ;Are we in transmitting mode?
      mov S_OUT,C   ;Yes, so output correct character
  inc A           ;Increment State
  xch A,SWUCTR       ;Backup state and restore accumulator
  pop PSW       ;and user carry
reti           ;and exit

  mov TH0,#BITTIM   ;Set timer to run every 1/2 bit time
  anl TMOD,#0F2h   
  orl TMOD,#02h       ;Set timer 0 to auto-reload mode and internal
  mov SWUCTR,#0FFh   ;Set state to all bits set so it waits for data
  clr SW_INT       ;clear interrupt
  setb SW_XMIT       ;start in transmit mode
  setb TR0       ;start timer 0
  setb EA       ;Enable all interrupts
  setb ET0       ;enable timer 0 interrupt

  mov P1,#0FFh ;restore ports
  mov P3,#0FFh
  clr LED1   ;turn on a light
  clr RCFG    ;no radio config
  lcall uartcfg ;setup UART
  mov DPTR,#test ;Set pointer to test message

  clr A
  movc A,@A+DPTR
  inc DPTR ;Load next character from rom
  jz endt1 ;end transmission if character is null
  ;wait until previous transmission is done
  jnb SW_INT,siw
  mov SWDAT,A  ;setup new byte to transmit
  setb SW_XMIT ;Let UART know we want to transmit
  clr SW_INT   ;Clear interrupt (starts transmission)
sjmp redo      ;Go back and do next character

  ;Change LED color
  cpl LED1
  cpl LED2
    clr SW_XMIT ;Let UART know we want to receive
    clr SW_INT  ;Clear interrupt (starts reception)

    ;wait until byte is received
    jnb SW_INT,sirw
    inc SWDAT ;add 1h to byte
    setb SW_XMIT ;Let UART know we want to transmit
    clr SW_INT    ;Clear interrupt (starts transmission)

    ;wait until byte is sent
    jnb SW_INT,sirw2
  sjmp endt   ;go back and wait for next character

;Test data
db '*************************************',0Dh,0Ah
db '* This is a test! OOOOOOOOOO        *',0Dh,0Ah
db '* This is a test! O        O        *',0Dh,0Ah
db '* This is a test! O        O        *',0Dh,0Ah
db '* This is a test! O        O        *',0Dh,0Ah
db '* This is a test! OOOOOOOOOO        *',0Dh,0Ah
db '* This is a test!          K    KK  *',0Dh,0Ah
db '* This is a test!          K  KK    *',0Dh,0Ah
db '* This is a test!          KKK      *',0Dh,0Ah
db '* This is a test!          K  KK    *',0Dh,0Ah
db '* This is a test!          K    KK  *',0Dh,0Ah
db '*************************************',0Dh,0Ah,0Dh,0Ah
db 00h,00h ;00h = end of data


P.S. For reference, all I see at most when I test on the screen with minicom for linux is maybe one character. So it makes me wonder if the MAX232 chip has a problem.


Ok, I narrowed down the problem to my code. I decided to put in a chip which outputs back to the PC whatever is inputted into the chip with this code:
mov C,Serialinpin
mov serialoutpin,C
sjmp redo
Testing this on any baud rate on PC seems to work. At least this code worked for me at 1200bps and 115200bps. Now its a matter of recognizing and processing characters.

Ian Rogers

User Extraordinaire
Forum Supporter
Most Helpful Member
Where did you get that code?? I examined this last night and I don't get anything correct on the scope output...

19200 baud, but nothing legible... There seems to be a random "half bit" ( 26uS )... I think this is the idle. This would need to be longer than 26uS..

Ian Rogers

User Extraordinaire
Forum Supporter
Most Helpful Member
I borrowed code from https://www.8051projects.net/wiki/8051_Software_UART_Tutorial and now I'm starting to realize that his code has a bug when transmitting a character to the PC
Not only that.... But your clock cycle 1s 0.54uS... With your timer reload set to 0xD0 after the ISR the timer is sitting at 0xE5.. that means "normal" operations only get 26 clock cycles, which is why the idle state is soooooo short..

I got the timing to exactly 52uS and still crap came out!!

Ian Rogers

User Extraordinaire
Forum Supporter
Most Helpful Member
I also read that that code must be for baud's under 9600 AND!! I don't think it was intended for interrupt use..

You also missed the tiny part of -5 in the equation.. (((FOSC/BAUD) / 12 ) - 5) / 2

I will stick this code in my sim and see if it works without using interrupts..


Ok, turns out issues were completely timing. This code allowed me to use 56kbps on a 22Mhz crystal, and I had to call the functions to send/receive data. Though it would have been nice to run an interrupt for automatic input detection, but I didn't know enough about its timings to factor that in the equations. I might have missed two "nops" but my linux computer is not complaining with this code. Heck, windows might be 20x more forgiving, but I don't have a recent version of windows to test with. (my latest is XP).

;56kbps channel half duplex
;Base Math: (((22Mhz\56kbps)\12) = 32
BITTIM equ 13 ;bit time (base - 6) \ 2
BITTIMH equ 3 ;half bit time ((base \ 2)-10) \ 2
S_IN bit P3.2
S_OUT bit P3.3
RCVD bit 01h ;1=Data Received
SBUF2 equ 17h ;=R7
PBANK equ 10h ;Register Bank setup (Functions use bank 2)

;send to PC
clr ES ;Turn other interrupt off since they can wreck timing
push PSW ;Save old bank
mov PSW,#PBANK ;and load new (Now R0 through R7 have their own space)
mov R1,A ;Save accumulator

clr S_OUT ;Send Start bit
mov R2,#BITTIM 
djnz R2,$ ;Wait 1 bit time + 26c (13 x 2)
mov R3,#8h ;While Loading up + 1c
mov A,R7 ;data + 1c
RRC A ;and shifting bit + 1c
nop ;and stalling + 1c
nop ;for alignment + 1c = 31c
;Probably should have added another NOP here to reach better alignment
;but my PC forgives me. I guess because I was only off by 0.53uS

;Now process bits...
;BUG FIX: moving from C means 2 clock cycles, not 1!!!
mov S_OUT,C ;Load bit + 2c
mov R2,#BITTIM ;Set delay + 1c
djnz R2,$ ;Stall + 26c
RRC A ;get next bit + 1c
djnz R3,PCsnd2 ;repeat for rest + 2c = 32c (ACCURATE!)

setb S_OUT ;Turn on stop bit
mov R2,#BITTIM ;and wait + 1c
djnz R2,$ ;full bit time +26c
mov A,R1 ;Restore ACC + 1c
pop PSW ;Restore PSW + 1c
setb ES ;Enable INT + 1c
ret ;Exit + 2c = 32c (ACCURATE!)

;Receive Data from PC
jb S_IN,endr ;Don't run function if incoming bit isn't start bit (+2c)
;NOTE: everything is timed. The moment the input pin is lowered, the clock starts.
clr ES ;Again, disable interrupt +1c
push PSW ;and save old bank +2c
mov PSW,#PBANK ;and load new one +2c
mov R6,A ;save accumulator +1c
mov R4,#8h ;setup bit count +1c
mov R5,#BITTIMH ;setup 1/2 bit time +1c
djnz R5,$ ;stall +6c (3 x 2) = 16c = 1/2 bit time

;Now let remote process character then we receive it.
mov R5,#BITTIM ;setup bit time +1c
djnz R5,$ ;and wait +26c (13 x 2)
mov C,S_IN ;load in bit +1c
RRC A ;shift in bit +1c
nop ;must stall +1c
djnz R4,PCrcv2 ;do remaining bits +2c = 32c (ACCURATE!)
mov R5,#BITTIM ;Wait a bit more +1c
djnz R5,$ ; +26c
mov C,S_IN ;I guess I'm off 2uS but PC forgives me
jnc norcv
setb RCVD ;set received flag if last bit is stop bit
mov R7,A ;R7 = received byte
mov A,R6 ;restore accumulator
pop PSW ;and bank
setb ES ;and turn on interrupt
Not open for further replies.

Latest threads

EE World Online Articles