Serial Port Complete
Serial Port Complete
Complete
COM Ports,
USB Virtual COM Ports,
and
Ports for Embedded Systems
Second Edition
Jan Axelson
Lakeview Research LLC
Madison, WI 53704
Serial Port Complete:
COM Ports, USB Virtual COM Ports, and Ports for Embedded Systems
Second Edition
Jan Axelson
Copyright 1998, 1999, 2000, and 2007 by Janet L. Axelson
All rights reserved. No part of the contents of this book, except the code examples, may be
reproduced or transmitted in any form or by any means without the written permission of
the publisher. The code examples may be stored and executed in a computer system and
may be incorporated into computer programs developed by the reader.
The information, computer programs, schematic diagrams, documentation, and other
material in this book are provided as is, without warranty of any kind, expressed or
implied, including without limitation any warranty concerning the accuracy, adequacy, or
completeness of the material or the results obtained from using the material. Neither the
publisher nor the author shall be responsible for any claims attributable to errors, omissions,
or other inaccuracies in the material in this book. In no event shall the publisher or author
be liable for direct, indirect, special, incidental, or consequential damages in connection
with, or arising out of, the construction, performance, or other use of the materials con-
tained herein.
MPLAB, PICDEM, and PIC are registered trademarks of Microchip Technology Inc. in the
U.S.A. and other countries. Other product and company names mentioned herein may be
trademarks of their respective holders.
Published by Lakeview Research LLC, 5310 Chinook Ln., Madison WI 53704
On the web at www.Lvr.com
Distributed by Independent Publishers Group (www.ipgbook.com).
14 13 12 11 10 9 8 7 6 5 4 3 2 1
ISBN 978-1931448-07-9
Print ISBN: 9781931448062
iii
%QPVGPVU
+PVTQFWEVKQP ZKKK
#EMPQYNGFIOGPVU ZKZ
1RVKQPU CPF %JQKEGU
Wben tc use a Ser|a| Pcrt 2
Advantages 2
Limits 4
System Ccmcnents 4
The Computers 4
The Physical Link 6
Programming 6
A||cat|cns B
Example Systems 8
Managing Communications 9
Special-purpose Modules 9
iv
2 FQrmaVU aPd PrQVQcQ|U 11
Send|ng Ser|a| Data 11
Asynchronous and Synchronous Communications 11
Word Formats 12
Bit Rate and Baud Rate 13
System Support for Low-level Protocols 14
Send|ng |ts 15
The Format 15
The Need for Accurate Timing 15
Autodetecting the Bit Rate 17
Autodetecting a COM Port 18
Data Fcrmats 1B
Binary Data 18
Text Data 19
ASCII Hex 22
Application-specific Protocols 24
Prevent|ng M|ssed Data 25
Flow Control 26
Buffers 27
Event-driven Programming and Polling 28
Acknowledgments 29
Error Checking 29
3 %CM PQrVU QP P%U 31
Pcrt Arcb|tecture 31
Device Manager 31
Port Resources 36
Serial Servers 37
Access|ng Pcrts 3B
Drivers 38
Identifying Ports 39
GUIDs for COM Ports 39
COM Port Numbering 40
INF Files 40
Options for Application Programming 41
v
4 IPU|dG PS-232 43
Tbe Hardware InterIace 43
Signals 43
Voltages 46
Timing Limits 48
Ccnvert|ng Vc|tages 4B
Interface Chips 49
Short-range Circuits 53
Pcrt-cwered C|rcu|ts 55
Using Outputs as a Power Source 56
Regulating the Voltage 57
A|ternate InterIaces 5B
Direct Connection 58
Other Unbalanced Interfaces 58
5 DGU|gP|Pg PS-232 L|PkU 61
Ccnnectcrs and Adaters 61
Connector Options 62
Adapters 63
Using Microcontroller Development Boards 65
Cab|es 6?
Length Limits 67
Surge Protection 69
Isc|ated L|nes ?0
Ways to Achieve Isolation 70
About Grounds 70
Power Supply Grounds 72
Optoisolating 75
Debugg|ng Tcc|s ?6
Using a Breakout Box 76
Monitoring with a Voltmeter 77
Oscilloscopes and Logic Analyzers 78
vi
6 IPU|dG PS-4B5 ?9
Abcut PS-4B5 ?9
Balanced and Unbalanced Lines 80
Voltage Requirements 84
Current and Power 85
Speed 87
Internal Protection Circuits 88
InterIac|ng Ct|cns B9
Chips 89
Adding a Port on a PC 91
Converting 3.3/5V Logic 91
Converting RS-232 93
Ccntrc|||ng tbe Dr|ver Enab|e 96
Re-enabling the Driver 97
Software-assisted Control 97
Hardware Control 99
? DGU|gP|Pg PS-4B5 L|PkU aPd NGVwQrkU 105
Lcng and Sbcrt L|nes 106
When Is a Line Long? 106
Calculating Line Length 109
Choosing a Driver Chip 111
L|ne Term|nat|cns 112
Characteristic Impedance 112
Adding a Termination 113
Effects of Terminations 115
Reflections 117
Series Terminations 122
Terminations for Short Lines 122
AC Terminations 123
Network Topologies 125
|as|ng tbe L|ne 12?
Open-circuit Protection 127
Short-circuit Protection 130
vii
Cab|e Tyes 131
How a Wire Picks Up Noise 132
Twisted-pair Cable 133
Selecting Cable 133
Grcunds and D|IIerent|a| L|nes 134
Ensuring a Common Ground 134
Isolated Lines 137
Us|ng Mu|t||e uses 141
Adding a Repeater 141
Implementing a Star Topology 141
B GQ|Pg W|rG|GUU 145
Med|a and Mcdu|at|cn 146
Using a Carrier Frequency 146
Spread Spectrum Technology 147
Ensuring Reliable Transfers 147
InIrared 14B
Transmitters and Receivers 148
IrDA 149
Pad|c Frequency 149
Complying with Regulations 149
Choosing an RF Band 150
Implementing a Link 151
Using Other RF Standards 152
9 UU|Pg .NFTU SGr|a|PQrV %|aUU 155
Ga|n|ng Access tc a Pcrt 156
Finding Ports 156
Opening a Port 156
Timeouts 160
Receive Threshold 161
Closing a Port 161
TransIerr|ng Data 163
Transferring Bytes 167
Transferring Text 170
viii
Us|ng Stream Cb[ects 1?6
BinaryReader and BinaryWriter 177
StreamReader and StreamWriter 182
Sav|ng a Pcrt and Parameters 1B6
The Application Settings Architecture 186
Combo Box Example 187
10 MaPag|Pg PQrVU aPd TraPUIGrU |P .NFT 1B9
Pece|v|ng Data 190
Setting Timeouts 190
Detecting Received Data 190
Collecting Received Data 197
Ensuring Efficient Transfers 202
Send|ng Data 203
Avoiding Timeouts 203
Sending without Blocking the Application 203
Preventing Buffer Overflows 207
Ensuring Efficient Transfers 208
F|cw Ccntrc| 209
Selecting a Method 209
Monitoring and Controlling the Signals 209
Hand||ng Errcrs 214
Exceptions 214
The ErrorReceived Event 214
Verifying Received Data 218
Structur|ng an A||cat|cn 21B
Defining a ComPorts Class 218
Setting Parameters with Combo Boxes 221
Defining Application-specific Events 224
11 PQrVU IQr FmbGddGd SyUVGmU 229
A M|crcccntrc||er Ser|a| Pcrt 229
About the PIC18F4520 230
The Enhanced UART 230
ix
Peg|sters 231
Configuring and Accessing the Port 231
Setting the Bit Rate 234
Interrupts 237
Basic Operations 239
Access|ng a Pcrt 241
Configuring the Port 241
Sending Data 243
Receiving Data 244
Using Interrupts 253
Using Flow Control 256
Add|ng Pcrts 262
Multiple On-chip UARTs 263
Firmware UARTs 263
External UARTs 263
12 NGVwQrk PrQgramm|Pg 26?
Manag|ng TraII|c 26?
Steps in Exchanging a Message 268
Protocols 268
Using Existing Protocols 270
Debugging Tips 271
Address|ng 2?2
Assigning Addresses 272
Detecting Addresses 272
Reserving Address Values 273
Defining a Message Format 273
9-bit Format 274
13 AP PS-4B5 NGVwQrk 2B1
Ccnnect|ng tbe Ncdes 2B1
Transceivers 281
Terminating and Biasing 283
Cabling 283
Exam|e Prctccc| 2B3
Addresses 283
Message Format 283
x
Ccmmands 2B4
Reading a Byte 284
Writing a Byte 286
Pc|||ng tbe Ncdes 2B?
Configuring the Driver-enable Line 287
Sending Commands 288
Pescnd|ng tc Pc||s 291
Auxiliary Routines 291
Decoding Received Data 303
14 IPU|dG US 31?
Hcsts and Dev|ces 31?
Assigning a Driver on the Host 318
Requirements 318
Host Responsibilities 319
Device Responsibilities 319
Speed 320
Endpoints 320
US TransIers 321
Transfer Types 321
Transactions 322
The Data Toggle 323
15 UU|Pg SGc|a|-IuPcV|QP US %QPVrQ||GrU 325
Ins|de tbe Cb|s 326
Serial Interface (FT232R) 326
Parallel Interface (FT245R) 328
Prototyping Modules 329
Us|ng tbe Ccntrc||ers 330
Drivers 330
Adding Vendor-specific Data 330
Implementing a Virtual COM Port 331
Converting from RS-232 to USB 332
xi
16 UU|Pg GGPGr|c US %QPVrQ||GrU 335
Tbe Ccmmun|cat|cn Dev|ces C|ass 335
Documentation 336
Overview 336
Device Controllers 338
Host Drivers 338
Us|ng tbe Abstract Ccntrc| Mcde| 339
POTS Models 339
Virtual COM Ports 340
Requests 341
Notifications 344
Maximizing Performance 345
Descr|tcrs and INF F||es 346
Device Descriptor 346
Configuration Descriptor 346
Communication Class Interface Descriptors 351
Data Class Interface Descriptors 353
String Descriptors 355
The INF File 356
Composite Devices 356
IPdGx 365
This page intentionally left blank
xiii
+PVTQFWEVKQP
When I wrote the first edition of this book, the RS-232 serial port was the
workhorse of PC interfaces. Modems and scores of other peripherals connected
to PCs via the serial ports that were present on every machine.
When the Universal Serial Bus (USB) took hold in the late 1990s, many pre-
dicted that serial ports would soon be obsolete. Plenty of peripherals that for-
merly used the serial port have switched to USB. But some devices cant use
USB or have requirements that USB alone cant provide. Many embedded sys-
tems use serial ports because theyre inexpensive and less complex to program
compared to USB. Serial ports can use longer cables than USB allows. And the
RS-485 serial interface supports networks suitable for many monitoring and
control applications.
While most PCs no longer have built-in serial (COM) ports, the ports are easy
to add via USB converters. With converters, the number of expansion slots no
longer limits the number of serial ports a system can have. The SerialPort class
included in Microsofts .NET Framework shows that PC applications continue
to find COM-port communications useful.
xiv
9JCVU +PUKFG
This book explores wide and varied territory, including hardware and software;
ports in PCs and in embedded systems; and RS-232, RS-485, and wireless
interfaces. You dont need to read the book straight through. If youre interested
in a particular topic, you can skip right to it.
The first chapters focus on hardware and interfacing. Chapters 12 are an
introduction to asynchronous serial communications. Chapter 3 discusses serial
ports in PCs, and chapters 48 are a guide to interfacing using RS-232,
RS-485, and wireless technologies.
The next chapters are a guide to programming. Chapters 910 show how to
program serial ports on PCs using Visual Basic .NET and Visual C# .NET.
Chapter 11 shows how to program serial ports for embedded systems with
examples for microEngineering Labss PICBASIC PRO compiler and Micro-
chip Technologys MPLAB C18 C compiler.
Chapters 1213 focus on hardware and programming for RS-485 serial net-
works.
Chapters 1416 explain how to implement USB virtual COM ports using spe-
cial-purpose and generic USB controllers.
If youre looking for example code, see the entries under code example (embed-
ded) and code example (PC) in the index.
9JCVU 0GY KP VJG 5GEQPF 'FKVKQP
Much has happened in the world of computing since the first edition of this
book was released. For this second edition, Ive revised and updated the con-
tents from start to finish.
One addition is example code in C/C# as well as Basic. This book includes
code examples for PCs and for embedded systems (microcontrollers).
Also new in the Second Edition are these topics:
Designing and programming USB virtual COM ports.
Using wireless technologies to transmit serial data.
Accessing serial ports over Ethernet or Wi-Fi networks.
Transferring any kind of text data using Unicode encoding.
xv
9JQ 5JQWNF 4GCF VJKU $QQM!
Whether your interest is hardware or software and whether you work with PCs,
embedded systems, or both, youll find useful guidance in this book.
Programmers will learn how to communicate via serial ports, including USB
virtual COM ports, in PCs and embedded systems. The example code for PCs
and microcontrollers in Basic and C/C# provides a quick start for a variety of
applications.
Circuit designers will find designs for a variety of applications including con-
verters that translate between RS-232, RS-485, and 3V/5V logic. Designs with
fail-safe features, high noise immunity, and low power consumption are
included.
Hobbyists and experimenters will find inspiration for projects.
Teachers and students can learn about serial ports and use the examples in this
book to demonstrate concepts.
This book assumes you have a basic knowledge of electronics and either
Basic/Visual Basic or C/C# programming. I assume no previous knowledge or
experience with serial-port hardware or programming.
'ZCORNG %QFG CPF 7RFCVGU
At the start of each code example, a sidehead indicates the programming lan-
guage used:
Example applications are available for free download from www.Lvr.com. This
is also the place to find updates, corrections, and other links to information and
tools for serial-port applications.
5KFGJGCF 2TQITCOOKPI .CPIWCIG 2TQXKFGT
VB Visual Basic .NET Microsoft
VC# Visual C# .NET Microsoft
PBP PICBASIC PRO microEngineering Labs, Inc.
C18 MPLAB C18 compiler Microchip Technology Inc.
xvi
#DDTGXKCVKQPU
This book uses the following abbreviations to express quantities and units:
/WNVKRNKGTU
'NGEVTKECN
6KOG
&KUVCPEG
#DDTGXKCVKQP &GUETKRVKQP 8CNWG
p pico
10
-12
n nano
10
-9
micro
10
-6
m milli
10
-3
k kilo
1
3
M mega
10
6
#DDTGXKCVKQP /GCPKPI
A
amperes
F farads
ohms
V volts
#DDTGXKCVKQP /GCPKPI
s seconds
hr hours
Hz Hertz (cycles per second)
#DDTGXKCVKQP /GCPKPI
in. inches
ft feet
xvii
bps = bits per second
Some expressions contain multiple units, such as ps (picoseconds) and mA
(milliamperes).
0WODGT 5[UVGOU
The following conventions apply to numeric values in the text:
Binary values have a trailing subscript b. Example: 10100011
b.
Hexadecimal values have a trailing h. Example: A3h
All other values are decimal. Example: 163
This page intentionally left blank
xix
#EMPQYNGFIOGPVU
First, I want to thank the readers of Serial Port Completes first edition and the
readers of my other books and articles. This book is much improved due to the
the many suggestions and comments Ive received from readers over the years.
For help in preparing the second edition, I thank my technical reviewers: John
Hyde for his generosity, encouragement, and good suggestions; Rawin Rojvanit
for once again providing a thoughtful and expert critique; and Tsuneo Chinzei
for sharing his knowledge on USB virtual COM ports. Thanks to Ron Smith
for wonderful circuits and great conversations about RS-485. Thanks to Steve
Drake for helping me see my writing through the eyes of a typical reader. And
thanks to Jim Hughes for taking good photos.
This book is dedicated to Michele, Pat, and Isie.
This page intentionally left blank
1
Chapter 9
176
A receiving computer using ASCIIEncoding will display ? for each received
byte greater than 7Fh received:
??
Another option is to use UTF-16 and transmit and receive all characters as
16-bit code units:
8$ Dim utf16 As New System.Text.UnicodeEncoding()
myComPort.Encoding = utf16
8% System.Text.UnicodeEncoding utf16 = new System.Text.UnicodeEncoding();
myComPort.Encoding = utf16;
To send the copyright symbol, a transmitting computer using UTF-16 encod-
ing sends the bytes A9h 00h, and the receiving computer stores the bytes as the
character 00A9h. The down side is that data takes twice as long to transmit and
a receiving computer that uses 8-bit characters will have to convert the received
data. Note that the UTF-16 encoding property is called UnicodeEncoding (not
UTF16Encoding).
7UKPI 5VTGCO 1DLGEVU
Stream objects can represent a source or destination for a series of bytes. The
source or destination can be a storage medium such as a file, memory, or a con-
nection to a device such as a COM port (including virtual COM ports) or
TCP/IP socket. The Stream class provides methods for reading and writing to
stream objects.
A generic Stream object can read and write bytes. Objects created using auxil-
iary stream classes support reading and writing bytes as well as other data types.
SerialPort objects can use the auxialiary BinaryReader and BinaryWriter classes
to read and write numeric data and text and the auxiliary StreamReader and
StreamWriter classes to read and write text. Closing a SerialPort object disposes
of the ports Stream object.
The stream classes are in the System.IO namespace:
8$ Imports System.IO
8% using System.IO;
Using .NETs SerialPort Class
177
$KPCT[4GCFGT CPF $KPCT[9TKVGT
The BinaryReader and BinaryWriter classes support reading and writing data in
a variety of formats, including bytes and other numeric types, Boolean data,
Chars, and length-prefixed strings.
An application uses the SerialPort objects BaseStream property to obtain a
Stream object and then uses the object to create BinaryReader and Binary-
Writer objects:
8$ Friend binaryReader1 As BinaryReader
Friend binaryWriter1 As BinaryWriter
Dim serialPortStream As Stream
' Get the port's Stream object.
serialPortStream = myComPort.BaseStream
' Use the Stream object to create BinaryReader and BinaryWriter objects.
binaryReader1 = New BinaryReader(serialPortStream)
binaryWriter1 = New BinaryWriter(serialPortStream)
8% internal BinaryReader binaryReader1;
internal BinaryWriter binaryWriter1;
Stream serialPortStream = null;
// Get the port's Stream object.
serialPortStream = myComPort.BaseStream;
// Use the Stream object to create BinaryReader and BinaryWriter objects.
binaryReader1 = new BinaryReader( serialPortStream );
binaryWriter1 = new BinaryWriter( serialPortStream );
The Write method can write a byte to a port:
8$ Dim dataToWrite As Byte
dataToWrite = 65
binaryWriter1.Write(dataToWrite)
Chapter 9
178
8% byte dataToWrite;
dataToWrite = 65;
binaryWriter1.Write( dataToWrite );
The Read method can read a received byte into a byte array:
8$ Dim receivedData(0) As Byte
binaryReader1.Read(receivedData, 0, 1)
Console.WriteLine(ChrW(receivedData(0)))
8% byte[] receivedData = new byte[ 1 ];
binaryReader1.Read( receivedData, 0, 1 );
Console.WriteLine((char)( receivedData[ 0 ] ) );
The Write method can also write just about any data type. For example, a
BinaryWriter object can write an Int32 value:
8$ Dim valueToWrite As Int32
valueToWrite = 66
binaryWriter1.Write(valueToWrite)
8% Int32 dataToWrite;
dataToWrite = 66;
binaryWriter1.Write( dataToWrite );
Each Int32 value written to the port is four bytes. For example, writing an
Int32 with a value of 66 (42h) writes the bytes 42h, 00h, 00h, 00h to the port.
The receiving computer can store the value in an Int32 variable:
8$ Dim receivedData As Int32
receivedData = binaryReader1.ReadInt32
8% Int32 receivedData;
receivedData = binaryReader1.ReadInt32();
The ReadInt32 method returns on receiving four bytes.
Using .NETs SerialPort Class
179
The Write and Read methods can also write and read Chars and Char arrays.
8$ Write a Char.
Dim valueToWrite As Char
valueToWrite = CChar("A")
binaryWriter1.Write(valueToWrite)
' Read a Char.
Dim receivedData As Integer
receivedData = binaryReader1.Read
Console.WriteLine(ChrW(receivedData))
' Write an array of Chars.
Dim valueToWrite(1) As Char
valueToWrite(0) = CChar("O")
valueToWrite(1) = CChar("K")
binaryWriter1.Write(valueToWrite, 0, 2)
' Read Chars into an array.
Dim receivedData(1) As Char
binaryReader1.Read(receivedData, 0, 2)
Console.WriteLine(receivedData)
Chapter 9
180
8% // Write a Char.
Char valueToWrite;
valueToWrite = 'A';
binaryWriter1.Write( valueToWrite );
// Read a Char.
int receivedData;
receivedData = binaryReader1.Read();
Console.WriteLine( Strings.ChrW( receivedData ) );
// Write an array of Chars.
Char[] valueToWrite = new Char[2];
valueToWrite[0] = 'O';
valueToWrite[1] = 'K';
binaryWriter1.Write( valueToWrite,0,2 );
// Read Chars into an array.
Char[] receivedData = new Char[2];
binaryReader1.Read(receivedData,0,2);
Console.WriteLine(receivedData);
BinaryWriter and BinaryReader can also send and receive strings. Each string is
prefixed with a length value. Before transmitting a string, a BinaryWriter object
encodes a 32-bit length value using the Write7BitEncodedInt method. The
method encodes the length value as one or more bytes depending on the value
being encoded.
You dont have to understand the encoding method to use it in an application.
BinaryReader and BinaryWriter create and extract the length values automati-
cally. With that said, this is how the encoding works:
The encoding divides the value to transmit into units of 7 bits each. Each unit
is stored in bits 06 of a prefix byte to be transmitted. In the byte that contains
the most significant bits of the length value, bit 7 is zero. For all of the other
bytes, bit 7 is set to 1.
Using .NETs SerialPort Class
181
For example, a 5-byte string has a 1-byte prefix of 05h. A 128-byte string has a
2-byte prefix of 80h 01h. The length to be encoded (128) equals 10000000
b
.
The length value has 8 bits so the prefix is 2 bytes. In the prefixs first byte
(80h), bits 06 match bits 06 in the length value and bit 7 = 1 to indicate
there is another prefix byte. In the prefixs second byte (01h), bit 0 matches bit
7 in the length value, bits 16 are zeroes, and bit 7 equals zero to indicate that
the current byte is the last byte in the prefix.
The value being encoded is the number of bytes transmitted, not the number of
characters, and excludes the prefix. When a BinaryWriter sends a character
by writing the bytes C2h A9h, the prefix is 2 to indicate 2 bytes being transmit-
ted.
To write a String with BinaryWriter, use the Write method:
8$ Dim valueToWrite As String
valueToWrite = "hello"
binaryWriter1.Write(valueToWrite)
8% String valueToWrite ;
valueToWrite = "hello";
binaryWriter1.Write( valueToWrite );
To read a String with BinaryReader, use the ReadString method:
8$ Dim receivedData As String
receivedData = binaryReader1.ReadString
Console.WriteLine(receivedData)
8% String receivedData;
receivedData = binaryReader1.ReadString();
Console.WriteLine(receivedData):
The ReadString method uses the length prefix to determine the strings length,
discards the prefix, and returns the string that follows. If transmitted data
doesnt include a length prefix, the receiving computer interprets the initial
byte(s) as a length value instead of text data and thus doesnt read the correct
string and raises a TimeoutException if the expected number of bytes doesnt
arrive.
Chapter 9
182
The default text encoding for BinaryReader and BinaryWriter is UTF8. Con-
structors can specify a different encoding method for the streams:
8$ Friend binaryReader2 As BinaryReader
Friend binaryWriter2 As BinaryWriter
Dim serialPortStream As Stream
Dim utf16 As New System.Text.UnicodeEncoding
' Get the port's Stream object.
serialPortStream = myComPort.BaseStream
' Use the Stream object to create BinaryReader and BinaryWriter objects.
binaryReader2 = New BinaryReader(serialPortStream, utf16)
binaryWriter2 = New BinaryWriter(serialPortStream, utf16)
8% internal BinaryReader binaryReader2;
internal BinaryWriter binaryWriter2;
System.Text.UnicodeEncoding utf16 = new System.Text.UnicodeEncoding();
Stream serialPortStream = null;
// Get the port's Stream object.
serialPortStream = myComPort.BaseStream;
// Use the Stream object to create BinaryReader and BinaryWriter objects.
binaryReader2 = new BinaryReader( serialPortStream,utf16 );
binaryWriter2 = new BinaryWriter( serialPortStream,utf16 );
The PeekChar method, which returns the next value in a stream without
removing the value from the stream, isnt supported by SerialPort BinaryReader
objects.
5VTGCO4GCFGT CPF 5VTGCO9TKVGT
The StreamReader and StreamWriter classes support reading and writing text
using a specified Unicode encoding. The objects Encoding property determines
the encoding method. The default encoding is UTF-8. Unlike BinaryReader
and BinaryWriter, StreamReader and StreamWriter dont use length prefixes.
Using .NETs SerialPort Class
183
As with BinaryReader and BinaryWriter objects, an application can create
StreamReader and StreamWriter objects using the SerialPort objects Base-
Stream property:
8$ serialPortStream = myComPort.BaseStream
streamReader1 = New StreamReader(serialPortStream)
streamWriter1 = New StreamWriter(serialPortStream)
8% serialPortStream = myComPort.BaseStream;
streamReader1 = new StreamReader(serialPortStream);
streamWriter1 = new StreamWriter(serialPortStream);
Setting StreamWriters AutoFlush property to True causes Write operations to
send data immediately to the port:
8$ streamWriter1.AutoFlush = True
8% streamWriter1.AutoFlush = true;
If the AutoFlush property is False, data written to the stream may be delayed in
the streams buffer. When AutoFlush is False, calling the Flush method or clos-
ing the stream sends any buffered data to the port.
The Write and WriteLine methods can write Strings to a port:
8$ streamWriter1.Write("hello, ")
streamWriter1.WriteLine("world")
8% streamWriter1.Write("hello, ");
streamWriter1.WriteLine("world");
In addition to basic Strings, StreamWriters Write and WriteLine methods can
write Chars, text representations of numeric and Boolean values, text represen-
tations of objects as defined an objects ToString property, and strings formatted
as defined by the String.Format property.
Chapter 9
184
Here are some examples:
8$ Dim dataToSend1 As Char = CChar("A")
streamWriter1.WriteLine(dataToSend1)
Dim dataToSend2 As Int32 = 254
streamWriter1.WriteLine(dataToSend2)
Dim dataToSend3 As Decimal = 100
streamWriter1.WriteLine(dataToSend3)
Dim dataToSend4 As Boolean = True
streamWriter1.WriteLine(dataToSend4)
Dim dataToSend5 As Byte = 57
streamWriter1.WriteLine(dataToSend5.ToString)
streamWriter1.WriteLine("{0:t}", DateTime.Now)
8% Char dataToSend1 = 'A';
streamWriter1.WriteLine(dataToSend1);
Int32 dataToSend2 = 254;
streamWriter1.WriteLine(dataToSend2);
Decimal dataToSend3 = 100;
streamWriter1.WriteLine(dataToSend3);
Boolean dataToSend4 = true;
streamWriter1.WriteLine(dataToSend4);
Byte dataToSend5 = 57;
streamWriter1.WriteLine(dataToSend5.ToString ());
streamWriter1.WriteLine("{0:t}", DateTime.Now);
The code above writes the following text to the stream and port:
A
254
100
True
57
4:07 PM
Using .NETs SerialPort Class
185
The StreamReader class provides several methods for reading text from a Seri-
alPort object.
StreamReaders Read method can read a single character into an Integer or one
or more characters into a Char array. The method returns when at least one
character has been received:
8$ ' Read a character.
Dim receivedData1 As Integer
receivedData1 = streamReader1.Read
Console.WriteLine(Chr(receivedData1))
' Read two characters into an array.
Dim receivedData2(1) As Char
Dim count as Integer
count = streamReader1.Read(receivedData2, 0, 2)
Console.WriteLine(count & " characters received: ")
Console.WriteLine(receivedData2)
8% // Read a character.
int receivedData1;
receivedData1 = streamReader1.Read();
Console.WriteLine(Strings.ChrW(receivedData1));
// Read two characters into an array.
Char[] receivedData2 = new Char[2];
int count;
count = streamReader1.Read(receivedData2, 0, 2);
Console.WriteLine(count + " characters received: ");
Console.WriteLine(receivedData2);
ReadBlock is a blocking version of the Read method and stores received data in
a Char array. Unlike Read, ReadBlock waits for all of the requested characters to
arrive or a timeout.
Chapter 9
186
ReadLine returns a String containing everything up to the first NewLine char-
acter.
Peek and ReadToEnd are methods that might appear useful but in reality arent
practical for most COM-port applications.
Peek returns the next value in the stream without removing the value from the
stream or returns -1 if no character is available. The method can be useful when
reading from resources such as files but is less helpful in COM-port applica-
tions that send data continuously. When the buffer is empty, Peek returns -1
but then continues to return -1 even after more characters have arrived at the
port.
ReadToEnd returns a String containing everything from the current position to
the end of the stream. The stream normally has no way of knowing when it has
reached the end of a particular block of COM-port data. (When reading from a
StreamWriter object, ReadToEnd returns when the StreamWriter object closes.)
5CXKPI C 2QTV CPF 2CTCOGVGTU
Users often find it convenient when an application remembers and selects the
port used the last time the application ran. One way to save parameters is to
store them in the system registry using the RegistryKey class in the
Microsoft.Win32 namespace. The .NET Framework provides another option
in the Application Settings architecture, which enables storing and retrieving
settings in files that dont clog the registry.
6JG #RRNKECVKQP 5GVVKPIU #TEJKVGEVWTG
The System.Configuration namespace includes the AppSettingsSection class,
which provides the Settings property for saving and retrieving settings. The Set-
tings property is a NameValueCollection of strings and string values. (The
NameValueCollection class is in the System.Collections.Specialized
namespace.) The Application Settings architecture was introduced in .NET
2.0.
To create a setting, in Visual Studio, in the Project menu, select Project_name
Properties, where Project_name is the name of your project. In the window that
appears, select Settings (Figure 9-1). For each setting, enter a name and default
value and select a data type and scope. User scope is for settings that users can
change, while application scope is generally for settings that never change.
Using .NETs SerialPort Class
187
The settings are stored in the file user.config, where user is the user name of the
person running the application. To enable using the settings, you must deploy
Visual Studios app.exe file with the project.
%QODQ $QZ 'ZCORNG
An application might save its settings when executing the main forms
_FormClosing routine. This example saves the selected items in three combo
boxes (cmbBitRate, cmbPort, and cmbHandshaking):
8$ Settings.Default.BitRate = CInt(cmbBitRate.SelectedItem)
Settings.Default.ComPort = cmbPort.SelectedItem.ToString
Settings.Default.Handshaking = DirectCast(cmbHandshaking.SelectedItem, Handshake)
Settings.Default.Save()
Figure 9-1: In Visual Studios Project > Properties windows, the Settings pane enables
defining application-specific values to be saved with an application.
Chapter 9
188
8% // COMPortTerminal is the project's default namespace
// (specified in Project > Properties > Application).
using COMPortTerminal.Properties;
Settings.Default.BitRate = (int)cmbBitRate.SelectedItem;
Settings.Default.ComPort = cmbPort.SelectedItem.ToString();
Settings.Default.Handshaking = (Handshake)cmbHandshaking.SelectedItem;
Settings.Default.Save();
On running the application, the main forms _Load routine can retreive the set-
tings:
8$ cmbBitRate.SelectedItem = Settings.Default.BitRate
cmbHandshaking.SelectedItem = Settings.Default.Handshaking
cmbPort.SelectedItem = Settings.Default.ComPort
8% cmbBitRate.SelectedItem = Settings.Default.BitRate;
cmbHandshaking.SelectedItem = Settings.Default.Handshaking;
cmbPort.SelectedItem = Settings.Default.ComPort;
USB virtual COM ports can come and go on a PC as users attach and remove
devices. A retrieved port name might specify a port that isnt available. Before
attempting to open a default port, application code can search the array
returned by GetPortNames to determine if the port exists, as described earlier
in this chapter.
Managing Ports and Transfers in .NET
189
10
/CPCIKPI 2QTVU CPF
6TCPUHGTU KP 0'6
The .NET Framework and its SerialPort class provide many properties, meth-
ods, and events that are useful in managing transfers. Applications can define
buffers to hold data, implement flow control to prevent lost data, and use tim-
ers or DataReceived events to detect received data. Parity bits can enable detect-
ing errors in received data. This chapter demonstrates how to use these
capabilities to transfer data efficiently and without errors.
The code in this chapter uses resources in the following namespaces:
8$ Imports System.IO.Ports
Imports System.Runtime.Remoting.Messaging ' used with asynchronous delegates
8% using System.IO.Ports;
using System.Runtime.Remoting.Messaging; // used with asynchronous delegates
Several of the examples in this chapter use delegates to perform event-driven or
asynchronous (non-blocking) operations. A delegate is an object that defines
the number and type(s) of parameters a method accepts and the type of value
Chapter 10
190
the method returns, if any. A delegate enables application code to invoke a
method indirectly and safely. The examples use delegates in several ways. Dele-
gates specify routines to handle events that receive data, detect serial-port errors,
and detect changes on RS-232 status pins. Delegates also enable passing
received data to a form and performing write operations without blocking user
input or other operations performed by the applications main thread.
4GEGKXKPI &CVC
Many applications need to detect COM-port data that arrives at unpredictable
times. Some applications need to collect and manage data that arrives over a
period of time. This section shows strategies for accomplishing these tasks.
5GVVKPI 6KOGQWVU
The ReadTimeout property introduced in Chapter 9 can keep a program
thread from wasting too much time waiting for data to arrive. When ReadTim-
eout is greater than zero, all of the read methods except ReadExisting will block
the thread that called the method until data is available or a timeout. A thread
that calls a blocking method can do no other work, including responding to
user input, until the method returns.
On a timeout when transmitting data, the application might want to set a vari-
able that tells the application to retry. On a timeout when waiting to receive
expected data, the application might want to send a message to notify the trans-
mitting computer of the problem.
&GVGEVKPI 4GEGKXGF &CVC
To detect received data, applications can poll the port or use the DataReceived
event.
2QNNKPI
An application can poll a port at specific times when the application expects to
receive data or at defined intervals. If no data is available, a blocking read
method will wait for data or a timeout.
Managing Ports and Transfers in .NET
191
To avoid waits and timeouts, an application can read the BytesToRead property
and attempt to read data from the port only if data is available:
8$ Dim receivedData As Integer
If myComPort.BytesToRead > 0 Then
receivedData = myComPort.ReadByte
Console.WriteLine(ChrW(receivedData))
Else
Console.WriteLine("No data available.")
End If
8% int receivedData;
if (myComPort.BytesToRead > 0)
{
receivedData = myComPort.ReadByte();
Console.WriteLine((char)receivedData);
}
else
{
Console.WriteLine("No data available.");
}
If using a method such as BinaryReaders ReadInt32, which reads multi-byte
values, be sure to check for the needed number of bytes in the buffer.
For reading strings, the ReadExisting method always returns immediately,
returning an empty string if the buffer is empty. For the blocking read methods,
setting the ReadTimeout property determines how long the method waits if no
data is available.
Chapter 10
192
An example of an application that expects to receive data at specific times is a
computer that sends commands and expects a response after each command. If
the remote computer typically responds quickly, the application might wait for
the response:
8$ Dim receivedData As String
' Send a command.
myComPort.WriteLine("CMD1")
' Wait for a response.
receivedData = myComPort.ReadLine
Console.WriteLine(receivedData)
8% String receivedData;
// Send a command.
myComPort.WriteLine("CMD1");
// Wait for a response.
receivedData = myComPort.ReadLine();
Console.WriteLine(receivedData);
Another option for polling is to use a Timer component to trigger periodic
reads of the port. Drag a Timer component from Visual Studios Toolbox onto
a form. Set the interval in milliseconds for polling the port.
8$ tmrPollForReceivedData.Interval = 1000
tmrPollForReceivedData.Stop()
8% tmrPollForReceivedData.Interval = 1000;
tmrPollForReceivedData.Stop();
When the port has been opened, start the timer:
8$ tmrPollForReceivedData.Start()
8% tmrPollForReceivedData.Start();
Managing Ports and Transfers in .NET
193
The timers Tick routine executes when the timer is running and the specified
interval has elapsed. The routine should do whatever is needed with any
received data:
8$ Private Sub tmrPollForReceivedData_Tick _
(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles tmrPollForReceivedData.Tick
Dim receivedData As String
' Read and display any received data.
receivedData = myComPort.ReadExisting()
If Not (receivedData = "") Then
Console.WriteLine(receivedData)
End If
End Sub
8% private void tmrPollForReceivedData_Tick(object sender, EventArgs e)
{
String receivedData;
// Read and display any received data.
receivedData = myComPort.ReadExisting();
if (!(receivedData == "") )
{
Console.WriteLine(receivedData);
}
}
On closing the port, stop the timer.
8$ tmrPollForReceivedData.Stop()
8% tmrPollForReceivedData.Stop();
Chapter 10
194
7UKPI VJG &CVC4GEGKXGF 'XGPV
The DataReceived event provides an efficient way to detect received data. To
use the event, create a delegate for a routine that will execute when a DataRe-
ceived event occurs on an open port.
8$ ' Define a delegate class to handle DataReceived events.
Friend Delegate Sub SerialDataReceivedEventHandlerDelegate _
(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
' Create an instance of the delegate.
Private SerialDataReceivedEventHandler1 _
As New SerialDataReceivedEventHandler(AddressOf DataReceived)
8% // Define a delegate class to handle DataReceived events.
internal delegate void SerialDataReceivedEventHandlerDelegate
(object sender, SerialDataReceivedEventArgs e);
// Create an instance of the delegate.
private static SerialDataReceivedEventHandler SerialDataReceivedEventHandler1 =
new SerialDataReceivedEventHandler( ComPort.DataReceived );
Assign the delegate as the handler routine to execute when a DataReceived
event occurs on a port:
8$ AddHandler myComPort.DataReceived, SerialDataReceivedEventHandler1
8% myComPort.DataReceived += SerialDataReceivedEventHandler1;
The DataReceived routine can then read and process received data as needed.
8$ Friend Sub DataReceived _
(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
' Place code to read and process data here.
End Sub
Managing Ports and Transfers in .NET
195
8% internal void DataReceived( object sender, SerialDataReceivedEventArgs e )
{
' Place code to read and process data here.
}
The c ode i n t he r out i ne c a n be i de nt i c a l t o t he c ode i n t he
tmrPollForReceivedData_Tick routine above or whatever the application
requires. The ReceivedBytesThreshold property specifies how many bytes must
be available before the event is raised.
/CTUJCNKPI 4GEGKXGF &CVC VQ C (QTO
The DataReceived routine executes in a secondary thread. To display data, mes-
sages, or other information from the DataReceived routine on an applications
form, an application must marshal the data from the DataReceived routine to
the form.
In the example below, the DataReceived routine reads data from the port and
passes the data in a call to AccessFormMarshal. The AccessFormMarshal rou-
tine uses a delegate to pass the received data to the AccessForm routine, which
displays the received data on the form.
To enable marshaling data, the forms module defines a delegate class with
parameters that will hold the data to be passed to the form. This example passes
a string to display:
8$ ' MyMainForm is an instance of the form MainForm.
Friend MyMainForm As MainForm
Private Delegate Sub AccessFormMarshalDelegate(ByVal textToDisplay As String)
8% // MyMainForm is an instance of the form MainForm.
internal static MainForm MyMainForm;
private delegate void AccessFormMarshalDelegate(string textToDisplay);
Chapter 10
196
Create a routine with the same parameter(s) as the delegate:
8$ Private Sub AccessFormMarshal(ByVal textToDisplay As String)
' The parameter(s) to pass to the form.
Dim args() As Object = {textToDisplay}
' The AccessForm routine contains the code that accesses the form.
Dim AccessFormMarshalDelegate1 As _
New AccessFormMarshalDelegate(AddressOf AccessForm)
' Call AccessForm, passing the parameters in args.
MyBase.Invoke(AccessFormMarshalDelegate1, args)
End Sub
8% internal void AccessFormMarshal(string textToDisplay)
{
// The parameter(s) to pass to the form.
object[] args = {textToDisplay};
AccessFormMarshalDelegate AccessFormMarshalDelegate1 = null;
// The AccessForm routine contains the code that accesses the form.
AccessFormMarshalDelegate1 = new AccessFormMarshalDelegate(AccessForm);
// Call AccessForm, passing the parameters in args.
base.Invoke(AccessFormMarshalDelegate1, args);
}
Create the routine that accesses the form. This example appends the passed text
to a text box (txtUserDisplay) on the form:
8$ Private Sub AccessForm(ByVal textToDisplay As String)
txtUserDisplay.AppendText(textToDisplay)
End Sub
Managing Ports and Transfers in .NET
197
8% private void AccessForm(string textToDisplay)
{
txtUserDisplay.AppendText(textToDisplay);
}
In the DataReceived routine, call AccessFormMarshal to pass data to display in
the forms textbox:
8$ Dim receivedData As String = myComPort.ReadExisting()
MyMainForm.AccessFormMarshal(receivedData)
8% string receivedData = myComPort.ReadExisting();
MyMainForm.AccessFormMarshal(receivedData);
%QNNGEVKPI 4GEGKXGF &CVC
Many applications collect a quantity of received data before taking action. For
example, a remote computer might send a large file for the receiving computer
to store, execute, or use in another way. A transmitting computer might also
send large quantities of data in multiple files or other blocks of data. In either
case, the application may need to provide a location to store data in sequence
from multiple read operations.
The ReadBufferSize property sets the size of the receive buffer with a minimum
size of 4096 bytes. If you expect a port to collect more than 4096 bytes of
received data before the application can read the data, increase the size of the
buffer. An application that knows how many bytes to expect can let the data
collect in the SerialPort buffer and use the BytesReceived property to find out
when all of the data has arrived.
Sometimes an application doesnt know how much data will arrive, or the appli-
cation might need to examine the data as it comes in. The StringBuilder and
List(Of T) classes provide mechanisms for collecting and managing received
data.
7UKPI VJG 5VTKPI$WKNFGT %NCUU
The StringBuilder class can collect received strings in a buffer that grows as
needed when the application appends new strings. For example, if receiving a
text file, an application can store the file as a single StringBuilder object. The
class is in the System.Text namespace.
Chapter 10
198
8$ Imports System.Text
Friend stringBuffer As New StringBuilder
Dim newReceivedData As String
' Read received data into a String.
newReceivedData = myComPort.ReadExisting()
' Append the String to the StringBuilder object.
stringBuffer.Append(newReceivedData)
8% using System.Text;
internal StringBuilder stringBuffer = new StringBuilder();
String newReceivedData;
// Read received data into a String.
newReceivedData = myComPort.ReadExisting();
// Append the String to the StringBuilder object.
stringBuffer.Append(newReceivedData);
To convert a StringBuilder object to a String, use the ToString property:
8$ Dim receivedText as String
receivedText = stringBuffer.ToString
8% String receivedText;
receivedText = stringBuffer.ToString();
To clear a StringBuilder object, use the Remove method:
8$ stringBuffer.Remove(0, stringBuffer.Length)
8% stringBuffer.Remove(0, stringBuffer.Length);
Managing Ports and Transfers in .NET
199
The Remove method can also remove selected characters:
8$ ' Remove the first two characters:
stringBuffer.Remove(0, 2)
8% // Remove the first two characters:
stringBuffer.Remove(0, 2);
You can specify a capacity when declaring a StringBuilder object:
8$ Friend stringBuffer As New StringBuilder(1024)
8% internal StringBuilder stringBuffer = new StringBuilder(1024);
The StringBuilder object can store up to the specified capacity without having
to allocate more memory. On exceeding the specified capacity, the object grows
in size as needed to hold the data.
7UKPI VJG .KUV1H 6 %NCUU
For storing bytes and other data types as well as strings, the List(Of T) class (the
List class in C#) offers a solution. The class is in the System.Collections.Generic
namespace and supports methods for manipulating, searching, and sorting list
elements. The AddRange method can add the contents of an array to the end of
a list, expanding the lists capacity as needed. The RemoveRange method can
remove a range of elements from a list. The Clear method removes all elements
in a list.
A List(Of T) object can hold a series of bytes:
8$ Imports System.Collections.Generic
Friend portBuffer As New List(Of Byte)
8% using System.Collections.Generic;
internal List<Byte> portBuffer = new List<Byte>();
Chapter 10
200
To reduce the number of resizing operations needed as elements are added to
the list, you can specify an initial capacity when creating the list object:
8$ Friend portBuffer As New List(Of Byte)(1024)
8% internal List<Byte> portBuffer = new List<Byte>(1024);
The code below appends received bytes to the end of a list and calls a routine to
process the data. The code can execute in a DataReceived event or Timer event:
8$ Dim numberOfBytesToRead As Integer
' Get the number of bytes available to read.
numberOfBytesToRead = myComPort.BytesToRead
' Create a byte array large enough to hold the bytes to be read.
Dim newReceivedData(numberOfBytesToRead - 1) As Byte
' Read the bytes into the byte array.
myComPort.Read(newReceivedData, 0, numberOfBytesToRead)
' Add the bytes to the end of the list.
portBuffer.AddRange(newReceivedData)
' Call a routine to process the data.
ProcessData()
Managing Ports and Transfers in .NET
201
8% int numberOfBytesToRead;
// Get the number of bytes available to read.
numberOfBytesToRead = myComPort.BytesToRead;
// Create a byte array large enough to hold the bytes to be read.
Byte[] newReceivedData = new Byte[numberOfBytesToRead];
// Read the bytes into the byte array.
myComPort.Read(newReceivedData, 0, numberOfBytesToRead);
// Add the bytes to the end of the list.
portBuffer.AddRange(newReceivedData);
// Call a routine to process the data.
ProcessData();
The routine below illustrates how to use a List(Of T) object to collect data. The
routine waits for eight bytes to arrive, removes the bytes from the list, and dis-
plays the bytes as text characters.
8$ Private Sub ProcessData()
' When eight bytes have arrived, display them and remove them from the buffer.
Dim count As Integer
Dim numberOfBytesToRead As Integer = 8
If portBuffer.Count >= numberOfBytesToRead Then
For count = 0 To numberOfBytesToRead - 1
Console.WriteLine(ChrW(portBuffer(count)))
Next count
' Remove the bytes read from the list.
portBuffer.RemoveRange(0, numberOfBytesToRead)
End If
End Sub
Chapter 10
202
8% private void ProcessData()
{
// When eight bytes have arrived, display them and remove them from the buffer.
int count;
int numberOfBytesToRead = 8;
if (portBuffer.Count >= numberOfBytesToRead)
{
for (count = 0; count < numberOfBytesToRead; count++)
{
Console.WriteLine((char)(portBuffer[count]));
}
// Remove the bytes read from the list.
portBuffer.RemoveRange(0, numberOfBytesToRead);
}
}
In a similar way, an application can create a list of Chars or Strings for storing
received text. To add a received string to a list, use the Add method.
'PUWTKPI 'HHKEKGPV 6TCPUHGTU
Each read operation adds overhead, which can degrade performance of applica-
tions that are reading large amounts of data. For example, an application read-
ing a large file from a port might want to read the file using as few read
operations as possible rather than collecting each byte as it arrives. An applica-
tion can read a ports BytesToRead property to find out if the needed number of
bytes is available before reading from the port.
When using the ReadExisting method, call the method less often to retrieve
more data on each call. When using the DataReceived event, set Received-
BytesThreshold to a large block size or to the expected number of bytes if
known. A timer can trigger a read of any amount smaller than Received-
BytesThreshold that remains unread in the buffer. When waiting for expected
data, set ReadTimeout long enough to prevent timeouts under normal condi-
tions. Be sure to add in typical delays due to flow control.
An application that must take action in response to received data might need to
read each byte as soon as it arrives at the port. In that case, use the DataRe-
ceived event for fast response and set ReceivedBytesThreshold to 1.
Managing Ports and Transfers in .NET
203
5GPFKPI &CVC
When sending data, the application should prevent or minimize timeouts and
prevent buffer overflows. An application can perform write operations in a sep-
arate thread to avoid blocking the applications thread. This section shows strat-
egies for accomplishing these tasks.
#XQKFKPI 6KOGQWVU
The likelihood of a timeout increases when an application transfers large quan-
tities of data, uses a slow bit rate, or uses flow control.
When a write operation times out, any data that was waiting to transmit in that
operation is lost. Data queued to the port in subsequent write operations
remains in the buffer. Setting WriteTimeout to InfiniteTimeout ensures that
write operations never time out, but a write operation could wait forever if the
remote computer hangs and doesnt allow the data to transmit. Applications
that need to send large blocks of data can use multiple write operations to send
the data in chunks with generous but finite timeouts. Writing data in smaller
chunks also helps prevent buffer overflows.
5GPFKPI YKVJQWV $NQEMKPI VJG #RRNKECVKQP
Using the standard Windows drivers, a write operation to a physical COM port
will block until the UART has transmitted all of the data or a timeout has
occurred. While the data is transmitting or waiting to transmit, the applications
thread cant perform other operations, including responding to user input. Ven-
dor-provided drivers might behave differently but are still likely to block on
write operations.
Write operations that complete quickly might have no noticeable or significant
effect on performance. Writing large blocks of data, especially at slow bit rates,
or writes that experience long delays due to flow control can hang an applica-
tion for more time than desired.
To enable doing other things while a write operation is in progress, an applica-
tion can perform the write operation in a separate thread. If needed, the appli-
cation can assign a routine to be called after the data has transmitted.
To perform write operations in a separate thread, define a delegate for the rou-
tine that writes to the port. The example delegate below includes a parameter
that holds a string to write to the port.
Chapter 10
204
8$ ' Define a delegate class to handle writes to the port.
Friend Delegate Function WriteToComPortDelegate(ByVal textToWrite As String) _
As Boolean
' Create an instance of the delegate.
Friend WriteToComPortDelegate1 _
As New WriteToComPortDelegate(AddressOf WriteToComPort)
8% // Define a delegate class to handle writes to the port.
internal delegate Boolean WriteToComPortDelegate(string textToWrite);
// Create an instance of the delegate.
internal static WriteToComPortDelegate WriteToComPortDelegate1 =
new WriteToComPortDelegate(WriteToComPort);
When the application has data to send, call the delegates BeginInvoke method,
passing the delegates parameter (textToWrite), the address of an AsyncCallback
routine to execute when the write operation completes (WriteCompleted), and
a parameter that can be either Nothing/null or a value to pass to the callback
routine (msg):
8$ Dim ar As IAsyncResult
Dim msg As String
Dim textToWrite As String
' Text to write to the port.
textToWrite = "hello, world"
' A value to pass to the callback routine (optional).
msg = DateTime.Now.ToString
' Call a routine to write to the COM port.
ar = WriteToComPortDelegate1.BeginInvoke (textToWrite, _
New AsyncCallback(AddressOf WriteCompleted), msg)
Managing Ports and Transfers in .NET
205
8% IAsyncResult ar;
String msg;
String textToWrite;
// Text to write to the port.
textToWrite = "hello, world";
// A value to pass to the callback routine (optional).
msg = DateTime.Now.ToString();
// Call a routine to write to the COM port.
ar = WriteToComPortDelegate1.BeginInvoke
(textToWrite, new AsyncCallback(WriteCompleted), msg);
The BeginInvoke method calls WriteToComPort, which is the routine named
in the declaration of WriteToComPortDelegate1:
8$ Friend Function WriteToComPort(ByVal textToWrite As String) As Boolean
Dim success As Boolean
If myComPort.IsOpen Then
' Write the passed String to the port.
myComPort.Write(textToWrite)
success = True
End If
Return success
End Sub
Chapter 10
206
8% internal static Boolean WriteToComPort( string textToWrite )
{
Boolean success;
if (myComPort.IsOpen)
{
// Write the passed String to the port.
myComPort.Write(textToWrite);
success = true;
}
return success;
}
After calling BeginInvoke, the applications thread is free to do other things
while the write operation is in progress. When the write operation completes,
the callback routine executes. The callback routine can perform any actions
required after the write operation completes.
8$ Friend Sub WriteCompleted(ByVal ar As IAsyncResult)
Dim msg As String
Dim deleg As WriteToComPortDelegate
Dim success As Boolean
' To obtain the msg value passed to the BeginInvoke method,
' cast BeginInvoke's IAsyncResult value to an AsyncResult object
' and get the object's AsyncDelegate property.
deleg = DirectCast(DirectCast(ar, AsyncResult).AsyncDelegate, _
WriteToComPortDelegate)
' The msg value is in the AsyncState property.
msg = = DirectCast(ar.AsyncState, String)
' The EndInvoke method returns the value returned by WriteToComPort.
success = WriteToComPortDelegate1.EndInvoke(ar)
Console.WriteLine("Write operation began: " & msg)
Console.WriteLine("Write operation succeeded: " & success)
End Sub
Managing Ports and Transfers in .NET
207
8% internal static void WriteCompleted( IAsyncResult ar )
{
String msg;
WriteToComPortDelegate deleg;
Boolean success;
// To obtain the msg value passed to the BeginInvoke method,
// cast BeginInvoke's IAsyncResult value to an AsyncResult object
// and get the object's AsyncDelegate property.
deleg = (WriteToComPortDelegate)((AsyncResult)ar).AsyncDelegate;
// The msg value is in the AsyncState property.
msg = (String)ar.AsyncState;
// The EndInvoke method returns the value returned by WriteToComPort.
success = WriteToComPortDelegate1.EndInvoke(ar);
Console.WriteLine("Write operation began: " + msg);
Console.WriteLine("Write operation succeeded: " + success);
}
An application can call the delegate multiple times, queuing more data in the
transmit buffer with each call.
2TGXGPVKPI $WHHGT 1XGTHNQYU
The WriteBufferSize property sets the size of the transmit buffer with a mini-
mum size of 2048 bytes. To enable writing more bytes at once to a port for
transmitting, increase the size of the transmit buffer.
Chapter 10
208
The safest approach is to check before each write to a port to verify that the
transmit buffer has room for the data:
8$ Dim stringToWrite as String
stringToWrite = "hello, world"
' Find out if the transmit buffer has room for the new data.
If ((myComPort.WriteBufferSize - myComPort.BytesToWrite) > _
stringToWrite.Length) Then
myComPort.Write(stringToWrite)
Else
Console.WriteLine("Not enough room in transmit buffer.")
End If
8% String stringToWrite;
stringToWrite = "hello, world";
// Find out if the transmit buffer has room for the new data.
if ((myComPort.WriteBufferSize - myComPort.BytesToWrite) > stringToWrite.Length)
{
myComPort.Write(stringToWrite);
}
else
{
Console.WriteLine("Not enough room in transmit buffer.");
}
Even writing a small amount of data can overflow the write buffer if previous
write operations have filled the buffer. A loop or timer routine can check for
room in the buffer at intervals and write the data when the buffer has room.
'PUWTKPI 'HHKEKGPV 6TCPUHGTU
As with read operations, each write operation adds overhead. To keep from
hanging the application, perform the write operations in a separate thread as
described above. Write large amounts of data in one operation or a series of
large blocks. Set WriteTimeout long enough to prevent timeouts under normal
conditions, including delays due to flow control.
Managing Ports and Transfers in .NET
209
(NQY %QPVTQN
Chapter 2 introduced flow control. The SerialPort classs Handshake property
offers four flow-control options: None, Request to Send, Xon/Xoff, and
Request to Send with Xon/Xoff.
5GNGEVKPI C /GVJQF
With Request to Send flow control selected, the COM-port driver sets RTS
false when the InBufferCount property equals (ReadBufferSize - 1024) bytes or
greater. The remote computer should then stop sending data until RTS is true.
In the other direction, the driver doesnt send data when CTS is false.
With Xon/Xoff flow control selected, the driver sends an Xoff code when the
InBufferCount property reaches (ReadBufferSize - 1024) bytes. The remote
computer should then stop sending data until receiving an Xon code. In the
other direction, after receiving an Xoff code, the driver doesnt send data until
receiving an Xon code.
Request to Send with Xon/Xoff flow control uses both flow-control protocols.
If CTS is false indicating that the remote computer cant receive data, the driver
wont send an Xoff.
After the local computer asserts RTS or sends an Xoff code, the remote com-
puter can send 1024 bytes before the buffer is full. A computer sending data to
such a port should check the flow-control input at least once every 1024 bytes.
For example, assume the SerialPort object has 1030 bytes free in its receive
buffer and is too busy to process received data immediately. If the sending com-
puter checks the flow-control input, sees that its OK to send data, and sends
1031 bytes without re-checking, the receive buffer could overflow.
Embedded systems are likely to have much smaller buffers. A computer sending
data to embedded systems should check the flow-control input often, ideally
before sending each byte.
/QPKVQTKPI CPF %QPVTQNNKPI VJG 5KIPCNU
The SerialPort class also provides properties for monitoring and controlling the
flow-control lines explicitly and an event to announce changes at flow-control
inputs. An application might need to monitor or control flow-control signals
for non-standard uses such as controlling a synchronous serial interface. An
application can also send and detect software flow-control codes without rely-
ing on the SerialPort object to do so.
Chapter 10
210
Table 10-1 shows the signals that applications can monitor and control. An
application can control the RTS and DTR outputs and can read the CD, CTS,
and DSR inputs. A True state corresponds to a positive RS-232 voltage, and a
False state corresponds to a negative RS-232 voltage. As Chapter 16 explains,
the drivers for some USB virtual COM ports dont support reading CTS explic-
itly, and applications that access these ports cant use the value of CTSHolding.
The BreakState property enables controlling the TX line. Setting the property
True brings the RS-232 TX line positive. When TX is True, writing data to the
port fails and raises an InvalidOperationException. Setting the property False
brings TX negative and enables transmitting data on the line.
The PinChanged event can notify an application when a change has occurred
on the CTS, DSR, or CD input, when RI has changed from False to True, and
when RX has entered the Break state (but not when RX exits the Break state).
RX is in the Break state when the lines state has equaled logic 0 for longer than
the time required to transmit one word at the current bit rate.
Table 10-1: The SerialPort class includes properties for reading and writing to
flow-control signals.
2TQRGTV[ 7UG
BreakState Gets or sets the state of the TX output.
If set to True, no data can transmit on TX.
CDHolding Gets the state of the CD input.
CtsHolding Gets the state of the CTS input.
DsrHolding Gets the state of the DSR input.
DtrEnable Gets or sets the state the of the DTR output.
RtsEnable Gets or sets the state the of the RTS output.
Managing Ports and Transfers in .NET
211
The code to use the PinChanged event is similar to the code for the DataRe-
ceived event earlier in this chapter. The application assigns a routine to execute
when a PinChanged event occurs on an open port:
8$ ' Define a delegate class to handle PinChanged events.
Friend Delegate Sub SerialPinChangedEventHandlerDelegate _
(ByVal sender As Object, ByVal e As SerialPinChangedEventArgs)
' Create an instance of the delegate.
Private SerialPinChangedEventHandler1 _
As New SerialPinChangedEventHandler(AddressOf PinChanged)
8% // Define a delegate class to handle PinChanged events.
internal delegate void SerialPinChangedEventHandlerDelegate
(object sender, SerialPinChangedEventArgs e);
// Create an instance of the delegate.
private SerialPinChangedEventHandler SerialPinChangedEventHandler1;
SerialPinChangedEventHandler1 = new SerialPinChangedEventHandler(PinChanged);
The application assigns the delegate as the handler routine that will execute
when a PinChanged event occurs on an open COM port:
8$ AddHandler myComPort.PinChanged, SerialPinChangedEventHandler1
8% myComPort.PinChanged += SerialPinChangedEventHandler1;
Chapter 10
212
The routine can check to find out which pin has changed state and take action
as needed. This example just displays information about the event:
8$ Friend Sub PinChanged(ByVal sender As Object, ByVal e As SerialPinChangedEventArgs)
Dim SerialPinChange1 As SerialPinChange
Dim signalState As Boolean
SerialPinChange1 = e.EventType
Select Case SerialPinChange1
Case SerialPinChange.Break
Console.WriteLine("Break is set")
Case SerialPinChange.CDChanged
signalState = myComPort.CDHolding
Console.WriteLine("CD = " & signalState)
Case SerialPinChange.CtsChanged
signalState = myComPort.CtsHolding
Console.WriteLine("CTS = " & signalState)
Case SerialPinChange.DsrChanged
signalState = myComPort.DsrHolding
Console.WriteLine("DSR = " & signalState)
Case SerialPinChange.Ring
Console.WriteLine("Ring detected")
End Select
End Sub
Managing Ports and Transfers in .NET
213
8% internal void PinChanged( object sender, SerialPinChangedEventArgs e )
{
SerialPinChange SerialPinChange1 = 0;
bool signalState = false;
SerialPinChange1 = e.EventType;
switch ( SerialPinChange1 )
{
case SerialPinChange.Break:
Console.WriteLine( "Break is set" );
break;
case SerialPinChange.CDChanged:
signalState = myComPort.CDHolding;
Console.WriteLine( "CD = " + signalState );
break;
case SerialPinChange.CtsChanged:
signalState = myComPort.CtsHolding;
Console.WriteLine( "CTS = " + signalState );
break;
case SerialPinChange.DsrChanged:
signalState = myComPort.DsrHolding;
Console.WriteLine( "DSR = " + signalState );
break;
case SerialPinChange.Ring:
Console.WriteLine( "Ring detected" );
break;
}
}
Chapter 10
214
*CPFNKPI 'TTQTU
Some errors in COM-port communications raise exceptions while others raise
the SerialPort objects ErrorReceived event. Application code can also use
checksums to detect errors in transmitted data.
'ZEGRVKQPU
This chapter and Chapter 9 have discussed some of the COM-port events and
conditions that can raise exceptions. In general, application code can minimize
the likelihood of exceptions by checking for valid ports and port states before
accessing a port. When needed, a Catch block can display messages to help the
user fix the problem by selecting another port, attaching a port, or taking other
action.
6JG 'TTQT4GEGKXGF 'XGPV
Several error conditions can raise SerialPorts ErrorReceived event. The Error-
Received code below is similar to the code for the PinChanged and DataRe-
ceived events in this chapter.
To use the ErrorReceived event, the application must assign a routine that will
execute when an ErrorReceived event occurs on an open port:
8$ ' Define a delegate class to handle ErrorReceived events.
Friend Delegate Sub SerialErrorReceivedEventHandlerDelegate _
(ByVal sender As Object, ByVal e As SerialErrorReceivedEventArgs)
' Create an instance of the delegate.
Private SerialErrorReceivedEventHandler1 _
As New SerialErrorReceivedEventHandler(AddressOf ErrorReceived)
Managing Ports and Transfers in .NET
215
8% // Define a delegate class to handle ErrorReceived events.
internal delegate void SerialErrorReceivedEventHandlerDelegate(object sender,
SerialErrorReceivedEventArgs e);
// Create an instance of the delegate.
private SerialErrorReceivedEventHandler SerialErrorReceivedEventHandler1;
SerialErrorReceivedEventHandler1 =
new SerialErrorReceivedEventHandler(ErrorReceived);
The application can assign the delegate as the handler routine that will execute
when an ErrorReceived event occurs on the port:
8$ AddHandler myComPort.ErrorReceived, SerialErrorReceivedEventHandler1
8% myComPort.ErrorReceived += SerialErrorReceivedEventHandler1;
Chapter 10
216
The ErrorReceived routine can check to find out which error has occurred and
can take action as needed. This example just displays a message on error:
8$ Private Sub ErrorReceived _
(ByVal sender As Object, ByVal e As SerialErrorReceivedEventArgs)
Dim SerialErrorReceived1 As SerialError
SerialErrorReceived1 = e.EventType
Select Case SerialErrorReceived1
Case SerialError.Frame
Console.WriteLine("Framing error.")
Case SerialError.Overrun
Console.WriteLine("Character buffer overrun.")
Case SerialError.RXOver
Console.WriteLine("Input buffer overflow.")
Case SerialError.RXParity
Console.WriteLine("Parity error.")
Case SerialError.TXFull
Console.WriteLine("Output buffer full.")
End Select
End Sub
Managing Ports and Transfers in .NET
217
8% private void ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
{
SerialError SerialErrorReceived1;
SerialErrorReceived1 = e.EventType;
switch (SerialErrorReceived1)
{
case SerialError.Frame:
Console.WriteLine("Framing error.");
break;
case SerialError.Overrun:
Console.WriteLine("Character buffer overrun.");
break;
case SerialError.RXOver:
Console.WriteLine("Input buffer overflow.");
break;
case SerialError.RXParity:
Console.WriteLine("Parity error.");
break;
case SerialError.TXFull:
Console.WriteLine("Output buffer full.");
break;
}
}
The Frame, Overrun, and RXParity errors originate in the UARTs Line Status
register. The RXOver and TXFull errors report the status of the drivers soft-
ware buffers.
With a well-structured application and robust hardware design, all of these
errors should be rare. Very frequent Framing and Parity errors typically indicate
port parameters that dont match on the sending and receiving computers.
Occasional Framing or Parity errors can indicate a noisy line that requires a dif-
ferent cable or another hardware fix. Increasing ReadBufferSize can eliminate
RXOver errors. To eliminate TXFull errors, ensure that the buffer has room
before writing data to the port. Increasing WriteBufferSize can also eliminate
TXFull errors and can make the application code more efficient by enabling
writing larger blocks of data to the port.
Chapter 10
218
Overrun errors indicate that UARTs buffer filled and overflowed because the
driver was unable to retrieve data from a full buffer before new data arrived. A
slower bit rate can eliminate this error.
8GTKH[KPI 4GEGKXGF &CVC
As Chapter 2 explained, applications can use hash values and other checksums
to verify that received data hasnt been corrupted while transmitting. The Sys-
tem.Security.Cryptography namespace provides classes that implement hash
values. To implement error checking with embedded systems that dont support
hash values, applications can use simpler checksum algorithms or parity bits.
5VTWEVWTKPI CP #RRNKECVKQP
An application that accesses COM ports might define a class for managing
COM-port communications, events for passing notifications and data, and
combo boxes for setting port parameters. This section shows how to use these
elements in applications.
&GHKPKPI C %QO2QTVU %NCUU
An application can define a class to hold the code that configures and accesses
COM ports:
8$ Public Class ComPorts
' Place class code here.
End Class
8% public class ComPorts
{
// Place class code here.
}
Benefits of using a separate class can include code that is more portable, more
readable, and easier to debug. A ComPorts class is likely to have both shared
and private members, The shared members can handle tasks that arent specific
to a single port, such as finding and maintaining a record of the systems ports.
Private members can handle tasks that are specific to a port, such as opening
Managing Ports and Transfers in .NET
219
and closing the port, sending and receiving data, and storing port parameters
and other information about the port.
Applications that need to open multiple ports at the same time can create mul-
tiple instances of the class. Each instance of the class can use the common
shared members while also maintaining private fields, properties, methods, and
events that apply to a specific port.
Chapter 9 showed how to use the GetPortNames method to obtain an array of
all of the names of the COM ports in a system, A ComPorts class can imple-
ment a nameArray variable as a shared member:
8$ Friend Shared nameArray() As String
nameArray = SerialPort.GetPortNames
8% internal static string[] nameArray;
nameArray = SerialPort.GetPortNames();
The class can enable accessing a specific COM port by defining a private vari-
able that holds a SerialPort object and a property with Friend scope (internal
scope in C#) that provides methods for setting and getting the object:
8$ Private m_SelectedPort As New SerialPort
Friend Property SelectedPort() As SerialPort
Get
Return m_SelectedPort
End Get
Set(ByVal value As SerialPort)
m_SelectedPort = value
End Set
End Property
Chapter 10
220
8% private SerialPort m_SelectedPort = new SerialPort();
internal SerialPort SelectedPort
{
get
{
return m_SelectedPort;
}
set
{
m_SelectedPort = value;
}
}
To access a specific COM port, an application creates an instance of the Com-
Ports class:
8$ Friend UserPort1 As ComPorts
UserPort1 = New ComPorts
8% internal ComPorts UserPort1;
UserPort1 = new ComPorts();
The application can select a port name from the array retrieved with GetPort-
Names and set SelectedPorts PortName property to the selected name. The
application can then set port parameters and open, read, write to, and close the
port. The application can call routines in the ComPorts class to set port param-
eters and open, read, write to, and close the port as needed.
8$ Dim comPortIndex as Integer
Dim dataToWrite as String = "hello"
Dim newReceivedData As String
' The index of the selected COM port in the name array.
comPortIndex = 1
UserPort1.SelectedPort.PortName = nameArray(comPortIndex)
UserPort1.SelectedPort.BaudRate = 115200
UserPort1.SelectedPort.Open()
newReceivedData = UserPort1.SelectedPort.ReadExisting
UserPort1.SelectedPort.Write(dataToWrite)
UserPort1.SelectedPort.Close()
Managing Ports and Transfers in .NET
221
8% int comPortIndex;
String dataToWrite = "hello";
String newReceivedData;
// The index of the selected COM port in the name array.
comPortIndex = 3;
UserPort1.SelectedPort.PortName = ComPorts.myPortNames[comPortIndex];
UserPort1.SelectedPort.BaudRate = 115200;
UserPort1.SelectedPort.Open();
newReceivedData = UserPort1.SelectedPort.ReadExisting();
UserPort1.SelectedPort.Write(dataToWrite);
UserPort1.SelectedPort.Close();
5GVVKPI 2CTCOGVGTU YKVJ %QODQ $QZGU
The user interface in COM-port applications often provides a way for users to
select a COM port and port parameters using combo boxes. The combo boxes
can reside on the applications main form, or to minimize clutter, the main form
can have a button the user clicks to open a dialog box that contains the combo
boxes (Figure 10-1). The _Load event for the dialog box that displays the
combo boxes can call a routine that initializes the combo boxes.
To initialize a combo box with the names of the systems ports, use the Data-
Source property to populate the combo box with the port names retrieved with
GetPortNames:
8$ cmbPort.DataSource = SerialPort.GetPortNames
8% cmbPort.DataSource = SerialPort.GetPortNames;
An application might need to change the contents of this combo box at times to
reflect the attachment or removal of virtual COM ports. For example, an appli-
cation might update the contents every time a user displays the form that con-
tains the combo box. To update the contents, set the DataSource property
again.
Chapter 10
222
To initialize a combo box with available bit rates, create an array of the bit rates
and set the DataSource property to the array:
8$ Dim bitRates(10) As Integer
'Bit rates to select from.
bitRates(0) = 300
bitRates(1) = 600
bitRates(2) = 1200
bitRates(3) = 2400
bitRates(4) = 9600
bitRates(5) = 14400
bitRates(6) = 19200
bitRates(7) = 38400
bitRates(8) = 57600
bitRates(9) = 115200
bitRates(10) = 128000
Figure 10-1: In this application, clicking the Port Settings button brings up a window
that enables selecting a port and setting port parameters.
Managing Ports and Transfers in .NET
223
cmbBitRate.DataSource = bitRates
8% private int[] bitRates = new int[ 11 ];
bitRates[ 0 ] = 300;
bitRates[ 1 ] = 600;
bitRates[ 2 ] = 1200;
bitRates[ 3 ] = 2400;
bitRates[ 4 ] = 9600;
bitRates[ 5 ] = 14400;
bitRates[ 6 ] = 19200;
bitRates[ 7 ] = 38400;
bitRates[ 8 ] = 57600;
bitRates[ 9 ] = 115200;
bitRates[ 10 ] = 128000;
cmbBitRate.DataSource = bitRates;
The combo box displays the bit rates as text.
To initialize a combo box with flow-control methods, add each item to the
combo box:
8$ cmbHandshaking.Items.Add(Handshake.None)
cmbHandshaking.Items.Add(Handshake.XOnXOff)
cmbHandshaking.Items.Add(Handshake.RequestToSend)
cmbHandshaking.Items.Add(Handshake.RequestToSendXOnXOff)
8% cmbHandshaking.Items.Add( Handshake.None );
cmbHandshaking.Items.Add( Handshake.XOnXOff );
cmbHandshaking.Items.Add( Handshake.RequestToSend );
cmbHandshaking.Items.Add( Handshake.RequestToSendXOnXOff );
The combo box displays the ToString property of each Handshake object.
In similar ways, you can add combo boxes for setting the number of data bits,
parity, and Stop bits as needed.
Chapter 10
224
A combo boxs SelectedItem property contains the selected item expressed as
text, which code can convert to other object types as needed:
8$ ' Get the selected bit rate as an Integer.
Dim myBitRate As Integer = _
CInt(cmbBitRate.SelectedItem)
' Get the selected flow-control method as a Handshake object.
Dim myHandshake As Handshake = _
DirectCast(cmbHandshaking.SelectedItem, Handshake)
Console.WriteLine(myBitRate)
Console.WriteLine(myHandshake.ToString)
8% // Get the selected bit rate as an Integer.
int myBitRate = (int)(cmbBitRate.SelectedItem);
// Get the selected flow-control method as a Handshake object.
Handshake myHandshake = (Handshake)(cmbHandshaking.SelectedItem);
Console.WriteLine(myBitRate);
Console.WriteLine(myHandshake.ToString());
Chapter 9 showed how code can select an item from a combo box by setting its
SelectedItem property to match a retrieved user setting. If a statement attempts
to set SelectedItem to a value that doesnt exist in the combo box, the combo
box ignores the statement and the combo boxs SelectedIndex property remains
unchanged.
&GHKPKPI #RRNKECVKQPURGEKHKE 'XGPVU
Many applications need to pass data to other modules or notify other modules
that an event has occurred. For example, a routine that executes on receiving
COM-port data might need to pass the received data to the applications form
for displaying.
The AccessFormMarshal routine in this chapter showed how a DataReceived
routine can pass data to a form in another thread. In the example, the routine
explicitly named the instance of the form and the AccessFormMarshal routine.
Managing Ports and Transfers in .NET
225
Events provide a way to pass data and event notifications between modules
while requiring only loose coupling between modules. The module that raises
the event doesnt have to know anything about a module that handles the event.
The module that raises the event just makes the event and its parameters avail-
able to any module that wants to receive data or take other action when the
event is raised. On being notified of an event, a module can run a routine that
accepts passed data and performs any other actions as needed.
For example, a ComPorts class can declare an event that passes text to a form
module:
8$ Friend Shared Event UserInterfaceData (ByVal textToAdd As String)
8% internal delegate void UserInterfaceDataEventHandler(string textToAdd);
internal static event UserInterfaceDataEventHandler UserInterfaceData;
A routine that reads data from the port can raise the event:
8$ Dim newReceivedData As String
' Get data from the COM port.
newReceivedData = selectedPort.ReadExisting
' Raise the event to make the data available to other modules.
RaiseEvent UserInterfaceData(newReceivedData)
8% string newReceivedData = null;
// Get data from the COM port.
newReceivedData = SelectedPort.ReadExisting();
// Raise the event to make the data available to other modules.
if (null != UserInterfaceData)
{
UserInterfaceData(newReceivedData);
}
To receive the data, the forms module specifies a routine to execute when the
event occurs. The routine must have the same type and number of parameters
Chapter 10
226
as the event. This example names the AccessFormMarshal routine presented
earlier in this chapter:
8$ AddHandler ComPorts.UserInterfaceData, AddressOf AccessFormMarshal
8% ComPorts.UserInterfaceData +=
new ComPorts.UserInterfaceDataEventHandler(AccessFormMarshal);
When a routine raises the UserInterfaceData event, the AccessFormMarshal
routine executes on the form. The AccessFormMarshal routine marshals the
textToAdd string to the form for displaying.
In a similar way, other routines can use events to pass data or notify other mod-
ules of events. In an application that uses a separate dialog box with combo
boxes for setting port parameters, clicking the forms OK button can raise an
event that passes the selected port parameters to the main form.
In the Form that contains the combo boxes, declare the event:
8$ Friend Shared Event UserInterfacePortSettings(ByVal selectedPort As String, _
ByVal selectedBitRate As Integer, ByVal selectedHandshake As Handshake)
8% internal delegate void UserInterfacePortSettingsEventHandler
(string selectedPort, int selectedBitRate, Handshake selectedHandshake);
internal static event UserInterfacePortSettingsEventHandler UserInterfacePortSettings;
In the Click event for the forms OK button, pass parameters from the combo
boxes. In the example below, the combo boxes are cmbPort, which contains
port names, cmbBitRate, which contains bit rates, and cmbHandshaking,
which contains handshaking options:
8$ RaiseEvent UserInterfacePortSettings(cmbPort.SelectedItem.ToString, _
CInt(cmbBitRate.SelectedItem), -
DirectCast(cmbHandshaking.SelectedItem, Handshake))
8% if ( null != UserInterfacePortSettings )
{
UserInterfacePortSettings( cmbPort.SelectedItem.ToString(),
System.Convert.ToInt32( cmbBitRate.SelectedItem ),
( ( Handshake )( cmbHandshaking.SelectedItem ) ) );
}
Managing Ports and Transfers in .NET
227
In the applications main form, specify a routine that will execute when the
event is raised. PortSettingsDialog is the form containing the combo boxes, and
SetPortParameters is the routine to execute:
8$ AddHandler PortSettingsDialog.UserInterfacePortSettings, AddressOf SetPortParameters
8% PortSettingsDialog.UserInterfacePortSettings += new
PortSettingsDialog.UserInterfacePortSettingsEventHandler(SetPortParameters);
The SetPortParameters routine does whatever is needed with the passed param-
eters. For example, the routine can set the passed parameters on a SerialPort
object:
8$ Private Sub SetPortParameters(ByVal userPort As String, ByVal userBitRate As Integer, _
ByVal userHandshake As Handshake)
UserPort1.SelectedPort.PortName = userPort
UserPort1.SelectedPort.BaudRate = userBitRate
UserPort1.SelectedPort.Handshake = userHandshake
End Sub
8% private void SetPortParameters
(string userPort, int userBitRate, Handshake userHandshake)
{
UserPort1.SelectedPort.PortName = userPort;
UserPort1.SelectedPort.BaudRate = userBitRate;
UserPort1.SelectedPort.Handshake = userHandshake;
}
This page intentionally left blank
229
11
2QTVU HQT
'ODGFFGF 5[UVGOU
Many serial-port communications are between embedded systems or between
embedded systems and PCs. This chapter shows how to program serial commu-
nications for an embedded systems microcontroller. The code examples are for
Microchip Technologys PIC18F4520, with each example provided for both
microEngineering Labs, Inc.s PICBASIC PRO compiler and Microchips
MPLAB C18 C compiler. The concepts and much of the code are portable to
other chip architectures and compilers.
# /KETQEQPVTQNNGT 5GTKCN 2QTV
Microchips PIC microcontrollers are inexpensive, widely available, and include
variants to suit a range of applications. Many of the chips have hardware sup-
port for asynchronous serial communications.
Other microcontrollers support serial ports as well. The details vary, but sup-
port typically consists of a hardware UART or USART with a series of registers
to configure the port, monitor events, and store received data and data to send.
Chapter 11
230
Compilers often provide library functions that simplify accessing the port hard-
ware.
AbQuV VbG PI%1BF4520
The PIC18F4520 has an 8-bit data bus, 32 kilobytes (KB) of flash memory for
program storage, and 1.5 KB of RAM and 256 bytes of EEPROM for data stor-
age. The power supply can range from +4.2V to +5.5V. The PIC18LF4520
variant can use a power supply as low as +2V.
For serial communications, the chip contains an enhanced USART (EUSART)
that supports asynchronous and synchronous serial communications. Other
on-chip functions include four timers, a 10-bit analog-to-digital converter with
up to 13 channels, an analog comparator, and two modules that can perform
capture, compare, and pulse-width-modulation (PWM) functions. The
40/44-pin versions also have an 8-bit parallel port. Most of the chips I/O pins
have assigned functions, such as serial-port input and output, but the pins can
have other uses if firmware isnt using the assigned functions.
Other chips in the PIC18F4520 series have different combinations of functions
and package options. The data sheet for the series has complete documentation
for the chips.
TbG FPbaPcGd UAPT
The PIC18F4520s EUSART supports using either an asynchronous or syn-
chronous interface but not both at the same time. However, the PIC18F4520
has an additional synchronous serial port module that supports synchronous
communications. The enhanced features of the EUSART include automatic
bit-rate detecting and a 16-bit register for finer control of bit rates. Other PIC
microcontrollers have similar USARTs, though not every variant has the
enhanced features of the EUSART.
The PIC18F4520 dedicates two port bits for the asynchronous port. Port C, bit
6 is the data output (TX), and Port C, bit 7 is the data input (RX). Hardware
flow control can use any spare port pins. The pins can interface directly to most
TTL and CMOS logic chips, including the TTL/CMOS interfaces on many
RS-232 and RS-485 converter chips.
Ports for Embedded Systems
231
4GIKUVGTU
Firmware accesses the PIC18F4520s asynchronous serial port via a series of
registers. When a register bit enables and disables a feature, a value of 1 enables
the feature and zero disables the feature.
%QPHKIWTKPI CPF #EEGUUKPI VJG 2QTV
Registers hold received data, data waiting to transmit, and status and control
information for the port. Figure 11-1 shows how data travels between the port
pins and the CPU.
'75#46 4GEGKXG &CVC 4GIKUVGT 4%4')
The EUSART receive data register (RCREG) holds up to two bytes of received
data in a first-in, first-out (FIFO) buffer. Firmware reads data from the buffer in
the same order as it was received.
'75#46 6TCPUOKV &CVC 4GIKUVGT 6:4')
The EUSART transmit data register (TXREG) holds one byte of data to trans-
mit.
4GEGKXG 5GTKCN 5JKHV 4GIKUVGT 454
The receive serial shift register (RSR) stores bits received on the RX pin. After
detecting a Stop bit, if RCREG has room, the RSR loads the received byte into
RCREG. If RCREG is full, the RSR waits and loads the byte when space
becomes available. Firmware cant access the RSR directly.
6TCPUOKV 5GTKCN 5JKHV 4GIKUVGT 654
The transmit serial shift register (TSR) loads data from TXREG and writes the
bits to the TX pin. The TSR loads data when TXREG contains a byte to send
and the TSR is empty. Firmware cant access the TSR directly.
6TCPUOKV 5VCVWU CPF %QPVTQN 4GIKUVGT 6:56#
The transmit status and control register (TXSTA) contains bits used in config-
uring and enabling the port. The register also holds the optional ninth bit for
transmitting. To enable receiving 8-bit asynchronous data, set TXSTA to 20h
or 24h.
Chapter 11
232
Figure 11-1: The CPU reads and writes to the serial port by accessing registers.
Ports for Embedded Systems
233
Bit 7 isnt used in asynchronous communications and should maintain the
default value.
Bit 6 (TX9) sets the number of bits to transmit to 8 (0) or 9 (1).
Bit 5 (TXEN) enables transmitting.
Bit 4 (SYNC) is set to zero for asynchronous communications.
Bit 3 (SENDB) is set to zero for normal communications and is set to 1 to
enable sending a Sync Break character, which is a special character defined by
the LIN bus protocol used in automotive applications. The character consists of
a Start bit, 12 zeros, and a Stop bit. When SENDB = 1, TXEN = 1, and a value
is loaded from TXREG into the TSR, the EUSART ignores the data in the
TSR and instead sends a Sync Break character.
Bit 2 (BRGH) selects whether to use the high speed (1) or low speed (0) value
for the bit rate specified in the SPBRG and SPBRGH registers as described later
in this chapter.
Bit 1 (TRMT) indicates whether the TSR is empty (1) or full (0). This bit
doesnt control an interrupt but firmware can read the bit.
Bit 0 (TX9D) holds the ninth bit of data to send when TX9 = 1. Firmware
must provide the value. If used, firmware should write a value to TX9D before
writing a byte to send to TXREG.
4GEGKXG 5VCVWU CPF %QPVTQN 4GIKUVGT 4%56#
The receive status and control register (RCSTA) enables monitoring and con-
trolling received data. The register also contains the SPEN bit, which enables
the serial port by configuring the dedicated port pins for serial data. The RX9D
bit holds an optional received ninth data or parity bit. To enable receiving 8-bit
asynchronous data, set the register to 90h.
To ensure reading valid values, firmware should read any bits of interest in
RCSTA before reading RCREG to obtain a received byte. Reading RCREG
causes bits RX9D, OERR, and FERR to update for the next received byte.
Bit 7 (SPEN), enables the serial port by configuring the RX and TX pins as the
input and output for USART data.
Bit 6 (RX9) enables receiving 9-bit data.
Bit 5 (SREN) has no function in asynchronous communications and should
maintain the default value.
Bit 4 (CREN) enables the receiver.
Chapter 11
234
Bit 3 (ADDEN) enables the address-detect feature used with 9-bit data.
Bit 2 (FERR) is set to 1 on detecting a framing error, which occurs on detecting
a logic-low Stop bit at the RX pin.
Bit 1 (OERR) is set to 1 on detecting an overrun error. The error occurs when
RCREG holds two unread bytes and the RSR detects the Stop bit of a new
received word. The RSR has nowhere to load the new byte, so the byte and any
data that follows while OERR is set is lost. Firmware is responsible for reading
bytes from RCREG frequently enough to prevent overrun errors. Hardware
flow control can help by de-asserting the flow-control output when RCREG is
full. To clear OERR, set CREN = 0. Then set CREN = 1 to re-enable receiving
data.
Bit 0 (RX9D) holds the ninth bit of received data when RX9 = 1. If the bit is a
parity bit, firmware is responsible for reading the bit and detecting parity errors.
5GVVKPI VJG $KV 4CVG
The serial ports bit rate depends on a variety of register settings and the fre-
quency of the CPUs clock source, or FOSC. The chips support multiple oper-
ating modes that can use different clock sources to conserve power when
possible.
$CWF 4CVG %QPVTQN 4GIKUVGT $#7&%10
The Baud Rate Control Register (BAUDCON) contains bits that relate to
enhanced features of the chips bit-rate generator for serial communications.
Bit 7 (ABDOVF) = 1 when a rollover has occurred while in auto-baud detect
mode. Firmware must clear the bit.
Bit 6 (RCIDL) = 1 when the receiver is idle (no receive operation is in
progress).
Bit 5 (RXDTP) sets the polarity for received data as inverted (1) or
non-inverted (0).
Bit 4 (TXCKP) sets the polarity for the idle state as low (1) or high (0).
Bit 3 (BRG16) determines whether to use an eight (0) or sixteen (1) bit value to
set the bit rate. In general, 16 bits enables greater accuracy in selecting rates.
Bit 2 is unimplemented and reads zero.
Bit 1 (WUE) is set to 1 to cause the EUSART to trigger an interrupt on a fall-
ing edge at RX. The interrupt can wake the chip from Sleep mode.
Ports for Embedded Systems
235
Bit 0 (ADBDEN) is set to 1 to cause the chip to detect the bit rate on the next
received byte. The received byte must equal 55h for accurate detecting of the
rate.
Csc|||aIcr CcnIrc| Peg|sIer (CSCCCN)
In the oscillator control register (OSCCON), the SCS1 and SCS0 bits select
the CPUs clock source. In normal, full-power operation, the clock source is the
primary clock specified in CONFIG1H. For reduced power consumption, the
clock source can be the Timer1 oscillator or the internal oscillator block. When
using the internal oscillator block, the IRCF2..IRCF0 bits select the frequency.
When using the Timer1 oscillator, the frequency is determined by the clock
source connected to the T1OSI and T1OSO pins.
After changing the clock source, firmware that wants to continue using the
serial port will likely need to change the values in the SPBRG and SPBRGH
registers as described below.
Csc|||aIcr Tun|ng Peg|sIer (CSCTUNF)
The oscillator tuning register (OSCTUNE) provides additional control of the
internal oscillator including selecting a source for the internal 31-kHz oscillator
(INTSRC), enabling a 4x frequency multiplier (PLLEN), and fine-tuning the
oscillators frequency (TUN4..TUN0).
CcnI|guraI|cn Peg|sIer 1 H|gb (CCNFIG1H)
When OSCCON has configured the CPU to use the primary clock, Configu-
ration register 1 high (CONFIG1H) selects the clocks source. FOSC3..FOSC0
set the source as a timing input on the OSC1 and OSC2 pins (in some cases
only OSC1 is used) or as the chips internal oscillator block.
Depending on the values of the FOSC bits, the primary clocks frequency can
be the same as the frequency at OSC1 and OSC2 or four times that value. Set-
ting FOSC3..FOSC0 to 0001
b
configures the chip to use an external oscillator
for FOSC. A 10-MHz oscillator results in FOSC = 10 MHz. Setting the bits to
0110
b
enables using slower external clocks by setting FOSC equal to the exter-
nal oscillators frequency multiplied by four. A 10-MHz oscillator results in
FOSC = 40 MHz.
The functions of the bits in the configuration registers vary with the chip, so
always check the data sheet for the chip youre using.
Chapter 11
236
$CWF 4CVG )GPGTCVQT 4GIKUVGTU 52$4) CPF 52$4)*
The baud rate generator registers (SPBRG and optionally SPBRGH) contain a
value used in setting the bit rate. If the BAUDCON registers BRG16 = 0, the
value is 8 bits and SPBRG contains the entire value. If BRG16 = 1, the value is
16 bits, SPBRG contains the low byte, and SPBRGH contains the high byte.
5VGRU KP 5GNGEVKPI C $KV 4CVG
To set the register values for a desired bit rate, do the following:
1. Obtain the value of FOSC. Begin by determining the clock source from the
OSCCON register (SCS1..SCS0). If using the primary clock, determine FOSC
from the frequency of the clock source and the value of FOSC3..FOSC0 in the
CONFIG1H register. If using the internal oscillator block, determine FOSC
from the settings in the OSCCON and OSCTUNE registers. If using the
Timer1 oscillator, FOSC is the frequency of the timing source connected to
T1OSI and T1OSO.
2. Determine the values to store in the registers. Two ways to do so are by con-
sulting the tables provided in the data sheet and by calculating the values.
For many applications, you can find a match in the tables in the chips data
sheet. For common FOSC values, the tables show four options depending on
the values of BRGH in the TXSTA register and BRG16 in the BAUDCON
register. Select the value with the smallest error. For example, if FOSC = 20
MHz and you want a bit rate of 115,200 bps, the smallest error (0.94%) is with
BRGH = 1, BRG16 = 1, SPBRG = 42 and SPBGRH = 0.
If your bit rate or FOSC value isnt in the tables, youll need to calculate the best
value. Computer assistance is useful. See www.Lvr.com for a link to an applica-
tion that selects values for a desired bit rate and oscillator frequency
To find a value to store in SPBRG (and SPBRGH if used) for a desired bit rate
and FOSC, BRGH, and BRG16 settings, use this formula:
spbrg = Math.Round((fosc / (multiplier * bitRate)) - 1)
spbrg is the 8-bit value in SPBRG or the 16-bit value in SPBRGH and SPBRG.
If BRG16 = 0, SPBRG must be 255 or less. If the calculated spbrg value is
slightly higher than 255, setting SPBRG to 255 might result in a usable bit rate.
fosc is the FOSC value in Hz.
Ports for Embedded Systems
237
The value of the multiplier variable depends on the values in BRG16 and
BRGH:
If multiplier = 16 and spbrg < 256, firmware can achieve the same bit rate by
setting BRGH = 1 and BRG16 = 0 or by setting BRGH = 0 and BRG16 =1. If
multiplier = 16 and spbrg > 255, firmware must set BRGH = 0 and BRG16 = 1.
bitRate is the desired bit rate.
To find the setting that results in the closest bit rate to the desired value, per-
form the calculation with each of the three multiplier values. This formula cal-
culates the actual bit rate that results from specific values:
actualBitRate = Round(fosc / (multiplier * (spbrg + 1)))
The fosc and multiplier variables are set as described above. Depending on the
value of BRG16, spbrg is the 8-bit value in SPBRG or the 16-bit value in
SPBRGH and SPBRG. If BRG16 = 1 and spbrg is greater than 255, you need
to divide spbrg into its lower and higher bytes:
spbrgl = spbrg Mod 256
spbrgh = Int(spbrg / 256)
A carefully selected FOSC frequency can result in a calculated bit rate with zero
error. However, the hardware oscillator can still introduce error if the oscillator
doesnt operate at exactly its rated frequency.
+PVGTTWRVU
Firmware can use hardware interrupts to cause program code to branch to an
interrupt service routine (ISR) when the serial port has received data or when
the ports transmit buffer is empty and ready to store new data to send. Firm-
ware can also set priority levels for specific interrupts to service critical inter-
rupts as quickly as possible. For serial communications at fast bit rates, using a
high-priority interrupt can be useful in preventing overflows of the receive
buffer.
$4)* $4) /WNVKRNKGT
0 0 64
0 1 16
1 0 16
1 1 4
Chapter 11
238
For register bits that enable and disable specific interrupts or groups of inter-
rupts, a value of 1 enables the interrupt and a value of 0 disables the interrupt.
Per|bera| InIerruI PequesI Peg|sIer 1 (PIP1)
The peripheral interrupt request register 1 (PIR1) contains bits that indicate the
status of individual peripheral interrupt sources.
Bit 5 (RCIF) indicates whether RCREG contains at least one byte (1) or is
empty (0).
Bit 4 (TXIF) indicates whether TXREG is full (0) or empty (1). The bit is set
to 1 when the TXSTA registers TXEN bit is set to 1 and thereafter whenever
the TSR loads a byte to transmit from TXREG. The bit is set to zero when
firmware writes a byte to TXREG.
Note that RCIF = 1 when its buffer contains data and TXIF = 1 when its buffer
is empty. These are the conditions when firmware is likely to need to take
action. If the interrupts arent enabled, firmware can read the bits to learn the
states of the buffers.
PeseI CcnIrc| Peg|sIer (PCCN)
The Reset Control Register (RCON) has one bit that can affect serial inter-
rupts.
Bit 7 (IPEN) enables priority levels on interrupts.
Per|bera| InIerruI Pr|cr|Iy Peg|sIer 1 (IPP1)
The Peripheral Interrupt Priority Register 1 (IPR1) has two bits that set the
interrupt priority level for serial-port communications. These levels take effect
if the RCON registers IPEN bit = 1. A value of 1 sets high priority, and zero
sets low priority. A high-priority interrupt will interrupt a low-priority interrupt
being serviced.
Bit 5 (RCIP) sets the receive interrupt priority.
Bit 4 (TXIP) sets the transmit interrupt priority.
InIerruI CcnIrc| Peg|sIer (INTCCN)
The interrupt control register (INTCON) has two bits that affect serial-port
interrupts. The functions of the bits vary depending on the value of the IPEN
bit in the RCON register.
Ports for Embedded Systems
239
When IPEN = 0:
Bit 7 (GIE) enables or disables all unmasked interrupts.
Bit 6 (PEIE) enables or disables all unmasked peripheral interrupts.
When IPEN = 1:
Bit 7 (GIEH) enables or disables all high-priority interrupts.
Bit 6 (GIEL) enables or disables all low-priority peripheral interrupts.
Per|bera| InIerruI Fnab|e Peg|sIer 1 (PIF1)
The peripheral interrupt enable register 1 (PIE1) enables interrupts for a variety
of peripheral sources. Two bits relate to serial ports.
Bit 5 (RCIE) enables the serial-ports receive interrupt. The interrupt occurs
when the PIR1 registers RCIF bit = 1.
Bit 4 (TXIE) enables the serial ports transmit interrupt. The interrupt occurs
when the PIR1 registers TXIF bit = 1.
$CUKE 1RGTCVKQPU
Using a serial port involves the operations of enabling the port, configuring
interrupts if used, and transmitting and receiving data.
Fnab||ng Ibe PcrI
To enable the asynchronous serial port for transmitting and receiving 8-bit
data, firmware should perform the following tasks:
1. Select values to configure the chip for the desired bit rate. Set the TXSTA
registers BRGH bit and the BAUDCON registers BRG16 bit to the desired
values. To set the bit rate, write a value to SPBRG and to SPBRGH if used.
2. In the TXSTA register, set SYNC = 0 to select asynchronous mode.
3. In the RCSTA register, set SPEN = 1 to enable the serial port.
Chapter 11
240
7UKPI +PVGTTWRVU
To use the transmit and received interrupts without using interrupt priority:
1. In the PIE1 register, set TXIE = 1 and RCIE = 1.
2. In the INTCON register, set GIE = 1 and PIE = 1.
To use the transmit and received interrupts with interrupt priority:
1. In the RCON register, set IPEN = 1.
2. In the PIE1 register, set TXIE = 1 and RCIE = 1.
3. In the IPR1 register, set the interrupt priority in RCIP and TXIP as desired.
4. In the INTCON register, set GIEH and GIEH to enable interrupts as appro-
priate for the IPR1 settings.
6TCPUOKVVKPI
To transmit a byte, firmware performs these tasks:
1. To enable transmitting, in the TXSTA register, set TXEN = 1.
2. If using hardware flow control, check the state of the firmware-defined
flow-control input bit and wait as needed for permission to transmit.
3. Write a byte to transmit to TXREG. The byte transfers to the TSR. When
TXREG is empty, in the PIR1 register, TXIF = 1. On the TX pin, the TSR
transmits a Start bit, the byte of data, and a Stop bit.
Firmware can write another byte to TXEN as soon as TXIF = 1. If using inter-
rupts, firmware will jump to the ISR when TXIF = 1.
4GEGKXKPI
To receive a byte, firmware performs these tasks:
1. To enable receiving, in the RCSTA register, set CREN = 1.
2. If using hardware flow control, set the firmware-defined flow-control output
bit to enable receiving.
3. In the PIR1 register, wait for RCIF = 1, indicating the receive buffer contains
as least one byte. If using interrupts, firmware will jump to the ISR when RCIF
= 1.
4. In the RCSTA register, check the framing error (FERR) and overrun
(OERR) bits.
5. Read the received byte in RCREG. Reading the register clears RCIF. If FERR
= 1, firmware will likely want to discard the received byte.
Ports for Embedded Systems
241
6. If the RCSTA register indicated an error, set CREN = 0 to clear the error. To
enable receiving another byte, set CREN = 1.
#EEGUUKPI C 2QTV
Both the PICBASIC PRO and MPLAB C18 compiler provide support for con-
figuring and reading and writing to serial ports. The PICBASIC PRO compiler
defines statements for accessing up to two hardware serial ports and two firm-
ware-only serial ports. The C18 compiler provides a series of library functions
for accessing serial ports. For chips that have more than one USART, the C18
compiler enables firmware to specify which USART to access. The compiler
also provides functions that implement one or more firmware UARTs using any
spare port pins. The examples that follow are for hardware UARTs.
%QPHKIWTKPI VJG 2QTV
To prepare to use the serial port for asynchronous communications, firmware
must set the EUSART to asynchronous mode, set the bit rate and number of
data bits, enable the port and the receiver, and enable the transmit and receive
interrupts if desired.
2$2
DEFINE statements can set many port parameters, including the values in the
TXSTA and RCSTA registers:
DEFINE HSER_RCSTA 90h
DEFINE HSER_TXSTA 20h
The HSER_BAUD definition sets the value in the SPBRG register for the spec-
ified bit rate using the selected setting of the TXSTA registers BRGH bit:
DEFINE HSER_BAUD 2400
An alternate way to set the bit rate is to write a value directly to SPBRG:
DEFINE HSER_SPBRG 19h
If FOSC doesnt equal the default value of 4 MHz, include a DEFINE OSC
statement to specify the FOSC frequency in MHz:
DEFINE OSC 20
DEFINE HSER_BAUD 2400
Chapter 11
242
The example below assumes a 4-MHz oscillator and sets the bit rate to 2400
using a 16-bit value. In the TXSTA register, bit 2 (BRGH) is set to 1 to select
using the high-speed value. In the BAUDCON register, bit 3 (BRG16) is set to
1 to enable using a 16-bit value in SPBRG and SPBRGH. The value stored in
the registers is 19Fh.
DEFINE HSER_TXSTA 24h
DEFINE HSER_SPBRGH 1
DEFINE HSER_SPBRG 9fh
BAUDCON = 8
%
The OpenUSART function sets the port parameters. The statement below dis-
ables the transmit and receive interrupts, sets the EUSART to asynchronous
mode, configures the port for 8 data bits, enables the receiver, and sets the bit
rate to 2400 bps assuming FOSC = 4 MHz. The final parameter is the value for
the SPBRG register.
BAUDCONbits.BRG16 = 0;
OpenUSART (USART_TX_INT_OFF &
USART_RX_INT_OFF &
USART_ASYNCH_MODE &
USART_EIGHT_BIT &
USART_CONT_RX &
USART_BRGH_LOW,
0x19);
This example is identical except FOSC = 20 MHz, which requires a different
value in SPBRG:
OpenUSART (USART_TX_INT_OFF &
USART_RX_INT_OFF &
USART_ASYNCH_MODE &
USART_EIGHT_BIT &
USART_CONT_RX &
USART_BRGH_LOW,
0x81);
Ports for Embedded Systems
243
Using a 16-bit value to set the baud rate requires setting BRG16 = 1 and storing
a value in SPBRGH. This example sets a bit rate of 2400 bps assuming FOSC =
4 MHz:
BAUDCONbits.BRG16 = 1;
SPBRGH = 1;
OpenUSART (USART_TX_INT_OFF &
USART_RX_INT_OFF &
USART_ASYNCH_MODE &
USART_EIGHT_BIT &
USART_CONT_RX &
USART_BRGH_HIGH,
0x9f);
5GPFKPI &CVC
Firmware can write individual bytes or arrays of bytes to a port. Before writing
to a port, firmware should wait for TXIF = 0, indicating that the port isnt busy
sending a byte.
2$2
The hserout statement waits for TXIF = 0 and writes data to the port.
Each of the hserout statements below write the same value (41h, the code for
the character A) to the port:
hserout ["A"]
hserout [$41]
test var byte
test = $41
hserout[test]
An hserout statement can also write multiple bytes to a port. This statement
writes a string:
hserout [hello, world"]
This statement writes the values stored in a byte array:
test var byte[2]
test[0] = "O"
test[1] = "K"
hserout[STR test]
Chapter 11
244
%
The putcUSART function writes a byte to the port. The BusyUSART function
returns 1 when the port is busy transmitting. To ensure that the port is avail-
able, place a while(BusyUSART()) statement before attempting to send data.
The WriteUSART function is identical to putcUSART.
Both of the putcUSART statements below write the same value (41h, the code
for the character A) to the port:
while(BusyUSART());
putcUSART('A')
while(BusyUSART());
putcUSART(0x41)
The putsUSART and putrsUSART functions can write strings to a port. This
example uses putrsUSART to write a string, including a null terminator (0),
from program memory to the port:
putrsUSART((rom char*) "Hello World!" );
This example uses putsUSART to write a character array from data memory up
to and including the null terminator:
unsigned char my_string[3] = {'\0'};
my_string[0] = '0';
my_string[1] = 'K';
my_string[2] = '\0';
putsUSART((char*)my_string);
4GEGKXKPI &CVC
Firmware that reads bytes at the serial port can check for errors, store received
bytes in an array, and perform other tasks while waiting for data to arrive. A
protocol can define a terminating character to indicate the end of a command
or string.
4GCFKPI C $[VG
When receiving data, firmware can read the RCIF bit in the PIR1 register to
find out if a byte is available for reading.
Ports for Embedded Systems
245
2$2
An hserin statement can read a byte at the serial port:
serial_in var byte
if (PIR1.5 = 1) then
' A byte is available. Read it.
hserin [serial_in]
endif
Instead of checking RCIF before calling hserin, a statement can call hserin with
a timeout that jumps to a label if a byte doesnt arrive. Reading RCIF is more
efficient because the code doesnt waste time waiting, but a timeout can be use-
ful if the application is expecting data and has no other urgent tasks.
serial_in var byte
timeout var word
timeout = 1000
' Wait for a byte on the serial port.
hserin timeout, give_up, [serial_in]
' A byte was received. Do something with it.
' Jump here if hserin times out.
give_up:
%
The DataRdyUSART function returns 1 when RCIF =1. The getcUSART
function reads a byte from the port. The ReadUSART function is identical to
getcUSART.
unsigned char serial_in;
if DataRdyUSART()
{
// A byte is available.
serial_in = getcUSART();
}
Chapter 11
246
&GVGEVKPI 'TTQTU
Firmware can check for framing or overrun errors on the port. The following
examples show how to check for these errors. To avoid repetition, the additional
code examples in this book dont check for overrun or framing errors, but most
working code should include the code below or an equivalent when reading
data from a port.
2$2
This statement causes the code to automatically clear any overrun errors:
DEFINE HSER_CLROERR
Code can instead detect and clear overrun errors as they occur:
if (RCSTA.1 = 1) then
' Overrun error. Clear and set CREN to clear the error and re-enable the receiver.
RCSTA.4 = 0
RCSTA.4 = 1
' (Perform any other required actions.)
endif
To check for framing errors, firmware must read the RCSTA register after the
byte has arrived at the port and before firmware reads the byte from RCREG:
if (PIR1.5 = 1) then
' A byte has arrived.
if (RCSTA.2 = 1) then
' Framing error.
' Read RCREG to clear the error but don't use the data.
hserin [serial_in]
else
' No framing error occurred. The data is valid.
hserin [serial_in]
' Do something with the received byte.
endif
endif
Ports for Embedded Systems
247
If the firmware uses hserin to read multiple bytes or to wait for a byte, there is
no way to detect overrun and framing errors because firmware has no way to
read RCSTA after executing hserin but before reading RCREG. For some appli-
cations, detecting framing and overrun errors isnt essential.
%
After determining that a byte is available and before reading the byte, code can
check for overrun and framing errors.
unsigned char serial_in;
if DataRdyUSART()
{
// A character is available.
if (RCSTAbits.OERR == 1)
{
// Overrun error.
// Clear the error and re-enable the receiver.
RCSTAbits.CREN = 0;
RCSTAbits.CREN = 1;
}
if (RCSTAbits.FERR == 1)
{
// Framing error.
// Read the byte to clear the error but don't use the byte.
serial_in = getcUSART();
}
else
{
// A byte was received without error.
serial_in = getcUSART();
// Do something with the byte.
}
}
7UKPI C 6CUM .QQR
A task loop is an endless loop that can perform serial-communications tasks and
any other tasks a device is responsible for. Firmware performs each task in
Chapter 11
248
sequence, starting over on reaching the end of the task list. For example, one
task can check for received serial-port data while another task reads and stores
sensor data from another port.
2$2 serial_in var byte
timeout var word
loop:
'Task loop.
gosub receive_serial_data
' Call routines fo perform other tasks.
goto loop
receive_serial_data:
if (PIR1.5 = 1) then
' A byte is available. Read it.
hserin [serial_in]
' Do something with the received byte.
endif
return
Ports for Embedded Systems
249
% unsigned char serial_in;
while(1)
{
// Task loop.
receive_serial_data();
// Call routines to perform other tasks.
}
void receive_serial_data(void)
{
unsigned char serial_in;
if DataRdyUSART()
{
// A byte is available.
serial_in = getcUSART();
// Do something with the received byte.
}
}
4GCFKPI /WNVKRNG $[VGU
When expecting multiple bytes, firmware can wait for the bytes to arrive or read
and store individual bytes until all have arrived.
2$2
An hserin statement can wait to receive a specified number of bytes to store in a
byte array. This example waits for 7 bytes and stores each received byte in
sequence in received_text:
received_text var byte[7]
hserin [STR received_text\7]
A receiving computer cant guarantee that a remote computer will send the
desired number of bytes. Using a timeout will keep the code from hanging for-
ever while waiting for bytes to arrive.
received_text var byte[7]
hserin 1000, continue, [STR received_text\7]
continue:
Chapter 11
250
On a timeout, the STR array contains any bytes that arrived. If you need to
know how many bytes were received, initialize the STR array with a value that
the remote computer doesnt transmit. For example, if receiving text data, ini-
tialize the array with null characters (zeros) before calling herserin:
for index = 0 to 9
received_text[index] = 0
next index
Another way to read multiple bytes is to read one byte at a time and store the
bytes in an array. A task loop can call the routine below to read a specified num-
ber of bytes into an array:
index var byte
receive_serial_data:
received_data var byte[8]
bytes_to_read var byte
bytes_to_read = 8
if (PIR1.5 = 1) then
' A byte is available to read.
hserin [received_data[index]]
index = index + 1
if index >= bytes_to_read then
' Do something with the received bytes.
' Reset the index for the set of received bytes.
index = 0
endif
endif
return
Ports for Embedded Systems
251
%
The getsUSART function can read multiple characters but requires specifying
how many characters to read and blocks until all of the characters arrive:
char received_data[8];
getsUSART(received_data, 8);
To read a string without blocking, firmware can call the getcUSART function
repeatedly to read individual bytes into a char array. A task loop can call the
routine below to read a specified number of bytes into an array:
byte index;
void receive_serial_data(void)
{
byte bytes_to_read;
char received_data[8];
bytes_to_read = 8;
if DataRdyUSART()
{
// A byte is available. Read it into the array.
received_data[index] = getcUSART();
// Increment the position in the array.
index++;
if (index == bytes_to_read)
{
// Do something with the received bytes.
// Reset the index for the set of received bytes.
index = 0;
}
}
&GVGEVKPI C 6GTOKPCVKPI %JCTCEVGT
A defined terminating character can indicate the end of a command, string, or
other block of data. The examples below check for a CR character (0Dh) in
received data.
Chapter 11
252
2$2
An hserin statement can return on receiving a specified value or receiving the
specified number of bytes, whichever comes first. This statement waits to
receive 10 bytes or a CR code:
hserin [STR received_text\10\$0d]
Another option is to store bytes as they arrive and check each byte for the CR
code. Reading individual bytes in a task loop eliminates blocking while waiting
for the CR.
index var byte
received_data var byte[80]
receive_serial_data:
if (PIR1.5 = 1) then
' A byte is available to read.
hserin [received_data[index]]
if received_data[index] = 13 then
' It's a CR code. Do something with the received data.
' Reset the index.
index = 0
else
' Increment the index, resetting to 0 if the buffer is full.
if index < 79 then
index = index + 1
else
index = 0
endif
endif
endif
return
Ports for Embedded Systems
253
%
A device that is receiving commands that end with a CR can check for the char-
acter on receiving data and take action when received:
byte index;
char received_data[80];
void receive_serial_data(void)
{
if DataRdyUSART()
{
// A byte is available.
received_data[index] = getcUSART();
if (received_data[index] == 0x0d)
{
// It's a CR code. Do something with the received data.
// Reset the index.
index = 0;
}
else
{
// Increment the index, resetting to 0 if the buffer is full.
if (index < 79 )
index++;
else
index = 0;
}
}
}
7UKPI +PVGTTWRVU
When data arrives at unpredictable times, firmware can use the receive inter-
rupt to read bytes as they arrive.
With the receive interrupt enabled, the CPU jumps to the interrupt vector (a
defined location in program memory) every time a byte arrives at the port. An
ISR can read the received byte and perform any other needed actions.
In a similar way, with the transmit interrupt enabled, the CPU jumps to the
interrupt vector every time TXREG is newly empty. The ISR can then write
Chapter 11
254
another byte to TXREG for transmitting and perform any other necessary
actions.
The PIC18F4520 has two interrupt vectors. Multiple interrupts with the same
priority share an interrupt vector. If not using interrupt priority, all interrupts
use vector 08h. If using interrupt priority, the high-priority interrupts use vec-
tor 08h and the low-priority interrupt uses vector 18h. If more than one inter-
rupt is enabled and uses the same vector, the code in the ISR must read the
interrupt flags to determine the source of the interrupt and take appropriate
action.
2$2
To use the receive interrupt, set the appropriate values in the INTCON and
PIE1 registers:
' Enable unmasked peripheral interrupts
INTCON = %11000000
' Enable the serial receive interrupt
PIE1 = %00100000
The ON INTERRUPT statement specifies what routine to run when an inter-
rupt occurs:
ON INTERRUPT GOTO interrupt_service_routine
The ISR begins with a label and ends with a RESUME statement. A DISABLE
statement before the label holds all interrupt processing except for the current
interrupt until the next ENABLE statement.
DISABLE
interrupt_service_routine:
if (PIR1bits.RCIF == 1)
' A serial receive interrupt has occurred.
' Place code to read and respond to received data here.
else
' Check other flags for enabled interrupts and take action as needed.
endif
RESUME
ENABLE
Ports for Embedded Systems
255
%
The OpenUSART function enables the receive interrupt by setting the second
parameter to USART_RX_INT_ON:
OpenUSART (USART_TX_INT_OFF &
USART_RX_INT_ON &
USART_ASYNCH_MODE &
USART_EIGHT_BIT &
USART_CONT_RX &
USART_BRGH_LOW,
77);
Another way to enable the serial-port receive interrupt is to set the bit directly:
PIE1bits.RCIE = 1;
If not using interrupt priority (IPEN = 0), set these bits:
// Disable using interrupt priority.
RCONbits.IPEN = 0;
// Enable all unmasked interrupts.
INTCONbits.GIE = 1;
// Enable all unmasked peripheral interrupts.
INTCONbits.PEIE = 1;
If using interrupt priority (IPEN = 1) and the serial interrupt is high priority,
set these bits:
// Enable using interrupt priority.
RCONbits.IPEN = 1;
// Configure the receive interrupt as high priority.
IPIR1bits.RCIP = 1;
// Enable all high-priority interrupts.
INTCONbits.GIEH = 1;
Chapter 11
256
This code sets up an ISR to respond to received serial-port data:
// Interrupt vector for high-priority interrupts.
#pragma code high_vector = 0x8
void interrupt_at_high_vector (void)
{
// Define the function to execute on an interrupt.
_asm goto high_isr _endasm
}
// The ISR function.
#pragma interrupt high_isr
void high_isr(void)
{
if ((PIR1bits.RCIF == 1) && (PIE1bits.RCIE == 1)
{
// The serial port has received a byte.
// Place code to read and respond to received data here.
// Reset the ISR flag.
PIR1bits.RCIF = 0;
}
// Check the interrupt flags for any other enabled interrupts.
}
7UKPI (NQY %QPVTQN
Microcontrollers typically require firmware support for hardware flow control.
When the firmware has data to send, a hardware interrupt or firmware polling
can detect when the flow-control input is in the required state. When receiving
data, firmware can set the flow-control output as needed to prevent overruns.
To use hardware flow control on a hardware serial port, select any two otherwise
unused port bits and configure one bit as an input and the other as an output.
Ports for Embedded Systems
257
5GPFKPI &CVC
When sending data, firmware can read the flow-control input and wait if
needed before writing a byte to TXREG. In normal operation, the waits should
be short, but its always possible the remote computer will malfunction and
leave its flow-control output de-asserted for a long time.
For efficient operation, firmware should minimize the time devoted to waiting
for the flow-control input to change state and provide a way to bail out if the
wait goes on too long. Tools that can help include timers, task loops, and hard-
ware interrupts.
Firmware with data to send can start a timeout timer and end the wait if the
flow-control input isnt asserted after the timer times out. This approach can
work well if the waits are normally short or infrequent.
In a task loop, the device performs communications and other tasks as needed
in an endless loop. On each pass through the loop, a device with data to send
reads the flow-control input. If the input isnt asserted, firmware moves on to
the next task. If the input is asserted, the firmware writes the data to the port
and performs any other related actions before moving on to the next task. Task
loops can work well for many applications.
A hardware interrupt can detect a rising or falling edge on the flow-control
input. Firmware can use the ISR to send data when the flow-control pin has
indicated that the remote interface is ready to receive data. An interrupt can
give the quickest response to changes at a flow-control input.
The PIC18F4520 supports three hardware interrupts on Port B: bit 0 (INT0),
bit 1 (INT1), and bit 2 (INT2). Three registers contain bits that monitor or
control the interrupts.
Interrupt Control Register (INTCON)
Bit 1 (INT0IF) is set to 1 when INT0 interrupts. Firmware must clear the bit.
Bit 4 (INT0IE) enables INT0.
Bits 6 (GIE) and bit 7 (PEIE) enable the interrupts as described earlier in this
chapter.
Interrupt Control Register 2 (INTCON2)
Bit 0 (INTEDG0), bit 1 (INTEDG1), and bit 2 (INTEDG2) configure their
corresponding interrupts to generate interrupts on the rising (1) or falling (0)
edges on the pins.
Chapter 11
258
Interrupt Control Register 3 (INTCON3)
Bit 0 (INT1IF) and bit 1 (INT2IF) are set to 1 when their corresponding exter-
nal interrupts occur. Firmware must clear the bits.
Bit 3 (INT1IE) and bit 4 (INT2IE) enable their corresponding external inter-
rupts.
Bit 6 (INT1IP) and bit 7 (INT2IP) set the interrupt priority for their corre-
sponding interrupts. INT0 is always high priority.
4GEGKXKPI &CVC
As explained earlier in this chapter, the PIC18F4520 has a 2-byte receive buffer
(RCREG) and a shift register (RSR) that stores the bits of an incoming byte. To
prevent overrun errors, firmware should de-assert the flow-control output when
both of the following are true: RCREG contains a byte, and the Stop bit of the
next byte might arrive at the RX pin before firmware can read RCREG.
For example, assume that the bit rate is 9600 bps with 8 data bits and 1 Stop
bit, and RCREG contains 1 byte. Another byte can arrive at the RSR and be
stored in RCREG in about 1 millisecond. If the flow-control output remains
asserted at this point, the remote computer will assume its OK to send a
another byte and may do so. On receiving the Stop bit of the next byte, if both
previously received bytes remain in RCREG, the RSR is unable to copy the new
byte into RCREG, the microcontroller generates an overrun error, and the
recently arrived byte is lost. Note that a slower bit rate gives the receiving com-
puter more time to retrieve received bytes from RCREG.
Many applications never need to de-assert the flow-control output. If firmware
can read RCREG at intervals shorter than the time required to transmit three
words, the buffer will never overflow.
#RRNKECVKQP 'ZCORNG
The example that follows demonstrates how to use hardware flow control. On
receiving a byte, the firmware de-asserts its flow-control output to tell the
remote computer not to send more data while the firmware is preparing its
response to the received byte. If the received byte is a code for a lower-case text
character (az), the receiving computer sends back the character converted to
upper case. Otherwise, the receiving computer sends back the same value
received. In either case, firmware waits for its flow-control input to be asserted
before sending the response.
Ports for Embedded Systems
259
2$2 data_ready_to_send var bit
flow_control_input var PORTB.4
flow_control_output var PORTB.5
serial_in var byte
serial_out var byte
' Configure the flow-control pins as one input and one output.
TRISB.4 = 1
TRISB.5 = 0
' Assert the flow-control output to enable receiving data on the serial port.
flow_control_output = 0
This task loop calls two routines in an endless loop:
loop:
gosub receive_serial_data
gosub transmit_serial_data
GOTO loop
This routine checks for a new received byte on the serial port.
receive_serial_data:
if (PIR1.5 = 1) then
' A byte has arrived at the serial port. Read it.
hserin [serial_in]
' Tell the remote computer to stop sending data .
flow_control_output = 1
' Set serial_out to a character code to send on the serial port.
if ((serial_in > 96) and (serial_in < 123)) then
' The received character code was lower case (a-z).
' Send back the character in upper case.
serial_out = serial_in - 32
Chapter 11
260
else
' For other characters, send back the same character.
serial_out = serial_in
endif
' Set a variable to indicate that a byte is ready for transmitting.
data_ready_to_send = 1
endif
return
This routine checks the data_ready_to_send variable, which indicates whether
serial_out contains a byte to be sent on the serial port. If data_ready_to_send =
1 and fl ow_control _i nput = 0, the routi ne sends the byte and sets
flow_control_output = 1 to enable receiving another byte.
transmit_serial_data:
if (data_ready_to_send = 1) then
' A byte is ready to send on the serial port.
if (flow_control_input = 0) then
' The flow control input is asserted.
' Reset the variable that indicates a byte is ready to send
' and send the byte.
data_ready_to_send = 0
hserout [serial_out]
' Tell the remote computer its OK to send more data .
flow_control_output = 0
endif
endif
return
Ports for Embedded Systems
261
% #define flow_control_input PORTBbits.RB4
#define flow_control_output PORTBbits.RB5
// Configure the flow-control pins as one input and one output.
TRISBbits.TRISB4 = 1;
TRISBbits.TRISB5 = 0;
byte data_ready_to_send = 0;
// Assert the flow-control output to enable receiving data on the serial port.
flow_control_output = 0
This task loop calls two routines in an endless loop:
while(1)
{
receive_serial_data();
transmit_serial_data();
}
This function looks for a new received byte on the serial port. If a byte has
arrived, the routine reads it.
void receive_serial_data(void)
{
if (PIR1bits.RCIF == 1)
{
// A byte has been received. Read it.
serial_in = getcUSART();
// Tell the remote computer to stop sending data .
flow_control_output = 1;
// Set serial_out to a character code to send on the serial port.
if ((serial_in > 96) & (serial_in < 123))
{
// The received character code was lower case (a-z).
// Send back the character in upper case.
serial_out = serial_in - 32;
}
Chapter 11
262
else
{
// For other characters, send back the same character.
serial_out = serial_in;
}
// Set a variable to indicate that a byte is ready for transmitting.
data_ready_to_send = 1;
}
}
This function checks the data_ready_to_send variable, which indicates whether
serial_out contains a byte to be sent on the serial port. If data_ready_to_send =
1 and flow_control_input = 0, the function sends the byte and sets
flow_control_output = 1 to enable receiving another byte.
void transmit_serial_data()
{
if (data_ready_to_send == 1)
{
// A byte is ready to send.
// If the flow control input is asserted, send the byte.
// Otherwise wait until the routine is called again.
if (flow_control_input == 0)
{
data_ready_to_send = 0;
while(BusyUSART());
putcUSART(serial_out);
// Tell the remote computer its OK to send more data .
flow_control_output = 0;
}
}
}
#FFKPI 2QTVU
The options for adding serial ports to an embedded system include using a
microcontroller that has the needed number of ports, using firmware UARTs,
and interfacing to external UARTs.
Ports for Embedded Systems
263
/WNVKRNG 1PEJKR 7#46U
Hardware UARTs handle much of the burden of serial communications. If your
microcontroller has an available hardware UART, it makes sense to use it. For
applications that need multiple serial ports, some microcontrollers, including
the PIC18F8722 and other PIC microcontrollers, have multiple hardware
UARTs. The Rabbit 4000 microprocessor from Rabbit Semiconductor Inc.
(www.rabbit.com) has six on-chip asynchronous serial ports.
(KTOYCTG 7#46U
Another way to implement serial communications is via a firmware UART such
as the ports supported by the PICBASIC PRO and C18 compilers. These ports
can use any generic I/O pins for serial communications. Firmware ports can
have features not available on a hardware port. For example, PICBASIC PROs
firmware ports can use serin2 and serout2 statements with an optional FlowPin
parameter that specifies a pin to use for flow control. When using flow control,
a serin2 statement sets its FlowPin output to enable receiving data. A serout2
statement waits for its FlowPin input to be asserted before sending data. An
optional timeout value causes the code to jump to a label if the pin isnt asserted
within the specified time. In most cases, however, a hardware UARTs capabili-
ties are more useful than the added features of a firmware UART.
'ZVGTPCN 7#46U
Another way to add a serial port is via an interface to an external UART.
%QORQPGPVU
The 16550 UART used in PCs for many years has eight data pins plus various
address, status, and control pins. Many microcontrollers dont have enough
port pins to interface to this and similar chips. An alternative is a UART with a
synchronous serial interface.
#P 52+/KETQYKTG 7#46
A chip that requires as few as three lines for a bidirectional link is Maxims
MAX3100 SPI/Microwire UART. The chip converts between synchronous and
asynchronous serial data (Figure 11-2). The synchronous data is compatible
with SPI and Microwire, which require a clock line plus a data line for each
direction.
Chapter 11
264
A microcontroller can communicate with the MAX3100 by sending and receiv-
ing synchronous data. The microcontroller can toggle SCLK as needed without
worrying about maintaining a specific bit rate. The only restrictions on SCLKs
frequency are a minimum pulse width (high or low) of 100 ns and a minimum
clock period (high + low) of 238 ns.
The synchronous interface exchanges 16-bit words. Eight bits are data, and the
other bits can hold status and control information. The chip can also send and
receive configuration data.
A crystal or ceramic resonator provides the timing reference for the UARTs
bit-rate generator. A 1.8432MHz crystal supports bit rates from 300 to
115,200.
On receiving synchronous data to transmit, the MAX3100 writes a Start bit,
the data bits, a parity bit if used, and a Stop bit to TX. The data can have 7 or 8
bits plus an optional parity bit. The MAX3100 doesnt calculate a value for a
parity bit so the sending device must set the parity as desired. The Max3100
can generate an interrupt request on receiving a parity bit of 1.
Figure 11-2: The MAX3100 converts between SPI or Microwire serial data and
asynchronous format.
Ports for Embedded Systems
265
The chip has an 8-byte FIFO for each direction, so the microcontroller or other
CPU doesnt have to read each received byte from the UART before the next
byte arrives.
This page intentionally left blank
267
12
0GVYQTM 2TQITCOOKPI
When three or more devices share a communication path, serial-port program-
ming becomes more complicated. Each computer, or node, in the network,
needs to know when its OK to transmit and whether to respond to or ignore a
received message. This chapter shows how to ensure reliable and efficient com-
munications in a serial network and how to implement a basic network proto-
col.
/CPCIKPI 6TCHHKE
One of the first things to decide is how the network will manage its traffic. A
typical network has the following features:
Each node can send and receive data.
Only one node transmits at a time.
Each node recognizes and responds to messages intended for it and ignores
all others.
The transmitting node detects when a node didnt receive a message and
takes appropriate action.
Chapter 12
268
5VGRU KP 'ZEJCPIKPI C /GUUCIG
In even the most basic networks, all messages must arrive at their intended des-
tinations without errors, and each node must respond only to those messages
intended for it.
For example, assume that Node A wants to send a message to Node B, telling
Node B to set a port to a value and to send back a value read from another port.
In a typical serial network, all of the following must take place:
Node A does the following:
Enables its network driver.
Sends the address of the node to transmit to.
Sends the message.
Disables the network driver and waits for a response.
Node B does the following:
Reads incoming data.
Detects the nodes address.
Reads the message associated with the address.
Detects when the message has ended.
Takes the requested action.
Prepares a response.
Enables the network driver.
Sends the response.
Disables the network driver.
Node A then does the following:
Reads the response.
Takes any required action.
At the same time, all of the other nodes must do the following:
Read incoming data.
Detect the transmitted address and ignore the message associated with the
address.
2TQVQEQNU
The nodes in a network must agree on a protocol for managing communica-
tions. Three types of network protocol are primary/secondary, token passing,
and collision detecting.
Network Programming
269
2TKOCT[5GEQPFCT[
A primary/secondary protocol, also called the master/slave protocol, is often the
least complex network protocol a network can implement. One computer is
designated the primary node in charge of controlling all network traffic. The
other computers are secondary nodes that respond to communications from the
primary node. A network might have a PC as the primary node and embedded
systems as secondary nodes.
To give each node a chance to communicate, the primary node can poll, or send
a message to, each of the secondary nodes in sequence. Each poll can request a
response, which might contain an acknowledgment, requested data, an error
message, or other information. A secondary node transmits only when the pri-
mary node has requested a response. Any message from one secondary node to
another must pass through the primary node.
The main limitation of the protocol is the delays that occur as each node waits
to be polled. For a critical alarm system, waiting to be polled could be a prob-
lem, while a data-acquisition system might tolerate long delays.
6QMGP 2CUUKPI
A token-passing protocol allows any node to obtain control of the network.
The node in control is said to have the token.
The network protocol defines how a node knows if it has the token and how to
pass the token to another node. The token can be a defined bit or variable that
each node sets to indicate whether or not the node holds the token.
A node that wants to pass the token to another node gives up the token it has
(by clearing its token bit, for example) and sends a message to inform another
node that it now has the token. This node then takes whatever action it wants
and passes the token on. The nodes can pass the token in a defined sequence or
use another method to decide who gets the token next.
This protocol enables any node that has the token to talk directly to another
node. But a node that doesnt have the token cant interrupt with an emergency
message.
%QNNKUKQP &GVGEVKPI
A collision-detecting protocol allows any node to transmit whenever the trans-
mission path is free. If two or more nodes try to transmit at the same time, all of
the nodes (or with some protocols, all nodes but one), must detect the collision,
Chapter 12
270
stop transmitting, and try again after a delay. This protocol is useful when any
node needs to be able to transmit whenever it wants and when the overall net-
work traffic is light enough that delays due to collisions are infrequent. Ethernet
and I
2
C use collision-detecting protocols.
To use the protocol, the nodes must be capable of detecting collisions, and the
drivers must be able to survive multiple drivers enabled at the same time, if only
briefly.
One way to detect a collision is for each transmitting node to attempt to read
back what it sends. If the data read matches the data written, the node assumes
there was no collision and the transmission succeeded. If the data read doesnt
match the data written, the node assumes that another node is trying to trans-
mit and tries again later. Different nodes should use different delay times, either
by assigning each a different, fixed delay or by using random values. Otherwise,
the nodes will all retry at the same time and no one will ever get through. The
receiving nodes must also recognize and ignore failed attempts at transmitting.
Collision-detecting protocols typically arent practical for RS-485 networks.
With the UARTs used for most RS-485 interfaces, the nodes arent able to
examine each bit as it arrives, so bit-by-bit collision detecting isnt possible.
If two or more drivers are enabled, RS-485 chips have protection circuits that
limit the current and eventually disable the outputs, but the currents can be as
high as 250 mA before the output is disabled. These safety features are useful
for protecting the circuits during occasional malfunctions, but a protocol that
routinely causes these high currents wastes power and can stress the circuits.
In contrast, open-collector and open-drain logic used in I
2
C synchronous com-
munications can have multiple drivers enabled without drawing large currents,
and the software is often capable of bit-by-bit monitoring.
7UKPI 'ZKUVKPI 2TQVQEQNU
One way to get network programming up and running is to use an existing pro-
tocol. Various organizations and companies have developed protocols used in
serial communications.
A fieldbus is a digital communications link developed for monitoring and con-
trol systems. These are some examples of standard fieldbuses used in RS-485
networks:
BACnet (Building Automation Control network). For building automation
and other monitoring and control applications. From the American Society
Network Programming
271
of Heating, Refrigerating, and Air-Conditioning Engineers (ASHRAE)
(www.ashrae.com).
BITBUS. Introduced by Intel. General-purpose primary/secondary proto-
col. BITBUS European Users Group (www.bitbus.org) has documentation.
An expanded version is defined by the withdrawn IEEE Standard
1118-1990.
CTNet. Features de-centralized control and high performance (www.con-
troltechniques.com).
INTERBUS. Used in automotive applications. Promoted by the INTER-
BUS Club (www.interbusclub.com).
Modbus. Developed by Modicon and now an open standard. Modbus RTU
transmits binary data. Modbus ASCII transmits text data. Promoted by
Modbus-IDA (www.modbus.org)
PROFIBUS (Process Fieldbus). Used in manufacturing, process control, and
building automation. From Profibus International (www.profibus.org).
SAE J1708. For vehicle applications. From the Society of Automotive Engi-
neers (www.sae.org).
High Tech Horizon (www.hth.com) offers the free, generic S.N.A.P. protocol.
Some data-acquisition devices support vendor-specific ASCII command sets.
&GDWIIKPI 6KRU
Because of their complexity, networks can be challenging to debug. A gradual
and deliberate approach to putting together or debugging a network can save
time in the long run. These are some techniques that can be useful:
Start with two nodes. In a primary/secondary network, connect the primary
node to one secondary node and get that working before adding more nodes.
Keep the distance short. If possible, place the nodes in the same room,
side-by-side, until things are working.
Use a slow bit rate. A slow bit rate is especially useful if your debugging tools
are limited. At 300 bps, you can watch LEDs flicker on a breakout box when a
node transmits.
Use your programming environments debugging capabilities. Breakpoints,
watch variables, single-stepping, and other debugging tools can help isolate the
source of problems.
Monitor the network traffic. As with RS-232, a digital oscilloscope or logic
analyzer can help by showing exactly whats happening on the line. Many
Chapter 12
272
scopes have a math function that enables viewing a differential voltage as the
difference between the voltages on the two channels.
Humble components such as LEDs and switches can also be useful. On a
microcontroller, interface LEDs to spare output bits and write code that toggles
the bits to indicate events. For example, firmware can turn on an LED when
the node recognizes its address and turn on another LED after receiving a mes-
sage. Add toggle or slide switches to spare input port bits and have the program
send the values of the bits in its messages.
#FFTGUUKPI
In a typical network, each node has an assigned address and each message con-
tains the address of the recipient. On receiving a message, a node must detect
the address to determine whether to process or ignore the message.
#UUKIPKPI #FFTGUUGU
A node address is a value unique to the node and can be any number of bits.
Some networks use addresses that correspond to ASCII codes.
If there are fewer than 128 nodes, you dont need 8 bits to specify the node and
can get the most use out of a transmitted byte by assigning extra bits to other
uses. For example, in a 16-node network, bits 03 can specify the node number,
with bits 47 holding a command or other information.
&GVGEVKPI #FFTGUUGU
One challenge in sending addresses is that the nodes have to distinguish
between addresses and other information. For example, imagine a network
where each transmitted message begins with a byte containing the address of
the recipient. On recognizing its address, a node knows that the bytes that fol-
low are intended for it.
If Node 05h receives the byte 03h followed by 05h, how does Node 05h know
whether the second byte is part of a message meant for another node or an
address that begins a new message?
There are several ways to distinguish between addresses and other data:
The addresses can use a reserved set of values that messages never use.
The network can define a message format that specifies where the address is
stored in messages.
Network Programming
273
Communications can dedicate one data bit to indicate whether the other
data bits contain an address or data.
4GUGTXKPI #FFTGUU 8CNWGU
Reserving a set of values for use only as addresses makes it easy to distinguish
addresses from data with the limitation that messages cant uses the reserved val-
ues for other purposes.
If the messages contain text characters (which can include characters for numer-
als), network addresses can use any values that dont represent characters. For
example, if the messages use only US ASCII (codes 0127), addresses can use
the values 128255.
If the messages contain binary data, one solution is to send the data in ASCII
Hex format as described in Chapter 2. Because ASCII Hex can represent any
binary value using just 16 codes, plenty of codes remain for use as addresses.
&GHKPKPI C /GUUCIG (QTOCV
Many network protocols define a message format with the address and other
information in assigned locations in the message.
For example, an 8-byte message might consist of an address byte followed by
seven message bytes. On receiving a byte, a node examines the value to see if it
matches the nodes address. If its a match, the node reads and acts on the seven
bytes that follow, then waits for another address byte to examine. If the address
doesnt match, the node counts but otherwise ignores the seven bytes that fol-
low.
With this method, every node has to detect every byte sent, if only to know
when the message is finished. A node that misses a byte for any reason wont
detect the correct address bytes in future messages.
7UKPI &GFKECVGF %QFGU HQT 5VCTV CPF 'PF
To make it easier to detect addresses, a protocol can define values that indicate
Start of Transmission and End of Transmission. A node that gets lost can then
recover on the next Start of Transmission code. Conventional values are 02h
(Control+B) for Start of Transmission and 03h (Control+C) for End of Trans-
mission. Some networks use other characters such as : or $. These values are
then unavailable for other uses, so using dedicated codes is useful mainly for
communications that send data as plain text.
Chapter 12
274
A message format can also define a field that indicates the length of the data
that follows, and receiving nodes can use this value to determine when a mes-
sage ends.
*GCFGTU
A header is information that uses a defined format and appears at the beginning
of a message or other block of data. The header typically consists of a series of
fields, with each field having a defined size and location. The information
included in a header can vary. A header might include some or all of these
items:
Start-of-communication code.
Address of the receiving node.
Address of the sending node.
Length of the data that follows the header.
Checksum or other value used for error detection.
Description of the type of data that follows.
Time and date information.
DKV (QTOCV
Another option for distinguishing between addresses and data uses a 9-bit for-
mat. Bit 8 (the ninth bit) indicates whether bits 07 contain data (0) or an
address (1). Not all UARTs support the 9-bit format. The longer words in 9-bit
data mean that communications are slightly more sensitive to mismatches in
the rates at the transmitting and receiving ports. Messages that consist of only
US ASCII text can use seven data bits for a text character and the eighth bit to
indicate address or data.
'ODGFFGF 5[UVGOU
Some microcontrollers have a built-in ability to use the ninth bit to detect an
address. The PIC18F4520 introduced in Chapter 11 is an example.
To transmit 9-bit data, PIC18F4520 firmware must set the TX9 bit in the
TXSTA register to 1. Before writing a byte to transmit to TXREG, firmware
should write the ninth bit to the TX9D bit in TXSTA.
To receive 9-bit data, firmware must set the RCSTA registers RX9 bit to 1.
Before reading RCREG to obtain a received byte, firmware should read the
RCSTA registers RX9D bit to obtain the ninth bit.
Network Programming
275
Firmware is responsible for determining the value of the ninth bit to transmit
and interpreting the value of a received ninth bit. The hardware has no built-in
support for setting and detecting address/data bytes. Firmware must also define
an 8-bit address for the device.
To use address detecting with 9-bit data, in the RCSTA register, set ADDEN =
1. A computer that wants to communicate with the device must begin by send-
ing a 9-bit word consisting of the devices address with the ninth (most signifi-
cant) bit = 1. In the PIR1 register, RCIF will be set only after receiving a word
whose ninth bit = 1. When RCIF = 1, firmware should read the received byte. If
the byte matches the devices address, firmware sets ADDEN = 0 to enable
reading data. All received words that follow with bit 9 = 0 contain data
intended for the device. On receiving a word with bit 9 = 1, firmware should
again check for a matching address. If its not a match, firmware should set
ADDEN = 1 to disable setting RCIF on any data that follows.
Other microcontrollers, such as Maxim Integrated Products, Inc.s DS89C420,
can store a node address and a broadcast address and respond only when a
received address matches one of these values.
2%U
The UARTs in PCs typically dont have full hardware support for 9-bit proto-
cols, but applications can use Mark and Space parity to implement a soft-
ware-assisted 9-bit protocol.
Before changing the parity type, an application should ensure that all data writ-
ten with the previous parity setting has transmitted. Chapter 6 described meth-
ods for finding out when the data has finished transmitting to determine when
its OK to disable an RS-485 driver. The same methods are also useful for deter-
mining when its OK to change the parity type. An application that sends 9-bit
data might also need to add delays to ensure that the receiving computer pro-
cesses received bytes in the correct order, as described below.
To use 9-bit data in a .NET application, set the SerialPort objects DataBits
property to 8 and set the ParityReplace property to zero to disable the par-
ity-replace feature. The ParityReplace property can specify a byte that will
replace any byte received with a parity error. With 9-bit data, you want to pre-
serve the data rather than overwrite it.
8$ myComPort.DataBits = 8
myComPort.ParityReplace = 0
myComPort.Open
Chapter 12
276
8% myComPort.DataBits = 8;
myComPort.ParityReplace = 0;
myComPort.Open();
To write to a node, select Mark parity, write the nodes address, and then select
Space parity and write the data intended for that node.
8$ ' Write a node's address.
myComPort.Parity = Parity.Mark
myComPort.Write("h")
' Delay as needed to ensure that the address has transmitted.
' Write data to the node.
myComPort.Parity = Parity.Space
myComPort.WriteLine("Test data for node h.")
' Delay as needed to ensure that the data has transmitted.
' Write a node's address.
myComPort.Parity = Parity.Mark
myComPort.Write("m")
' Delay as needed to ensure that the address has transmitted.
' Write data to the node.
myComPort.Parity = Parity.Space
myComPort.WriteLine("Test data for node m.")
Network Programming
277
8% // Write a node's address.
myComPort.Parity = Parity.Mark;
myComPort.Write("h");
// Delay as needed to ensure that the address has transmitted.
// Write data to the node.
myComPort.Parity = Parity.Space;
myComPort.WriteLine("Test data for node h.");
// Delay as needed to ensure that the data has transmitted.
// Write a node's address.
myComPort.Parity = Parity.Mark;
myComPort.Write("m");
// Delay as needed to ensure that the address has transmitted.
// Write data to the node.
myComPort.Parity = Parity.Space;
myComPort.WriteLine("Test data for node m.");
When a byte with a parity error is the next byte to be read from the port, the
ErrorReceived event fires with an EventType of SerialError.RXParity. Chapter
10 showed how to assign a method to the ErrorReceived event.
A computer receiving data waits for a parity error and then reads the byte with
the error. If the byte matches the nodes address, the application accepts and
uses all data that follows until the next parity error. If a byte with a parity error
doesnt match the nodes address, the application reads all data that follows up
to the next parity error but doesnt use the data in any other way.
Chapter 12
278
An application can set a variable (acceptData in the example below) that indi-
cates whether the most recent address was a match (True) or not (False):
8$ Dim acceptData as Boolean
Dim myAddress As Integer = Microsoft.VisualBasic.AscW("h")
Private Sub ErrorReceived _
(ByVal sender As Object, ByVal e As SerialErrorReceivedEventArgs)
Dim SerialErrorReceived1 As SerialError
SerialErrorReceived1 = e.EventType
Select Case SerialErrorReceived1
Case SerialError.RXParity
If (myComPort.ReadChar = myAddress) Then
acceptData = True
Else
acceptData = False
End If
End Select
End Sub
Network Programming
279
8% internal bool acceptData;
internal int myAddress = (int)'h';
private void ErrorReceived( object sender, SerialErrorReceivedEventArgs e )
{
SerialError SerialErrorReceived1 = 0;
SerialErrorReceived1 = e.EventType;
switch ( SerialErrorReceived1 )
{
case SerialError.RXParity:
if ( ( SelectedPort.ReadChar() == myAddress ) )
{
acceptData = true;
}
else
{
acceptData = false;
}
break;
}
}
A DataReceived event can check the state of acceptData before deciding what
to do with new received data. Chapter 10 showed how to assign a method to
the DataReceived event.
8$ Friend Sub DataReceived _
(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
Dim newReceivedData As String
newReceivedData = selectedPort.ReadExisting
If acceptData Then
' The received address matches our address.
End If
End Sub
Chapter 12
280
8% internal void DataReceived( object sender, SerialDataReceivedEventArgs e )
{
string newReceivedData = null;
newReceivedData = SelectedPort.ReadExisting();
if ( acceptData )
{
// The received address matches our address.
}
}
Using events to detect received data and addresses assumes that the events are
raised in the order they occur. However, .NET doesnt guarantee that the
DataReceived and ErrorReceived events will fire in order. For example, if the
transmitting computer sends an address immediately followed by data, the
DataReceived event might fire before the ErrorReceived event and cause the
receiving computer to think the data is for a different address. To prevent prob-
lems, the transmitting computer can insert a delay either before or after chang-
ing parity. The delay allows time for the receiving computer to process an
address before the data arrives and time to process received data before another
address arrives.
To simplify the programming, a primary/secondary network can use the address
bit only when the primary node transmits to a secondary node. If the secondary
nodes transmit only in response to received communications, the primary node
can assume that any incoming data is from the node most recently addressed.
The other secondary nodes ignore data sent by the other secondary nodes
because a matching address wasnt detected.
281
13
#P PS-4B5 NGVYQTk
This chapter presents a basic network that uses a primary/secondary protocol. A
primary node sends commands to the secondary nodes. Each secondary node
detects, reads, and responds to commands directed to it.
You can use the network as a base for designing projects using PCs and embed-
ded systems of any type, in any combination. The example code includes PC
code for the primary node and microcontroller code for the secondary nodes.
%QPPGcV|Pg VbG NQdGs
Chapter 12 introduced the options for wiring the nodes in an RS-485 network.
Figure 13-1 shows a configuration you can use for the example network in this
chapter.
6TCPscG|XGTs
The network can use any appropriate transceivers for half-duplex RS-485 ports,
including high-speed, low-speed, low-voltage, short-distance, and isolated com-
ponents. A PC can use an expansion card with an RS-485 interface, a
Chapter 13
282
Figure 13-1: The RS-485 network can use this wiring to connect the nodes.
An RS-485 Network
283
USB/RS-485 converter, or an RS-232/RS-485 converter that connects to an
RS-232 port. Microcontroller ports can interface to RS-485 transceivers.
To control the driver-enable inputs, the nodes can use automatic driver-enable
circuits as described in Chapter 7 or software-controlled driver-enable lines.
The methods used to control the driver-enable lines determine in part whether
the nodes need to insert delays before sending or responding to commands.
6GTOKPCVKPI CPF $KCUKPI
At one end of the network, three resistors in series hold the inputs high when
no drivers are enabled on the network. The 130 resistor provides a parallel line
termination and the two 620 resistors provide biasing. At the other end of the
network, a 120 resistor provides a parallel termination. If the line is electri-
cally short as defined in Chapter 7, you dont need terminating or biasing resis-
tors.
%CDNKPI
For the network wires, unshielded twisted pair works well. Include a ground
wire to each node unless youre sure that all nodes already have a common sig-
nal-ground connection.
'ZCORNG 2TQVQEQN
The example network uses a command/response protocol. You can expand on
the protocol as needed, adding additional commands, error codes, error check-
ing of received communications, or other features.
#FFTGUUGU
Each secondary node has a unique assigned address from 97 to 122, which cor-
respond to ASCII codes for the characters az. The primary node doesnt have
an address. Each command has a value from 48 to 57, which correspond to
ASCII codes for the characters 09.
/GUUCIG (QTOCV
Messages from the primary node use this format:
Byte 0 is a : character that signals the start of a message.
Byte 1 is the address of the node being addressed.
Chapter 13
284
Byte 2 is a command code.
Additional bytes can contain data specific to the command. Some com-
mands have no command-specific bytes.
The final byte is a line-feed code (0Ah) that signifies the end of the message.
A CR code (0Dh) can precede or follow the LF but isnt required.
On receiving a message from the primary node, a secondary node returns a
message with this format:
Byte 0 is a : character that signals the start of a response.
Byte 1 is the address of the node returning the response.
Additional bytes can contain data specific to the command. Some responses
have no command-specific bytes.
The final byte is a line-feed code that signifies the end of the message. A CR
code can precede the LF but isnt required.
%QOOCPFU
The code example in this chapter implements two commands: read_byte and
write_byte. On receiving a valid command, a node returns a response. A node
that receives an unsupported command or detects an error returns no response.
4GCFKPI C $[VG
The read_byte command requests a byte of data from a specified location in a
secondary node. The location is an application-defined byte that can indicate a
port or variable:
yte
Number
Ccntents
(ASCII|ASCII Hex)
Descr|t|cn
0 : (ASCII 58) Start-of-communication indicator
1 address Identifies the recipient
2 1 (ASCII 48) read_byte command code
3 location Identifies the location to read
4 LF (ASCII 10) End-of-communication indicator
An RS-485 Network
285
The response to read_byte contains the requested byte in ASCII Hex format:
A command sent to node h to read a byte from location b is:
:h1b
followed by a LF.
These are the transmitted values in hexadecimal:
3a 68 31 62 0a
On accepting the command, node h reads location b. If the location contains
the value A5h, the node responds with:
:ha5
followed by a LF.
These are the values transmitted in hexadecimal:
3a 68 61 35 0a
yte Number Ccntents
(ASCII|ASCII Hex)
Descr|t|cn
0 : (ASCII 58) Start-of-communication indicator
1 address Identifies the sender
2 data Requested data, most significant nibble
(ASCII Hex format)
3 data Requested data, least significant nibble
(ASCII Hex format)
4 LF (ASCII 10) End-of-communication indicator
Chapter 13
286
9TKVKPI C $[VG
The write_byte command writes a byte of data to a specified location in a sec-
ondary node. The location is an application-defined byte that can indicate a
port or variable. The byte is sent in ASCII Hex format:
The response to write_byte identifies the sender and serves as an acknowledge-
ment that the command was received:
A command sent to node i to write the byte FEh to location c is:
:i2cfe
followed by a LF.
These are the transmitted values in hexadecimal:
3a 69 32 63 66 65 0a
On accepting the command, node i writes the value FEh to location c and
responds with:
:i
followed by a LF.
yte
Number
Ccntents
(ASCII|ASCII Hex)
Descr|t|cn
0 : (ASCII 58) Start-of-communication indicator
1 address Identifies the recipient
2 2 (ASCII 49) write_byte command
3 location Identifies the location to write to
4 data Data to write, most significant nibble
(ASCII Hex format)
5 data Data to write, least significant nibble
(ASCII Hex format)
6 LF (ASCII 10) End-of-communication indicator
yte Number Ccntents
(ASCII|ASCII Hex)
Descr|t|cn
0 : (ASCII 58) Start-of-communication indicator
1 address Identifies the sender
2 LF (ASCII 10) End-of-communication indicator
An RS-485 Network
287
These are the values transmitted in hexadecimal:
3a 69 0a
2QNNKPI VJG 0QFGU
On a PC that functions as the primary node, an application can send com-
mands, log received data, and take other actions as needed. Application code
can control the RS-485 transceivers driver-enable line if needed. If the RS-485
interface uses automatic driver-enable control, you can use terminal-emulator
software to access the nodes by typing commands and viewing responses.
The example code below supports both automatic and software-controlled
driver-enable lines. With automatic control, the transceivers receiver-enable
input connects to the driver-enable input to disable the receiver while the PC is
transmitting. With software control, the RS-485 transceivers receiver-enable
input is tied low so the PC can read back the data it sends and disable the driver
on detecting that all of the data has transmitted. (See Chapter 6 for more about
driver-enable circuits.)
The routines below assume myComPort is an open SerialPort object.
%QPHKIWTKPI VJG &TKXGTGPCDNG .KPG
For circuits with software driver-enable control, the routine below sets the state
of the driver-enable output. As shown, the routine uses the RtsEnable property
to control the RTS. If the driver-enable line is DTR, change the code to use the
DtrEnable property instead.
8$ Private Sub Rs485DriverEnableControl(ByVal driverDisable As Boolean)
If driverDisable Then
myComPort.SelectedPort.RtsEnable = False
Else
myComPort.SelectedPort.RtsEnable = True
End If
End Sub
Chapter 13
288
8% private void Rs485DriverEnableControl(bool driverDisable)
{
if (driverDisable)
{
myComPort.RtsEnable = false;
}
else
{
myComPort.RtsEnable = true;
}
}
5GPFKPI %QOOCPFU
The Send_Command routine accepts command parameters, sends a command,
and displays a received response.
8$ Private driverDisable As Boolean
' Uncomment one of these lines to indicate the method of controlling the RS-485
' driver-enable line.
Private softwareDriverEnable As Boolean = False ' software control
'Private softwareDriverEnable As Boolean = True ' hardware control
Private Sub Send_Command(ByVal node As String, ByVal command As String, _
ByVal address As String, ByVal data As String)
Dim dataSent As String
Dim response As String
' Define LF as the NewLine character for serial data.
myComPort.NewLine = System.Convert.ToString((char)10)
If softwareDriverEnable Then
Rs485DriverEnableControl(True)
End If
' Write a command to the serial port.
myComPort.WriteLine(":" + node + command + address + data)
An RS-485 Network
289
If softwareDriverEnable Then
' Read back what was transmitted to ensure that all of the data
' has gone out.
' Then disable the RS-485 driver.
' The RS-485 transceiver's receiver must be permanently enabled
' in hardware.
dataSent = myComPort.ReadLine()
Rs485DriverEnableControl(False)
End If
' Read and decode the response.
response = myComPort.ReadLine()
If response.StartsWith(":") Then
If response.Substring(1, 1) = node Then
Select Case command
Case "1"
' write_byte command
Console.WriteLine _
("write_byte command acknowledged.")
Case "2"
' read_byte command. Display the received data.
Console.WriteLine("read_byte response data: " + _
response.Substring(2, 2))
End Select
End If
End If
End Sub
Chapter 13
290
8% // Uncomment one of these lines to indicate the method of controlling the RS-485
// driver-enable line.
private bool softwareDriverEnable = true; // software control
//private bool softwareDriverEnable = false; // hardware control
private bool driverDisable;
private void Send_Command( string node, string command, string address, string data )
{
string dataSent = null;
string response = null;
// Define LF as the NewLine character for serial data.
myComPort.NewLine = System.Convert.ToString((char)10);
if (softwareDriverEnable)
{
Rs485DriverEnableControl(true);
}
// Write a command to the serial port.
myComPort.WriteLine( ":" + node + command + address + data );
if (softwareDriverEnable)
{
// Read back what was transmitted to ensure that all of the data goes out.
// Then disable the RS-485 driver.
// The RS-485 transceiver's receiver must be permanently enabled
// in hardware.
dataSent = myComPort.ReadLine();
Rs485DriverEnableControl(false);
}
// Read and decode the response.
response = myComPort.ReadLine( );
An RS-485 Network
291
if (response.StartsWith(":"))
{
if (response.Substring(1, 1) == node)
{
switch (command)
{
case "1":
Console.WriteLine(
"write_byte command acknowledged.");
break;
case "2":
Console.WriteLine("read_byte response data: " +
response.Substring(2, 2));
break;
}
}
}
}
4GURQPFKPI VQ 2QNNU
A secondary node reads all incoming bytes looking for the : character that
indicates the start of a message. On receiving :, the node can check the
address right away or wait to receive a LF code. In the first approach, the node
reads the next received byte to find out if it matches the nodes address. If the
byte matches, the node stores the data that follows the address up to a LF and
then acts on the received command. If the byte doesnt match the address, the
node ignores any received data up to the next :. In the second approach, the
node stores data that follows any : up to a LF. After receiving a LF, the node
examines the received address and acts on the received command only if the
address matches. The code that follows uses the second approach.
#WZKNKCT[ 4QWVKPGU
The example firmware for network communications includes a task loop and
routines that implement delays and convert between ASCII Hex and binary
data.
Chapter 13
292
&GHKPKVKQPU
The code supports a firmware-controlled or hardware-controlled driver-enable
line. If needed, the firmware can implement a delay before responding to a
command.
2$2 DEFINE HSER_CLROERR
COMMAND_START con ":"
MAX_COMMAND_LENGTH con 5
MY_ADDRESS con "h"
' Use any spare output port bit to control the driver enable line
' Unneeded if using automatic hardware driver enable:
driver_enable var PORTB.3
command_index var byte
command_response var byte[6]
converted_byte var byte
data_ready_to_send var bit
delay_before_responding var bit
firmware_driver_enable var bit
index var byte
lower_nibble var byte
network_state var byte
received_command var byte[6]
response_index var byte
serial_in var byte
success var bit
upper_nibble var byte
value_to_convert var byte
command_index = 0
command_response[0] = COMMAND_START
command_response[1] = MY_ADDRESS
network_state = "r"
response_index = 0
' Output port bit used only for testing:
TRISB.0 = 0
low PORTB.0
An RS-485 Network
293
' Uncomment one of these lines.
' delay_before_responding = 0 ' No delay before responding.
delay_before_responding = 1 ' Delay before responding
' Uncomment one of these lines:
' firmware_driver_enable = 0 ' Circuits have automatic driver-enable control.
firmware_driver_enable = 1 ' Firmware controls the driver-enable line.
if (firmware_driver_enable = 1) then
' Define the driver-enable line and disable the driver.
TRISB.3 = 0
driver_enable = 0
endif
% // Comment out this line if using automatic hardware control of the
// RS-485 driver-enable line.
#define firmware_driver_enable
// Comment out this line if the remote computer requires no extra time to disable
// its RS-485 driver before the microcontroller responds to a received command.
#define delay_before_responding
#if defined(firmware_driver_enable)
#define driver_enable PORTBbits.RB3
#endif
#define COMMAND_START 58
const byte MAX_COMMAND_LENGTH = 5;
// The microcontroller's network address:
const char MY_ADDRESS = 'h';
Chapter 13
294
byte command_index;
char command_response[6];
char network_state = 'r';
char received_command[6];
byte response_index = 0;
unsigned char serial_in;
// Define the driver-enable line and disable the driver.
#if defined(firmware_driver_enable)
TRISBbits.TRISB3 = 0;
driver_enable = 0;
#endif
// Output port bit used only for testing:
TRISBbits.TRISB0 = 0;
PORTBbits.RB0 = 0;
6CUM .QQR
A task loop can handle network communications and other activities. In the
example below, the serial_communications routine implements a state machine
with three states. In state r, the device is waiting to receive data. In state d,
the device has received a command and is delaying before responding to allow
the primary node time to disable its driver. If the primary node disables its
driver immediately after transmitting, the firmware can be configured so it
never enters state d. In state t, the device is ready to transmit data.
An RS-485 Network
295
2$2 loop:
gosub serial_communications
' Add other tasks here.
goto loop
serial_communications:
select case network_state
case "r"
gosub receive_serial_data
case "d"
gosub check_response_delay
case "t"
gosub transmit_serial_data
case else
end select
return
Chapter 13
296
% while(1)
{
serial_communications();
// Add other tasks here.
}
void serial_communications(void)
{
switch (network_state)
{
case 'r':
{
receive_serial_data();
break;
}
case 'd':
{
check_response_delay();
break;
}
case 't':
{
transmit_serial_data();
break;
}
default:
break;
}
}
+ORNGOGPVKPI &GNC[U
On receiving a valid command, the code calls the prepare_to_respond function,
which either starts the delay timer and sets network_state to d or sets
network_state to t as appropriate.
If delay_before_responding is defined, on receiving a valid command, the
device starts a timer and serial communications enter the d state. On each
pass through the task loop, the code calls check_response_delay to find out if
the delay time has elapsed. If so, the routine stops the timer and switches to the
t state.
An RS-485 Network
297
If delay_before_responding isnt defined, the timer never starts and communi-
cations never enter the d state.
2$2 prepare_to_respond:
response_index = 0;
if (delay_before_responding = 1) then
gosub start_response_delay_timer
network_state = "d"
else
network_state = "t"
endif
return
% void prepare_to_respond(void)
{
response_index = 0;
#if defined(delay_before_responding)
start_response_delay_timer();
network_state = 'd';
#else
network_state = 't';
#endif
}
The start_response_delay_timer function initializes and starts the timer.
2$2 start_response_delay_timer:
' This example sets a delay of around 0.5 second assuming FOSC = 4 MHz.
' Timer enabled, 16-bit, internal clock, prescaler = 256.
T0CON = $87
' Load the timer with F800h.
TMR0L = $00
TMR0H =$F8
return
Chapter 13
298
% void start_response_delay_timer(void)
{
// This example sets a delay of around 0.5 second assuming FOSC = 4 MHz.
OpenTimer0( TIMER_INT_OFF &
T0_16BIT &
T0_SOURCE_INT &
T0_PS_1_256 );
WriteTimer0(0xf800);
}
The check_response_delay function finds out if the delay time has elapsed and
if so, changes the network_state variable to enable sending a response:
2$2 check_response_delay:
if (delay_before_responding = 1) then
if (INTCON.2 = 1) then
' The delay time has elapsed.
' Stop the timer and set network_state to enable responding.
T0CON = 0
INTCON.2 = 0
network_state = "t"
endif
endif
return
% void check_response_delay(void)
{
#if defined(delay_before_responding)
if (INTCONbits.TMR0IF == 1)
{
// The delay time has elapsed.
// Stop the timer and set network_state to enable responding.
CloseTimer0;
INTCONbits.TMR0IF = 0;
network_state = 't';
}
#endif
}
An RS-485 Network
299
%QPXGTVKPI DGVYGGP $[VGU CPF #5%++ *GZ
The network encodes data in ASCII Hex format. Chapter 2 showed .NET code
for converting between binary and ASCII Hex bytes. Microcontroller code can
perform these conversions as well.
The byte_to_ascii_hex function converts a byte (value_to_convert) to two
ASCII hex characters that represent the bytes value (upper_nibble and
lower_nibble):
2$2 byte_to_ascii_hex:
' Represent the byte variable value_to_convert as
' ASCII Hex characters upper_nibble and lower_nibble.
upper_nibble = (value_to_convert & $f0) >> 4
if ((upper_nibble >= 0) AND (upper_nibble <= 9)) then
upper_nibble = upper_nibble + 48
else
' The value is between 10 (a) and 15 (f).
upper_nibble = upper_nibble + 87
endif
lower_nibble = (value_to_convert & $0f)
if ((lower_nibble >= 0) AND (lower_nibble <= 9)) then
lower_nibble = lower_nibble + 48
else
' The value is between 10 (a) and 15 (f).
lower_nibble = lower_nibble + 87
endif
return
Chapter 13
300
% void byte_to_ascii_hex(unsigned char value_to_convert, char converted_value[])
{
char upper_nibble;
char lower_nibble;
// Get each hex digit and convert it to a character code.
upper_nibble = (value_to_convert & 0xf0) >> 4;
if ((upper_nibble >= 0) && (upper_nibble <= 9))
upper_nibble += 48;
else
{
// The value is between 10 (a) and 15 (f).
upper_nibble += 87;
}
lower_nibble = value_to_convert & 0x0f;
if ((lower_nibble >=0) && (lower_nibble <= 9))
lower_nibble += 48;
else
{
// The value is between 10 (a) and 15 (f).
lower_nibble += 87;
}
converted_value[0] = upper_nibble;
converted_value[1] = lower_nibble;
}
The ascii_hex_to_byte function converts two ASCII hex bytes (upper_nibble
and lower_nibble) to the binary value they represent:
2$2 ascii_hex_to_byte:
' Set converted_byte to the value represented by ASCII Hex
' characters upper_nibble and lower_nibble
' Set success = 1 on success, 0 on failure.
success = 1
An RS-485 Network
301
' Convert each character code to the value it represents.
select case upper_nibble
case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
upper_nibble = upper_nibble - 48
case "a", "b", "c", "d", "e", "f"
upper_nibble = upper_nibble - 87
case "A", "B", "C", "D", "E", "F"
upper_nibble = upper_nibble - 55
case else
' The text character isn't 0-9, a-f, or A-F.
success = 0
end select
select case lower_nibble
case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
lower_nibble = lower_nibble - 48
case "a", "b", "c", "d", "e", "f"
lower_nibble = lower_nibble - 87
case "A", "B", "C", "D", "E", "F"
lower_nibble = lower_nibble - 55
case else
' The text character isn't 0-9, a-f, or A-F.
success = 0
end select
if (success = 1) then
' Combine the nibbles in a byte.
converted_byte = (upper_nibble << 4) + lower_nibble
endif
return
Chapter 13
302
% int ascii_hex_to_byte(char upper_nibble, char lower_nibble)
{
// Return the byte value represented by the ASCII Hex
// characters upper_nibble and lower_nibble
// Return -1 on failure.
byte return_value = 0;
// Convert each char into its value.
switch (upper_nibble)
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
upper_nibble -= 48;
break;
}
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
{
upper_nibble -= 87;
break;
}
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
{
upper_nibble -= 55;
break;
}
default:
{
return_value = -1;
break;
}
}
An RS-485 Network
303
switch (lower_nibble)
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
lower_nibble -= 48;
break;
}
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
{
lower_nibble -= 87;
break;
}
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
{
lower_nibble -= 55;
break;
}
default:
{
lower_nibble = -1;
break;
}
}
if ((upper_nibble > -1) && (lower_nibble > -1))
return_value = (upper_nibble << 4) + lower_nibble;
else
return_value = -1;
return return_value;
}
&GEQFKPI 4GEGKXGF &CVC
Firmware must decode received data to find out if it contains a supported com-
mand.
Chapter 13
304
'ZCOKPKPI 4GEGKXGF $[VGU
On receiving a byte and storing it in the char variable serial_in, a node can
examine and process the byte:
2$2 receive_serial_data:
' Process received bytes.
if (PIR1.5 = 1) then
' A byte is available to read.
if (RCSTA.2 = 1) then
' Framing error. Read RCREG to clear the error
' but don't use the data.
hserin [serial_in]
else
' A character was received without a framing error.
hserin [serial_in]
select case serial_in
case $0a
' A LF character was received,
' indicating the end of a command.
gosub respond_to_command
case $0d
' Ignore a received CR character.
An RS-485 Network
305
case COMMAND_START
' A new command has begun.
' Initialize the array that holds received bytes.
received_command[0] = COMMAND_START
command_index = 1
case else
' A character was received and it's not a LF or CR.
' If at the end of the array, ignore additional received data.
if (command_index <= MAX_COMMAND_LENGTH) then
' Convert characters A-Z to lower case.
if ((serial_in >= "A") and (serial_in <= "Z")) then
serial_in = serial_in + 32
endif
' Save the character and increment the position
' in the array that stores received text.
received_command[command_index] = serial_in
command_index = command_index + 1
endif
end select
endif
endif
return
Chapter 13
306
% void receive_serial_data(void)
{
// Process received bytes.
if (DataRdyUSART())
{
// The serial port has received a byte.
// Read a received character.
if (RCSTAbits.OERR == 1)
{
// Overrun error.
// Clear the error and re-enable the receiver.
RCSTAbits.CREN = 0;
RCSTAbits.CREN = 1;
}
if (RCSTAbits.FERR == 1)
{
// Framing error.
// Reading a byte clears the error.
// Read the byte but don't use it.
serial_in = getcUSART();
}
else
{
// A character was received without a framing error.
serial_in = getcUSART();
switch (serial_in)
{
case 0x0a:
{
// A LF character was received, indicating the end
// of a command.
respond_to_command();
break;
}
An RS-485 Network
307
case 0x0d:
{
// Ignore a received CR character.
break;
}
case COMMAND_START:
{
// A new command has begun.
// Initialize the array that holds received characters.
received_command[0]=COMMAND_START;
command_index = 1;
break;
}
default:
{
// A character was received and it's not a LF or CR.
// Save the character and increment the position in
// the variable that stores received text.
// Convert the char to lower case to make the input
// case insensitive.
// If at the end of the array,
// ignore additional received data.
if (command_index <= MAX_COMMAND_LENGTH)
{
received_command[command_index] =
tolower(serial_in);
command_index++;
}
break;
}
}
}
}
}
Chapter 13
308
4GURQPFKPI VQ %QOOCPFU
The respond_to_command routine responds to the two commands defined
earlier in this chapter. The function executes on detecting a received LF.
2$2 respond_to_command:
' On receiving a valid command, call a routine to handle the specific command.
' Every command begins with a COMMAND_START code, the node's address,
' and a command code.
if (received_command[0] = COMMAND_START) then
if (received_command[1] = MY_ADDRESS) then
select case received_command[2]
case "1"
gosub command_write_byte
case "2"
gosub command_read_byte
Add additonal supported commands here.
case else
gosub initialize_serial_buffers
end select
else
gosub initialize_serial_buffers
endif
else
gosub initialize_serial_buffers
endif
return
An RS-485 Network
309
% void respond_to_command(void)
{
// On receiving a valid command, call a routine to handle the specific command.
// Every command begins with a COMMAND_START code, the node's address,
// and a command code.
int received_data;
if (received_command[0] == COMMAND_START)
{
if (received_command[1] == MY_ADDRESS)
{
// Respond to supported commands.
switch (received_command[2])
{
case '1':
{
command_write_byte();
break;
}
case '2':
{
command_read_byte();
break;
}
// Add additional supported commands here.
default:
{
initialize_serial_buffers;
break;
}
}
}
else
{
initialize_serial_buffers;
}
}
Chapter 13
310
else
{
initialize_serial_buffers;
}
}
On receiving an invalid command, the initialize_serial_buffers routine re-ini-
tializes the buffers that hold the command and response and thus prepares to
receive a new command.
2$2 initialize_serial_buffers:
command_index = 0
response_index = 0
received_command[0]= 0
return
% void initialize_serial_buffers(void)
{
command_index = 0;
response_index = 0;
received_command[0]= '\0';
}
An RS-485 Network
311
9TKVKPI C $[VG
The routine below handles the write_byte command. The routine sets a port
bit to match bit 0 of the received byte and prepares to send a response to the
primary node. For testing, the port bit can control an LED. A real-world appli-
cation can use the received data in any way it wants.
2$2 command_write_byte:
' A write_byte command has been received.
select case received_command[3]
case "b"
' Get the data to write.
' Convert the received ASCII Hex bytes to a byte value.
upper_nibble = received_command[4]
lower_nibble = received_command[5]
gosub ascii_hex_to_byte
' Set bit 0 of PortB to match bit 0 in the received byte.
if ((converted_byte & 1) = 1) then
high PORTB.0
else
low PORTB.0
endif
gosub prepare_response
' Add more cases as needed.
case else
end select
return
Chapter 13
312
% void command_write_byte(void)
{
int received_data;
switch (received_command[3])
{
case 'b':
{
// Get the data to write.
received_data = ascii_hex_to_byte(received_command[4],
received_command[5]);
if (received_data > -1)
{
// The received data was valid.
// Set a port bit to match bit 0 of the received byte.
if ((received_data & 1) == 1)
{
PORTBbits.RB0 = 1;
}
else
{
PORTBbits.RB0 = 0;
}
command_response[2] = 0x0a;
prepare_to_respond();
break;
}
}
// Add more cases as needed.
default:
{
break;
}
}
}
An RS-485 Network
313
4GCFKPI C $[VG
The function below handles the read_byte command. The function reads a port
and prepares to send its value in a response to the primary node.
2$2 command_read_byte:
' A read_byte command has been received.
select case received_command[3]
case "b"
' Read Port B.
value_to_convert = PORTB
' Convert the value read to ASCII Hex
gosub byte_to_ascii_hex
' Prepare to send the ASCII Hex characters in a response.
command_response[2] = upper_nibble
command_response[3] = lower_nibble
command_response[4] = $0a
gosub prepare_to_respond
' Add more cases as needed.
case else
end select
return
Chapter 13
314
% void command_read_byte(void)
{
switch (received_command[3])
{
case 'b':
{
// Read Port B.
// Store the value read and a LF to send in the response.
byte_to_ascii_hex(PORTB, &command_response[2]);
command_response[4] = 0x0a;
prepare_to_respond();
break;
}
// Add more cases as needed.
default:
break;
}
}
An RS-485 Network
315
After the prepare_to_respond routine executes and any required delay elapses,
the network is in the t state. The task loop calls the transmit_serial_data rou-
tine to send the characters in the command_response array to the primary
node.
2$2 transmit_serial_data:
' If a byte is waiting to transmit, send it.
if firmware_driver_enable then
driver_enable = 1
endif
' Wait for the transmit shift register to empty.
while (TXSTA.1 = 0)
wend
hserout[command_response[response_index]]
if (command_response[response_index] = $0a) then
' The entire response has been sent.
if firmware_driver_enable then
while (TXSTA.1 = 0)
wend
driver_enable = 0
endif
' Prepare to receive another command.
gosub initialize_serial_buffers
network_state + "r"
else
' Prepare to send the next byte.
response_index = response_index + 1
endif
return
Chapter 13
316
% void transmit_serial_data(void)
{
#if defined firmware_driver_enable
driver_enable = 1;
#endif
while(BusyUSART());
putcUSART (command_response[response_index]);
if (command_response[response_index] == 0x0a)
{
// The entire response has been sent.
#if defined(firmware_driver_enable)
while(BusyUSART());
driver_enable = 0;
#endif
// Prepare to receive another command.
initialize_serial_buffers;
network_state = 'r';
}
else
{
// Prepare to send the next byte.
response_index++;
}
}
317
14
+PUKFG 75$
USB is a hardware interface with defined protocols that enable a host computer
to communicate with a variety of peripheral devices. As Chapter 1 explained,
PC software can access some USB devices as virtual COM ports (VCPs). This
chapter introduces USB and how it relates to virtual COM-port devices.
The USB 2.0 specification is the main document that defines the interface.
USB specifications are available from the USB Implementers Forum (USB-IF)
(www.usb.org). For more a more detailed discussion of USB protocols, see the
specification or my book, USB Complete: Everything You Need to Develop Cus-
tom USB Peripherals.
*QUVU CPF &GXKEGU
An RS-232 interface assumes nothing about the contents of the data being
transferred. The computer at each end of the cable can use any method to
define what the data means.
In contrast, USB is an intelligent interface with defined protocols. Every USB
communication is between a host and a device. The host manages communica-
tions on the bus, and devices respond to communications from the host. The
Chapter 14
318
host is a PC or another computer that contains host-controller hardware and
software. A device is a peripheral or other computer-controlled system that con-
tains device-controller hardware and firmware.
The host assigns a driver or a series of drivers to each attached device. The driv-
ers manage communications between applications and the USB host control-
lers driver and define how applications can access a device. Drivers can insulate
applications from the details of a devices hardware interface. RS-232 and USB
are very different interfaces, but with driver support, application software can
use the same COM-port functions to access internal RS-232 ports and USB
devices that function as virtual COM ports.
On power up or device attachment, the host computer and the device exchange
information in a process called enumeration. The host requests a series of data
structures called descriptors from the device. The descriptors contain informa-
tion that identify the device and its intended use. The information includes the
devices Vendor ID, Product ID, Release Number and codes that identify any
standard USB classes the device belongs to.
During enumeration, the host also assigns a bus address to the device and
requests the device to use a configuration that specifies how much current the
device can draw from the bus.
#UUKIPKPI C &TKXGT QP VJG *QUV
For each device, the host computer looks for the best match between the infor-
mation in the devices descriptors and the information in the hosts INF files (or
an equivalent information source for non-Windows systems). The information
can be a Vendor ID/Product ID pair or USB class and subclass codes. On find-
ing a match, the host uses the driver or drivers named in the INF file. After the
device accepts the requested configuration, the host and device can begin com-
municating using the assigned drivers.
A virtual COM-port device can use the serial.sys and usbser.sys drivers provided
with Windows or an alternate driver obtained from a device vendor or other
source. Some companies provide free COM-port drivers for use with the com-
panys USB controllers.
4GSWKTGOGPVU
A USB host can be a desktop or notebook computer, a handheld, or an embed-
ded system. The host must contain host-controller hardware and must imple-
Inside USB
319
ment USB protocols. To communicate with COM-port devices, the host must
support the COM-port software interface and must either support the USB
communication devices class or use a vendor-specific driver that implements a
virtual COM port.
A USB device can contain a microcontroller with an on-chip USB device con-
troller, or the USB device controller can be on a separate chip that interfaces to
a microcontroller or other CPU. The device can use a USB controller designed
specifically for COM-port applications or a generic controller that can be pro-
grammed for any use. The hardware that implements the low-level USB proto-
cols in the device controller is called the serial interface engine (SIE). Some
devices manage USB communications entirely in hardware and require no
firmware programming to support USB-specific protocols.
An On-The-Go (OTG) device is a USB device that can function as a lim-
ited-capability host and as a device (but not both at the same time). An OTG
device can function as a host to virtual COM-port devices or as a virtual
COM-port device.
*QUV 4GURQPUKDKNKVKGU
A USB host manages power and communications on the bus and has these
responsibilities:
Detect and enumerate all attached devices,.
Detect when a device has been removed from the bus.
Provide power to devices and work with the devices to conserve power when
possible.
Manage data flow on the bus.
Perform error checking.
&GXKEG 4GURQPUKDKNKVKGU
In many ways, a devices responsibilities mirror the hosts, but devices also have
unique duties. A USB device has these responsibilities:
Detect voltage on the buss power-supply line and on detecting the voltage,
switch in a pull-up resistor to announce the devices presence to the host.
Manage power. In normal operation, a device must limit the bus current con-
sumed to either 100 mA or a higher amount, up to 500 mA, in a configuration
supported by the device and requested by the host during enumeration. A
device must also detect the presence of the hosts periodic timing markers and
enter the low-power Suspend state when the markers are absent. While in the
Chapter 14
320
Suspend state, the device must limit its current consumption and monitor the
bus, exiting the Suspend state when bus activity resumes.
Respond to requests sent by the host during and after enumeration.
Perform error checking.
Exchange data with the host. A virtual COM-port device receives COM-port
data from the host and sends COM-port data as needed to the host.
As needed, send and receive COM-port parameters and status and control
information.
5RGGF
The USB 2.0 specification defines three bus speeds: high speed at 480 Mbps,
full speed at 12 Mbps, and low speed at 1.5 Mbps. USB devices in the commu-
nication devices class must support full speed, high speed, or both. Almost all
high-speed devices also support full speed because adding support for full speed
is rarely difficult and enables the device to work when attached to full-speed
hosts. USB hosts in recent PCs support all three speeds.
The bus speeds describe the rate that information travels on the bus. The theo-
retical maximum data-transfer rate for the bulk endpoints used on most virtual
COM ports is 1.216 Megabytes/s at full speed and 53.248 Megabytes/s at high
speed. The real-world maximum throughput is less and varies with the pro-
gramming on the host and device, the hardware capabilities of the host and
device, and how busy the bus is.
'PFRQKPVU
All bus traffic travels to or from device endpoints. An endpoint serves as a
buffer for received data or data waiting to transmit. Typically an endpoint is a
block of data memory or a register in the device controller.
Every device must implement endpoint zero, which is bidirectional. A device
can have up to 30 additional endpoint addresses. Each of these endpoint
addresses has a number (1 to 15) and direction (IN or OUT). The direction is
defined from the hosts perspective: an IN endpoint provides data to send to the
host and an OUT endpoint stores data received from the host. Device hardware
or firmware configures each endpoint address for a specific USB transfer type
and direction. The number of available endpoints varies with the device con-
troller. A USB virtual COM port typically uses three endpoint addresses in
addition to endpoint zero.
Inside USB
321
Each endpoint address except endpoint zero has an endpoint descriptor. The
descriptors wMaxPacketSize field specifies how many bytes the endpoint can
transfer in a data packet. For bulk endpoints, wMaxPacketSize can be up to 64
for full-speed endpoints and must be 512 for high-speed endpoints. (The
device descriptor specifies the maximum packet size for endpoint zero.)
75$ 6TCPUHGTU
The USB specification defines structures for transferring data on the bus and
ensuring that transmitted data reaches its receiver.
6TCPUHGT 6[RGU
One reason why USB is suitable for a wide range of devices is its support for
four types of data transfers (Table 14-1).
Control transfers enable the host to learn about a device, set a devices address,
and select configurations and other settings. The host uses control transfers to
learn about the device during enumeration. Control transfers can also send
class-specific and vendor-specific requests that transfer data for any purpose. A
USB host typically uses control transfers to send serial-port parameters for the
device to implement. Control transfers can also request to set serial-port control
signals. All USB devices must support control transfers. Every device must sup-
port control transfers on endpoint zero.
A control transfer has two or three stages. In the Setup stage, the host sends a
request and related information in defined fields. The bmRequestType field
specifies the direction of data flow, the type of request, and the recipient. The
bRequest field identifies the request. The wValue and wIndex fields can contain
information specific to the request. The wLength field indicates the number of
bytes in the Data state that follows. In the Data stage, the host or device sends
data. Some requests dont have a Data stage. In the Status stage, the receiver of
data in the Data stage returns status information. If there is no Data stage, the
device returns the status information.
The other transfer types dont have stages. A class specification or vendor-spe-
cific protocol determines the length of a transfer. Bulk transfers are intended for
situations where the rate of transfer isnt critical. If the bus is very busy, bulk
transfers must wait, but if the bus is otherwise idle, bulk transfers are the fastest
of all. USB virtual COM-port devices typically use bulk transfers for
COM-port data. Interrupt transfers are for devices that must receive or send
Chapter 14
322
data periodically. USB virtual COM-port devices use interrupt transfers to send
status information to the host. Isochronous transfers have guaranteed delivery
time but no error correcting. Real-time audio uses isochronous transfers. With
driver support, USB virtual COM-port devices can use isochronous transfers
for COM-port data in place of bulk transfers.
6TCPUCEVKQPU
Each USB transfer consists of one or more transactions (Table 14-2). Each
transaction contains a token packet, a data packet, and a handshake packet. (An
exception is isochronous transfers, which dont have handshake packets.) Each
packet begins with a packet ID (PID). The function of the PID varies with the
packet type.
The token packet contains the device address and the endpoint number the
transaction is directed to. The token packets PID identifies the type of packet:
SETUP (the first packet in a control transfer), OUT (other host-to-device
packet), IN (device-to-host packet), or SOF (start-of-frame marker).
The data packet contains any data the host or device is sending in the transac-
tion. For control transfers, the transfers stage and the specific request determine
who sends the data. For other transfers, the endpoints direction determines
who sends the data. The PID contains the data-toggle value, explained below.
The receiver of the data packet (or the device if there is no data packet) sends
the handshake packet. The PID contains a code to indicate the status of the
transaction. ACK means success. NAK on an IN transaction means the device
has no data to send. NAK on an OUT transaction means the device was too
busy to accept the data sent. (The host can try again later.) STALL means the
device doesnt support a received request in a control transfer or the endpoints
Table 14-1: Each of USBs four transfer types suit different uses.
6TCPUHGT
6[RG
(GCVWTG 7UG KP 8KTVWCN %1/ 2QTVU
Control Three stages (Setup, Data, Status). Enumeration. Set and get serial-port
parameters. Set serial-port control
signals.
Interrupt Guaranteed maximum latency. Status information.
Bulk Fastest on an otherwise idle bus. COM-port data.
Isochronous Guaranteed transfer rate but no error
detecting.
COM-port data (requires driver
support from vendor).
Inside USB
323
Halt feature is set. High-speed bulk OUT endpoints can also return a NYET
handshake code, which means that the endpoint accepted the data in the cur-
rent transaction but isnt yet ready for more data.
6JG &CVC 6QIING
The data toggle is a data-sequencing value that guards against lost or duplicated
data. If youre debugging a device where it appears that the proper data is trans-
mitting on the bus but the receiver is discarding the data, chances are good that
the device isnt sending or expecting the correct data toggle.
Each endpoint address maintains its own data-toggle value, which alternates
between DATA0 and DATA1. Devices typically store the value in a register bit.
When the host configures a device on power up or attachment, the host and
device each set their data toggles to DATA0. On detecting an incoming data
packet, the host or device compares the state of its data toggle with the data tog-
gle in the received data packet. If the values match, the data packets receiver
toggles its value for the next transaction and returns an ACK. On receiving the
ACK, the data packets sender toggles its value for the next transaction.
The next received packet should contain a data toggle of DATA1, and again the
receiver toggles its bit and returns an ACK. In additional transactions, the data
toggle continues to alternate between DATA0 and DATA1. An exception is
control transfers, where the Status stage always uses DATA1.
If the receiver is busy and returns a NAK, or if the receiver detects corrupted
data and returns no response, the sender doesnt toggle its bit and tries again
with the same data and data toggle.
Table 14-2: Transactions that carry control-transfer requests and other data contain
token, data, and handshake packets.
2CEMGV 6[RG %QPVGPVU 5QWTEG
Token Device address, endpoint
number and direction,
transaction type
Host
Data Transaction-specific data.
Not used in some
transactions.
Host or device
Handshake Status code In transactions with a data packet, the receiver of
the data packet.
In transactions with no data packet, the device.
Not used in isochronous transactions.
Chapter 14
324
Control transfers always use DATA0 in the Setup stage, use DATA1 in the first
transaction of the Data stage, toggle the value in any additional Data-stage
transactions, and use DATA1 in the Status stage. Bulk and interrupt endpoints
toggle the value in every transaction, resetting the data toggle only after a bus
reset or on completing a Set Configuration, Set Interface, or Clear Fea-
ture(ENDPOINT HALT) request.
325
15
7UKPI 5RGEKCNHWPEVKQP
75$ %QPVTQNNGTU
Vendors who offer USB controllers designed specifically for use in virtual COM
ports are Future Technology Devices International (FTDI) (www.ftdi-
chip.com), Prolific Technology, Inc. (www.prolific.com.tw), and Silicon Labora-
tories (www.silabs.com). The chips from these vendors function in similar ways.
These chips are sometimes called USB to UART bridges. This chapter describes
FTDIs chips and driver.
FTDI has several USB interface chips that manage enumeration and other bus
communications completely in hardware. The company provides free USB vir-
tual COM-port drivers for use with the chips. The chips dont require any
USB-specific firmware at all but can store device-specific descriptor values in
EEPROM if desired. A microcontroller or other CPU interfaces to the control-
ler via a serial or parallel interface. The CPU provides COM-port data for the
controller to send to the USB host and retrieves COM-port data received from
the USB host.
Chapter 15
326
+PUKFG VJG %JKRU
The FT232R USB UART and FT245R USB FIFO are USB controllers that
can each appear as a virtual COM port on a USB host. Each chip has a
full-speed USB device port. The FT232R converts between USB and an asyn-
chronous serial interface, and the FT245R converts between USB and a parallel
interface. Both chips have a 128-byte transmit buffer and a 256-byte receive
buffer. The chips use bulk transfers to send and receive COM-port data.
Another controller from FTDI is the FT2232C Dual USB UART/FIFO. The
chip contains two controllers that each support several configurations, includ-
ing two virtual COM ports in one chip.
SGrKa| +PVGrIacG (FT232P)
Figure 15-1 shows the pin functions for the FT232R USB UART. The
USBDM and USBDP pins interface to the USB data lines. TXD and RXD are
the asynchronous serial output and input. Six pins support status and control
signals that correspond to the signals defined in the RS-232 standard. TXD,
RTS#, and DTR# are outputs, and RXD, CTS#, DSR#, RI#, and DCD# are
inputs.
On receiving asynchronous serial data at RXD, the chip sends the data to the
USB host in bulk transfers. On receiving data in bulk transfers from the USB
host, the chip writes the data in asynchronous serial format to the TXD pin.
The asynchronous serial port can transfer up to 300 kbytes/s of data assuming
one Start bit and one Stop bit per byte.
Five additional pins form the configurable CBUS. The pins are programmable
via a utility provided by FTDI and can have these functions:
LED driver that pulses when transmitting or receiving via USB.
Output that goes low when the device is in the USB Suspend state.
General-purpose I/O.
Output that goes high when data is transmitting on TXD. The output can
interface directly to the transmit-enable input of an RS-485 transceiver,
eliminating the need to enable the transmitter using firmware or additional
hardware.
Configurable clock output for driving an external CPU or microcontroller.
Using Special-function USB Controllers
327
The chip can obtain its power from the VBUS line in the USB cable or from an
external supply that connects to the VCC pin. The 3V3OUT pin is a +3.3V
output. VCCIO is an input whose voltage determines the logic levels on the
UART and CBUS pins. The pin can connect to VCC, 3V3OUT, or an external
supply. The chip doesnt require an external oscillator.
To convert the asynchronous serial interface to RS-232, use a Maxim MAX232
or similar interface chip. To convert to RS-485, use an SN75176B or similar
RS-485 transceiver.
Figure 15-1: The FT232R converts between USB and asynchronous serial data.
Chapter 15
328
Para||G| IPVGrIacG (FT245P)
Figure 15-2 shows the pin functions for the FT245R USB FIFO. The USB
interface and power options are the same as for the FT232R. But instead of an
asynchronous serial port, the chip has an 8-bit, bidirectional parallel data bus
with status and control pins that control reading and writing to the buffer. (A
FIFO is a buffer whose contents are read in the same order as they were stored.
In other words, the first byte written to the buffer is the first byte read from the
buffer.) On receiving data on the parallel port, the chip sends the data to the
USB host. On receiving data from the USB host, the chip makes the data avail-
able to read at the parallel port.
The status and control signals are named from the perspective of the external
CPU or microcontroller that interfaces to the chip. The RXF# output is low
Figure 15-2: The FT245R converts between USB and parallel data.
Using Special-function USB Controllers
329
when the CPU can read a byte from the FT245R, and the CPU strobes RD# to
read the byte. In the other direction, the TXE# output goes low when the CPU
can write a byte to the FT245R, and the CPU strobes WR# to write the byte
into the FT245Rs buffer. An external CPU can use a data bus or any spare port
pins to access the FT245Rs parallel port. Parameters such as bit rate and parity
dont apply to this chip.
2TQVQV[RKPI /QFWNGU
FTDIs controllers are available only in surface-mount packages. For prototyp-
ing, FTDI and other vendors offer a variety of converters and development
boards. FTDIs UM232R and UM245R modules (Figure 15-3) each consist of
a circuit board with a controller chip, USB connector, and related circuits
mounted on a 24-pin dual in-line package (DIP).
For interfacing a PC to a microcontrollers serial port, FTDIs TTL-232R serial
converter cable provides a quick solution. Inside the cables USB connector is
an FT232R circuit. The other end of the cable has a 6-pin socket header that
Figure 15-3: For easy prototyping with FTDIs controllers, use the UM232R and
UM245R modules.
Chapter 15
330
provides access to the pins most often used by microcontroller serial interfaces:
data (TX, RX) and flow control (RTS#, CTS#) plus power and ground (VCC,
and GND.
DLP Design (www.dlpdesign.com) offers modules that add PIC microcontrol-
lers, sensors, and other useful components interfaced to FTDIs controllers.
7UKPI VJG %QPVTQNNGTU
Unlike other USB controllers, the FT232R and FT245R arent designed as gen-
eral-purpose devices that can be programmed to use any host driver. Instead,
the devices are intended for use with drivers provided by FTDI.
&TKXGTU
Using the driver provided by FTDI, applications can access a chip as a virtual
COM port or via a vendor-specific API.
In most cases, using an FT232R to convert an RS-232 device to USB requires
no changes to application software. Applications can continue to access the
device as a COM port. An FT245R functions as a virtual COM Port that trans-
fers parallel data.
For applications that dont want to use COM-port functions, the drivers D2XX
DLL defines a vendor-specific API. Both the FT232R and FT245R can use this
API to access the devices. FTDI provides INF files that enable Windows to
match the driver to a device.
#FFKPI 8GPFQTURGEKHKE &CVC
Both controllers contain on-chip EEPROM that can store vendor-specific val-
ues for items such as a Vendor ID, Product ID, serial-number string, other
descriptive strings, and values that specify whether the device is bus- or
self-powered. If there is no EEPROM data for an item, the controller uses a
default value. FTDI provides a utility that programs the information into the
EEPROM. Older versions of the controllers interface to external EEPROMs.
By default, the chips use FTDIs Vendor ID and Product ID. On request,
FTDI will grant the right for your device to use their Vendor ID with a Product
ID that FTDI assigns to you. Of course you can use your own Vendor ID and
Product ID if you wish.
Using Special-function USB Controllers
331
+ORNGOGPVKPI C 8KTVWCN %1/ 2QTV
Figure 15-4 shows a circuit that can serve as a virtual COM port. The USB
controller is an FT232R, which interfaces to a PIC18F4520 microcontroller.
The PIC18F4520s asynchronous serial output (TX) connects to the FT232Rs
asynchronous serial input (RXD), and the FT232Rs asynchronous serial out-
put (TXD) connects to the PIC18F4520s asynchronous serial input (RX).
The example circuit uses the RTS# and CTS# handshaking lines on the
FT232R. These lines can connect to any otherwise unused port pins on the
PIC18F4520. If needed, the FT232Rs other status and control pins can con-
nect to other port pins on the PIC18F4520.
Attach the USB connector to a port on a PC and follow the on-screen instruc-
tions to install FTDIs drivers. (See FTDIs website for a detailed driver-installa-
tion guide.) When the drivers are installed, the Device Manager shows a new
COM port, and the port is ready for use.
In a similar way, any microcontroller with an asynchronous serial port (and
additional port pins if needed) can interface to an FT232R and function as a
USB virtual COM port.
Figure 15-4: This USB virtual COM port uses an asynchronous serial interface to a PIC
microcontroller.
Chapter 15
332
%QPvGrV|Pg IrQm PS-232 VQ US
If you have an existing design that uses RS-232, an FT232R chip can quickly
convert the design to USB. Figure 15-5 shows a circuit that uses RS-232 along
with the same circuit converted to use USB.
In the RS-232 circuit, microcontroller pins connect to a MAX237 interface
chip that converts between TTL and RS-232 logic levels. The example circuit
includes the asynchronous serial interface (TX and RX) plus six generic I/O
pins to support RS-232s status and control signals. A circuit that doesnt use all
of the status and control signals requires fewer drivers and receivers. The sche-
matic doesnt show the MAX237s capacitors or power and ground connections.
In the USB circuit, the microcontroller port pins connect to the corresponding
pins on an FT232R USB UART. Instead of an RS-232 connector, the circuit
has a USB connector that interfaces to the FT232R. The pins for any unimple-
mented signals can remain open on the FT232R or can be tied to a desired
state.
In most cases, a circuit modified in this way requires no changes to device firm-
ware or applications that communicate with the device. Compared to a an
internal RS-232 port, a USB virtual COM port can have these differences at
the host:
Longer delays when reading or writing to RS-232 status and control signals.
Longer delays when changing the parity type (such as in 9-bit networking).
In addition, data throughput at the receiving application can be slow in these
situations:
Receiving less than 62 data bytes. The device prefers sending data packets of
wMaxPacketSize with each packet consisting of 62 data byes plus two status
bytes. A device that has less than 62 data bytes to send will wait for one of
the following events to be true: the chip has accumulated 62 bytes to send,
or the chips internal latency timer has timed out. The default latency time is
16 ms.
Receiving large blocks of data at bit rates of 38.75 kbps or higher. When the
device continuously provides data packets of wMaxPacketSize, the driver
holds the received data and passes it to the application when one of the fol-
lowing is true: the driver has received 4096 bytes, or the latency timer has
timed out. At fast bit rates, the result can be delayed data at the application.
Using Special-function USB Controllers
333
Figure 15-5: To convert a circuit from RS-232 to USB, replace the connections to
RS-232 interface chip(s) with connections to an FT232R USB UART.
Chapter 15
334
For techniques to improve data throughput in the above situations, see FTDIs
application note AN232B-04: Data Throughput, Latency, and Handshaking.
335
16
7UKPI )GPGTKE 75$
%QPVTQNNGTU
Chapter 15 described how to use USB device controllers designed specifically
for virtual COM-port applications. USB virtual COM-port devices can also use
just about any generic full- or high-speed USB device controller with support-
ing firmware. As with any USB device, the descriptors returned by the device
determine which INF file the PC uses to identify drivers for the device. A USB
virtual COM-port device can use drivers provided by Windows or vendor-spe-
cific drivers.
USB virtual COM-port devices that use the drivers provided by Windows
belong to the USB communication devices class. This chapter introduces the
class and its use in virtual COM-port devices.
6JG %QOOWPKECVKQP &GXKEGU %NCUU
The USB communication devices class (CDC) encompasses a variety of
devices, including telephones and medium-speed networking devices. The
telephones group includes generic virtual COM port devices as well as analog
Chapter 16
336
phones and modems, ISDN terminal adapters, and cell phones. Networking
devices include ADSL modems, cable modems, and Ethernet adapters and
hubs. The USB interface in these devices can carry data that uses applica-
tion-specific protocols such as V.25ter/V.250 for modem control or Ethernet
for a network.
&QEWOGPVCVKQP
The CDC specifications are available from the USB-IF (www.usb.org). The
class includes a series of subclasses (Table 16-1). The main CDC specification
defines most of the subclasses. WMC and EEM were defined after the release of
the CDC specification and thus have their own documents. The CDC standard
refers to the V.25ter standard, which evolved from the AT command set for
modems. A more recent edition of the standard is V.250 (www.itu.int). The
OBEX protocol used by some WMC devices is defined in IrDA Object
Exchange (OBEX) Protocol from www.irda.org.
1XGTXKGY
A USB CDC device is responsible for device management, call management if
needed, and data transmission. Device management includes controlling and
configuring the device and notifying the host of events. Call management
includes establishing and terminating telephone calls or other connections.
Some devices dont need call management. Data transmission is the sending
and receiving of application data such as phone conversations, files, or other
data sent over a modem or network.
CDC supports five basic models for communicating. Each model encompasses
one or more subclasses.
The POTS (Plain Old Telephone Service) model is for devices that commu-
nicate via ordinary phone lines and generic COM-port devices. Ethernet
devices that comply with Microsofts USB Remote Network Driver Interface
Specification (NDIS) also use the POTS model.
The ISDN model is for communications via phone lines with ISDN inter-
faces.
The networking model is for communicating via Ethernet or ATM (asyn-
chronous transfer mode) networks.
The wireless mobile communications (WMC) model includes cell phones
that support voice and data communications.
Using Generic USB Controllers
337
Table 16-1: The CDC communication class interface supports a variety of subclasses. In
an interface descriptor, the bInterfaceSubClass field contains the subclass code.
/QFGN
%CVGIQT[
%QFG *GZ 5WD%NCUU 7UG
00 Reserved
POTS 01 Direct Line Control Phone modem with the host
providing any data
compression and error
correction. The device or host
may modulate and demodulate
the modem data.
02 Abstract Control Phone modem with the device
providing any data
compression, error correction,
and data modulation and
demodulation. Also used for
generic virtual COM ports
(serial emulation) and
Ethernet via Remote NDIS
03 Telephone Control Multi-line phone control
ISDN 04 Multi-Channel Control Multiple channels multiplexed
on a network interface
05 CAPI Control COMMON-ISDN-API (CAPI)
commands and messages.
Networking 06 Ethernet Networking Control Ethernet-framed data
07 ATM Networking Control Asynchronous transfer mode
(ATM) functions
WMC 08 Wireless Handset Control Control of logical handset
09 Device Management Management communications
between the handset and host
0A Mobile Direct Line Direct control of the mobile
terminal radio component
0B OBEX Support for Object Exchange
(OBEX) protocol
EEM 0C Ethernet Emulation Efficient transfer of
Ethernet-framed data
0D7F Reserved (future use)
80FE Reserved (vendor specific)
Chapter 16
338
The Ethernet emulation model (EEM) defines an efficient way for devices
to send and receive Ethernet frames.
Note that there are three options for Ethernet devices: the networking model
(Ethernet networking control subclass), the more recently defined Ethernet
emulation model, and the POTs model (abstract control subclass) using the
Microsoft-specific NDIS.
Notifications announce events such as changes in RS-232 status signals.
Devices typically use an interrupt endpoint to send notifications, though the
standard allows using bulk endpoints. Each notification contains an 8-byte
header with a 2-byte field for data. Some notification types include additional
data that follows the header.
Class-specific control requests enable the host to get and set communication
parameters and status and control signals.
For reliable data transfers, many cell phones use the OBEX protocol originally
developed for infrared communications. The WMC specification defines an
OBEX model subclass for this purpose.
&GXKEG %QPVTQNNGTU
Most virtual COM-port devices use one interrupt endpoint address and two
bulk endpoint addresses. The controller must support full or high speed
because low-speed USB doesnt allow bulk transfers. Just about any full- or
high-speed USB device controller will have the needed number of endpoints for
a CDC device.
*QUV &TKXGTU
The usbser.sys driver included with Windows 98 SE and later is suitable for use
with modems and virtual COM ports. Each device that uses usbser.sys must
have an INF file that contains the devices USB Vendor ID and Product ID.
Windows doesnt provide a generic INF file for USB virtual COM-port devices
as it does for other device classes.
For better performance, some devices use vendor-specific drivers that perform
the functions of the serial.sys, usbser.sys, and serenum.sys drivers included with
Windows. Limitations of the Windows drivers in USB virtual COM-port com-
munications include slow performance, lack of support for reading RS-232s
CTS status signal at the host, and limited support for composite devices with
CDC interfaces. OBEX support requires a vendor-provided driver. New Win-
Using Generic USB Controllers
339
dows editions and service packs have brought improvements in usbser.sys, so
using a recent and updated Windows edition is likely to give the best results.
An alternate USB virtual COM-port driver from Walter Oney Software
(www.oneysoft.com) provides enhancements such as the ability for an applica-
tion to retain an open handle when a device is removed and re-attached. Other
sources for USB virtual COM-port drivers include MCCI (www.mcci.com)
and Thesycon Systemsoftware & Consulting (www.thesycon.de).
For telephone functions, the Windows Telephony Application Programming
Interface (TAPI) defines a standard way for applications to control telephone
functions for data, fax, and voice calls. TAPI manages signaling, including dial-
ing, answering, and ending calls and supplemental services such as holding,
transferring, and conference calls.
7UKPI VJG #DUVTCEV %QPVTQN /QFGN
The POTS model encompasses several models that vary in the amount of data
processing the device is responsible for. Each model supports different requests
and notifications. Processing can include modulation and demodulation, error
correction, and data compression.
As Chapter 8 explained, modulation is the process of encoding information by
varying the amplitude, frequency, phase, or a combination of these on a signal
to be transmitted. Demodulation is the reverse process of extracting data from a
modulated signal. In a basic wired, digital interface, the data doesnt use a car-
rier or modulation.
2165 /QFGNU
The most basic POTS model is the direct line control model, which in turn
encompasses the Direct Line (DL), datapump, abstract control, and telephone
control models.
In the DL model, the device converts between the phone lines analog signal
and digital data, and the host handles modulation and demodulation, error cor-
rection, and data compression. The data uses an audio interface. The datapump
model is similar except the device handles modulation and demodulation, and
the data uses a vendor-specific interface.
In the abstract control model, the device handles modulation and demodula-
tion and detects and responds to V.25ter commands. The host or device can
handle error correction and data compression. The model supports requests
Chapter 16
340
and notifications to get and set RS-232 status and control signals and asynchro-
nous port parameters. Data uses a CDC data interface.
Devices that use the abstract control model include virtual COM ports and
Ethernet devices that comply with the Microsoft-specific Remote NDIS proto-
col. The Remote NDIS USB Driver Kit, available from Microsoft, includes
Microsofts specification.
The CDC telephone control model supports call management and notifica-
tions and typically uses other classes such as audio for data and HID for a key-
pad interface.
8KTVWCN %1/ 2QTVU
The function performed by Virtual COM-port devices is sometimes called
serial emulation. The CDC specification says that abstract-control-model
devices understand AT commands. In practice, however, a vitual COM-port
device that doesnt communicate with a modem that uses the commands will
never receive a command and thus doesnt need to support them.
A generic COM-port device that supports the abstract control model and uses
the standard Windows drivers performs these tasks:
Returns descriptors that identify the device as a COM-port device that sup-
ports the abstract control model and common AT commands.
Accepts COM-port data on a bulk OUT endpoint.
Sends COM-port data as needed on a bulk IN endpoint.
Sends SERIAL_STATE notifications as needed on an interrupt IN end-
point.
In addition, most abstract control model devices support class-specific requests
to get and set asynchronous port parameters and to control the RS-232 signals
RTS, DTR, and Break.
The abstract control model requires devices to support the class-specific control
r e que s t s SEND_ENCAPSULATED_COMMAND a nd
GET_ENCAPSULATED_RESPONSE and the class-specific notification
RESPONSE_AVAILABLE. Again, devices that dont use AT commands will
never receive these requests or need to send the notification.
The model doesnt define a way for the host to read the state of RS-232s CTS
status signal. Device firmware can still read CTS on a local asynchronous port
and take appropriate action. For example, if a virtual COM-port device has
data to send to a remote device that hasnt asserted CTS, the virtual COM-port
device can store the data in a buffer and wait to transmit. If the buffer is full,
Using Generic USB Controllers
341
the virtual COM-port device can NAK attempts by the USB host to send data.
When the remote device asserts CTS, the virtual COM-port device can send
the buffered data and begin accepting new data from the host. To use CTS in
this way, the USB host doesnt need to know the signals state.
If you want to use CTS in an unconventional way, such as having a host appli-
cation read a switch state on a device, youre out of luck unless you can define a
vendor-specific command that travels on the same bulk pipes that carry applica-
tion data or use a vendor-specific driver that supports reading CTS.
'ZCORNG (KTOYCTG
Some chip companies provide example firmware for USB virtual COM ports
using generic microcontrollers that contain USB device controllers. Chips with
example code include Atmel Corporations AT89C5131, Microchip Technol-
ogys PIC18F4550, and NXP Semiconductors LPX214x. Example firmware
can be extremely helpful in getting a device up and running. Any of these exam-
ples is a good supplement to the material in this chapter.
4GSWGUVU
Table 16-2 shows required and optional requests for devices that use the
abstract control model.
The two required requests, SEND_ENCAPSULATED_COMMAND and
GET_ENCAPUSULATED_RESPONSE, enable the host to perform proto-
col-specific communications such as issuing an AT command or requesting a
response to a command. If the COM-port device doesnt use AT commands,
the host will never send these requests.
For devices that have asynchronous serial interfaces, other requests monitor and
control the states of RS-232 signals that these devices might use. The
SET_LINE_CODING and GET_LINE_CODING requests set and request
Chapter 16
342
Table 16-2: The abstract control model defines a set of required and optional requests
that travel in control transfers.
4GSWGUV %QFG &GUETKRVKQP 4GSWKTGF!
SEND_ENCAPSULATED_COMMAND 0x00 Issues a command in the
format of the supported
control protocol.
Required but not
used if AT
commands arent
implemented.
GET_ENCAPSULATED_RESPONSE 0x01 Requests a response in
the format of the
supported control
protocol.
Required but not
used if AT
commands arent
implemented.
SET_COMM_FEATURE 0x02 Controls a
communication feature.
Optional.
GET_COMM_FEATURE 0x03 Returns the current
settings for a
communication feature.
Optional.
CLEAR_COMM_FEATURE 0x04 Clears a communication
feature.
Optional.
SET_LINE_CODING 0x20 Sets asynchronous serial
parameters: bit rate,
number of Stop bits,
parity, and number of
data bits.
Recommended
for devices that
support these
parameters.
GET_LINE_CODING 0x21 Requests asynchronous
serial parameters: bit
rate, number of Stop
bits, parity, and number
of data bits.
Recommended
for devices that
support these
parameters.
SET_CONTROL_LINE_STATE 0x22 Set RS-232 signals RTS
and DTR.
Optional.
SEND_BREAK 0x23 Set RS-232 Break signal. Optional.
Using Generic USB Controllers
343
serial port parameters. The line-coding information travels in the data stage of
the request:
In the SET_CONTROL_LINE_STATE request, the host tells the device how
to set the RS-232 control signals RTS and DTR. Two bits in the requests
wValue field contain the information:
The SEND_BREAK request requests the device to send an RS-232 break signal
for the number of milliseconds specified in the wValue field of the request. If
wValue = FFFFh, the device should maintain the break signal until receiving
another SEND_BREAK signal with wValue = 0000h.
The GET_COMM_FEATURE, SET_COMM_FEATURE, and
CLEAR_COMM_FEATURE requests can request, set, or cl ear an
ABSTRACT_STATE data structure used in call management functions and a
COUNTRY_SETTING code. These requests arent relevant for generic
COM-port devices.
1HHUGV (KGNF 5K\G
D[VGU
&GUETKRVKQP
0 dwDTERate 4 Bit rate (bits per second)
4 bCharFormat 1 Stop bits:
0 = 1 Stop bit
1 = 1.5 Stop bits
2 = 2 Stop bits
5 bParityType 1 Parity:
0 = None
1 = Odd
2 = Even
3 = Mark
4 = Space
6 bDataBits 1 Number of data bits (5, 6, 7, 8, or 16)
Y8CNWG $KV &GUETKRVKQP
15..2 Reserved. (Set to zero.)
1 RTS:
0: de-assert (RS-232 negative voltage)
1: assert (RS-232 positive voltage)
0 DTR:
0: de-assert (RS-232 negative voltage)
1: assert (RS-232 positive voltage)
Chapter 16
344
0QVKHKECVKQPU
Abstract control model devices support up to three notifications:
Each notification begins with an 8-byte header:
The device typically returns notifications on the interfaces interrupt IN end-
point. The host doesnt request specific notifications. Instead, the host periodi-
cally issues IN token packets to the endpoint, and the endpoint returns
notifications as available. The endpoint descriptors bInterval field specifies the
maximum latency between IN token packets. An endpoint that has no notifica-
tion to return should return NAK on receiving an IN token packet.
Devi ces that support the abstract control model must support the
RESPONSE_AVAILABLE notification. However, if the device is a generic
COM-port device that doesnt connect to a modem that supports AT com-
mands, the device will have no need for this notification and will return NAK
on every IN token packet to the notification endpoint.
The SERIAL_STATE notification is optional but recommended for devices
with asynchronous serial interfaces. The notification sends the states of RS-232
0QVKHKECVKQP %QFG &GUETKRVKQP 4GSWKTGF!
NETWORK_CONNECTION 0x00 Network or modem
connection status.
Optional.
RESPONSE_AVAILABLE 0x01 Requests the host to issue a
GET_ENCAPSULATED_
RESPONSE request.
Required but not used if
AT commands arent
implemented.
SERIAL_STATE 0x20 State of CD, DSR, Break,
and RI
Optional but
recommended for devices
that support these signals.
(KGNF $[VGU &GUETKRVKQP
bmRequestType 1 0x0A
bNotification 1 Notification code
wValue 2 Notification-specific data
wIndex 2 bInterfaceNumber
wLength 2 Number of data bytes to follow (zero or more)
Using Generic USB Controllers
345
signals and other status information. In the notification, wValue = 0x0000,
wLength = 0x0002, and the returned data has this format:
/CZKOK\KPI 2GTHQTOCPEG
Firmware and host-application programmers can do much to ensure efficient
data transfers for CDC USB virtual COM-port devices.
These are issues that effect the performance of device firmware:
Set wMaxPacketSize in the bulk endpoint descriptors to 64 (recommended)
for full speed or 512 (required) for high speed. These sizes enable transfer-
ring the most data possible in each USB transaction. Plus, if a full-speed
bulk endpoints wMaxPacketSize is less than 64, some USB host controllers
(UHCI type) will schedule no more than one transaction per millisecond for
the endpoint.
To transfer large amounts of data to the host as quickly as possible, use
wMaxPacketSize data packets. Larger packets mean fewer transactions are
needed to transfer the data.
When sending data to the host in multiple transactions, avoiding returning
NAK. Immediately after sending a packet of data, refill the endpoint buffer
and arm the endpoint for the next transaction. For the fastest response, con-
figure the endpoint to cause an interrupt after sending data.
When receiving data from the host, avoid returning NAK. Immediately
after receiving a packet of data, retrieve the data from the endpoint buffer
and arm the endpoint for the next transaction. For the fastest response, con-
figure the endpoint to cause an interrupt on receiving data.
On the host, be aware that writing to control lines or changing the parity type
or other parameters can be slow compared to performing the same operations
$KVU (KGNF &GUETKRVKQP
15..7 -- Reserved.
6 bOverRun Received data has been discarded
due to an overrun in the device.
5 bParity A parity error has occurred.
4 bFraming A framing error has occurred.
3 bRingSignal State of ring indicator (RI).
2 bBreak Break state.
1 bTxCarrier State of data set ready (DSR).
0 bRxCarrier State of carrier detect (CD).
Chapter 16
346
on an internal serial port. On a USB virtual COM-port device, the host must
send a request in a control transfer to perform these actions.
&GUETKRVQTU CPF +0( (KNGU
Figure 16-1 shows descriptors for a typical generic, full-speed COM-port device
that supports the abstract control model. The descriptors identify the device as
a CDC device with two interfaces. The communication interface names the
abstract control model subclass and defines an interrupt IN endpoint for send-
ing notifications. The data interface defines two bulk endpoints for exchanging
COM-port data.
Table 16-3 shows a complete set of descriptors for this type of device. For a
high-speed device, the descriptors are much the same except the bulk endpoints
must use wMaxPacketSize = 512 instead of 64. Devices that support both full
and high speeds have additional descriptors that inform the host about device
characteristics and behavior when using the speed that isnt currently selected.
&GXKEG &GUETKRVQT
In the example device descriptor, bDeviceClass = 02h to indicate that the device
belongs to the communication devices class. Windows uses the values of idVen-
dor and idProduct to identify drivers for the device. All devices with the same
idVendor and idProduct pair should use the same driver on the host.
The descriptor includes three string indexes (iProduct, iManufacturer, and iSer-
ialNumber). Each index references a string descriptor. String descriptors are
optional, but a serial-number string referenced by iSerialNumber is recom-
mended for virtual COM-port devices. If there is no string, the string index is
zero.
%QPHKIWTCVKQP &GUETKRVQT
The configuration descriptor indicates how the device uses power. In the exam-
ple descriptor, the device is bus powered and doesnt support the remote
wakeup feature. The bNumInterfaces field indicates that the configuration has
two interfaces. The wTotalLength field is the number of bytes in the configura-
tion descriptor and all subordinate descriptors, including interface descriptors
and any class-specific and endpoint descriptors for the interfaces, but not
including string descriptors. When the host computer requests a configuration
Using Generic USB Controllers
347
Figure 16-1: A typical CDC device that supports the abstract control model has these
descriptors.
Chapter 16
348
Table 16-3: Example descriptors for a full-speed CDC device (Sheet 1 of 4).
&GXKEG &GUETKRVQT
bLength 0x12 Descriptor size in bytes (18)
bDescriptorType 0x01 Descriptor type (DEVICE)
bcdUSB 0x0200 USB specification release (BCD) (2.00)
bDeviceClass 0x02 Class (communication devices)
bDeviceSubclass 0x00 Subclass (specified at interface level)
bDeviceProtocol 0x00 Protocol (specified at interface level)
bMaxPacketSize0 0x40 Maximum packet size for endpoint zero (64)
idVendor 0x0925 Vendor ID (Lakeview Research; assigned by
USB-IF)
idProduct 0x9050 Product ID (assigned by vendor)
bcdDevice 0x0100 Device release number (BCD, assigned by vendor)
(1.00)
iManufacturer 0x01 Manufacturer string index
iProduct 0x02 Product string index
iSerialNumber 0x03 Serial number string index
bNumConfigurations 0x01 Number of possible configurations
%QPHKIWTCVKQP &GUETKRVQT
bLength 0x09 Descriptor size in bytes (9)
bDescriptorType 0x02 Descriptor type (CONFIGURATION)
wTotalLength 0x0043 Total length of this and subordinate descriptors
bNumInterfaces 0x02 Number of interfaces in this configuration
bConfigurationValue 0x01 Identifier for this configuration
iConfiguration 0x00 Configuration string index (no string defined)
bmAttributes 0x00 Attributes: bus powered, no remote wakeup
bMaxPower 0x32 Maximum power consumption (100 mA)
Using Generic USB Controllers
349
+PVGTHCEG &GUETKRVQT
bLength 0x09 Descriptor size in bytes (9)
bDescriptorType 0x04 Descriptor type (INTERFACE)
bInterfaceNumber 0x00 Interface Number
bAlternateSetting 0x00 Alternate Setting Number
bNumEndpoints 0x01 Number of endpoints in this interface
bInterfaceClass 0x02 Class (communication)
bInterfaceSubclass 0x02 Subclass code (abstract control model (ACM))
bInterfaceProtocol 0x01 Protocol code (V.25ter, common AT commands)
iInterface 0x00 Interface string index (no string defined)
*GCFGT (WPEVKQPCN &GUETKRVQT
bFunctionLength 0x05 Descriptor size in bytes (5)
bDescriptorType 0x24 CS_INTERFACE
bDescriptorSubtype 0x00 Header functional descriptor
bcdCDC 0x0110 CDC specification release number in BCD format (1.1)
#DUVTCEV %QPVTQN /CPCIGOGPV (WPEVKQPCN &GUETKRVQT
bFunctionLength 0x04 Descriptor size in bytes (4)
bDescriptorType 0x24 CS_INTERFACE
bDescriptorSubtype 0x02 Abstract Control Management Functional
Descriptor
bmCapabilities 0x02 Support for the SET_LINE_CODING,
SET_CONTROL_LINE, and GET_LINE_CODING
requests and the SERIAL_STATE notification.
7PKQP (WPEVKQPCN &GUETKRVQT
bFunctionLength 0x05 Descriptor size in bytes (5)
bDescriptorType 0x24 CS_INTERFACE
bDescriptorSubtype 0x06 Union Functional Descriptor
bMasterInterface 0x00 The controlling interface for the union (bInterfaceNumber
of a Communication or Data Class interface in this
configuration)
bSlaveInterface0 0x01 The controlled interface in the union (bInterfaceNumber of
an interface in this configuration)
Table 16-3: Example descriptors for a full-speed CDC device (Sheet 2 of 4).
Chapter 16
350
%CNN /CPCIGOGPV (WPEVKQPCN &GUETKRVQT
bFunctionLength 0x05 Descriptor size in bytes (5)
bDescriptorType 0x24 CS_INTERFACE
bDescriptorSubtype 0x01 Call Management Functional Descriptor
bmCapabilities 0x00 Device doesnt handle call management itself
dDataInterface 0x01 Interface used for call management (bInterfaceNumber of a
data class interface in this configuration).
'PFRQKPV &GUETKRVQT
bLength 0x07 Descriptor size in bytes (7)
bDescriptorType 0x05 Descriptor type (ENDPOINT)
bEndpointAddress 0x82 Endpoint number and direction (2 IN)
bmAttributes 0x03 Transfer type (interrupt)
wMaxPacketSize 0x0008 Maximum packet size (8)
bInterval 0x02 Maximum latency
+PVGTHCEG &GUETKRVQT
bLength 0x09 Descriptor size in bytes (9)
bDescriptorType 0x04 Descriptor type (INTERFACE)
bInterfaceNumber 0x01 Interface Number
bAlternateSetting 0x00 Alternate Setting Number
bNumEndpoints 0x02 Number of endpoints in this interface
bInterfaceClass 0x0A Class (data)
bInterfaceSubClass 0x00 Subclass code (no subclass)
bInterfaceProtocol 0x00 Protocol code (no class-specific protocol)
iInterface 0x00 Interface string index (no string defined)
'PFRQKPV &GUETKRVQT
bLength 0x07 Descriptor size in bytes (7)
bDescriptorType 0x05 Descriptor type (ENDPOINT)
bEndpointAddress 0x81 Endpoint number and direction (1 IN)
bmAttributes 0x02 Transfer type (bulk)
wMaxPacketSize 0x0040 Maximum packet size (64)
bInterval 0x00 Maximum latency
(doesn't apply to full-speed bulk endpoints)
Table 16-3: Example descriptors for a full-speed CDC device (Sheet 3 of 4).
Using Generic USB Controllers
351
descriptor from a device, the device returns the lesser of wTotalLength bytes
and the number of bytes requested.
%QOOWPKECVKQP %NCUU +PVGTHCEG &GUETKRVQTU
The first interface descriptor in the example is the communication class inter-
face descriptor. Every CDC device must have an interface descriptor with
bInterfaceClass = 02h to indicate a communication class interface. This inter-
Endc|nt Descr|tcr
bLength 0x07 Descriptor size in bytes (7)
bDescriptorType 0x05 Descriptor type (ENDPOINT)
bEndpointAddress 0x01 Endpoint number and direction (1 OUT)
bmAttributes 0x02 Transfer type (bulk)
wMaxPacketSize 0x0040 Maximum packet size (64)
bInterval 0x00 Maximum latency/high-speed OUT NAK rate
(doesn't apply to full-speed bulk endpoints)
Str|ng Descr|tcr 0 (Language ID)
0x04 Descriptor size in bytes (4)
0x03 Descriptor type (STRING)
0x0409 Language ID (U.S. English)
Str|ng Descr|tcr 1 (ManuIacturer Str|ng)
0x2C Descriptor size in bytes (44)
0x03 Descriptor type (STRING)
Lakeview Research LLC (String contents, 2 bytes per character)
Str|ng Descr|tcr 2 (Prcduct Str|ng)
0x20 Descriptor size in bytes (32)
0x03 Descriptor type (STRING)
COM Port Device (String contents, 2 bytes per character)
Str|ng Descr|tcr 3 (Ser|a| Number)
0x1A Descriptor size in bytes (26)
0x03 Descriptor type (STRING)
123456789ABC (String contents, 2 bytes per character)
Table 16-3: Example descriptors for a full-speed CDC device (Sheet 4 of 4).
Chapter 16
352
face handles device management and call management. The bInterfaceSubClass
field specifies a communication model. Table 16-1 shows defined values for
bInterfaceSubClass. The bInterfaceProtocol field can specify a protocol sup-
ported by a subclass. Table 16-4 shows defined values for this field.
In the example descriptors, bInterfaceNumber identifies the interface as inter-
face 00h. The bInterfaceClass, bInterfaceSubClass, and bInterfaceProtocol
fields identify the interface as a CDC communication class interface that sup-
ports the abstract control model and common AT commands. To comply with
the specification for the abstract control model, bInterfaceProtocol should be
set to 0x01 (AT commands) even if the device doesnt communicate with
modems and thus doesnt receive AT commands.
Following the communication class interface descriptor is a series of class-spe-
cific descriptors consisting of a header functional descriptor followed by one or
more functional descriptors that provide information about a specific commu-
nication function. Table 16-5 contains defined values for the functional
descriptors.
The example descriptors include four functional descriptors.
The header functional descriptor names the release number of the CDC specifi-
cation the device complies with.
The abstract control management functional descriptor contains a bmCapabili-
ties field that identifies which requests and notifications the interface supports
(Table 16-6).
The union functional descriptor defines a relationship among multiple inter-
faces that form a functional unit. The descriptor designates one interface as the
controlling, or master, interface, which can send and receive messages that
apply to the other controlled, or slave, interface(s) in the unit. For example, a
communication class interface can be a controlling interface for a data class
interface that carries virtual-COM-port data. The interfaces in a unit can also
include interfaces that belong to another USB class such as audio or human
interface device (HID).
In the example descriptors, the union functional descriptor names the current
interface as the controlling interface and names interface 01h as the controlled
interface. When the union has multiple controlled interfaces, the additional
interface numbers follow bSlaveInterface0 in the union functional descriptor.
In the call management functional descriptor, the bmCapabilities field is set to
zero to indicate that the device doesnt handle call management. The
bDataInterface field contains the bInterfaceNumber value for the devices data
Using Generic USB Controllers
353
class interface. However, the value isnt used if the device doesnt handle call
management.
If the communication interface has an interrupt or bulk endpoint for event
notifications, that endpoint has a standard endpoint descriptor. The example
descriptors include an endpoint descriptor for an interrupt IN endpoint. The
wMaxPacketSize field specifies the number of bytes the device sends in each
transaction. The bInterval field specifies the requested maximum period
between IN token packets from the host. At full speed, bInterval equals a num-
ber of milliseconds. At high speed, bInterval can range from 1 to 16, and the
requested maximum interval in milliseconds equals the following value:
2
bInterval-1
* 0.125
Thus, for a 1-ms maximum interval at full speed, bInterval =1, and for a maxi-
mum interval of 1 ms at high speed, bInterval = 4.
&CVC %NCUU +PVGTHCEG &GUETKRVQTU
In addition to the communication class interface, a communication device typ-
ically has an interface to carry application data. A CDC data class interface can
perform this function. If bInterfaceSubClass in the communication class inter-
face specifies the abstract control model, the CDC specification requires a data
class interface.
In the descriptor for a data class interface, bInterfaceClass = 0Ah. The interface
can have bulk or isochronous endpoints for carrying application data. Each
endpoint has a standard endpoint descriptor. Instead of a data class interface,
some CDC devices use other class or vendor-specific interfaces to carry applica-
Table 16-4: In the interface descriptor for a communication device, the
bInterfaceProtocol field can indicate a protocol the communications model
supports.
%QFG &GUETKRVKQP
00h No class-specific protocol required
01h AT commands (specified in ITU V.25ter)
02h06h AT commands for WMC devices
07hFDh Reserved for future use
FEh External protocol for WMC devices
FFh Vendor specific
Chapter 16
354
tion data. For example, a telephone device might use an audio interface to send
and receive voice data.
The example descriptors include a data class interface with two bulk endpoint
addresses. In the data class interface descriptor, bInterfaceNumber identifies the
interface as interface 01h. The bInterfaceClass field identifies the interface as a
CDC data class interface. The interface has a bulk IN endpoint for sending
data to the host and a bulk OUT endpoint for receiving data from the host.
The wMaxPacketSize field specifies the maximum number of bytes an endpoint
can transfer in one transactions data packet. Endpoints in full-speed devices
typically use the maximum allowed values of 64. Endpoints in high-speed
devices must use 512. A transfer larger than wMaxPacketSize bytes uses multi-
ple transactions. A short packet is a data packet that contains less than wMax-
Table 16-5: A Functional descriptor consists of a Header functional descriptor
followed by one or more function-specific descriptors.
D+PVGTHCEG5WD%NCUU (WPEVKQPCN &GUETKRVQT 6[RG
00h Header
01h Call Management
02h Abstract Control Management
03h Direct Line Control Management
04h Telephone Ringer
05h Telephone Call and Line State Reporting Capabilities
06h Union
07h Country Selection
08h Telephone Operational Modes
09h USB Terminal
0Ah Network Channel Terminal
0Bh Protocol Unit
0Ch Extension Unit
0Dh Multi-channel Management
0Eh CAPI Control Management
0Fh Ethernet Networking
10h ATM Networking
11h18h WMC Functional Descriptors
19hFFh Reserved
Using Generic USB Controllers
355
PacketSize bytes. A zero-length packet (ZLP) is a short packet that contains a
PID and error-checking bits but no data bytes at all.
When a host requests data from a CDC devices bulk IN endpoint, the end-
point can return the requested number of bytes or fewer. In each transaction
that makes up a transfer, each data packet except the last contains wMaxPacket-
Size bytes. If the number of bytes in a transfer isnt a multiple of wMaxPacket-
Size, the last data packet is a short packet.
If the number of bytes returned is an exact multiple of wMaxPacketSize, the
endpoint returns all of the data in wMaxPacketSize packets and then responds
to a received IN token packet with a ZLP. The ZLP tells the host that the end-
point has no more data to send in the transfer. The host then knows the transfer
is complete. Even if the host has requested and received an even multiple of
wMaxPacketSize, the host may send another IN token packet, and the device
should respond with a ZLP. Dont forget to implement this final ZLP in device
firmware.
5VTKPI &GUETKRVQTU
String descriptors are optional but can be useful for storing serial numbers and
providing text to display to users.
The example descriptors include string descriptor zero and three string descrip-
tors indexed by the device descriptor. String descriptor zero contains the lan-
guage ID for U.S. English. Every device that has one or more string descriptors
must have string descriptor zero. The strings in U.S. English use two bytes per
Table 16-6: The bmCapabilities field of the Abstract Control Management
descriptor tells the host what requests and notifications the device supports.
$KV 5WRRQTVGF 4GSWGUVU CPF 0QVKHKECVKQPU 9JGP $KV KU 5GV VQ
7..4 Reserved. (Reset to zero.)
3 NETWORK_CONNECTION notification
2 SEND_BREAK request
1 GET_LINE _CODING request
SET_CONTROL_LINE_STATE request
SET_LINE_CODING request
SERIAL_STATE notification
0 CLEAR_COMM_FEATURE request
GET_COMM_FEATURE request
SET_COMM_FEATURE request
Chapter 16
356
character and are not null terminated. A string descriptor can contain up to 126
U.S. English characters.
As Chapter 3 explained, serial numbers are recommended for USB virtual
COM-port devices to enable a device to retain its COM-port number when
moved to a different port.
6JG +0( (KNG
Every USB virtual COM-port device must have an INF file that contains
device-specific information to enable Windows to identify drivers to load for
the device. The INF file is required even if the device uses only the drivers pro-
vided with Windows (rather than vendor-specific drivers).
Listing 16-1 shows an example INF file for a virtual COM-port device that uses
the Windows CDC drivers. To adapt the example INF file for your device,
make the following changes:
1. In the Version section, replace LAKEVIEW with a string that describes your
company.
2. In the Manufacturer section, replace Lakeview with a string that describes
your company.
3. Rename the Lakeview section to the string used in #2 above.
4. In the (renamed) Lakeview section, replace 0925 with your devices USB
Vendor ID and replace 9010 with your devices USB Product ID with both val-
ues expressed in hexadecimal. These values must match the values in idVendor
and idProduct in the device descriptor returned by the device.
5. In the Strings section, replace both instances of Lakeview Research LLC
with your companys name or other provider and manufacturer information as
appropriate.
For more about creating INF files, see Microsofts documentation or USB
Complete.
%QORQUKVG &GXKEGU
A USB composite device is a device that performs multiple, independent func-
tions. For example, a single device can function as both a virtual COM port
and as a mass-storage device. The host computer loads a driver for each func-
tion, and each function operates independently.
For most USB classes, creating composite devices is straightforward. Each func-
tion has an interface descriptor with bInterfaceClass equal to a class code. In the
device descriptor, bDeviceClass = 00h to tell the host computer to look for class
Using Generic USB Controllers
357
; Windows INF File for a USB virtual COM-port device
; in the communication devices class (CDC)
[Version]
Signature="$Windows NT$"
Class=Ports
ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318}
Provider=%LAKEVIEW%
LayoutFile=layout.inf
DriverVer=08/17/2001,5.1.2600.0
[Manufacturer]
%MFGNAME%=Lakeview
[DestinationDirs]
DefaultDestDir=12
DriverCopyFiles=12
[Lakeview]
%DESCRIPTION%=DriverInstall, USB\VID_0925&PID_9010
[DriverInstall.nt]
include=mdmcpq.inf
CopyFiles=DriverCopyFiles
AddReg=DriverInstall.nt.AddReg
[DriverCopyFiles]
usbser.sys
[DriverInstall.nt.AddReg]
HKR,,DevLoader,,*ntkern
HKR,,NTMPDriver,,usbser.sys
HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider"
[DriverInstall.nt.Services]
include=mdmcpq.inf
AddService=usbser, 0x00000002, DriverService
Listing 16-1: An INF file for a generic USB virtual COM port using the CDC
drivers (Sheet 1 of 2).
Chapter 16
358
codes in the interface descriptors. When a single function uses multiple inter-
faces, a device can use an interface association descriptor or a class-specific
method to identify the interfaces associated with a function.
%&% &KHHGTGPEGU
CDC differs from other classes because it defines a code for bDeviceClass (02h)
in the device descriptor. In theory, this difference shouldnt present a problem
for composite devices. The union functional descriptor defines which interfaces
belong to the CDC function, and the host can assume that any additional
interface descriptors belong to independent functions. In practice, however, the
drivers in earlier Windows editions arent always capable of loading the drivers
correctly for composite devices that contain CDC functions.
+PVGTHCEG #UUQEKCVKQP &GUETKRVQT
Under Windows Vista, a composite device with a CDC virtual COM-port
function can use an interface association descriptor (IAD) to identify the inter-
faces that belong to the CDC function. Windows can then identify and load
the correct drivers for all of the composite devices functions. Figure 16-2
shows the descriptors for an example composite device of this type.
Table 16-7 shows the contents of the IAD as defined in the USB 2.0 engineer-
ing change notice (ECN) Interface Association Descriptors. To inform the host
that the device uses an IAD, the device descriptor should use the values in Table
[DriverService]
DisplayName=%SERVICE%
ServiceType=1
StartType=3
ErrorControl=1
ServiceBinary=%12%\usbser.sys
[Strings]
LAKEVIEW="Lakeview Research LLC"
MFGNAME="Lakeview Research LLC"
DESCRIPTION="USB COM Port"
SERVICE="USB COM-port Driver"
Listing 16-1: An INF file for a generic USB virtual COM port using the CDC
drivers (Sheet 2 of 2).
Using Generic USB Controllers
359
Device
Configuration
Interface
(CDC Communication)
Interface
(CDC Data)
Header
Functional
Abstract Control
Model
Functional
Union
Functional
Call Management
Functional
Endpoint
(Interrupt IN)
Endpoint
(Bulk OUT)
Endpoint
(Bulk IN)
USB standard
descriptor type
Class-specific
descriptor type
Interface
(HID)
Endpoint
(Interrupt IN)
HID
Endpoint
(Interrupt OUT)
Interface Association
Figure 16-2: A composite device that contains a CDC function and a HID function
can use an interface association descriptor to specify the CDC interfaces.
Chapter 16
360
16-8. The document USB Interface Association Descriptor Device Class Code
and Use Model defines these values. Both documents are available from usb.org.
Note that bDeviceClass = EFh. A composite device with a CDC function and
IAD doesnt use bDeviceClass = 02h.
&GUETKRVQTU
Table 16-9 shows example device, configuration, and interface association
descriptors for a composite device that includes a CDC function with two
interfaces and an additional function with one interface. In the device descrip-
tor, the bDeviceClass, bDeviceSubClass, and bDeviceProtocol codes indicate
that the device contains an IAD. In the configuration descriptor, bNumInter-
faces specifies that the configuration contains a total of three interfaces.
In the IAD, bFirstInterface equals the bInterfaceNumber of the CDC commu-
nication interface and bInterfaceCount = 02h to indicate that the CDC func-
tion has two interfaces. The bFunctionClass, bFunctionSubClass, and
bFunctionProtocol fields are set to match the values of bInterfaceClass, bInter-
faceSubClass, and bInterfaceProtocol in the CDC functions first interface (the
communication interface).
Following the IAD are descriptors for the associated interfaces and their subor-
dinate descriptors. For a CDC virtual COM port, the interfaces are the CDC
communication and data interfaces. Following these interfaces are the interface
descriptor for the third, independent interface and its subordinate descriptors.
Table 16-7: An Interface Association Descriptor can specify multiple interfaces
associated with a single function.
1HHUGV (KGNF &GUETKRVKQP
0 bLength Size of the descriptor in bytes (08h)
1 bDescriptorType INTERFACE ASSOCIATION (0Bh)
2 bFirstInterface bInterfaceNumber of the first interface associated with
the function
3 bInterfaceCount Number of contiguous interfaces associated with the
function
4 bFunctionClass USB class code or FFh for vendor specific class
5 bFunctionSubClass Subclass code
6 bFunctionProtocol Protocol code
7 iFunction Index of string descriptor describing the function
Using Generic USB Controllers
361
Under Windows XP SP2, composite devices that use the usbser.sys driver can
use an IAD only if the host has a hotfix from Microsoft (http://sup-
port.microsoft.com/kb/918365). The hotfix replaces the usbser.sys driver with an
updated version.
Earlier Windows editions dont support the IAD and dont have a mechanism
for loading the drivers correctly for a composite device that uses usbser.sys and
contains a CDC virtual COM port with multiple interfaces. Alternatives are to
use a vendor-specific driver or create a compound device with an embedded
hub that connects to multiple devices.
A third option for earlier Windows editions is to use the communication class
interface for data. According to the CDC specification, the communication
class interface doesnt handle COM-port data. However, a host driver is free to
be more tolerant and allow devices that use one interface for both notifications
and data.
The Windows XP driver allows composite devices that include a CDC virtual
COM-port function with a single interface plus a second, independent inter-
face. In the device descriptor, bDeviceClass = 00h. The CDC interface has an
interrupt IN endpoint for notifications plus two bulk endpoints for data. The
device has no CDC data class interface descriptor, no CDC union functional
descriptor, and no interface association descriptor.
On seeing bDeviceClass = 00h, Windows looks for class codes in the interface
descriptors and loads drivers for the CDC function and the independent func-
tion. Devices of this type dont fully comply with the CDC specification so
compatibility with other operating systems or other Windows editions isnt
guaranteed.
+0( (KNG HQT C %&% (WPEVKQP
When a composite device includes a CDC communication interface for a vir-
tual COM port, the interface must have an INF file that is similar to Listing
16-1s INF file except that the file specifies an interface in a device rather than
Table 16-8: These values in the device descriptor inform the host that the device
contains an IAD.
(KGNF 8CNWG &GUETKRVKQP
bDeviceClass EFh Miscellaneous device class
bDeviceSubClass 02h Common class
bDeviceProtocol 01h Interface Association Descriptor
Chapter 16
362
the device as a whole. The text &MI_xx identifies the interface, with xx equal
to the interface number.
This INF section specifies vendor ID 0925h, product ID 9010h, and interface
00h:
[Lakeview]
%DESCRIPTION%=DriverInstall, USB\VID_0925&PID_9010&MI_00
The other independent interfaces in the device may require INF files, or they
may use INF files provided with Windows depending on the interface type.
Using Generic USB Controllers
363
Table 16-9: Example device, configuration, and interface association descriptors for a
composite device that includes a CDC function (Sheet 1 of 2).
&GXKEG &GUETKRVQT
bLength 0x12 Descriptor size in bytes (18)
bDescriptorType 0x01 Descriptor type (DEVICE)
bcdUSB 0x0200 USB specification release (BCD) (2.00)
bDeviceClass 0xEF Class (miscellaneous)
bDeviceSubclass 0x02 Subclass (common)
bDeviceProtocol 0x01 Protocol (IAD)
bMaxPacketSize0 0x40 Maximum packet size for endpoint zero (64)
idVendor 0x0925 Vendor ID (Lakeview Research; assigned by USB-IF)
idProduct 0x9055 Product ID (assigned by vendor)
bcdDevice 0x0100 Device release number (BCD, assigned by vendor) (1.00)
iManufacturer 0x01 Manufacturer string index
iProduct 0x02 Product string index
iSerialNumber 0x03 Serial number string index
bNumConfigurations 0x01 Number of possible configurations
%QPHKIWTCVKQP &GUETKRVQT
bLength 0x09 Descriptor size in bytes (9)
bDescriptorType 0x02 Descriptor type (CONFIGURATION)
wTotalLength 0x006B Total length of this and subordinate descriptors
bNumInterfaces 0x03 Number of interfaces in this configuration
bConfigurationValue 0x01 Identifier for this configuration
iConfiguration 0x00 Configuration string index (no string defined)
bmAttributes 0x00 Attributes: bus powered, no remote wakeup
bMaxPower 0x32 Maximum power consumption (100 mA)
Chapter 16
364
+PVGTHCEG #UUQEKCVKQP &GUETKRVQT
bLength 0x08 Descriptor size in bytes (8)
bDescriptorType 0x0B Descriptor type (IAD)
bFirstInterface 0x00 First associated interface
bInterfaceCount 0x02 Number of associated interfaces
bFunctionClass 0x02 Class code (communication)
bFunctionSubClass 0x02 Subclass code (abstract control model)
bFunctionProtocol 0x02 Protocol code (V.25ter)
iFunction 0x01 String descriptor index
Table 16-9: Example device, configuration, and interface association descriptors for a
composite device that includes a CDC function (Sheet 2 of 2).
365
+PFGZ
16550 263
60-Hz noise 133
74HCT logic 49
74HCT541 58
75ALS180B 130
#
A7 Engineering, Inc. 153
abstract control management functional
descriptor 349, 352
abstract control model 337, 339
virtual COM ports and 340362
AC termination 123125
adapters
RS-232 6365
See also converters
address
COM port 3637
network 272280
alarm wire. See twisted-pair cable
analog ground 71
analyzers 78
ANSI encoding 21
Application Settings, .NET 186188
ASCII Hex 2223
ASCIIEncoding 173176
asserted, defined 47
asynchronous protocol, defined 11
AT commands 24, 336
USB virtual COM port and 341, 344
AT89C5131 341
ATM networking control model 337
Atmel Corporation 341
autodetecting the bit rate 1718
$
B&B Electronics Manufacturing Company
63, 89
back termination 122
BACnet 270
balanced line 8083
Basic language (embedded). See code example
(embedded); PICBASIC Pro
Basic language (PC). See Visual Basic
Basic Stamp 14, 54
baud rate 13
See also bit rate
BAUDCON 234235, 236, 242
biasing, RS-485 127131
BinaryReader and BinaryWriter 177182
bInterval 353
bit rate
autodetecting 1718
defined 13
setting (.NET) 221223
setting (PIC18F4520) 234237
transmission line effects and 107
BITBUS 271
Bluetooth 153
bmRequestType 321
Break signal 46, 210, 233
breakout box 7677
BreakState 210
bRequest 321
bridge chip, USB to UART 325
buffers 2728
bus topology 125
multiple RS-485 buses 141143
366
BusyUSART 244
byte, defined 18
byte-order mark 21
BytesToRead 191
%
C#. See code example (PC); SerialPort class
C18 compiler. See code example (embedded);
MPLAB C18 compiler
cable
Category 5/6 133
delay 108109
IBM Type 1 133
RS-232 6768
RS-485 131134
twisted pair 131134
call management functional descriptor 350
capacitive coupling 132
CAPI control model 337
carriage return. See CR
carrier detect 45, 46
carrier frequency 146
carrier-present carrier-absent modulation 146
Category 5/6 cable 133
CBUS 326
CD 45, 46
CDC. See communication devices class
CDHolding 210
ceramic resonator 264
characteristic impedance 112113
charge pump 52
checksum 29
.NET 218
clear to send. See CTS
CLEAR_COMM_FEATURE 342, 343
clock
accuracy 1516
asynchronous interfaces and 11
synchronous interfaces and 12
Close method 161163
CMOS logic 49
code example (embedded)
ASCII Hex convert 299303
configure port 241243
error detect 246247
flow control 258262
interrupt 254256
read multiple bytes 249251
receive data 244245
send data 243244
task loop 247249
terminating character 251253
USB virtual COM port 341
code example (PC)
ASCII Hex convert 299303
character encoding 174176
close a port 161163
detect pin change 211213
error handling 214218
event-driven receive 194197
find ports 156
get RS-485 parallel resistance 8687
line, short or long 109111
manage communications 218221
9-bit data 275280
open a port 156160
parameters, save 187188
parameters, set 221223
poll for data 190193
read bytes 168169, 199202
read text 171173, 197202
RS-485 network 287316
RS-485 termination resistor 129
Stream object 176186
write buffer management 207208
367
write bytes 168169
write text 164166, 170171
write without blocking 203207
code page 21
code unit 20
collision-detecting protocol 269
COM port
address 3637
autodetecting 18
driver tab 34
examples 5
identifying 39
number 33, 40
parameters, save 186188
parameters, set 33
redirector software 38, 39
symbolic link name 40
See also SerialPort class
common-impedance coupling 132
COMMON-ISDN-API 337
common-mode voltage 135137
communication class interface descriptor 351,
352
communication devices class
abstract control model 339362
device controllers 338
host driver 338339
overview 336338
composite device 356362
conductive coupling 132
CONFIG1H 235
configuration descriptor 346, 348
configuration, USB 318
connectors
RS-232 6163
RS-485 134
contention, line 88
control transfer 321, 324
CDC abstract control model 342
converters
RS-232 4855
RS-485 9196
CPCA modulation 146
CR 170, 251253
See also WriteLine; ReadLine
CRC 30
crosstalk 132
crystal, timing 1516
CTNet 271
CTS 26, 4546
virtual COM port support 340
CtsHolding 210
cyclic redundancy check 30
&
Dallas Semiconductor 53
data class interface descriptor 353
data set ready (DSR) 45
data terminal ready (DTR) 45
datapump model 339
DataRdyUSART 245
DataReceived 194197
DataSource property 221223
DB-9 connector 62
DCE
adapter 64
defined 44
DE-9 connector 62
debugging
networks 271
tools 7678
delay, cable 108109
delay, driver enable 97103
delegate, .NET 189190
368
descriptors. See USB: descriptors; specific
descriptor
devcon 38
development boards 65
devguid.h 39
device descriptor 346, 348
device interface GUID 40
device management model 337
Device Manager 3136
device setup GUID 39
differential line 8083
digital ground 71
diode, parasitic 136
Direct Line (DL) model 339
direct line control model 337, 339
Dispose 161
DLP Design 330
driver enable, RS-485 96103
driver, hardware. See specific interface
driver, Windows 38
USB CDC 338339
USB FTDI 330
DS16F95 90
DS275/6 53
DS89C420 275
DSR 45
DsrHolding 210
D-sub connector 62
DTE
adapter 64
defined 44
DTR 45
DtrEnable 210
duplex 7
'
earth ground 72
EEM 337, 338
EEPROM, and FTDI chips 325
EIA 43
EIA/TIA-232. See RS-232
electromagnetic
coupling 133
interference 112
Electronics Industries Association 43
embedded system, defined 6
EMI 112
End of Transmission code 273
endpoint descriptor
bulk 350351, 354
interrupt 350, 353
enumeration 318
error detecting
ErrorReceived event 214218
exceptions 214
See also parity
ErrorReceived 214218
Ethernet
interface for serial port 37
vs. other interfaces 3
Ethernet emulation model 337, 338
Ethernet networking control model 337
EUSART. See UART; PIC18F4550
event-driven programming 28
DataReceived (SerialPort) 194195
ErrorReceived (SerialPort) 214218
PinChanged (SerialPort) 211213
See also .NET; interrupt
exception
InvalidOperationException 166
TimeoutException 161
UnauthorizedAccessException 159
(
fail safe, RS-485 127
369
Federal Communications Commission (FCC)
149150
fiber optics 76
field strength 149
FireWire 3
flow control 2627
defined 7
.NET and 209213
PIC18F4520 256262
See also Handshake; RTS/CTS;
DTR/DSR; Xon/Xoff
FOSC 234, 235237
frequency shift key (FSK) modulation 147
FT2232C 326
FT232R 326327
RS-485 and 103
FT245R 328329
FTDI
D2XX DLL 330
EEPROM 330
host driver 330
prototyping modules 329330
Vendor ID and Product ID 330
See also specific chip
full duplex
defined 7
RS-485 circuit 9192
functional descriptor 349350, 352353
Future Technology Devices International. See
FTDI
Fyre-Fly 148
)
gas-discharge tube 69
gender changer 64
GET_COMM_FEATURE 342, 343
GET_ENCAPSULATED_RESPONSE 340,
341, 342
GET_LINE_CODING 341, 342
getcUSART 245
GetPortNames 221
getsUSART 251
GHI Electronics 9
Globally Unique Identifier 3940
GND
pin (RS-232) 45
See also ground
GPIB. See IEEE-488
GPS 25
Greenleaf Software 41
ground
loop 72
pin, RS-232 45
RS-232 shield 46
RS-485 134137
types 7075
GUID 3940
*
half duplex
defined 7
RS-485 circuit 9293
Handshake
SerialPort (.NET) 209213, 223
See also flow control; RTS/CTS;
DTR/DSR; Xon/Xoff
harmonics 107
hash value 30
.NET 218
Hayes modem 24
header functional descriptor 349, 352
High Tech Horizon 271
Hilgraeve 41
host drivers 318
HSER_BAUD 241
HSER_CLROERR 246
370
HSER_RCSTA 241
HSER_SPBRG 242
HSER_SPBRGH 242
HSER_TXSTA 241, 242
hserin 245, 249
hserout 243
hub-and-spoke topology 125
HyperACCESS 41
Hyperterminal 41
+
I2C vs. other interfaces 3
IAD 360, 364
IBM Type 1 cable 133
IEEE 802.11b 154
IEEE 802.15.4 153
IEEE-1394b 3
IEEE-488 3
impedance, characteristic 112113
InBufferCount 209
inductive coupling 132
INF file 40, 318
CDC 356
CDC function in composite device 361
362
FTDI controllers and 330
usbser.sys and 338
infrared 148149
INTCON 238, 257
INTCON2 257
INTCON3 258
Intel Hex 30
INTERBUS 271
interface association descriptor 360, 364
interface descriptor
communication class 349, 351, 352
data 350
International Organization for
Standardization 44
International Telecommunication Union 24,
44, 336
interrupt
IRQ line 37
PIC18F4520 253254, 257258
Intersil Corporation 49, 89
InvalidOperationException 166
IPR1 238
IrDA 149
IRQ 37
ISDN model 336, 337
ISO 44
isolation
RS-232 7076
RS-485 137139
ITU 24, 44, 336
,
Jameco Electronics 63
-
Kermit 30
.
LCD modules 9
LED for debugging 271272
legacy ports 39
LF 170, 251253
See also code example (PC): terminating
character: WriteLine; ReadLine
LIN bus 233
line contention 88
line feed. See LF
Linear Technology 49, 89, 90
See also specific chip (LTC_)
Linx Technologies, Inc. 151
371
List(of T) 199202
Local Interconnect Bus 233
logic analyzer 78
long line 106112
LPX214x 341
LSb, defined 12
LTC1385 58
LTC168 90
LTC1685 84
LTC2859 90
lumped system 106
/
magnetic coupling 132
Mark
logic state 47
See also parity
marshaling 195197
MarshallSoft Computing 41
master/slave protocol 269
MAX13444E 90
MAX1480A 90
MAX1483 90
MAX1488E/9E 53
MAX232 50
MAX233 50, 75
MAX250/1 53
MAX3080 111, 130
MAX3082 90
MAX3083 112
MAX3085 90
MAX3088 87
MAX3100 263265
MAX3160E 95
MAX3162E 95
MAX3212 52
MAX3218 52
MAX3221 50
MAX3224 52
MAX3225 50
MAX3314E 53
MAX3386E 52
MAX3485 90
MAX3490 112
MAX481 90
MAX483 90, 112
MAX485 112
MAX491 130
MAX667 57
MAX689 57
MAX770 57
Maxim Integrated Products 8990
regulators 57
RS-232 chips 4953
See also specific chip (MAX_, DS_)
MaxStream, Inc. 151, 153
MCCI 339
Microchip Technology 341
C compiler
PICDEM 2 Plus 66
See also specific chip (PIC_, MSC_) 18
microcontroller
defined 6
development boards 65
See also USB virtual COM port; specific
manufacturer
Microsoft
Remote NDIS 336
See also .NET; Windows
Microwire vs. other interfaces 3
MIDI vs. other interfaces 3
mobile direct line model 337
Modbus 271
modem 2425
modem, null 65
372
modular connector 63
modulation 146147
Motorola S-Record 30
MPLAB C18 compiler 241
See also code example (embedded)
MSb, defined 19
MSC2120 149
msports.inf 40
multi-channel control model 337
multidrop interface 80
MultiportSerial class 39
My.Computer.Ports 159
0
National Semiconductor 49, 89, 90, 136
NDIS 336
.NET
Application Settings 186188
delegate 189190
events, order of 280
hash value 218
List(of T) 199202
marshaling 195197
My.Computer.Ports 159
See also SerialPort class 155
network
addressing 272280
debugging 271
message format 273274
protocol, example 283287
protocols 268271
tasks 267268
topologies 125127
network cable. See twisted-pair cable
NETWORK_CONNECTION notification
344
Networking model, CDC 336, 337
NewLine 170
9-bit format 274280
NMEA 0183 25
node 267
noise
cable 132133
margin, RS-232 47
margin, RS-485 84
notification, CDC 344345
ntddser.h 39, 40
null modem 65
NXP Semiconductors 341
1
OBEX 336, 338
model 337, 338
On state, RS-232 47
on/off Key modulation. See OOK modulation
On-The-Go 319
OOK modulation 146147
IR and 148
RF and 152
open circuit biasing, RS-485 127129
Open method 156160
OpenSerialPort 159
OpenUSART 242243
optoisolation 7576
OSC (PICBASIC) 241
OSCCON 235, 236
oscilloscope 78
OSCTUNE 235, 236
OTG 319
2
Parallax, Inc. 14
Basic Stamp 14, 54
RF modules 152
373
parallel port
USB virtual COM port with 328329
vs. other interfaces 3
parallel termination 120
parameters, port
setting 33
See also code example (embedded); code
example (PC)
parity
defined 12
9-bit data and 275
PC Card (PCMCIA) 5
PIC microcontroller
FTDI chips and 330
See also Microchip Technology; specific
chip (PIC_)
PIC18F4520
bit rate 18, 234237
EUSART 230237
features 230
flow control 256262
interrupt 237241, 253254, 257258
9-bit data 274275
See also code example (embedded) 230
PIC18F4550 341
PIC18F8722 263
PICBASIC 241
See also code example (embedded)
PICDEM 2 Plus 66
PIE1 239
PinChanged 211213
PIR1 238
Plug and Play 39
pnpports 38, 40
polling 28
.NET 190193
port power 5558
Portmon utility 78
PortName property 157
Ports class, Windows 39
POTS model 336
abstract control model 337, 339, 340362
datapump model 339
Direct Line (DL) model 339
direct line control model 339
telephone control model 340
See also abstract control model
power from port 5558
power supply, ground and 7275
primary/secondary protocol 269
PROFIBUS 271
Prolific Technology, Inc. 325
propagation rate 108109
protective ground 71
putcUSART 244
putrsUSART 244
putsUSART 244
4
R.E. Smith 89
Rabbit Semiconductor Inc. 263
radio frequency communications. See RF
RCON 238
RCREG 231, 238, 258
RCSTA 233234
RD 45
Read (SerialPort class) 168169, 171
ReadBufferSize 197
ReadByte (SerialPort class) 169
ReadChar (SerialPort class) 172
ReadExisting (SerialPort class) 171
ReadFile 41
ReadLine (SerialPort class) 171
ReadTimeout 160, 190
ReadTo (SerialPort class) 171
374
ReadUSART 245
received data pin. See RX
ReceivedBytesThreshold 161
receiver. See specific interface
redirector software 39
reflections 117120
registry, system 39
Remote NDIS 336
repeater, RS-485 141
request to send (RTS) 26, 4546
request to send. See RTS
resistor
biasing 127131
in ground wire 137
terminating 8587, 114125
resonator, ceramic 264
resources, COM port 3637
RESPONSE_AVAILABLE notification 340,
344
Reynolds Electronics 148
RF
interfaces 152154
regulations 149150
unlicensed bands 150151
ribbon cable 68
ring indicator (RI) 45, 46
ring topology 125127
ringing, on short line 122123
rise time 107, 111112
RJ-11 63
RJ-45 63
RS-232
adapters 6365
cable 6768
chips 4953
connectors 6163
converters, RS-485 9396
converters, TTL/CMOS 4855
converting to USB 332334
shield ground 46
short circuit and 46
signals 45
specifications 4344
timing limits 48
voltages 4647
vs. other interfaces 3, 59
RS-422 80, 81
RS-423 58, 59
RS-485
advantages 7980
biasing, line 127131
cable 131134
chips 8990
connectors 134
converters, RS-232 9396
converters, TTL/CMOS 9196
current 8587
driver enable 96103
driver, selecting 111112
fail safe 127
flow control and 80
FT232R and 326
full duplex circuit 9192
guidelines 105
half duplex circuit 9293
internal protection 8889
isolation 137139
multiple buses 141143
network example circuit 281283
PCs and 91
speed 87
termination 8587
voltages 8485
vs. other interfaces 3, 80, 81
RSR 231, 258
375
RTS 26, 4546
RtsEnable 210
RX 45
RXD 45
5
SAE J1708 271
safety ground 71
Sax.net 41
Scott Edwards Electronics 9
SEND_BREAK 342, 343
SEND_ENCAPSULATED_COMMAND
340, 341, 342
serenum.sys 38, 39
serial emulation 340
serial interface engine 319
serial number 40, 346, 351
serial port
advantages 24
defined 1
limits 4
vs. other interfaces 3
See also specific interface
serial server 3738, 39
serial.sys 38, 39, 318
SERIAL_STATE notification 344
SerialPort class
BreakState 210
BytesToRead 191
CDHolding 210
character encoding 173176
Close 161163
CtsHolding 210
DataReceived 194197
Dispose 161
DsrHolding 210
DtrEnable 210
ErrorReceived 214218
GetPortNames 221
Handshake 209213, 223
InBufferCount 209
List(of T) and 199202
Open 156160
parameters, set 221223
PinChanged 211213
polling for data 190193
PortName 157
Read 168169, 171
read and write methods 163, 164, 165
ReadBufferSize 197
ReadByte 169
ReadChar 172
ReadExisting 171
ReadLine 171
ReadTimeout 160, 190
ReadTo 171
ReceivedBytesThreshold 161
RtsEnable 210
send without blocking 203207
StringBuilder 197199
Timer component 192193
Using block 163
Write 164166, 168, 170171
write buffer management 207208
WriteBufferSize 207
WriteLine 166, 170
WriteTimeout 160, 203
See also code example (PC), exception,
Stream object
series termination 122
serin2 and serout2 263
server, serial 3738
SET_COMM_FEATURE 342, 343
SET_CONTROL_LINE_STATE 342, 343
376
SET_LINE_CODING 341, 342
SetupDi functions 40
SG/SGND 45
See also ground
shielded cable
RS-232 and 68
RS-485 and 134
short line 106112
terminating 122123
short packet 354
short-circuit biasing, RS-485 130131
Shutdown input 50
SIE 319
signal ground 71
Silicon Labs 325
simplex 7
Sipex Corporation 49, 89
skew 84
slew rate 48
SN65LBC184 90
SN75176B 90, 93, 327
SN75179B 91, 130
S.N.A.P. protocol 271
Space
logic state 47
See also parity
SPBRG and SPBRGH 236
SPI vs. other interfaces 3
spread spectrum 147, 152
square wave 107108
S-Record 30
star topology 125
multiple buses and 141
Start bit 15
Start of Transmission code 273
stick parity 12
Stop bit
defined 15
lengthening 13
Stream object 176186
See also BinaryReader and BinaryWriter;
StreamReader and
StreamWriter
StreamReader and StreamWriter 182186
string descriptor 351, 355
StringBuilder 197199
subminiature D 62
surge protection 69
Suspend state 319
symbolic link name 40
Sync Break 233
synchronous protocol
defined 12
USART and 14
6
TAPI 339
TCP 37
TD 45
Telecommunications Industry Association 43
telephone control model 337, 340
Telnet 37
Tera Term Pro 41
terminal
dumb 44
emulator 41
termination, RS-485 8587, 112131
Texas Instruments 49, 89, 90
text data 1923
Thesycon Systemsoftware & Consulting 339
TIA 43
TIA/EIA-423 58, 59
TIA/EIA-485 3
See also RS-485
TIA-232 43
See also RS-232
377
TIA-422. See RS-422
TIA-485 79
See also RS-485
TIA-530 58, 59
TIA-561 63
TIA-562 58, 59
TIA-574 63
timeout
embedded system 245, 249, 257
.NET 160, 190
TimeoutException 161
Timer component 192193
token-passing protocol 269
topologies, network 125127
transceiver. See RS-485: chips 79
transient protection. See surge protection
transmission line. See long line
transmission velocity 109
Transmit data pin. See TX
transmitter. See specific interface
triaxial cable 131
TSB-89-A 79
TSOP7000 148
TSR 231
TTL logic 49
TVS diode 69
twisted-pair cable 131134
RS-232 and 68
TX 45
TXD 45
TXREG 231, 238
TXSTA 231233, 236
Type 1 cable 133
7
UART
16550 263
chip 263
clock 1516
defined 45
errors 217218
external 263265
firmware-only 263
responsibilities 14
USB bridge chip 325
See also PIC18F4520
UDP 37
UnauthorizedAccessException 159
unbalanced interfaces 5859
Unicode 1922
.NET and 173176
union functional descriptor 349, 352
unit load 8687
Universal Asynchronous
Transmitter/Receiver. See UART
Universal Serial Bus. See USB
USART 14
See also UART
USB
ACK 322, 323
bulk transfers 321, 324
bus speed 320
composite device 356362
configuration, USB 318
control transfer 321, 324
converting to RS-232 332334
data packet 322
Data stage 321
data toggle 323
descriptors, abstract control model 346
356
descriptors, composite CDC 360361
device 317, 319
endpoint 320
enumeration 318
handshake packet 322
378
host 317, 318319
interrupt transfers 322, 324
NAK 322
NYET 323
OTG 319
packet 322323
packet ID (PID) 322
protocol analyzer 78
Setup stage 321
short packet 354
SIE 319
STALL 322
Status stage 321
Suspend state 319
token packet 322323
transactions 322323
transfer types 321322
vs. other interfaces 3
USB FIFO. See FT245R
USB Implementers Forum 317
USB UART. See FT232R; FT2232C
USB virtual COM port 318
abstract control model and 340362
driver, third party 339
FTDI controllers and 330
host driver 338339
INF file 318
parallel interface and 328
serial number and 40
See also USB
USB-IF 317
usbser.sys 38, 318, 338339
hotfix 361
USBwiz 9
Using block 163
UTF-16
defined 21
.NET and 173175, 176
UTF-32 21
UTF-8
defined 20
.NET and 175
8
V.24 and V.28 43
V.250 24, 336
V.25ter 336, 339
virtual COM port (VCP). See USB virtual
COM port
Vishay Semiconductors 148
Visual Basic. See code example (PC);
SerialPort class
Visual C#. See code example (PC); SerialPort
class
voltage margin. See noise: margin
voltage, common mode 135137
voltmeter 77
9
Walter Oney Software 339
Wi-Fi 37, 154
wIndex
in control transfers 321
in notifications 344
Windows
Device Manager 3136
driver 38
driver (USB CDC) 338339
driver (USB FTDI) 330
Ports class 39
real-time performance and 4
registry 39
Windows Driver Kit (WDK) 38
See also .NET; SerialPort class
Windows Telephony Application
Programming Interface 339
379
wireless
modulation 146148
serial server 37
See also infrared; RF
wireless handset control model 337
wireless mobile communications model 336
wLength 321
in notifications 344
wMaxPacketSize 353, 354
WMC model 336, 337
word, transmitted (defined) 12
Write (SerialPort class) 164166, 170171
Write7BitEncodedInt encoding 180
WriteBufferSize 207
WriteFile 41
WriteLine (SerialPort class) 166, 170
WriteTimeout 160, 203
WriteUSART 244
wValue 321
in control transfers 343
in notifications 344345
:
XBee-PRO 153
XModem 30
Xon/Xoff 27
;
YModem 30
<
zero-length packet 355
Zigbee 152
ZLP 355
ZModem 30
Zywyn Corporation 49, 89