传输是从串口发送字节数据到串口服务器进行数据传输,发送的原理与接受相似。
传输原理:当计算机要从串行端口(到外部电缆)发送一个字节时,CPU 将计算机内部总线上的字节发送到串行端口的 I/O 地址。串行端口获取字节,并在串行电缆连接器的传输引脚上一次一位(串行位流)发送出去。
UART芯片在串口完成了大部分工作,可以说UART就是串口引擎。为了传输一个数据字节,串口设备驱动程序向串口I/O地址发送一个数据。该数据进入串行端口中的 1 字节“传输移位寄存器”。从这个移位寄存器中,一个比特一个字节地从数据中取出,并在串行线上被一个比特地发送出去。然后,当最后一位发送完毕并且移位寄存器需要发送另一个字节数据时,它可以只要求确认 CPU 向其发送另一个字节数据。这样很简单,但可能会引入延迟,因为 CPU 可能无法立即获取字节。毕竟,CPU 通常除了处理串口之外,还会做其他事情。
消除这种延迟的一种方法是安排一些事情,以便 CPU 在移位寄存器需要之前获取字节数据并将其存储在串行端口硬件缓冲区中。然后当移位寄存器发送完字节数据并立即需要一个新字节时,串口硬件只是将下一个字节从自己的缓冲区传输到移位寄存器。无需调用 CPU 来获取新字节。
这个串口缓冲区的大小原本只有一个字节;今天它通常是 16 个字节。现在仍然存在一个问题,即保持这个缓冲区足够的字节数,以便当移位寄存器需要一个字节来传输时,它总是会在那里找到一个字节(除非没有更多的字节要发送)。CPU 使用中断来做到这一点。
当移位寄存器从缓冲区中取出字节并且缓冲区需要另一个字节时,它会通过在计算机总线上的专用线上施加电压来向 CPU 发送中断。除非 CPU 正在做一些非常重要的事情,否则中断会强制它停止正在做的事情并开始运行一个程序,该程序将为端口的缓冲区提供另一个字节。此缓冲区的目的是在硬件中保持一个额外的字节(等待发送)排队,以便在从串行端口电缆传输字节时不会出现间隙。
一旦 CPU 得到中断,它就会知道是谁发送了中断,因为每个串口都有一条专用的中断线(除非中断是共享的)。
然后 CPU 将开始运行串行设备驱动程序,它检查 I/0 地址上的寄存器以找出发生了什么。它发现串行的发送缓冲区为空并等待另一个字节。因此,如果要发送更多字节,它将下一个字节发送到串行端口的 I/0 地址。当前一个字节仍在传输移位寄存器中并且仍在逐位传输时,下一个字节应该到达。
回顾一下,当一个字节从串行端口的传输线完全传输出去并且移位寄存器现在是空的时,以下 3 件事几乎同时发生:
● 下一个字节从发送缓冲器移入发送移位寄存器。
● 这个新字节的传输(逐位)开始。
● 发出另一个中断以告诉设备驱动程序将另一个字节发送到现在为空的传输缓冲区。
因此我们说串口是中断驱动的。每次串口发出中断;CPU 再发送一个字节。一旦一个字节被 CPU 发送到发送缓冲区,CPU 就可以自由地进行一些其他活动,直到它得到下一个中断。串口以固定速率传输比特,由用户(或应用程序)选择。它有时被称为波特率。串行端口还会为每个字节(开始、停止和奇偶校验位)添加额外的位,因此每个字节通常会发送 10 位。以每秒 19,200 位 (bps) 的速率(也称为速度),因此有 1,920 字节/秒(以及 1,920 个中断/秒)。
完成所有这些工作对 CPU 来说是大量的工作。这是事实,原因有很多。首先,仅通过 32 位数据总线(甚至 64 位)一次发送一个 8 位字节并不是对总线宽度的非常有效的使用。此外,处理每个中断有很多开销。当接收到中断时,设备驱动程序只知道有什么东西在串口上引起了中断,但不知道这是因为发送了一个字符。设备驱动程序必须进行各种检查以找出发生了什么。相同的中断可能意味着接收到一个字符、其中一条控制线改变了状态等。
一个主要的改进是将串行端口的缓冲区大小从 1 字节扩大到 16 字节。这意味着当 CPU 收到中断时,它会给串行端口最多 16 个新字节进行传输。这是服务中断,但数据仍必须在宽总线上一次一个字节地传输。16 字节的缓冲区实际上是一个 FIFO(先进先出)队列,通常称为 FIFO。
通过串行端口接收字节类似于发送原理,只是方向相反。它也是中断驱动的。
对于带有 1 字节缓冲区的过时类型的串行端口,当从外部电缆完全接收到一个字节时,它会进入 1 字节接收缓冲区。然后端口给 CPU 一个中断,告诉它选择那个字节,这样串行端口就有空间来存储当前正在接收的下一个字节。对于具有 16 字节缓冲区的较新串行端口,可以在接收缓冲区中有 14 个字节后发送此中断(以获取字节)。然后 CPU 停止正在执行的操作,运行中断服务程序,并从端口获取 14 到 16 个字节。对于在接收到第 14 个字节时发送的中断,如果自中断以来还有 2 个字节到达,则可能有 16 个字节要获取。但是如果还有 3 个字节到达(而不是 2 个),那么 16 字节的缓冲区就会溢出。通过以这种方式设置或由于超时,它也可能拾取少于 14 个字节。
我们已经讨论了小型 16 字节串行端口硬件缓冲区,但主内存中还有更大的缓冲区。当 CPU 从硬件的接收缓冲区中取出一些字节时,它会将它们放入主内存中一个更大(比如 8k 字节)的接收缓冲区中。然后,从串行端口获取字节的程序从大缓冲区中获取它接收的字节(在程序中使用“读取”语句)。对于要传输的字节也存在类似的情况。当 CPU 需要获取一些要发送的字节时,它会将它们从主内存中的大(8k 字节)发送缓冲区中取出,并将它们放入硬件中的 16 字节小发送缓冲区中。