TCC8902串口驱动有六组串口(分别为COM0、COM1、COM2、COM3、COM4、COM5),运行WinCE系统,除去一个调试口(COM0),只剩下5组。原注册表只使能COM1和COM2,其它串口需要自己手动添加。而且COM4和COM5不支持DMA。
虚拟串口的概念:在调试GPS模块时,经常需要监视GPS数据又不影响GPS软件的运行,这时就需要内建虚拟串口。
发送:由不同的虚拟串口往物理串口发送的过程。
接收:将物理串口收到的数据往不同虚拟串口分发的过程。
COM_ChNum和COM_PortNum的对应关系如下:
COM_ChNum | COM_PortNum |
1 | 1 |
2 | 4 |
3 | 5 |
4 | 2 |
5 | 3 |
其对应关系,在PLATFORM\Magellan\Src\LIB\SOC\UART\TCC89x\tca_serial.c文件下,函数tca_serial_portinit()代码中有映射。
而注册表必须和代码一致。 中间为 Port 后面为channel。
下面是完整的串口设置列表(串口属性统计表):
序号 | Channel | Port | Tx | Rx | 注册表 | DMA |
调试口 | 0 | 0 | GPIO E[0] | GPIO E[1] | 无 | 支持 |
COM1
| 3 | 5 | GPIO D[17] | GPIO D[18] | ;=================COM1======================== ;[INFO] using UART ch3 , GPIO port5 ;============================================= [HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Serial1] "Prefix"="COM" "DeviceArrayIndex"=dword:0 "Index"=dword:1 "Port"="COM1:"
"Dll"="tcc_serial.dll" "Priority256"=dword:e0 "DeviceType"=dword:0 "FriendlyName"="Serial Cable on COM1:" "Tsp"="unimodem.dll" "Order"=dword:1 "IClass"="{CC5195AC-BA49-48a0-BE17-DF6D1B0173DD}" "DevConfig"=hex: 10,00, 00,00, 05,00,00,00, 10,01,00,00, 00,4B,00,00, 00,00, 08, 00, 00, 00,00,00,00
;UART phsical setting "COM_ChNum"=dword:3 "COM_PortNum"=dword:5 ;Setting TX ;0: UART ;1: DMA "TXLogic"=dword:1 "DmaTxNumber"=dword:1 "DmaTxChannel"=dword:0 ;Setting RX I ;0: UART ;1: DMA ;2: DMA + Timer "RXLogic"=dword:1 "DmaRxNumber"=dword:1 "DmaRxChannel"=dword:2 ;"TimerNumber"=dword:1 ;"TimerInterval"=dword:0A ; ms | 支持 |
COM2
| 1 | 1 | GPIO E[4] | GPIO E[5] | ;=================COM2======================== ;[INFO] using UART ch1 , GPIO port1 ;============================================= [HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Serial2] "Prefix"="COM" "DeviceArrayIndex"=dword:1 "Index"=dword:2 "Port"="COM2:" "Dll"="tcc_serial.dll" "Priority256"=dword:e0 "DeviceType"=dword:0 "FriendlyName"="Serial Cable on COM2:" "Tsp"="unimodem.dll" "Order"=dword:1 "IClass"="{CC5195AC-BA49-48a0-BE17-DF6D1B0173DD}" "DevConfig"=hex: 10,00, 00,00, 05,00,00,00, 10,01,00,00, 00,4B,00,00, 00,00, 08, 00, 00, 00,00,00,00 ;UART phsical setting "COM_ChNum"=dword:1 "COM_PortNum"=dword:1 ;Setting TX ;0: UART ;1: DMA "TXLogic"=dword:1 "DmaTxNumber"=dword:1 "DmaTxChannel"=dword:0 ;Setting RX I ;0: UART ;1: DMA ;2: DMA + Timer "RXLogic"=dword:1 "DmaRxNumber"=dword:1 "DmaRxChannel"=dword:1 ;"TimerNumber"=dword:2 ;"TimerInterval"=dword:0A ; ms | 支持 |
COM3
| 2 | 4 | GPIO D[13] | GPIO D[14] | ;=================COM3======================== ;[INFO] using UART ch2 , GPIO port4 ;============================================= [HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Serial3] "Prefix"="COM" "DeviceArrayIndex"=dword:2 "Index"=dword:3 "Port"="COM3:" "Dll"="tcc_serial.dll" "Priority256"=dword:e0 "DeviceType"=dword:0 "FriendlyName"="Serial Cable on COM3:" "Tsp"="unimodem.dll" "Order"=dword:1 "IClass"="{CC5195AC-BA49-48a0-BE17-DF6D1B0173DD}" "DevConfig"=hex: 10,00, 00,00, 05,00,00,00, 10,01,00,00, 00,4B,00,00, 00,00, 08, 00, 00, 00,00,00,00 ;UART phsical setting "COM_ChNum"=dword:2 "COM_PortNum"=dword:4
;Setting TX ;0: UART ;1: DMA "TXLogic"=dword:1 "DmaTxNumber"=dword:1 "DmaTxChannel"=dword:3 ;Setting RX I ;0: UART ;1: DMA ;2: DMA + Timer "RXLogic"=dword:2 "DmaRxNumber"=dword:2 "DmaRxChannel"=dword:4 "TimerNumber"=dword:2 "TimerInterval"=dword:0A ; ms | 支持 |
COM4
| 4 | 2 | GPIO E[8] | GPIO E[9] | ;=================COM4======================== ;[INFO] using UART ch4 , GPIO port2 ;============================================= [HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Serial4] "Prefix"="COM" "DeviceArrayIndex"=dword:3 "Index"=dword:4 "Port"="COM4:"
"Dll"="tcc_serial.dll" "Priority256"=dword:e0 "DeviceType"=dword:0 "FriendlyName"="Serial Cable on COM4:" "Tsp"="unimodem.dll" "Order"=dword:1 "IClass"="{CC5195AC-BA49-48a0-BE17-DF6D1B0173DD}" "DevConfig"=hex: 10,00, 00,00, 05,00,00,00, 10,01,00,00, 00,4B,00,00, 00,00, 08, 00, 00, 00,00,00,00
;UART phsical setting "COM_ChNum"=dword:4 "COM_PortNum"=dword:2
;Setting TX ;0: UART ;1: DMA "TXLogic"=dword:0 ;"DmaTxNumber"=dword:1 ;"DmaTxChannel"=dword:3
;Setting RX I ;0: UART ;1: DMA ;2: DMA + Timer "RXLogic"=dword:0 ;"DmaRxNumber"=dword:2 ;"DmaRxChannel"=dword:4 ;"TimerNumber"=dword:4 ;"TimerInterval"=dword:0A ; ms | 不支持 |
COM5
| 5 | 3 | GPIO E[10] | GPIO E[11] | ;=================COM5======================== ;[INFO] using UART ch5 , GPIO port3 ;============================================= [HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Serial5] "Prefix"="COM" "DeviceArrayIndex"=dword:4 "Index"=dword:5 "Port"="COM5:" "Dll"="tcc_serial.dll" "Priority256"=dword:e0 "DeviceType"=dword:0 "FriendlyName"="Serial Cable on COM5:" "Tsp"="unimodem.dll" "Order"=dword:1 "IClass"="{CC5195AC-BA49-48a0-BE17-DF6D1B0173DD}" "DevConfig"=hex: 10,00, 00,00, 05,00,00,00, 10,01,00,00, 00,4B,00,00, 00,00, 08, 00, 00, 00,00,00,00
;UART phsical setting "COM_ChNum"=dword:5 "COM_PortNum"=dword:3
;Setting TX ;0: UART ;1: DMA "TXLogic"=dword:0 ;"DmaTxNumber"=dword:1 ;"DmaTxChannel"=dword:3 ;Setting RX I ;0: UART ;1: DMA ;2: DMA + Timer "RXLogic"=dword:0 ;"DmaRxNumber"=dword:2 ;"DmaRxChannel"=dword:4 ;"TimerNumber"=dword:4 ;"TimerInterval"=dword:0A ; ms | 不支持 |
虚拟串口注册表
串口号 | 虚拟串口 | 注册表 | 备注 |
COM2 | VS_Serial6 | ;=================VS_Serial6======================== ;[INFO] Visual Serial VS_Serial6 ;============================================= [HKEY_LOCAL_MACHINE\Drivers\BuiltIn\VS_Serial6] "Priority"=dword:0 "DeviceArrayIndex"=dword:5 "PortIsRO"=dword:0 "SplitPort"=dword:2 "Prefix"="COM" "Index"=dword:6 "Port"="COM6:" "Dll"="vspd.dll" "DeviceType"=dword:0 "Tsp"="Unimodem.dll" "FriendlyName"="Visual port COM6" "DevConfig"=hex:10,00,00,00,05,00,00,00,10,01,00,00,00,4b,00,00,00,00,08,00,00,00,00,00,00 |
|
VS_Serial8 |
;=================VS_Serial8======================== ;[INFO] Visual Serial VS_Serial8 ;============================================= [HKEY_LOCAL_MACHINE\Drivers\BuiltIn\VS_Serial8] "Priority"=dword:0 "DeviceArrayIndex"=dword:7 "PortIsRO"=dword:0 "SplitPort"=dword:2 "Prefix"="COM" "Index"=dword:8 "Port"="COM8:" "Dll"="vspd.dll" "DeviceType"=dword:0 "Tsp"="Unimodem.dll" "FriendlyName"="Visual port COM8" "DevConfig"=hex:10,00,00,00,05,00,00,00,10,01,00,00,00,4b,00,00,00,00,08,00,00,00,00,00,00 |
| |
VS_Serial9 | ;=================VS_Serial9======================== ;[INFO] Visual Serial VS_Serial9 ;============================================= [HKEY_LOCAL_MACHINE\Drivers\BuiltIn\VS_Serial9] "Priority"=dword:0 "DeviceArrayIndex"=dword:7 "PortIsRO"=dword:0 "SplitPort"=dword:2 "Prefix"="COM" "Index"=dword:9 "Port"="COM9:" "Dll"="vspd.dll" "DeviceType"=dword:0 "Tsp"="Unimodem.dll" "FriendlyName"="Visual port COM9" "DevConfig"=hex:10,00,00,00,05,00,00,00,10,01,00,00,00,4b,00,00,00,00,08,00,00,00,00,00,00 |
|
2、驱动功能需求
1) 收星虚拟串口
源数据(收星数据、胎压检测等)由外部MCU发出,该驱动解析各类数据,并分发给上层应用程序。一般情况下,地图以串口方式读取数据,因此GPS数据应虚拟成串口方式发出。
2) 注册表与配置文件
据各地图要求设置注册表或者配置文件。
3) 特殊要求
如IGO地图对星历数据输出要求每秒都有输出,以及系统时间对收星的影响。
4) 数据测试
短接后,发送300KB,每隔10ms发送100Bytes数据测试。
5) 串口配置
包括波特率、奇偶校验位、停止位。
6) 串口属性
统计每个串口的属性,如DMA、奇偶校验位 。
7) 长字符短字符
长字符串和短字符串收发测试(测试方法待定)
8) 固定长度读数据,超时设置
(测试方法待定)
3、驱动总体设计
收发数据流程
在串行接口控制器上会有两个FIFO用作接收和发送的缓冲,当接收到数据后会直接将接收到的数据置入该缓冲器,并同时由控制电路向本地总线发出通知,以便让本地总线将缓冲器内的数据读走,这样在响应(等待和读取)的过程中仍然能通过缓冲器来接收数据。而发送的过程刚刚相反,本地总线可一直向发送缓冲写入数据直到器填满为止。
流接口函数
COM_Init
在设备管理器加载后首先被调用,用于初始化所需变量和硬件设备等资源。两个重要的变量:pSerialHead和pHWHead.。前者是描述相应串口的状态,后者是对应硬件的数据抽象。pSerialHead分配空间、初始化链表、临界区并同时为接收发送创建中断事件,然后再从注册表中获得当前的注册表项值,得到DeviceArrayIndex、Priority256键下的具体值后就可以调用GetSerialObject(在PDD中实现)来获得具体的HWObj对象,并通过该对象来调用硬件初始化函数。接下来就是分配和初始化缓冲,调用HWGetRxBufferSize(PDD代码)来获取PDD设定的缓冲区的大小(131072/1024=128k)。最后如果BindFlags被设定为THREAD_AT_INIT就再调用StartDispatchThread启动分发线程(实际的IST)。
1)pSerialHead->CommTimeouts.ReadIntervalTimeout = READ_TIMEOUT (250)
两个相邻字符间的时间间隔。当为0时不起作用;当为MAXDWORD时无限等待;当为250时,即等待250毫秒,如果没有接收到则返回已经接收到的字节数。
2)pSerialHead->CommTimeouts.ReadTotalTimeoutMultiplier = READ_TIMEOUT_MULTIPLIER (10)
pSerialHead->CommTimeouts.ReadTotalTimeoutConstant = READ_TIMEOUT_CONSTANT (100)二者指示读取操作的总时间。这个时间由ReadTotalTimeoutMultiplier * 要读取的字节 + ReadTotalTimeoutConstant组成;当这二者为0时,还受ReadIntervalTimeout影响;当三者则全为0时则无限等待。
发送类似(略)。
COM_Open
COM_Open在CreateFile后被调用,用于以读/写模式打开设备,并初始化所需要的空间/资源等,创建相应的实例,为后面的操作做好准备。第一个参数指向前面提到的HW_INDEP_INFO结构,第二个参数为操作权限码,也就是READ/WRITE这类的权限。第三个参数为共享模式,以确定是否支持独占。
准备好HW_OPEN_INFO结构后就可以调用HWOpen(PDD)来进行PDD所需的Open操作了。Open操作完成后调用HWPurgeComm(PDD)来处理(取消或等待)当前仍在通讯状态的任务。然后重置软件FIFO就基本完成了COM_Open的操作了。
If(!pHWObj->pFuncTbl->HWOpen(pSerialHead->pHWHead)){ open failed}
RxResetFifo(pSerialiHead);
COM_Close
COM_Close是COM_Open相对应的操作。目的是释放COM_Open所使用的系统资源,如果期间创建了IST,需要停止该线程,StopDispatchThread(pSerialHead),最后断开链表RemoveEntryList(&pOpenHead->llist)。
COM_PreClose
if(pOpenHead->AccessCode & (GENERIC_READ | GENERIC_WRITE))
{
pSerialHead->fAbortRead = 1; 关闭前 终止Read
SetEvent(pSerialHead->hReadEvent);
pSeiralHead->fAbortTransmit = 1; 关闭前 终止Transmit
SetEvent(pSerial->hTransmitEvent);
}
COM_PreDeinit
用COM_PreClose(pOpenHead)关闭打开的资源。
COM_Deinit
驱动卸载时启动该事件,和COM_Init相反,释放资源,停止期间创建的线程等操作。具体的说,停止MDD所有IST,释放内存资源和临界区资源,同时还调用HWDeinit()来释放PDD用到的系统资源。
用COM_Close(pOpenHead)关闭打开的资源。用CloseHandle()关闭事件pSerialHead->hSerialEvent、pSerialHead->hKillDispatchThread、pSerialHead->hTransmitEvent、pSerialHead->hReadEvent。
COM_Read
COM_Write
COM_Seek(略)
COM_PowerUp(略)
COM_PowerDown(略)
COM_IOControl(略)
串口读写数据流
1) 应用程序被调用,打开串口OpenPort(),CreateFile(COMx),获取串口属性参数和状态,重新设置波特率、数据位、奇偶校验、停止位、超时等相关参数,创建读写线程。
2) 读操作
读线程中检测串口是否打开,做清空操作,指定监视事件为“读”,通过WaitCommEvent等待receive event事件,当存在有效数据时则产生读取中断,接着读取该数据。读取过程:
COM_Read()从硬件Buffer读数据到驱动的Buffer中:
ReadFile()从驱动Buffer读数据到上层应用Buffer中。
关于WaitCommEvent
在驱动内实现,实质上是通过WaitForSingleObject()来实现的
当等待的通告已经存在(到来)时,则返回。
过程详细说明:
1 在COM_Init和COM_Open中调用StartDispatchThread()线程。
2 在StartDispatchThread()中通过 pSerialHead->pDispatchThread = CreateThread(NULL,0,SerialDispatchThread,pSerialHead,0,NULL)关联SerialDispatchThread线程。(SerialDispatchThread为 API函数)
3 SerialDispatchThread中进行WaitForSingleObject(pSerialHead->hSerialEvent,INFINITE)等待和调用SerialEventHandler(pSerialHead)。
4 在SerialEventHandler()中,当有数据到来时,产生INTR_RX类型的中断,然后设置SetEvent(pSerialHead->hReadEvent)读事件最后发出EvaluateEventFlag(pSerialHead,EV_RXCHAR)串口通告事件,通知上层应用程序读走该数据。
5 在COM_Read里面WaitForSingleObject(pSerialHead->hReadEvent)。
3) 写操作
WritePort(),将数据放到消息队列(PostThreadMessage),PeekMessage()捕获消息,如果捕获到消息队列里的消息,则向串口写数据WriteFile()。将上层应用数据存储区域来作为发送缓冲,当有数据发送时,产生INTR_TX类型中断,将数据写到硬件Buffer中。
过程详细说明:
1 在COM_Init和COM_Open中调用StartDispatchThread()线程.
2 在StartDispatchThread()中通过 pSerialHead->pDispatchThread = CreateThread(NULL,0,SerialDispatchThread,pSerialHead,0,NULL)关联SerialDispatchThread线程。(SerialDispatchThread为 API函数)
3 SerialDispatchThread中进行WaitForSingleObject(pSerialHead->hSerialEvent,INFINITE)等待和调用SerialEventHandler(pSerialHead)。
4 在SerialEventHandler()中,当有数据发送时,产生INTR_TX类型中断,调用DoTxData(里面SetEvent)并SetEvent(pSerialHead->hTransmitEvent),发送数据。
5 在COM_Write里面WaitForSingleObject(pSerialHead->hTransmitEvent)。
几个结构体
typedef struct __RX_BUFFER_INFO {
ULONG Read;
ULONG Write;
ULONG Length;
BOOL DataAvail;
PUCHAR RxCharBuffer;
CRITICAL_SECTION CS;
} RX_BUFFER_INFO, *PRX_BUFFER_INFO;
在RX_BUFFER_FIFO结构中的read成员为MDD取数据时在FIFO的位置标志,而PDD向软件写入数据的位标则对应被称作Write,DataAvail用作表示缓冲器内的数据是否有效。而RxCharBuffer则是指向软件FIFO的指针。当收到数据的时候就将write标示往上递增,而程序向驱动取数得时候Read递增,这样就可以根据Read和Write成员来维护这个FIFO。
typedef struct __TX_BUFFER_INFO {
DWORD Permissions;
ULONG Read;
ULONG Length;
PUCHAR TxCharBuffer;
CRITICAL_SECTION CS;
} TX_BUFFER_INFO, *PTX_BUFFER_INFO;
发送的流程因为可以直接使用所需发送的数据的存储区域来作为发送缓冲(意思就是将数据存储区的地址和长度都赋给该结构体的TxCharBuffer和Length,需要加强制转换。),所以这个缓冲没有独立存在的必要。由于使用其它进程的数据区域,所以这里增加了权限控制项的成分,用于突破进程间的访问限制。
typedef struct __HW_OPEN_INFO {
PHW_INDEP_INFO pSerialHead; //当前串口文件句柄关联的串口设备句柄。
DWORD AccessCode; //记录串口打开操作赋予当前文件(COM_Open)句柄的访问权限。
DWORD ShareMode; //记录本次打开操作的共享模式,CE中不使用。
DWORD StructUsers; //当前文件句柄使用者的计算,象征着使用该文件句柄的线程数。
COMM_EVENTS CommEvents;
LIST_ENTRY llist;
} HW_OPEN_INFO, *PHW_OPEN_INFO
结构中的第一个参数指向我们前面提到的HW_INDEP_INFO结构,第二个参数为读写权限,在COM_Open中为GENERIC_READ和GENERIC_WRITE。第三个参数为共享模式,以确定是否支持独占。这两个参数都是与文件系统的内容对应的。而CommEvent则对应于本实例的事件。
Typedef struct __COMM_EVENT{
HANDLE hCommEvent;//从PDD到MDD层的事件
ULONG hEventMask;//AP请求的EventMask 对应fEventMask。
ULONG fEventData;//Event Mask Flag标记 0/1
ULONG fAbort;//设置SetCommMask(0)后为TRUE//为0文件句柄允许接收来自PDD层的串口事件
//为1文件句柄停止接收串口事件。
CARITICAL_SECTION EventCS;
}COMM_EVENT,*PCOMM_EVENTS;
这个结构包含从PDD到MDD串口事件的结构信息,通过回调传递,因此这个结构限制在MDD层。
typedef struct __HWOBJ {
ULONG BindFlags;
DWORD dwIntID;
PHW_VTBL pFuncTbl;
} HWOBJ, *PHWOBJ;
BandFlags指定IST的启动时间,可选为在初始化过程启动或是在打开设备的时候起动ISR。而第二个参数则是指定拦截的具体的系统中断号(在MDD时,因为在PDD时就是串口端口号了)。最后一个参数是一个结构,该结构定义了硬件操作的各行为函数的指针,MDD通过这些函数来访问具体的PDD操作。
typedef _HW_INDEP_INFO{
CRITICAL_SECTION TransmitCritSec1;
CRITICAL_SECTION ReceiveCritSec1;
PHWOBJ pHWObj;//响应PDD对象
PVOID pHWHead;//PDD层
HANDLE hSerialEvent; //串口事件,包括读和写
HANDLE hReadEvent;//接收事件
HANDLE hKillDiapatchThread;//关闭分发线程
HANDLE hTransmitEvent;//发送事件
HANDLE hDispatchThread;//接收线程
ULONG Priority256;//优先级
ULONG DroppedBytesMDD;//MDD接收发送溢出的字节数
ULONG DroppedBytesPDD;//PDD层直接向接收数据缓冲区的字节数
ULONG RxBytes;//记录接收总字节数
ULONG TxBytes;//记录发送总字节数
ULONG TxBytesPending;//记录等待发送到总字节数
ULONG TxBytesSent;//记录一次发送到字节数
DCB DCB;
COMMTIMOUTS CommTimeouts;//超时控制
DWORD OpenCnt;//
DWORD KillRxThread:;//终止SerialDiapatchThread分发线程
DWORD Xflow:;//Xon Xoff流控制
DWORD StopXmit:1;//停止发送标志
DWORD SentXoff:1;//当Off sent 为 true
DWORD DtrFlow:1;//硬件流控制
DWORD TtsFlow:1;//硬件流控制
DWORD fAbortRead:1;//停止读
DWORD fAbortTransmit:1//停止写
DWORD Reserved:24;
ULONG fEventMask;//打开的EventMask总数//
RX_BUFFER_INFO RxBufferInfo;//接收信息//
TX_BUFFER_INFO TxBufferInfo;//发送信息//
LIST_ENTRY OpenList;//
CRITICAL_SECTION OpenCS;//
PHW_OPEN_INFO pAccessOwner;//打开串口操作获得的文件句柄
} HW_INDEP_INFO,*P HW_INDEP_INFO;
Typedef struct _DCB{
DWORD DCBlength;//长度
DWORD BaudRate;//波特率
DWORD fBinary :1;//是否以二进制(比特流)传输,true
DWORD fParity:1;//是否支持奇偶校验
DWORD fOutxCtsFlow:1;//硬件流控CTS
DWORD fOutxDsrFlow:1;// 硬件流控DSR
DWORD fDtrControl:2;// 硬件流控DTR
DWORD fDsrSensitivity:1;//
DWORD fTXContinueOnXoff:1;//
DWORD fOutX:1;//
DWORD fInX:1;//
DWORD fErrorChar:1;//
DWORD fNull:1;//
DWORD fRtsControl:1;// 硬件流控RTS
DWORD fAborOnError:1;//
DWORD fDummy2:17;//
WORD wReserved;//
WORD XonLim;//下限
WORD XoffLim;//上限
BYTE ByteSize;//数据位
BYTE Parity;//奇偶校验
BYTE StopBits;//停止位
char XonBits;//
char XoffChar;//
char ErrorChar;//
char EofChar;//
char EvtChar;//
WORD wReserved1;//
}DCB;*LPDCB;
虚拟串口的实现:
1) 使用vspd.dll实现虚拟多个串口的功能,将该驱动添加到到release目录下。
2) 在platform.bib下添加:
vspd.dll $(_FLATRELEASEDIR)\vspd.dll NK SHK
3) 在serial.reg注册表中添加上述虚拟串口表中的相应虚拟串口VS_Serial6、VS_Serial8、VS_Serial9。
4、详细设计
5、测试项设计
1) 自发自收测试
短接COM1的Tx和Rx(GPIOD[17]、GPIOD[18]),利用测试工具按照测试用例进行相关项的测试。其它串口未进行测试!
2) 虚拟串口测试
等待…
3) 长字符短字符测试
4) 固定长度字符测试
5) IGO地图测试
6) 串口属性、串口配置
7) 超时设置和测试
(具体测试步骤与测试结果,见测试用例。)
6、驱动测试
7、驱动功能评估
8、驱动问题点、关键性能指标
1) StartDispatchThread()和StopDispatchThread()
这两个函数是中断服务程序所需的IST启动和关闭的手段。
StartDispatchThread()用于启动IST,主要工作是调用InterruptInitialize()将系统中断和相应事件关联起来,并启动SerialDiatchThread作为IST,调用InterruptDone函数,该函数调用OAL中断OEMInterruptDone完成中断响应。
StopDispatchThread()开始设置线程优先级和分发线程相同,防止停止线程的动作比释放内存更快而导致出错。停止的动作是线程主动完成的,通过提交KillRxThread表示位,然后通过sleep请求调度,等到IST自己停止,最后就用InterruptDisable()屏蔽中断。
2) SerialDispatchThread()和SerialEventHandler()
SerialDispatchThread()和SerialEventHandler()是串口驱动的分发程序(IST所在)。整个程序分成两部分——循环主体和事件处理。SerialDispatchThread()反复等待并调用SerialEventHandler()处理,直到pSerialHead->KillRxThread被设置后退出。
3) WaitCommEvent
事实上该函数为SerialAPI WaitCommEvent在驱动内的实现,其作用为阻塞线程直到某一固定的串口通告(事件消息)发生。在具体的实现中,是用WaitForSingleObject来实现阻塞。在进入阻塞之前,函数适用一个循环主体首先查询是否存在已有的通告与等待通告相符,若没有就等待下一次事件发生,待事件发生再次进行检查。如此循环达到阻塞的目的。如果dwEventData和fEventMask都不为0或者fEventMask为0则获取*pfdwEventMask的值并返回。从而传给上层,通知应用程序进行相应的操作。
4) ApplyDCB(PHW_INDEP pSerialHead,DCB *pDCB,BOOL fOpen)
DCB数据结构是描述串行口波特率,流控制,奇偶效验等资料的载体。该函数是MDD设置DCB数据结构至驱动内部和硬件的手段,这里使用了大量的PDD操作来完成硬件设置。
5) 提示和说明
1)注意在打开串口大于9的串口时,需要使用”\\$device\\COMx”, 而不是通常的”COMx:” 。
2)OpenDeviceKey((LPCTSTR)Identifier),参数Identifier是指向注册表HKEY_LOCAL_MACHINE/Drivers/Active。下的一个键值,这也是决定驱动如何操作硬件的关键。
通过OpenDeviceKey驱动可以通过Identifier找到驱动的注册表键所在。
通过查询DeviceAarryIndex键值,我们可以知道需要打开的是普通串口还是其它类型的串口设备。
通过查询Priority256键值,我们可以知道IST的优先级,在后面会通过这个值来改变IST的优先级。
GetSerialObject是连接MDD和PDD层的关键函数,在这个函数里面决定了DeviceIndex是对什么设备进行操作,即COMx。
6) DoTxData
如果关闭了串口,则将TxBufferInfo参数置为0。
如果TxBufferInfo中存在有效数据,有效则SetProPermissions()。是否采用硬件流控制?用则设置,不用往下走。是否停止发送?停止则len=0,不停止则len=TxBytesAvail()。HWTxIntrHandler()发送数据,并更新FIFO。当TxBytesAvail()=0,并SetEvent(hTransmitEvent)再发送一次。
如果TxBufferInfo中不存在效数据,len=0, 将TxBufferInfo参数置为0。并SetEvent(hTransmitEvent)再发送一次。
7) SerialEventHandler()
While(1)
INTR_RX:
计算软件缓冲剩余空间RoomLeft,如果有剩余空间就直接调用HWRxIntrHandler()来从硬件缓冲获取剩余空间大小的数据,如果没有剩余空间,则创建一个16byte的临时缓冲区,并将数据放入该区。
软件流控制
Xflow,用软件流控制数据的收发,保证数据的完整接收。Xon和Xoff不是数据,只是一个标记,收到标记后需要将后面的数据向前移一位覆盖掉该标记位,即memmove()函数。如果是Xon,则需要恢复INTR_TX继续发送。
硬件流控制
硬件流控制是以3/4的大小为起停分位点,可以通过DTR或RTS两种连线方式。
INTR_MODEM:
INTR_LINE:
INTR_TX:
DoTxData(),如果TxBytesAvail等于0,则SetEvent(hTransmitEvent)再发送一次。
RxDataAvail
有RoomLeft,则SetEvent(hReadEvent),EvaluateEventFlag(,EV_RECHAR)。
8) EvaluateEventFlag(pHead,fdwEventMask)
if(pHWIHead->fEventMask & fdwEventMask),到OpenList链表,一个一个向下查找。再当if(pOpenHead->CommEvents.fEventMask & fdwEventMask)即当应用层请求fdwEventMask事件时,获取fEventData标记。循环获取,直到获取的fEventMask的fEventData与临时变量的不等。最后SetEvent(CommEvent.hCommEvent)。
COMM_EVENTS[]={
EV_BREAK,EV_CTS,EV_DSR,EV_ERR,EV_RING,EV_RLSD,EV_RXCHAR,EV_RXFLAG,EV_TXEMPTY};
9) SerialDispatchThread()
SerialEventHandler(pSerialHead)获取中断事件,InterruptDone(pSerialHead->pHWObj->dwIntID)完成ID号为dwIntID的中断。
10) StartDispatchThread()
InterruptInitialize()关联中断和事件,InterruptDone()完成中断响应,CreateThread(,,SerialDispatchThread,,,)启动SerialDispatchThread线程作为IST。
有不准确的地方 希望大家指正!多谢!