Introduction
A serial port is a serial communication interface through which information transfers in or out one bit at a time. This article explains implementation serial port communication application in different platforms android, Linux and Windows.
Serial UART test application
The below section explains basic steps in writing serial port applications in Linux, Windows and android (command line application).
Linux and Android
Like all devices, UNIX provides access to serial ports via device files. To access a serial port you simply open the corresponding device file. The serial ports are named as ttyS0, ttyS1, etc. in UNIX and LINUX. Since Android relies on Linux for core system, this is applicable for android platform too.Each serial port on a UNIX/Linux system has one or more device files (files in the /dev directory) associated with it.
Windows/Win32
In Windows ,Serial ports are named as COM1,COM2 ,COM3.. etc .COM1 and COM2.This article assumes you are familiar with the fundamentals of multiple threading ,synchronization in Windows.,Application programming interfaces (APIs) that control user interface features of windows and dialogue boxes. Since the samples provided blow in windows application uses them and they are not explained here.
Opening a Serial Port
Linux/Android
A serial port is a file, so the open(2) function is used to access it. Sometimes Workarounds like changing the access permissions to the file(s) , running your program as the super-user (root), or making your program set-userid as the owner of the device file are needed. Below is example code block for opening serial port.
char *_cl_port = NULL;
_cl_port = strdup(optarg);//Command line arguments like /dev/ttyTHS1
fd = open(_cl_port, O_RDWR | O_NONBLOCK);
if (_fd < 0) {
printf("Error opening serial port \n");
free(_cl_port);
exit(1);
}
Windows
The CreateFile function opens a communications port. There are two ways to call CreateFile to open the communications port: overlapped and nonoverlapped. The following code block shows how to open COM port in windows.
HANDLE hComm;
hComm = CreateFile( gszPort,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
0);
if (hComm == INVALID_HANDLE_VALUE)
// error opening port; abort
Writing Data to the Port
Linux/Android:
Writing data to the port is done by write(2) system call to send data into the previously open serial port.The write function returns the number of bytes sent or -1 if an error occurred.
ssize_t c = write(_fd, _write_data, _write_size);
if (c < 0) {
printf("write failed (%d)\n", errno);
c = 0;
}
Windows:
Transmitting data out the communications port is done by writefile. The code snippet below shows the example of writing date in the serial port
// Issue write.
if (!WriteFile(hComm, lpBuf, dwToWrite, &dwWritten, &osWrite)) {
if (GetLastError() != ERROR_IO_PENDING) {
// WriteFile failed, but isn't delayed. Report error and abort.
fRes = FALSE;
}
else
// Write is pending; Need to handle with synchronous objects
Reading Data from the Port
Linux and Android
Similar to writing, Reading data can be done by read(2) system call.
int c = read(_fd, &rb, sizeof(rb));
If no characters are available, the call will block (wait) until characters come in, an interval timer expires, or an error occurs. The read function can be made to return immediately by doing the following:
fcntl(fd, F_SETFL, FNDELAY);
Windows
The ReadFile function issues a read operation. ReadFileEx also issues a read operation,Here is a code snippet that details how to issue a read request. Notice that the function calls a function to process the data if the ReadFile returns TRUE. This is the same function called if the operation becomes overlapped. Note the fWaitingOnRead flag that is defined by the code; it indicates whether or not a read operation is overlapped. It is used to prevent the creation of a new read operation if one is outstanding.
if (!ReadFile(hComm, lpBuf, READ_BUF_SIZE, &dwRead, &osReader)) {
if (GetLastError() != ERROR_IO_PENDING) // read not delayed?
// Error in communications; report it.
else
fWaitingOnRead = TRUE;
}
else {
// read completed immediately
}
Configuring a Serial Port
Linux and Android
Before reading and writing, it is important to configure(like baud rate, flow control, parity ) the serial port appropriately to that transmitters and receivers work correctly. Below function shows the configuration of serial ports.
void setup_serial_port(int baud)
{
struct termios newtio;
_fd = open(_cl_port, O_RDWR | O_NONBLOCK);
if (_fd < 0) { printf("Error opening serial port \n"); free(_cl_port); exit(1); } bzero(&newtio, sizeof(newtio)); /* clear struct for new port settings */ /* man termios get more info on below settings */ newtio.c_cflag = baud | CS8 | CLOCAL | CREAD; if (_cl_rts_cts) { newtio.c_cflag |= CRTSCTS; } if (_cl_2_stop_bit) { newtio.c_cflag |= CSTOPB; } if (_cl_parity) { newtio.c_cflag |= PARENB; if (_cl_odd_parity) { newtio.c_cflag |= PARODD; } if (_cl_stick_parity) { newtio.c_cflag |= CMSPAR; } } newtio.c_iflag = 0; newtio.c_oflag = 0; newtio.c_lflag = 0; // block for up till 128 characters newtio.c_cc[VMIN] = 128; // 0.5 seconds read timeout newtio.c_cc[VTIME] = 5; /* now clean the modem line and activate the settings for the port */ tcflush(_fd, TCIOFLUSH); tcsetattr(_fd,TCSANOW,&newtio); /* enable rs485 direction control */ if (_cl_rs485_delay >= 0) {
struct serial_rs485 rs485;
if(ioctl(_fd, TIOCGRS485, &rs485) < 0) {
printf("Error getting rs485 mode\n");
} else {
rs485.flags |= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND;
rs485.delay_rts_after_send = _cl_rs485_delay;
rs485.delay_rts_before_send = 0;
if(ioctl(_fd, TIOCSRS485, &rs485) < 0) {
printf("Error setting rs485 mode\n");
}
}
}
}
Windows
There are number of factors effecting read and write behaviours baud-rate, time-outs,Software flow control etc. These all values should be properly configured before read and write operations.Below code blocks shows setting serial ports in windows. Setting time-outs
COMMTIMEOUTS timeouts;
timeouts.ReadIntervalTimeout = 20;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.ReadTotalTimeoutConstant = 100;
timeouts.WriteTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 100;
if (!SetCommTimeouts(hComm, &timeouts))
// Error setting time-outs.
//Getting a current buad rate
DCB dcb = {0};
if (!GetCommState(hComm, &dcb))
// Error getting current DCB settings
else
// DCB is ready for use.
//Setting a new baud rate
DCB dcb;
FillMemory(&dcb, sizeof(dcb), 0);
if (!GetCommState(hComm, &dcb)) // get current DCB
// Error in GetCommState
return FALSE;
// Update DCB rate.
dcb.BaudRate = CBR_9600 ;
// Set new state.
if (!SetCommState(hComm, &dcb))
// Error in SetCommState. Possibly a problem with the communications
// port handle or a problem with the DCB structure itself.
Closing a Serial Port
Linux and Android
To close the serial port, just use the close system call.Closing a serial port will also usually set the DTR signal low which causes most MODEMs to hang up.
close(fd);
Windows
To close the serial port here, CloseHandle function is used.
CloseHandle(hComm);
Sample/Test application source code
Use the below link for serial port test application in Linux, android and windows platform.
- For Android: https://github.com/draftedstuff/android-serial-test
- For Linux: https://github.com/cbrake/linux-serial-test
- For Windows: https://code.msdn.microsoft.com/windowsdesktop/Serial-Port-Sample
If have any queries, or suggestions please add it in comment sections.