Serial Communication

Serial communication is a low-level, point-to-point technology used to transfer data between two devices, typically at close range ”Series 60 supports serial communication over infrared and Bluetooth. Central to the Series 60 implementation is the Serial Communications Server (also referred to as the Comms Server or C32 ). This uses the familiar Symbian OS Client/Server framework to provide access to serial hardware, and it is generic (in other words, the same API is used for both infrared and Bluetooth serial communication ”note that other hardware can be supported by the addition of plug-ins) and shared (in other words, several client threads can safely use the same serial port concurrently).

All serial communication on Series 60 is implemented using the following basic steps:

1.
Load the serial device drivers.

2.
Start the Comms Server.

3.
Connect to the Comms Server.

4.
Load a comms module ( otherwise known as a CSY ) ”this is the Comms Server plug-in that determines which type of serial port you wish to use (for example, infrared or Bluetooth).

5.
Open a serial port.

6.
Configure the serial port.

7.
Write data to (and/or read data from) the port.

8.
Finally, close the port.

Using Serial Communication

To illustrate how to implement the basic steps that have been outlined, an example application is provided. This application is called IrSerial , and it allows the user to enter some text, which it then sends to an IR-enabled printer using serial communication. A screenshot of IrSerial is shown in Figure 9-1.

Figure 9-1. The IrSerial application.


All the example code shown here comes from the CIrSerialEngine class. Here are the relevant member variables from the class, which are featured throughout this section:

 RCommServ iCommServer;  // serial comms RComm iCommPort;        // comm port // data to transmit TBuf8<EIrSerialFormEdwinMaxLength> iDataBuf; 

You should also note that CIrSerialEngine is an Active Object ”in other words, it is derived from CActive .

The following subsections show how to implement each of the basic steps needed for serial communication.

Loading the Serial Device Drivers

Your first step is to load the serial communication drivers. There are two parts you need to load: a physical driver (which interacts with the hardware directly), and a logical driver (which provides an API onto the physical driver). The names for these are fixed and are defined here globally. Notice that the physical device driver for emulator builds (WINS) is different from that for target builds, as you would expect when dealing with different hardware.

 // Physical device driver names #if defined (__WINS__)    _LIT(KPddName, "ECDRV"); #else    _LIT(KPddName, "EUART1"); #endif    // Logical device driver name    _LIT(KLddName, "ECOMM"); 

These literals are then used to load the drivers in the CIrSerialEngine::InitialiseL() method:

 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);       } 

Note that a return value of KErrAlreadyExists from the loading functions indicates that the driver is already loaded. This is not considered an error, so you can call this code "blindly," without needing to explicitly check in advance whether the driver is already loaded. If your code is part of a UI application, these drivers will already have been loaded by the Series 60 UI framework.

Also, at the top of this example, note the unusual WINS-only lines, to connect an RFs file session and then immediately close it. The purpose is to ensure that the File Server is loaded before the calls to load the drivers. Opening a session will start the server if it is not already started, but since you do not use the file session itself, you should then just close it and discard it.

Starting the Comms Server

Next , you need to start the Comms Server itself. The code for this is very similar to the code you used to load the drivers. In particular, note that this code can also be safely called even if the Comms Server has already been started. Also, note that StartC32() is a global function (defined in c32comm.h ).

 // Start the comms server process err = StartC32(); if (err != KErrNone && err != KErrAlreadyExists)    {    User::Leave(err);    } 

Connecting to the Comms Server

RCommServ is a client-side handle on the Comms Server and is used by all subsequent code to communicate with it. First you need to connect the handle to the server:

 // Connect to the Serial comms server. User::LeaveIfError(iCommServer.Connect()); 

Note that here, unlike in the previous code snippets, any return value other than KErrNone is considered an error.

Loading a CSY

A CSY is a DLL plug-in to the Comms Server that needs to be explicitly loaded before data transfer can take place. As with the drivers, CSYs are loaded by name: " IRCOMM " for infrared and " BTCOMM " for Bluetooth. The IrSerial example illustrates how to load the infrared CSY:

 // Comms modules _LIT (KIrComm, "IRCOMM"); ... // Load the CSY module. User::LeaveIfError(iCommServer.LoadCommModule(KIrComm)); 

Again, note that any return value other than KErrNone should be treated as an error. To change your code to use Bluetooth instead of infrared, you would have to load the Bluetooth CSY ( BTCOMM ) here.

Opening a Serial Port

Your next step is to open a serial port. Again, this is opened by name. Note, however, that port names are not fixed and may need to be obtained dynamically. In the case of infrared, however, the port name is well known, so the IrSerial example defines this as a string literal and uses it directly:

 // Comm Port Name _LIT(KPortName, "IRCOMM::0"); ... User::LeaveIfError(iCommPort.Open(iCommServer, KPortName, ECommShared)); 

The third parameter to RComm::Open() indicates a mode in which the port should be opened. The possible values are as follows :

  • ECommExclusive ” Once open, the port cannot be used by any other RComm clients .

  • ECommShared ” The port can be shared by other RComm clients who open in the same mode.

  • ECommPreemptable ” The port will be lost if another clients tries to open it.

