这些例程提供一个中断驱动的串行输入和输出,其目的是取代CIN 和 COUT 的 串行I / O例程。此代码使用单独的发送和接收缓冲区内部RAM ,所以没有需要外部芯片。
这段代码的目的是为了让您的应用程序代码,以便能够发送和接收,而不必等待每个字节的数据,通过串口。这样做是与缓冲区(内部RAM),一个用于传输和接收。当你的程序需要传送数据时,它会调用法院 。使用简单的 调查的串行I / O例程,法院将检查8051的UART仍然是前一个字节发送和等待,直到它完成,然后把它被赋予进入SBUF时称为发送的字节。此代码不会等待。相反,字节写入到内存中的一个缓冲区,并改变跟踪缓冲区的部分是保存数据的指针 。如果有空间的缓冲区,这个法院的例行立即返回到您的程序。在一些点以后的时间,当在8051的UART的准备发送另一个字节时,它会产生一个中断的 uart_intr将转移从缓冲区的字节SBUF的注册,以发送它和更新指针,以反映,一个字节从缓冲区中删除 。这个过程允许您的程序,例如,发送“一次全部”大消息。重复调用 法院放入缓冲区中的所有字节,那么他们后来由UART发送,而你的程序是免费的,以执行其他任务。只要发送缓冲区是不完整的,您的程序可能调用法院并没有等待的时间需要的UART发送位 。
同样,对于接收的数据,8051的UART产生一个中断时,它已经收到了字节。再次,uart_intr 运行并删除读SBUF的字节从UART,并写入接收缓冲区的字节(这是一个由独立的发送缓冲区)。后来,当你的程序需要接收数据时,它调用CIN获取一个字节从接收缓冲区。如果有接收缓冲区中没有数据, CIN将等待,直到收到一个字节是摆在那里。你的程序可能会调用 num_recv找出多少个字节,目前在等待接收缓冲区 。通常这是为了验证是否有至少一个字节,为您的应用程序可能有其他的任务执行和将跳过 呼吁CIN和处理收到的数据时,实际上并没有任何收到 。例如,您的程序可能执行一些冗长的计算任务,并在这段时间内的数据可能到达的 UART。uart_intr 将接受的护理,使您的计算代码需要不会被写入到同时处理数据的接收。当计算完成后,您的程序可能调用num_recv ,看看是否已收到任何数据,CIN检索的字节。只要接收缓冲区空间,字节可以被正确地接收,后来牵强CIN。
使用此代码的优点是,它可以让你的程序发送一个字节的组,无需等待每一个要发送,接收一个或多个字节,而不必定期轮询RI位。许多应用程序无法tollerate等待发送字节时,或必须能够接收字节,而代码是忙着做一些计算,后来他们从缓冲区读取。
是要付出的代价。的代码比较复杂,虽然它已经编写和测试,所以与一些运气,你可以使用它作为是。,如果应用程序需要执行数据的同时发送或接收的任务,使用这些例程通常比试图使您的应用程序检查串行端口,其工作要简单得多。另一方面,如果你的程序并没有做很多同时使用串行端口,简单的轮询I / O例程可能是更好的选择。
发送或接收的每一个字节,它消耗一些CPU时间,尤其是在快速波特率,约30-40指令执行。在8051的尽可能最快的波特率(定时器自动重装设置为0xFF,这是在11.0592兆赫57600),有160个字节的指令周期由UART传输,因此在这个extreem案件中,CPU使用率可大幅。
当然,你必须在8051的内部RAM分配的内存。两个缓冲区,可任意大小(3字节或以上),它们可能位于任何地方,甚至在仅间接面积在8052芯片256字节的内部RAM(0x80到0xFF)。您还必须选择4个字节的RAM,以容纳指针,跟踪每个缓冲区中存储多少数据。这四个指针,在直接寻址的内部RAM(0x00到0x7F)。
对于这些程序来工作,UART中断保持启用状态。呼吁init_uart_intr将启用它。您的应用程序执行的一些操作可能无法tollerate中断。例如,推/拉
电路 驱动电机时,它可能是非常不可取的,如果UART导致的指令,关闭上拉和下拉设备将在下一条指令之间的中断。另一个例子是阅读在达拉斯线设备,其中一个位,必须在2到15微秒读取。中断程序暂时停止你的程序时,UART需要传输一个字节。在时间代码的关键部分(如果有的话)过程中,您将需要禁用中断后开始计时的关键代码(“CLR EA”),然后重新启用中断之后(“SETB EA “)。如果中断被禁用时间过长,这是可能的传输将被暂停或收到的字节可能会丢失。
配置 8个变量必须定义配置内部RAM的使用。编辑应仔细,以确保对缓冲区的内存和指针是没有程序或堆栈覆盖 。这8个变量,
常量 的定义使用EQU语句汇编。第6的定义在内存位置,在8051的内部RAM,这些例程将存储数据。前四个是单字节的指针,保持每个缓冲区的部分是在使用的轨道。另外两个是缓冲区将存在于内存中的位置。这两个“_size”变量定义每个缓冲区将是多么大 。rx_buf_headrx_buf_tailtx_buf_headtx_buf_tail这些指针保持跟踪缓冲区中的数据。每个人都必须设置一个字节的位置。这些字节必须驻留在内存0至7F。rx_buftx_buf缓冲区的起始位置。TX缓冲器不得含有的位置00。由于代码只用间接寻址访问这些缓冲区,他们可能会在内部RAM的任何位置,包括在8052的80到FF。rx_buf_sizetx_buf_size这些定义缓冲区的大小。每个缓冲区的一个字节是从来没有使用过(空/满的条件下,加快检查)。大小23将提供一个缓冲,它可以包含22个字节,但它会使用23字节的内部存储器。这些不应该设置为0或1。使用2提供不超过调查的串行I / O的优点,但它的工作。这是你的工作,融入你的项目时,此代码,以确定这8个变量。造成这些例程使用的内存,不会被其他代码的变量,寄存器和堆栈覆盖,您必须选择一个值。这也是你决定选择这两个缓冲区的大小,以适应你的程序需要。
一般有两种技术选择缓冲区大小。第一种方法是考虑如何频繁的应用程序发送和尝试接收数据相比,与实际所需的时间,发送和接收配置波特率的字节。例如,在9600波特(约1ms的每字节),最长的任务,如果该程序执行不检查接待(CIN或num_recv)是12 ms,然后一个缓冲区大小至少13会需要(一个字节的缓冲区不使用)。
第二种方法是,忽略定时,并考虑发送和接收消息的内容。例如,如果应用程序接收来自用户的消息,最长有效的消息是9个字节,然后10个字节的接收缓冲区是可以接受的的。这是假设它是非法用户,发送,直到收到了从第一个响应第二个消息。
每个应用程序不同的是,这些例程已经设计,可配置所需的缓冲区大小。这是极为重要的选择足够大的缓冲区,并设置这八个常数,正确地分配这些例程所需的内存。
例程 uart_intr由UART产生的服务中断... ... 放入接收缓冲器接收到的字符,或从发送缓冲区中的一个字符,并将其发送。这是不应该直接调用主程序。它只能运行到8051 UART中断。通常是一个“LJMP UART_INTR”指令被放置在位置0x0023(或相似的位置,如0x2023,如果是用来监控程序)。init_uart_intr中遇到的最常见的问题之一是正确初始化8051串口。这些中断驱动的串行端口程序,必须初始化,四头/尾指针和SCON寄存器必须初始化与RI和
ti 清零(用于轮询I / O的,他们通常作为的初始化)。另一个常见的问题是正确初始化TMOD的,它控制定时器1(波特率发生器)和Timer0。应使Timer1 TH1和TL1的初始化波特率常数,它不运行。此代码演示了一个正确和完整的8051 UART的初始化将与这些串口中断例程正确 。此代码使用一个固定的(硬编码)波特率。如果你想自动检测用户的波特率,你会发现自动波特率检测页面上的代码 。num_recv接收缓冲区中有多少字节?返回的值在ACCnum_xmit发送缓冲区中有多少字节?返回的值在ACCCIN从接收缓冲区获取一个字符。如果没有缓冲区,等待事情出现。法院放入发送缓冲器在ACC的性格。如果缓冲区已满,等待,直到有一个空间,把它。
;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
*** 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, ***uf
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, ***uf ;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 ***uf, @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
复制代码
0