Symbian串行通信——编程基础

这边文章是转载的,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。
Members
Defined 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();

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值