In cases where the port name is not well known, the following code (not taken from the IrSerial example) shows how you would obtain it dynamically:

 TSerialInfo portInfo; // Get the port information for your chosen CSY User::LeaveIfError(iCommServer.GetPortInfo(KIrComm, portInfo)); // Construct a descriptor to contain the full name of // the lowest port this CSY supports. This needs to be // big enough to contain the value of TSerialInfo.iName // (maximum: 16 characters, defined as KMaxPortName), // plus two colons and one or two digits. // Example: "IRCOMM::0" _LIT(KColons, "::"); TBuf<KMaxPortName + 4> portName; portName.Append(portInfo.iName); portName.Append(KColons); portName.AppendNum(portInfo.iLowUnit); // Now open the first serial port - here in exclusive mode User::LeaveIfError(iCommPort.Open(iCommServer, portName, ECommExclusive)); 

Configuring the Serial Port

Before you finally make use of the serial port you have just opened, you may need to configure it. This will depend on the requirements of the device you are connecting to; devices offering serial communication services will usually be accompanied by some form of documentation detailing the configuration required (such as maximum port speed and use of data format elements such as stop bit and parity bit). You can also query its capabilities to ensure that it supports the configuration you require.

The API Guide section of the Series 60 SDK help includes a section entitled "Serial Comms", which provides detailed information on configuring serial ports.


The port's capabilities are queried as follows:

 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);    } 

The constant values used here (such as KCapsBps19200 ) are all defined in the system header file d32comm.h .

Note that the reason for the odd syntax is that TCommCaps is defined as a package buffer ” operator() returns the underlying TCommCapsV01 object that the package buffer contains.

Package buffers are discussed in Chapter 3.


Note also that you do not need to leave if the port's capabilities do not match your requirements. You may choose to handle this condition differently ”for example, by prompting the user to select a different port or CSY.

Next, you can set the configuration of the port. You should start by obtaining the current configuration so that values you do not wish to set explicitly stay the same.

 TCommConfig portSettings; // Get current configuration iCommPort.Config(portSettings); // Set port characteristics portSettings().iRate = EBps19200; portSettings().iParity = EParityNone; portSettings().iDataBits = EData8; portSettings().iStopBits = EStop1; portSettings().iFifo = EFifoEnable; portSettings().iHandshake = KConfigObeyXoff  KConfigSendXoff; // line feed character portSettings().iTerminator[0] = 10; portSettings().iTerminatorCount = 1; User::LeaveIfError(iCommPort.SetConfig(portSettings)); 

Note again that TCommConfig is a package buffer and so requires slightly unusual syntax.

Finally, there are some miscellaneous configuration details that cannot be set using a TCommConfig object. For example, flow control signals such as DTR (Data Terminal Ready) and RTS (Ready To Send) are set using the RComm::SetSignals() function, as follows:

 // Turn on DTR and RTS iCommPort.SetSignals(KSignalDTR, 0); iCommPort.SetSignals(KSignalRTS, 0); // Set buffer size const TInt KBufferLength = 4096; iCommPort.SetReceiveBufferLength(KBufferLength); 

Once configuration is complete, the serial port is ready to use. The next subsection shows how data is sent.

Transfering Data over the Port

The CIrSerialEngine::Transmit() method is used to send data over the port you have just opened. The user provides data to send by typing text into a dialog, which is passed to this function as the aTxData parameter.

 void CIrSerialEngine::Transmit(const TDesC& aTxData)    {    // Timeout Value    const TTimeIntervalMicroSeconds32 KTimeOut(4000000);    Cancel();    iDataBuf.Copy(aTxData);    iCommPort.Write(iStatus, KTimeOut, iDataBuf);    SetActive();    } 

There are some interesting points to note here:

  • Cancel() is called before calling the Write() function. As Write() is asynchronous, this ensures that CIrSerialEngine does not attempt to make two write operations simultaneously , which would cause a panic.

  • aTxData is a 16-bit descriptor, as Series 60 is a Unicode platform. As such it cannot be passed directly to RComm::Write() , which requires an explicit 8-bit ( const TDesC8& ) descriptor ”so it is copied into an 8-bit descriptor ( iDataBuf ), and this is passed to the Write() function. Note that any characters with a Unicode value greater than 255 will be set to an 8-bit (ASCII) value of " 1 " ”this is covered further in the Descriptors section of Chapter 3.

  • The second parameter to Write() is a timeout value, defined here as four seconds. If the Write() request has not completed normally within this time, it will be forced to complete, posting a value of KErrTimedOut into the iStatus member of CIrSerialEngine .

Closing the Port

When you have finished sending and receiving data over the serial port, you must close the port (or more specifically , close the handle on the port), and likewise close the server. Fortunately, this is the simplest step of the whole process!

 void CIrSerialEngine::Close()    {    iCommPort.Close();    iCommServer.Close();    } 



Developing Series 60 Applications. A Guide for Symbian OS C++ Developers
Developing Series 60 Applications: A Guide for Symbian OS C++ Developers: A Guide for Symbian OS C++ Developers
ISBN: 0321227220
EAN: 2147483647
Year: 2003
Pages: 139

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net