0

I've read all the related questions here but didn't find the solution.

I'm trying to read one by one byte from the serial port.

  • When I'm in infinite loop and I check if some bytes are available, it always works fine and displays what ever I send to it.
  • But when I check outside of the infinite loop it just catch one byte, display it, then close the serial port.

Here is my code

// Checks if 1 data byte is available in the RX buffer at the moment
int serialHasChar(int fd)
{
  struct pollfd fds;
  fds.fd = fd;
  fds.events = (POLLIN | POLLPRI);  // POLLIN : There is data to read, POLLPRI: There is urgent data to read
  if(poll(&fds, 1, 0) > 0)
    {
      return 1;
    }
  else
    {
      return 0;
    }
}

int serialOpen(const char *port, const uint baud)
    {
      int fd = -1;

  fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);

  if (fd == -1)
    {
      printf("[ERROR] Couldn't open port \"%s\": %s\n", port, strerror(errno));
      return -1;
    }
  else
    {
      printf("Serial port %s successfully opened\n", port);
    }

  struct termios options;
  tcgetattr(fd, &options);      // Get the current attributes of the Serial port
  options.c_iflag = IGNPAR;
  options.c_oflag = 0;
  options.c_lflag = 0;
  options.c_cflag = B115200 | CS8 | CLOCAL | CREAD;
  tcflush(fd, TCIFLUSH);          
  tcsetattr(fd, TCSANOW, &options);   

  return fd;
}

void serialClose(int fd)
{
  tcflush(fd, TCIOFLUSH);
  close(fd);
      printf("Serial port successfully closed\n");
}


// Receive one byte
uint8_t serialReadChar(int fd)
{
  uint8_t ch;
  //tcflow(fd, TCOON);
  read(fd, &ch, 1);
  //tcflow(fd, TCOOFF);
  printf("One byte received : 0x%.2x\n", ch);
  return ch;
}

int main()
{
  uint8_t bytes = 0;
  uint8_t ch = 0;

  // Open serial port
  int fd = serialOpen("/dev/ttyAMA0", 115200);

  // This works
  while(1)
    {
      if (serialHasChar(fd)) {
    ch = serialReadChar(fd);
      }
    }

  /* This doesn't work
  while(serialHasChar(fd) == 0);

  while(serialHasChar(fd))
    {
      ch = serialReadChar(fd);
      bytes++;
      //bytes = serialNumOfAvailableBytes(fd);
    }
  */

  serialClose(fd);

  return 0;
} 

I don't understand why does it happen so!! Could someone help me? Thanks

UPDATE: I've added the definition of serialHasChar() function in the code above

Salahuddin
  • 1,617
  • 3
  • 23
  • 37
  • Why don't you check the result of the functions? – too honest for this site Nov 22 '16 at 14:56
  • Modern computers are fast, serial ports are slow. So when you read one byte then serialHasChar() will become false again. And your while loop terminates. – Hans Passant Nov 22 '16 at 14:57
  • @Olaf, thanks for your reply. I did read the result of the functions. When it works, it displays all the received bytes. When it doesn't, it just display the first received byte. I hope I get you right. – Salahuddin Nov 22 '16 at 15:08
  • @Hans, thanks for your reply. I thought that the input buffer is like a queue that buffers the coming bytes and I can then read them as slow as I want. Shouldn't it be like this? – Salahuddin Nov 22 '16 at 15:10
  • 1
    @SalahuddinAshraf: I don't see where you check the result of `read`! – too honest for this site Nov 22 '16 at 15:12
  • It is like that. But the buffer only really gets used when you don't read fast enough. That's not the problem, you read more than fast enough. – Hans Passant Nov 22 '16 at 15:14
  • @Hans, the serialReadChar() function prints every byte it receive. Is it what you mean? – Salahuddin Nov 22 '16 at 15:14
  • @sawdust, thanks a lot for your reply. (1) I'm really trying to learn how to solve my problem. I thought I understood what Olaf said, but you said otherwise. Could you please tell me what did he meant? (2) I added the missing definition in the code. (3) I don't know how to make a blocking-reading. Does it read one byte, block until the next arrives? Till when does it block? Could you please explain me more? Many thanks – Salahuddin Nov 23 '16 at 06:58
  • See [linux blocking vs non-blocking serial read](http://stackoverflow.com/questions/25996171/linux-blocking-vs-non-blocking-serial-read). Study [this example code](http://stackoverflow.com/questions/6947413/how-to-open-read-and-write-from-serial-port-in-c/38318768#38318768). Note that **open()** does not have O_NDELAY so that blocking mode is used. Also note in that example how the return code from **read()** is tested. That is what Olaf means. – sawdust Nov 23 '16 at 07:21

3 Answers3

0

The code that works it not quite the same as the code that doesn't work.

Here as soon as serialHasChar returns a non nul value, you call serialReadChar.

while(1)
{
  if (serialHasChar(fd)) {
    ch = serialReadChar(fd);
  }
}

But in following code (the one that doesn't work) it's a bit different: you call serialHasChar until it returns a non nul value. But then you call serialHasChar a second time instead of calling serialReadChar as in the snippet that works.

while(serialHasChar(fd) == 0);

while(serialHasChar(fd))
{
  ch = serialReadChar(fd);
}

You probably need this:

while(serialHasChar(fd) == 0);

do
{
  ch = serialReadChar(fd);
} while(serialHasChar(fd));
Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
0

Try this:

while(serialHasChar(fd) == 0);

while(serialHasChar(fd))
{
    ch = serialReadChar(fd);
    bytes++;
    while(serialHasChar(fd) == 0);
}
user2443447
  • 151
  • 1
  • 8
  • Thanks for your reply. It did work but the same as while(1). It never gets out of the loop. Every iteration doesn't finish until a new character is received, therefore, when it checks for the while condition it will always be true. – Salahuddin Nov 23 '16 at 06:54
  • You want to be able to do other things while waiting for a character to be available on the serial port. Got it. – user2443447 Nov 23 '16 at 15:32
  • Two things come to my mind. 1. multi-threading. While one thread waits for the input, the other thread does the processing. 2. asynchronous I/O and polling. Both need complicated logic. – user2443447 Nov 23 '16 at 15:36
0

The code will simply not work without an additional loop.

As soon as all available bytes are read from the serial interface, you will leave your loop and never read a byte again even if new data arrives at the port.

As you can read much faster than most serial ports can transfer data, you will run out of bytes in the buffer very soon, i.e. after the first byte as you already noticed. Then you need to bridge the gap until the next byte is available.

In your working solution you will most of the time get a 0 from serialHasChar(fd) and therefore not call serialReadChar(fd) but just continue the loop.

Gerhardh
  • 11,688
  • 4
  • 17
  • 39
  • Thanks for your reply. It explained me clearly why it happened like this. But if I have to add an additional loop, and I don't want to block the program (because it is a kind of real time program) , how can I achieve this? – Salahuddin Nov 23 '16 at 06:57
  • *" I don't want to block the program (because it is a kind of real time program)"* -- Bogus reasoning. The **read()** only copies data from the system buffer. Wasting CPU cycles to poll the system buffer for data actually consumes your process's time slice, so that your program ends up executing slower. – sawdust Nov 23 '16 at 08:12