Symbian串行通信——编程基础

发布时间:2014-10-25 2:24:37
来源:分享查询网

这边文章是转载的,Symbian串行通信的原理是相同的,转载这边文章的原因是,这边文章中包含一些与红外和蓝牙通信的说明。   串行通信是一种用于两台设备间(典型情况下是距离较近)传输数据的低级别点对点技术。Series60支持红外线和蓝牙上的串行通信。Series60实现的中心是串行通信服务器(Serial Communication Server,又称Comms服务器或C32)。它使用Symbian OS客户端/服务器框架,提供对串行硬件的访问,并且是通用的和共享的。通用是指红外线和蓝牙串行通信使用相同的API,共享是指多个客户端线程可以安全地并发使用同一个串行端口。 Series60中所有的串行通信都使用下列基本步骤实现:1. 装载串行设备驱动器2. 启动Comms服务器3. 连接到Comms服务器4. 装载一个comms模块(又称CSY: “Comms SYstem”)----Comms服务器的插件,它将决定使用哪种类型的串行端口(红外线or蓝牙)5. 打开一个串行端口6. 配置此串行端口7. 从端口读写数据8. 最后关闭端口 通信过程中会涉及到几个重要的类,下面简单介绍之:1. RCommServ串行通信服务器会话类。它描述了同Comms服务器的会话。提供了连接到服务器函数、装载/卸载不同comms模块函数、查询有效端口名字和数量函数等。同comms服务器间的会话是不可共享的。此类不能被继承。继承自RSessionBase。MembersDefined in RCommServ: Connect(), CreateThreadInCommProc(), GetPortInfo(), LoadCommModule(), NumPorts(), RCommServ(), UnloadCommModule(), Version(), __DbgCheckHeap(), __DbgFailNext(), __DbgMarkEnd(), __DbgMarkHeap(), __DbgSetTraceMask() Inherited from RHandleBase: Attributes(), Close(), Duplicate(), FullName(), Handle(), HandleInfo(), Name(), SetHandle(), SetHandleNC(), iHandle Inherited from RSessionBase: CreateSession(), EAutoAttach, EExplicitAttach, Open(), Send(), SendReceive(), SetReturnedHandle(), ShareAuto(), ShareProtected(), TAttachMode 2. RComm继承自RSubSessionBase,描述了一个子会话,使用某一个端口同C32服务器通信。提供的函数均通过操作端口来实现通信,包括打开、关闭、读写、端口配置和性能检测等。一旦使用了某端口来通信,就不能再改变此端口。 下面是串行通信步骤的详解:1. 装载串行设备驱动器 有两个部分需要装载:一个物理驱动器(直接与硬件交互)和一个逻辑驱动器(提供物理驱动器上的API)。它们的名字是固定的,在这里属于全局性定义。注意,用于模拟器生成(WINS)的物理设备驱动器和用于目标生成的不同。// Physical device driver names#if defined (__WINS__)    _LIT (KPddName, "ECDRV");#else    _LIT (KPddName, "EUART1");#endif // Logical device driver names_LIT (KLddName, "ECOMM"); 下面是装载驱动器的过程:     TInt err; #if defined (__WINS__) // File Server required in WINS to enable loading of device drivers    RFs fileServer;    User::LeaveIfError(fileServer.Connect());    fileServer.Close();#endif     // Load the physical device driver.    err = User::LoadPhysicalDevice(KPddName);    if (err != KErrNone && err != KErrAlreadyExists)        {        User::Leave(err);        }     // Load the logical device driver.    err = User::LoadLogicalDevice(KLddName);    if (err != KErrNone && err != KErrAlreadyExists)        {        User::Leave(err);        } 若装载函数返回KErrAlreadyExists,则说明此驱动器已装载,这不会被视为错误,因此不需要事先检测驱动器是否已装载。如果使用的代码是UI应用程序的一部分,则这些驱动器将已经被Series60的UI框架装载。前面的#ifdefined(__WINS_)的几行代码首先连接一个 RFs会话,然后关闭它,目的是确保调用装载驱动器前已经装载了File服务器。 2. 启动Comms服务器     // Start the comms server process    err = StartC32();    if (err != KErrNone && err != KErrAlreadyExists)        {        User::Leave(err);        } 3. 连接到Comms服务器 RCommServ是Comms服务器的客户端句柄,所有后续代码都将使用它与Comms服务器通信。     // Connect to the Serial comms server.    User::LeaveIfError(iCommServer.Connect()); 4. 装载CSY CSY是Comms服务器的DLL插件,进行数据传输之前需要显式装载它。和驱动器一样,分别使用名字"IRCOMM"和"BTCOMM"装载红外线和蓝牙CSY。     // Comms modules    _LIT (KIrComm, "IRCOMM");     // Load the CSY module.    User::LeaveIfError(iCommServer.LoadCommModule(KIrComm)); 5. 打开串行端口 接下来打开一个串行端口。同样,仍然按名字打开它。但注意端口名是不固定的,可能需要动态获取它。不过对于红外线的情况,端口名是公认的。     // Comm Port Name    _LIT (KPortName, "IRCOMM::0");    User::LeaveIfError(iCommPort.Open(iCommServer, KPortName, ECommShared)); RComm::Open()的第三个参数说明打开此端口的模式,可能的取值如下:ECommExclusive:此端口一旦打开,即不能被其他任意RComm客户端使用ECommShared:此端口可以被在相同模式下打开的其他RComm客户端共享ECommPreemptable:如果其他客户端试图打开此端口则会丢失 对于非公开的端口名,下面的代码演示了如何动态获取它: TSerialInfo portInfo; // 获取选择CSY所对应的端口信息User::LeaveIfError(iCommServer.GetPortInfo(KIrComm, portInfo)); // 构建一个描述符,包含该CSY所支持的最低端口// 要求它的大小足以包含TSerialInfo.iName的值(定义为KMaxPortName,最大值为16个字符)// 加上两个冒号和1或2个数字 _LIT(KColons, ":: "); TBuf<KMaxPortName + 4> portName;portName.Append(portInfo.iName);portName.Append(KColons);portName.AppendNum(portInfo.iLowUnit); // 现在打开第一个串行端口,在此为唯一模式User::LeaveIfError(iCommPort.Open(iCommServer, portName, ECommExclusive)); 6. 配置串行端口 最终使用已打开的串行端口之前,可能需要对它进行配置。这取决于希望连接到的设备的需求。提供串行通信的设备通常会附带某种形式的文档,详细说明需要的配置。同时可以通过查询端口的功能确保它支持需要的配置。查询端口功能的方法如下:    // Check port capabilities    TCommCaps portCapabilities;    iCommPort.Caps(portCapabilities);     if (((portCapabilities().iRate & KCapsBps19200) == 0) ||        ((portCapabilities().iDataBits & KCapsData8) == 0) ||        ((portCapabilities().iStopBits & KCapsStop1) == 0) ||        ((portCapabilities().iParity & KCapsParityNone) == 0))            User::Leave (KErrNotSupported);            这里使用的所有常数值(如KCapsBps19200)都在系统头文件d32comm.h中定义。此外,如果端口的功能与需求不匹配,并不需要异常退出,可以选择以不同的方式处理此情况,例如提示用户选择不同的端口或CSY。接下来,可以设置端口的配置。首先应该获取当前配置,这样可以使不希望显式设置的值保持不变:    // Configure port    TCommConfig portSettings;    iCommPort.Config(portSettings); // Get current configuration     // Set port characteristics    portSettings().iRate = EBps19200;    portSettings().iParity = EParityNone;    portSettings().iDataBits = EData8;    portSettings().iStopBits = EStop1;    portSettings().iFifo = EFifoEnable;    portSettings().iHandshake = KConfigObeyXoff|KConfigSendXoff;     portSettings().iTerminator[0] = 10; // line feed character    portSettings().iTerminatorCount = 1;     User::LeaveIfError(iCommPort.SetConfig(portSettings));最后,有些杂项配置信息不能使用TCommConfig对象设置,例如对于DTR(Data Terminal Ready,数据终端就绪)和RTS(Ready To Send,准备发送)之类的流控制信号,需要使用RComm::SetSignals()设置它们:     // Turn on DTR and RTS    iCommPort.SetSignals(KSignalDTR, 0);    iCommPort.SetSignals(KSignalRTS, 0);     // Set buffer size    const TInt KBufferLength = 4096;    iCommPort.SetReceiveBufferLength(KBufferLength);    7. 在端口上传输数据     // Timeout Value    const TTimeIntervalMicroSeconds32 KTimeOut(4000000);    Cancel();     iDataBuf.Copy(aTxData);    iCommPort.Write(iStatus, KTimeOut, iDataBuf);    SetActive();有几个重要的地方需要注意:* 调用Write()之前调用了Cancel(),这是因为Write()是异步的,这样可以确保不会试图同时执行两个写操作,这将导致错误。* Series60是一个Unicode平台,所以aTxData是一个16位描述符。因此不能把它直接传递到RComm::Write()---该函数需要一个显式的8位描述符,所以首先需要把它复制到一个8位描述符,然后传递到Write()。* Write()的第二个参数是一个超时值,这里定义为4秒。如果Write()请求未在此时限内完成将会强制结束,并把iStatus设置为KErrTimedOut。8. 关闭端口在串行端口上发送和接收数据结束后,必须关闭此端口(或更明确的说,关闭此端口的句柄),同时还要关闭服务器:    iCommPort.Close();    iCommServer.Close();

返回顶部
查看电脑版