When I’m working on embedded systems (running Linux), one of the most common task is waiting for data on the serial line.
Sometimes the read must be a blocking read, more often I need to do a non blocking read over the serial line.
Let’s start with an axample:
Imagine the EmbSys has to receive commands from an external device, and then it must execute some jobs depending on the commands received, in this case the blocking read is very useful since the EmbSys has to work only after he has received a command.
In this case I normally something like the following:
open_serial()
wait_for_command()
switch (received_command)
{
Com1: do some stuff;
Com2: do some other stuff;
Com(n): do some other stuff;
}
Running on Linux, this pseudo code becomes:
/*+----------------------------------------------------------------+
| SYSTEM INCLUDES |
+----------------------------------------------------------------+*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <termio.h>
#include <pthread.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
int readString(int fd, char *buffer, unsigned int length)
{
int sRetVal;
int i;
memset(buffer, 0, length);
for(i=0; i<length; i++)
{
sRetVal = read(fd, &buffer[i], 1);
if(sRetVal==-1)
{
perror("reading on fd");
return(-2); /* reading error */
}
}
return(i);
}
int main(int argc, char **argv)
{
char string[10];
int serialFd;
struct termio tbuf;
struct termios sOldTermio;
int retval = 0;
/* Open the serial line */
serialFd = open("/dev/ttyS0", O_RDWR | O_NDELAY | O_NONBLOCK);
if (serialFd == -1)
{
exit(EXIT_FAILURE);
}
/* get the current tty setting */
if (tcgetattr(serialFd, &sOldTermio) != 0)
{
close(serialFd);
exit(EXIT_FAILURE);
}
/* elimina il flag no_delay */
if (fcntl(serialFd,F_SETFL,fcntl(serialFd,F_GETFL,0) & ~FNDELAY) != 0)
{
exit(EXIT_FAILURE);
}
if (ioctl(serialFd,TCGETA,&tbuf)==-1)
{
exit(EXIT_FAILURE);
}
tbuf.c_cflag = (tbuf.c_cflag & (~CBAUD)) | B115200;
tbuf.c_lflag &= ~ICANON;
tbuf.c_lflag &= ~ECHO;
tbuf.c_iflag &= ~(INLCR | ICRNL | IUCLC | ISTRIP | IXON | BRKINT);
tbuf.c_oflag &= ~OPOST;
tbuf.c_lflag &= ~ISIG;
tbuf.c_cc[4] = 0; /* min */
tbuf.c_cc[5] = 64; /* time */
if (ioctl(serialFd,TCSETAF,&tbuf)==-1)
{
exit(EXIT_FAILURE);
}
retval = readString(serialFd, string, 10);
if(retval<10)
{
printf("Error while reading\n");
}
else
{
printf("string completely received\n");
printf("Received string: %s \tlength = %d\n", string, retval);
}
}
Second Example:
If the EmbSys asks for data to a device (let’s say a temperature sensor) over a serial line, and then the sensor has to reply, in this case I use the non blocking read over the serial for two reasons:
– if the external device is broken the EmbSys will never receive an answer, and it will be blocked forever.
– If the data I asked for are very important, if I use a non blocking I/O I can decide how to go on if I haven’t received the response during a predetermined interval of time.
In this case I normally something like the following:
open_serial()
non_block_read()
if timeout
print The device is not working fine
else
do_something with received data
Running on Linux, this pseudo code becomes:
1 /*+----------------------------------------------------------------+ 2 | SYSTEM INCLUDES | 3 +----------------------------------------------------------------+*/ 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <unistd.h> 7 #include <termios.h> 8 #include <termio.h> 9 #include <pthread.h> 10 #include <string.h> 11 #include <sys/time.h> 12 #include <sys/types.h> 13 #include <sys/stat.h> 14 #include <sys/ioctl.h> 15 #include <fcntl.h> 16 17 int nonBlockingRead(int fd, char *buffer, unsigned int length, unsigned int timeout) 18 { 19 fd_set rfds; 20 struct timeval tv; 21 int retval; 22 int sRetVal; 23 int i; 24 25 FD_ZERO(&rfds); 26 FD_SET(fd, &rfds); 27 28 tv.tv_sec = timeout/1000000; 29 tv.tv_usec = timeout%1000000; 30 31 memset(buffer, 0, length); 32 for(i=0; i<length; i++) 33 { 34 retval=select(fd+1, &rfds, NULL, NULL, &tv); 35 if(retval==-1) 36 { 37 perror("select"); 38 return(-1); 39 } 40 else if(retval) 41 { 42 sRetVal = read(fd, &buffer[i], 1); 43 if(sRetVal==-1) 44 { 45 perror("reading on fd"); 46 return(-2); /* reading error */ 47 } 48 } 49 else 50 { 51 /* timeout */ 52 break; 53 } 54 } 55 return(i); 56 } 57 58 int main(int argc, char **argv) 59 { 60 char string[10]; 61 int serialFd; 62 struct termio tbuf; 63 struct termios sOldTermio; 64 int retval = 0; 65 /* Open the serial line */ 66 serialFd = open("/dev/ttyS0", O_RDWR | O_NDELAY | O_NONBLOCK); 67 if (serialFd == -1) 68 { 69 exit(EXIT_FAILURE); 70 } 71 /* get the current tty setting */ 72 if (tcgetattr(serialFd, &sOldTermio) != 0) 73 { 74 close(serialFd); 75 exit(EXIT_FAILURE); 76 } 77 78 /* elimina il flag no_delay */ 79 if (fcntl(serialFd,F_SETFL,fcntl(serialFd,F_GETFL,0) & ~FNDELAY) != 0) 80 { 81 exit(EXIT_FAILURE); 82 } 83 84 if (ioctl(serialFd,TCGETA,&tbuf)==-1) 85 { 86 exit(EXIT_FAILURE); 87 } 88 tbuf.c_cflag = (tbuf.c_cflag & (~CBAUD)) | B115200; 89 tbuf.c_lflag &= ~ICANON; 90 tbuf.c_lflag &= ~ECHO; 91 tbuf.c_iflag &= ~(INLCR | ICRNL | IUCLC | ISTRIP | IXON | BRKINT); 92 tbuf.c_oflag &= ~OPOST; 93 tbuf.c_lflag &= ~ISIG; 94 95 tbuf.c_cc[4] = 0; /* min */ 96 tbuf.c_cc[5] = 64; /* time */ 97 if (ioctl(serialFd,TCSETAF,&tbuf)==-1) 98 { 99 exit(EXIT_FAILURE); 100 } 101 retval = nonBlockingRead(serialFd, string, 10, 1000000); 102 if(retval<0) 103 { 104 printf("Error while reading\n"); 105 } 106 else 107 { 108 if(retval<10) 109 printf("string not completely received\n"); 110 else 111 printf("string completely received\n"); 112 printf("Received string: %s \tlength = %d\n", string, retval); 113 } 114 }
that’s all.
Gg1