串口驱动分析-未完成
串口驱动分析
Auth:nasiry
date: 2005 年 4 月 12 日
abort: windowsCE.net 420 串口驱动分析
相关资料
虽然串口通讯已经是普遍的标准而且广为大家熟知,但驱动中涉及的部分内容也可能在平时
的应用中并不是很常用到,在这里做一个简单的介绍待后面说明到具体代码的时候可以连贯
一些。
串行通讯接口是目前十分流行的通讯接口之一。由于其电气界面的简单性使其在计算机领域
的应用相当的广泛。在这里提到的串行通讯接口主要是指 UART(通用串行)和 IRDA 两种。
通常的串行连接电气连接上有 3wire 和 9wire 两种。3wire 的接线方式下定义了发送、接收
和地三根连接。其用途就如名称一样分别用于发送、接收。下面是通常 3wire 连接的结构框
图
通常在串行接口控制器上会有两个 FIFO 用作接收和发送的缓冲,当接收到数据后会直接将
接收到的数据置入该缓冲器,并同时由控制电路向本地总线发出通知,以便让本地总线将缓
冲器内的数据读走,这样在响应(等待和读取)的过程中仍然能通过缓冲器来接收数据。而发
送发送的过程刚刚相反,本地总线可一直向发送缓冲写入数据直到器填满为止,而无需对每
个数据的发送进行等待。这就是基本的收发流程(这部分逻辑流程相信大家是最熟悉的)。这
一点在 3wire 和 9wire 中都是相同的。但是我们考虑下面的情况,如果接收一方的响应由于
某种原因的干扰(如处理器被其他中断服务占用)的时候可能就来不及相应之前
ReceiveFIFO 就可能被填满了,这样后续发送过来的数据就会丢失,这样在需要数据可靠
传输的情况下串行通讯的弊端也就显示出来了。如需要数据的可靠传输就需要对数据流的收
发进行控制。在 9wire 中将串行连接定义为如下形式。
针号 1 2 3 4 5 6 7 8 9
缩写 DCD RXD TXD DTR GND DSR RTS CTS DELL
功能
说明
数据载
波检测
接收数
据
发送数
据
数据终
端就绪
信号地 数据设
备就绪
请求发
送
清除发
送
振铃指
示
也就是说在原 3wire 的基础上增加了 DCD,DTR,DSR,RTS,CTS,DELL 六个控制线。其中
RTS/CTS 用于流控制,另外的 DCD 和 DELL 则留作连接 modem 使用。有了专门的硬件流
控制引脚也就使得流控制成为可能,以完成收发两端的匹配使得数据可以可靠的传输。用
RTS/CTS(请求发送/清除发送)流控制时,应将通讯两端的 RTS、CTS 线对应相连).在发
送端准备发送数据之前设置 RTS(Request to send)也就使发送请求线,若接收端以作好接
收准备,就启动响应的 CTS(Clear to send)引线。这样,收发双发就进入数据传输状态,
在此过程中如若接收端处理数据的速度低于发送端的发送速度,接收一端还可以设置 CTS
引线恢复原来阻塞得状态以暂时中断数据传输,之后若需要恢复数据传输恢复 CTS 状态即
可。这样 UART 的传输即实现了流控制,保障了数据传输的完备性。
在这里还要说一下软件流控制,虽然硬件已经可以完成流控制的任务但很多少时候受到连线
数的限制不能使用硬件流控制也就设计了专门的软件流控制的方法。现在回到 3 线传输的
情景,若接收端接收数据过程中缓冲器的负载到达某一限制(也就是留出一定的缓冲空间)时
接收端向发送端发送一个特殊的标示位(接收停止位),当发送端收到该标示的时候就停止发
送,直到接收端缓冲器低于另一限制后发送标示(接收许可位)给发送端,这样就可以控制数
据流的传输起停。这种软件流控制是在给缓冲器留余量来完成的,在收发双端处理器速度差
很大的时候就不太适用了,就必须要用硬件流控制。
其他几个引脚都是与 modem 相关的,DSR 数据装置准备好(Data set ready)用于表明 MODEM 处
于可以使用的状态。DTR 数据终端准备好(Data terminal ready)表明数据终端可以使用。这两个信
号用于检查 Modem 是否连接。DELL 脚当有电话拨入时 Modem 将会设置这个引脚。DCD 信号
是当 Modem 接收到数字载波信号的时候被设置,用于了解 Modem 接收信号的情况。
至于剩下的奇偶效验和停止位设置就只是需要针对寄存器设置无需软件干涉就可以完成了。
下面我们来看具体的驱动程序。
架构
在 wince 中串口的驱动实现是有固定模型的,ce 中的串口模型遵循 ISO/OSI 网络通讯模型
(7 层),就是说串口属于 CE 网络模块的一个部分。其中 rs232 界面(或其它的物理介质)实现
网络的物理层,而驱动和 serialAPI 共同组成数据链路层,其它部分都没有做定义。在典型
的应用中,serialAPI 与间接通过 TAPI 或直接与 ActiveSync 交互,组成 CE 网络的一部分。
而红外本身的协议就相对复杂的多,它有专门的一套模型来描述其使用规则,对红外设备本
身了解不多也就不能深入下去。在串口的这一侧,整个驱动模型也是相当的复杂的,但所幸
的是驱动仅仅使用到 SerialAPI 这一层,在这个层次上串口的行为还是相对简单的。
我们这里仅仅涉及上面所提到的 Serial/irda Driver 这部分(绿色部分)。在 wince 提供的驱
动例程中串口/红外驱动采用分层结构设计,MDD 提供框架性的实现,负责提供 OS 所需的
基本实现,并将代码设计与具体的硬件设计无关。而 PDD 提供了对硬件操作相应的代码。
这些代码通过结构 HWOBJ 来相互联系。对于 MDD+PDD 的整体驱动来看,串口驱动模型
是作为 Stream 来实现的。
两者合一以达到实现驱动的目的。DDSI 就是指这两个部分之间的接口,这个接口并非受
到强制的物理/逻辑关系来约束,而是人为的规定的。在涉及到一种特定硬件我们进行针对
实现的时候往往需要的是了解硬件的物理特性和控制逻辑,然后根据 DDSI 的约束就来进行
实现。对于这里描述的驱动模型而言结合关键在于结构指针 HWOBJ 的使用和具体实现。
在实际的驱动应用中仅仅需要实现 HWOBJ 相关的一系列函数,而无需从驱动顶层完全开
发。串口驱动模型作为一种常用驱动模型在 windowsCE 中常常用于串口/红外/USB Client
的具体实现。该驱动模型中对全功能的串口进行了定义,除了常用的 TX 和 RX 引线定义以
外,针对 DTR、RTS 等功能引脚都进行了支持,使得用该模型设计的串口驱动支持流控制、
具备驱动 Modem 等设备的能力。
事实上,如果需要的话完全可以将该驱动一体化设计(抛开 PDD-MDD 的划分,也就无须
DDSI)。也就是不使用现有的驱动架构来进行实现。考虑到串口驱动的使用频率和执行效率
要求都不是很苛刻的情况下抛弃驱动架构另外实现的就没有多大必要了。
对于驱动本身而言,串行驱动从功能和实现上相当的简单,确具被相当全面的成分,对该
驱动的分析和了解无疑是学习流式驱动程序很好的典范。
代码分析
在开始具体代码之前我们先来看看,相关的一些结构。 HWOBJ 是相应的硬件设备操作的
抽象集合。结构的定义后的注释与实际的用途有点点出入,BandFlags 指定 IST 的启动时
间,可选为在初始化过程启动或是在打开设备的时候起动 ISR.而第二个参数则是指定拦截
的具体的系统中断号。最后一个参数是一个结构,该结构定义了硬件操作的各式行为函数的
指针,MDD 正是通过这些函数来访问具体的 PDD 操作。
typedef struct __HWOBJ {
ULONG BindFlags; // Flags controlling MDD behaviour. Se above.
DWORD dwIntID; // Interrupt Identifier used if THREAD_AT_INIT or THREAD_AT_OPEN
PHW_VTBL pFuncTbl;
} HWOBJ, *PHWOBJ;
而 HW_VTBL 则是代表具体硬件操作函数指针的集合,该结构所指向的函数包括了初始化、打
开、关闭、接收、发送、设置 Baudrate 等一系列操作。结构存在就像纽带一样联系着 PDD 中
的具体实现和 MDD 中的抽象操作。PDD 的实现必须遵循 HW_VTBL 中所描述的函数形式,并
构造出相应的 HW_VTBL 实例。驱动的编写就是针对这些函数来一一进行实现。
typedef struct __HW_VTBL {
PVOID (*HWInit)(ULONG Identifier, PVOID pMDDContext, PHWOBJ pHWObj);
BOOL (*HWPostInit)(PVOID pHead);
ULONG (*HWDeinit)(PVOID pHead);
BOOL (*HWOpen)(PVOID pHead);
ULONG (*HWClose)(PVOID pHead);
INTERRUPT_TYPE (*HWGetIntrType)(PVOID pHead);
ULONG (*HWRxIntrHandler)(PVOID pHead, PUCHAR pTarget, PULONG pBytes);
VOID (*HWTxIntrHandler)(PVOID pHead, PUCHAR pSrc, PULONG pBytes);
VOID (*HWModemIntrHandler)(PVOID pHead);