;interrupt driven serial i/o for the built-in uart ; +---------------------------------------------------------------+ ; | | ; | Configuration | ; | | ; +---------------------------------------------------------------+ ;These four must specify memory locations between 0-7F (one byte each) ;to serve as the pointers. .equ rx_buf_head, 0x08 .equ rx_buf_tail, 0x09 .equ tx_buf_head, 0x0A .equ tx_buf_tail, 0x0B ;These two specify the locations for the transmit and receive buffers. ;The buffers may be placed anywhere from 0-FF (internal memory), since ;they are only accessed with indirect addressing that won't intefere ;with the special function registers from 80-FF. The transmit buffer ;must not include memory location 00. .equ rx_buf, 0x0C .equ tx_buf, 0x20 ;These two specify how many bytes to use for each buffer. Any number ;may be used (not just 4,8,16,etc), but care must be taken that nothing ;else will write to the memory of the buffers, including the stack. ;Actually, one byte of the buffer is never used (but this speeds up the ;checking for full/empty), so sizes of 0 and 1 should never be specified. ;If sizes greater than 127 are specified, num_xmit and num_recv won't ;always work properly. .equ rx_buf_size, 20 .equ tx_buf_size, 11 ; +---------------------------------------------------------------+ ; | | ; | Interrupt Service Code | ; | | ; +---------------------------------------------------------------+ .org 0x0023 ljmp uart_intr ;service interrupts generated by the uart... put a received character ;into the receive buffer or get a character from the transmit buffer ;and send it. uart_intr: push psw push acc jb ti, xmit recv: clr ri mov a, rx_buf_head inc a cjne a, #rx_buf+rx_buf_size, recv2 ;check for wrap around mov a, #rx_buf recv2: cjne a, rx_buf_tail, recv_ok ;check for buffer full ;If we got here, the recv buffer is full, so we just ;discard this character. Maybe there should be an overrun ;flag so the main program can know some data was lost... mov a, sbuf pop acc pop psw reti recv_ok:mov rx_buf_head, a ;update rx_buf_head while it's still in Acc. xch a, r0 ;put r0 onto the stack, since we need @r0 ptr push acc mov a, sbuf ;get the incoming data mov @r0, a ;and put it in the buffer pop acc mov r0, a pop acc pop psw reti xmit: clr ti mov a, tx_buf_tail cjne a, tx_buf_head, xmit2 ;check for buffer empty ;If we got here, there is no data waiting to transmit. The ;uart won't generate more interrupts, so the cout routine had ;better check set ti. tx_buf_head is loaded with zero to that ;cout will know to set ti. mov tx_buf_head, #0 pop acc pop psw reti xmit2: inc a cjne a, #tx_buf+tx_buf_size, xmit3 ;check for wrap around mov a, #tx_buf xmit3: mov tx_buf_tail, a ;update tx_buf_tail while it's still in Acc. xch a, r0 ;put r0 into acc, since we use @r0 as a pointer mov sbuf, @r0 mov r0, a pop acc pop psw reti ; +---------------------------------------------------------------+ ; | | ; | Main Program Routines | ; | | ; +---------------------------------------------------------------+ ;Initialize the uart and interrupt routines. This code uses a fixed ;(hard coded) baud rate. If you'd like to include automatic baud ;rate detection, you may find code that can detect the baud rate when ;it receives the carriage return character, at this page: ; http://www.pjrc.com/tech/8051/autobaud.html .equ baud_const, 250 ;9600 baud w/ 11.0592 MHz Crystal init_uart_intr: clr ea orl pcon, #10000000b ;set double baud rate anl tmod, #00001111b ;clear all timer1 bits in tmod orl tmod, #00100000b ;set timer1 as 8 bit auto reload clr tr1 ;make sure timer1 isn't running clr tf1 mov a, #baud_const ;256 - (crystal / (baud * 192)) mov th1, a ;set timer1 rate mov tl1, a mov scon, #01010000b ;config serial port (ri and ti cleared) mov rx_buf_head, #rx_buf ;set up empty receive buffer mov rx_buf_tail, #rx_buf mov tx_buf_head, #0 ;set up transmit buffer as empty mov tx_buf_tail, #0 ;and no tx interrupt expected setb tr1 ;start timer1 setb es ;enable serial port interrupt setb ea ;also turn on interrupts in general ret ;How many bytes are in the receive buffer? Value returned in Acc num_recv: clr c clr es mov a, rx_buf_head subb a, rx_buf_tail setb es jc nrecv2 ;check for wrap around ret nrecv2: add a, #rx_buf_size ret ;How many bytes are in the transmit buffer? Value returned in Acc num_xmit: clr c clr es mov a, tx_buf_head subb a, tx_buf_tail setb es jc nxmit2 ;check for wrap around ret nxmit2: add a, #tx_buf_size ret ;Get a character from the receive buffer. If nothing is in the ;buffer, wait for something to appear. cin: clr es mov a, rx_buf_tail cjne a, rx_buf_head, cin2 ;check if buffer empty setb es nop sjmp cin cin2: inc a cjne a, #rx_buf+rx_buf_size, cin3 ;check for wrap around mov a, #rx_buf cin3: mov rx_buf_tail, a xch a, r0 push acc ;keep r0 value on the stack mov a, @r0 setb es mov r0, a pop acc xch a, r0 ret ;Put the character in Acc into the transmit buffer. If the buffer is ;full, wait until these is a space to put it. cout: push psw xch a, r0 ;put char to send into r0 push acc ;keep r0 on stack cout_wt:clr es mov a, tx_buf_head jnz cout2 ;check if not transmitting setb ti ;set ti in software since buffer is empty mov a, #tx_buf mov tx_buf_head, a mov tx_buf_tail, a cout2: inc a cjne a, #tx_buf+tx_buf_size, cout3 mov a, #tx_buf cout3: cjne a, tx_buf_tail, cout4 ;check if buffer full setb es nop ;wait for space in buffer sjmp cout_wt cout4: mov tx_buf_head, a ;update tx_buf_head xch a, r0 ;char to send in Acc, tx_buf_head in r0. mov @r0, a setb es xch a, r0 ;restore r0 value and Acc pop acc xch a, r0 pop psw ret