Reading serial port

Hi,

My apologies if this seems really obvious. I need to read data from a device on a standard serial port that emits a few bytes once every 5 milliseconds. My application requires that I don’t waste time reading data from the serial port if there is no data there. Is there a way to poll the port to see if there is new data available, without making the program wait for data to arrive? (I’d rather not set up a separate thread to constantly monitor the port)

thanks!

James

Whoops… forgot to mention that I’m running QNX 6.1

thanks!

James

Try this:

uint64_t readTimeout = 0; TimerTimeout( CLOCK_MONOTONIC, _NTO_TIMEOUT_REPLY, NULL, &readTimeout, NULL); bytesRead=read( port, buff, sizeof(buff)); if( bytesRead == ETIMEDOUT) { // No data available }

ugh, timeouts are evil.

read the docs on readcond, ionotify and select and then choose one of them.

Thanks for the feedback Roverfan and cburgess. My apologies for not replying earlier; I wanted to try out your suggestions before asking more questions. cburgess, I gave select() a try but couldn’t seem to get the time taken to examine the serial port below about 2 milliseconds (I need to get it below 1ms). When I set the wait time to 0 it seems to completely skip reading the serial port.

RoverFan, I tried your suggestion and have been partially successful. I am encountering two problems. The first is that if I set the readTimeout value to 0 then the reading of the serial port returns a value of -1 for the number of bytes read (so no bytes were read). This is not the case when I increase the value of readTimeout to 1 or more (and have a device connected that is sending in data on the serial port). Next, it seems that if serial data is available on the port and I try to read it using the following code my measured value for elapsed time becomes erratic (gets negative, etc.). It’s very strange. Any ideas as to how I could go about fixing both of these problems?

thanks!!

James

Example calls and what the program responds with:

  1. No hardware connected to the serial port, readTimeout = 0;

./serial

Serial Port 1 opened.
Time to initialise the serial port: 0.001279 sec
Elapsed Time: 0.000043 sec; Bytes read: -1

  1. No hardware connected to the serial port, readTimeout = 1;

./serial

Serial Port 1 opened.
Time to initialise the serial port: 0.001218 sec
Elapsed Time: 0.000351 sec; Bytes read: -1

  1. Hardware connected to the serial port, readTimeout = 0;

./serial

Serial Port 1 opened.
Time to initialise the serial port: 0.001252 sec
Elapsed Time: 0.000035 sec; Bytes read: -1

  1. Hardware connected to the serial port, readTimeout = 1;
    (Note: the ACTUAL elapsed time isn’t actually 123 seconds)

./serial

Serial Port 1 opened.
Time to initialise the serial port: 0.001334 sec
Elapsed Time: 123.386620 sec; Bytes read: 4

--------- source code ----------

#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/select.h> /* for the select() function /
#include <time.h>
#include <sys/syspage.h> /
qnx.com/developers/docs/qnx_ … entry.html $
* for call $
* cycles_per_second = SYSPAGE_ENTRY(qtime)->cycles_per_sec;
/
#include <inttypes.h> /
for uint64_t /
#include <sys/neutrino.h> /
For ClockCycles() /
#include <errno.h> /
For Error Codes. */

int main( void )
{
int console, serial;
struct timeval tv;
fd_set rfd;
int n;

double initial_time=0.0;
double elapsed_time=0.0;
uint64_t cycles_per_second;

unsigned char buf[1];
unsigned char *p=buf, *q=buf;

uint64_t readTimeout = 0;
int bytesRead = 0;

p=buf;

/* - - - - - - - - - - - - - - - - - - -
* Initialise time-related stuff.
* Then record the current time.
* - - - - - - - - - - - - - - - - - - - */

/* see QNX Syspage_entry:
* qnx.com/developer/docs/qnx_6 … entry.html for details */
cycles_per_second = SYSPAGE_ENTRY(qtime)->cycles_per_sec;

/* Finding the time that has elapsed and record it as initial time */
initial_time = ((double)ClockCycles())/((double)cycles_per_second);

/* - - - - - - - - - - - - - - - - - - -
* Open the serial port and console
* - - - - - - - - - - - - - - - - - - - */
if(( serial = open( “/dev/ser1”, O_RDONLY| O_NOCTTY | O_NONBLOCK ) ) == -1)
{
perror( “open” );
return EXIT_FAILURE;
}
else
{
printf(“Serial Port 1 opened.\n”);
}

/*
* Clear the set of read file descriptors, and
* add the two we just got from the open calls.
* */
FD_ZERO( &rfd );
FD_SET( serial, &rfd );

elapsed_time =((double)ClockCycles())/((double)cycles_per_second)-initial_time;

printf(“Time to initialise the serial port: %f sec\n”, (float)elapsed_time);

initial_time = ((double)ClockCycles())/((double)cycles_per_second);

readTimeout = 1; /* */
TimerTimeout( CLOCK_MONOTONIC,
_NTO_TIMEOUT_REPLY,
NULL,
&readTimeout,
NULL);
bytesRead=read( serial, p, sizeof(p));

/* Tell the user how much time has elapsed and how many bytes were read */
elapsed_time =((double)ClockCycles())/((double)cycles_per_second)-initial_time;
printf(“Elapsed Time: %f sec; Bytes read: %d \n”, (float)elapsed_time, bytesRead);

close(serial); /* close the port. */
return EXIT_SUCCESS;
}

If you want to check if data is available on the port before calling read(), call tcischars(serial); this will return the number of bytes available for reading. If the tcischars() call returns > 0, then you know there is data that you can read. Colin’s assessment is right, though. If you can, avoid the TimerTimeout call and use ionotify/readcond/select. I prefer to use ionotify personally:

[code]#include <stdio.h> // printf
#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS
#include <string.h> // strerror
#include <errno.h> // errno
#include <termios.h> // tcischars
#include <unistd.h> // ionotify
#include <sys/iomsg.h> // ionotify
#include <sys/neutrino.h> // MsgReceivePulse

/**

  • Pulse code for the driver to send when data is available to read
    */
    #define SERIAL_NOTIFICATION_CODE 0x10

/**

  • Request a notification from the devc driver when data is available

  • for reading

  • @param serialFd File descriptor to the devc driver

  • @param notifyEvent

  •             Event for the driver to deliver to us when data is available for reading
    
  • @return 0 if successful, -1 if an error occurs
    */
    int SerialSubscribe(int serialFd, struct sigevent *notifyEvent)
    {
    int status;
    int bytesWaiting;

    // Tell the server to notify you when an IRQ occurs
    status = ionotify( serialFd, _NOTIFY_ACTION_POLLARM, _NOTIFY_COND_INPUT, &notifyEvent);
    if( -1 == status)
    {
    printf( “\tSerialSubscribe() ionotify failed: %s\n”, strerror( errno));
    }
    // If data is currently available
    else if( status & _NOTIFY_COND_INPUT)
    {
    if( (bytesWaiting=tcischars( serialFd)) > 0)
    {
    printf( “DriverDataRegister() %d bytes available, forwarding notification pulse\n”, bytesWaiting);
    // Send a pulse to your channel
    MsgSendPulse( notifyEvent->sigev_coid, notifyEvent->sigev_priority,
    notifyEvent->sigev_code, notifyEvent->sigev_value.sival_int+1);
    }
    else
    {
    printf( “\tSerial driver says data is ready, but tcischars() <= 0\n”);
    }
    status = 0;
    }

    return(status);
    }

int main( int argc, char *argv[])
{
// Channel/connection pair to receive data on / send notifications to
int channelId;
int connectionId;
// File descriptor for the serial port
int serial;
// Who sent you a message
int replyId;
// Number of bytes available for reading
int bytesAvailable;
// Number of bytes read
int bytesRead;
// Pulse to receive notifications into
struct _pulse serialPulse;
// Notification structure the driver can send you when data is available for reading
struct sigevent notifyEvent;
// Buffer to read serial data into
unsigned char *buff;

// Create a channel/connection pair to receive data on / send notifications to
if(( channelId = ChannelCreate(0) ) == -1)
{
    perror( "ChannelCreate" );
    return(EXIT_FAILURE);
}

if(( connectionId=ConnectAttach( 0, 0, channelId, _NTO_SIDE_CHANNEL, 0) ) == -1)
{
    perror( "ConnectAttach" );
    return(EXIT_FAILURE);
}

if(( serial = open( "/dev/ser1", O_RDONLY| O_NOCTTY | O_NONBLOCK ) ) == -1)
{
    perror( "open" );
    return(EXIT_FAILURE);
}
else
{
    printf("Serial Port 1 opened.\n");
}

// Initialize the notification event to deliver a pulse when data is available for reading
memset(&notifyEvent, 0, sizeof(notifyEvent));
SIGEV_PULSE_INIT(&notifyEvent, connectionId, 
                 SIGEV_PULSE_PRIO_INHERIT, 
                 SERIAL_NOTIFICATION_CODE, 
                 0);

if(SerialSubscribe( serial, &notifyEvent) == -1)
{
    perror("SerialSubscribe");
    return(EXIT_FAILURE);
}

// Loop until we get a pulse from devc
replyId = -1;
while(0 != replyId)
{
    // Wait for a pulse
    replyId = MsgReceivePulse( channelId, &serialPulse, sizeof( serialPulse), NULL);
    // You got a pulse
    if(0 == replyId)
    {
        // Check if this is a notification for data at the serial port
        if(serialPulse.code == SIGEV_PULSE_PRIO_INHERIT)
        {
            bytesAvailable = tcischars(serial);
            // Driver says data is available
            if(bytesAvailable > 0)
            {
                // Allocate our buffer
                buff = calloc(bytesAvailable, 1);
                // Read the data
                bytesRead = read(serial, buff, bytesAvailable);
            }
        }
        else
        {
            // Wait for a pulse from the devc driver
            replyId = -1;
        }
    }
}

return( EXIT_SUCCESS);

}[/code]

Thanks once again, RoverFan. I gave your code a try this morning and it seems to work (it tells me that I’ve got 2048 bytes on the serial port; but something is freezing up inside the while loop… not sure what is causing that). I had to make to little changes, though. First was to include fcntl.h and second was to call ionotify like this:
status = ionotify( serialFd,
_NOTIFY_ACTION_POLLARM,
_NOTIFY_COND_INPUT,
*(&notifyEvent));

(the compiler complained about “passing arg 4 of `ionotify’ from incompatible pointer type”)

thanks again!

James

Good Luck