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

[SOLVED for 56K] 8051 SOFTWARE UART with max232

Discussion in '8051/8951' started by mik3ca, Dec 12, 2017.

  1. mik3ca

    mik3ca Member

    Joined:
    Jun 24, 2017
    Messages:
    274
    Likes:
    1
    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.

    Code (text):

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

    SWUBUF equ 30h ;space for UART functions

    ;Math:
    ;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

    swruart:
      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
          nxmit2:
          jnc swreb           ;Is received character stop bit?
       mov SWDAT,SWIDAT   ;Yes, so update data
       setb SW_INT       ;and set Flag. Receive=DONE
          swreb:
          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
    swendb:
      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)
      nxmit:
      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)
        norst:
      xch A,SWUCTR       ;Backup state and restore accumulator
      pop PSW       ;and user carry
    reti           ;and exit

    ;State 0-15 - Data processing

    swrdat:
      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
      swskn:
      inc A           ;Increment State
      xch A,SWUCTR       ;Backup state and restore accumulator
      pop PSW       ;and user carry
    reti           ;and exit

    uartcfg:
      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
    ret

    main:
      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

    redo:
      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
      siw:
        nop
      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

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

        ;wait until byte is received
        sirw:
        nop
        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
        sirw2:
        nop
        jnb SW_INT,sirw2
       
      sjmp endt   ;go back and wait for next character


    ;Test data
    test:
    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

     
    circuit.png
     
  2. mik3ca

    mik3ca Member

    Joined:
    Jun 24, 2017
    Messages:
    274
    Likes:
    1
    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.
     
  3. mik3ca

    mik3ca Member

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

    redo:
    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.
     
  4. dave

    Dave New Member

    Joined:
    Jan 12, 1997
    Messages:
    -
    Likes:
    0


     
  5. Ian Rogers

    Ian Rogers Super Moderator Most Helpful Member

    Joined:
    Mar 28, 2011
    Messages:
    9,376
    Likes:
    920
    Location:
    Rochdale UK
    ONLINE

    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..
     
  6. mik3ca

    mik3ca Member

    Joined:
    Jun 24, 2017
    Messages:
    274
    Likes:
    1
  7. Ian Rogers

    Ian Rogers Super Moderator Most Helpful Member

    Joined:
    Mar 28, 2011
    Messages:
    9,376
    Likes:
    920
    Location:
    Rochdale UK
    ONLINE
    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!!
     
  8. Ian Rogers

    Ian Rogers Super Moderator Most Helpful Member

    Joined:
    Mar 28, 2011
    Messages:
    9,376
    Likes:
    920
    Location:
    Rochdale UK
    ONLINE
    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..
     
  9. mik3ca

    mik3ca Member

    Joined:
    Jun 24, 2017
    Messages:
    274
    Likes:
    1
    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).

    Code (text):

    ;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
    PCsnd:
    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...
    PCsnd2:
    ;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
    PCrcv:
    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.
    PCrcv2:
    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
    norcv:
    mov A,R6 ;restore accumulator
    pop PSW ;and bank
    setb ES ;and turn on interrupt
    endr:
    ret
     
     

Share This Page