串行控制器(或 UART)通常包括接收 FIFO。 此 FIFO 提供对从连接到串行端口的外围设备接收的数据进行硬件控制的缓冲处理。 若要从接收 FIFO 读取数据,此设备的外围驱动程序会将读取(IRP_MJ_READ)请求发送到串行端口。
如果串行端口继续接收数据的速度超过外围驱动程序可以读取数据的速度,则接收 FIFO 可能会溢出。 为了防止由于溢出而导致数据丢失,外围驱动程序通常应将串行端口配置为使用硬件流控制。 通过流控制,串行控制器硬件会自动向外围设备发出信号,以在接收 FIFO 几乎已满时停止发送数据。 根据规则,由 SerCx2 管理的串行端口应使用硬件流控制。 有关详细信息,请参阅 流控制详细信息。
但是,流控制不应用于阻止外围设备长时间发送数据,或者设备可能无法继续正常运行。 例如,外围设备可能有一个内部数据缓冲区,如果设备被阻止太长时间将数据从此缓冲区发送到串行端口,则可能会溢出。
本页内容
使用异步读取请求
为了避免作不正确和可能的数据丢失,外围驱动程序负责及时从串行控制器的接收 FIFO 中读取数据。 通常,在收到数据之前,外围驱动程序会向串行端口发送异步读取请求,以期待来自外围设备的数据将来到达。 此读取请求在 SerCx2 I/O 队列中保持挂起状态,直到可从接收 FIFO 读取数据。
在大多数硬件平台上,外围驱动程序不需要同时挂起多个此类读取请求。 在极少数情况下,如果在接收数据后,一个读取请求的处理时间过长,以致无法及时完成,以致导致的数据备份问题可能导致外围设备丢失数据或出现其他不正常行为,驱动程序可能需要存在多个未完成的读取请求。
假设外围驱动程序一次只有一个此类读取请求挂起,则此请求中的数据缓冲区所需的大小在很大程度上取决于外围设备的已知行为。 例如,如果驱动程序事先知道设备预期的数据字节数,驱动程序会将请求中的缓冲区大小设置为此字节数。 一旦缓冲区填充来自接收 FIFO 的数据,就会完成读取请求。 作为响应,驱动程序可以异步发送新的读取请求来等待下一个数据块。
但是,外围设备驱动程序可能事先不知道需要多少数据。 在这种情况下,驱动程序将读取请求中的数据缓冲区设置为适当的大小,然后依赖时间间隔超时来标识外围设备中的数据的末尾。 为读取缓冲区选择适当的大小可能需要详细了解外围设备的工作原理。 如果读取缓冲区太小,驱动程序必须发送一个或多个额外的读取请求才能完成数据读取。
时间间隔超时详细信息
若要设置读取和写入请求的超时参数,外围驱动程序可以将 IOCTL_SERIAL_SET_TIMEOUTS 请求发送到串行端口。 读取操作的超时时间由请求中的参数值 ReadIntervalTimeout、ReadTotalTimeoutMultiplier和 ReadTotalTimeoutConstant 控制。 ReadIntervalTimeout 指定接收事务中两个连续字节之间允许的最大时间间隔。 如果 ReadTotalTimeoutMultiplier 和 ReadTotalTimeoutConstant 均为零,并且当读取请求发送到串行端口时串行控制器的接收 FIFO 为空,则此请求不会超时(因此在 SerCx2 I/O 队列中保持挂起),直到端口接收至少一字节的新数据之后。 有关详细信息,请参阅 SERIAL_TIMEOUTS。
芯片(SoC)集成电路上的系统上的串行端口可能能够以每秒几兆位或更高的峰值速率从外围设备接收数据。 此设备的外围驱动程序开发人员可能会尝试将间隔超时值(由 ReadIntervalTimeout 参数指定)设置为毫秒或更少,但此值不太可能产生所需的效果。 这是因为用于检测间隔超时的计时器的准确性受系统时钟粒度的限制。
例如,如果系统时钟周期为 15 毫秒,并且驱动程序将 ReadIntervalTimeout 值设置为 1 毫秒,则介于 0 到 15 毫秒之间任意位置的字节到字节间隔可能会触发超时。有时,此设置可能会导致从外围设备进行数据传输的中间发生超时。 为了确保仅在传输完成后才会发生超时,驱动程序可以将 ReadIntervalTimeout 设置为略高于15毫秒的值。 例如,如果 ReadIntervalTimeout 设置为 20 毫秒,则 30 毫秒的字节到字节间隔可靠地触发超时,15 毫秒或更短的间隔不会触发超时。
有关计时器准确性如何取决于系统时钟的详细信息,请参阅 计时器准确性。
流控制详细信息
最佳做法是,使用 SerCx2 管理的串行端口的外围驱动程序应将这些端口配置为使用硬件流控制来防止接收 FIFO 溢出。 如果没有挂起的读取请求,SerCx2 不会对超出接收 FIFO 缓冲区容量的接收数据进行软件缓冲处理。 如果允许此 FIFO 溢出,则数据会丢失。
若要启用硬件流控制,外围驱动程序可能会发送 IOCTL_SERIAL_SET_HANDFLOW 请求来设置串行端口的握手和流控制设置。 或者,驱动程序可能会发送 IOCTL_SERIAL_APPLY_DEFAULT_CONFIGURATION 请求来配置串行端口,以使用包含硬件流控制的默认硬件设置集。 IOCTL_SERIAL_SET_HANDFLOW 请求使用 SERIAL_HANDFLOW 结构来描述流控制设置。 IOCTL_SERIAL_APPLY_DEFAULT_CONFIGURATION 请求中可能包含以供应商指定的数据格式表达的类似信息。
如果外围驱动程序使用 IOCTL_SERIAL_SET_HANDFLOW 请求来启用硬件流控制,驱动程序应在此请求的 SERIAL_HANDFLOW 结构中设置以下标志:
- 结构的 ControlHandShake 成员中的 SERIAL_CTS_HANDSHAKE 标志。 此标志允许串行端口在接收操作中使用流量控制。
- FlowReplace 成员中的 SERIAL_RTS_CONTROL 和 SERIAL_RTS_HANDSHAKE 标志。 这些标志使得串行端口能够在传输操作中使用流控制。