简体   繁体   中英

Reading from the serial port in a multi-threaded program on Linux

I'm writing a program in linux to interface, through serial, with a piece of hardware. The device sends packets of approximately 30-40 bytes at about 10Hz. This software module will interface with others and communicate via IPC so it must perform a specific IPC sleep to allow it to receive messages that it's subscribed to when it isn't doing anything useful.

Currently my code looks something like:

while(1){
  IPC_sleep(some_time);
  read_serial();
  process_serial_data();
}

The problem with this is that sometimes the read will be performed while only a fraction of the next packet is available at the serial port, which means that it isn't all read until next time around the loop. For the specific application it is preferable that the data is read as soon as it's available, and that the program doesn't block while reading.

What's the best solution to this problem?

Store away what you got so far of the message in a buffer of some sort.

If you don't want to block while waiting for new data, use something like select() on the serial port to check that more data is available. If not, you can continue doing some processing or whatever needs to be done instead of blocking until there is data to fetch.

When the rest of the data arrives, add to the buffer and check if there is enough to comprise a complete message. If there is, process it and remove it from the buffer.

The best solution is not to sleep ! What I mean is a good solution is probably to mix the IPC event and the serial event. select is a good tool to do this. Then you have to find and IPC mechanism that is select compatible.

  • socket based IPC is select() able
  • pipe based IPC is select() able
  • posix message queue are also selectable

And then your loop looks like this

while(1) {
    select(serial_fd | ipc_fd); //of course this is pseudo code
    if(FD_ISSET(fd_set, serial_fd)) {
        parse_serial(serial_fd, serial_context);
        if(complete_serial_message)
            process_serial_data(serial_context)
    }
    if(FD_ISSET(ipc_fd)) {
        do_ipc();
    }
}

read_serial is replaced with parse_serial , because if you spend all your time waiting for complete serial packet, then all the benefit of the select is lost. But from your question, it seems you are already doing that, since you mention getting serial data in two different loop.
With the proposed architecture you have good reactivity on both the IPC and the serial side. You read serial data as soon as they are available, but without stopping to process IPC.

Of course it assumes you can change the IPC mechanism. If you can't, perhaps you can make a "bridge process" that interface on one side with whatever IPC you are stuck with, and on the other side uses a select()able IPC to communicate with your serial code.

You must cache enough of a message to know whether or not it is a complete message or if you will have a complete valid message.

If it is not valid or won't be in an acceptable timeframe, then you toss it. Otherwise, you keep it and process it.

This is typically called implementing a parser for the device's protocol.

This is the algorithm (blocking) that is needed:

while(! complete_packet(p) &&  time_taken < timeout)
{
   p += reading_device.read(); //only blocks for t << 1sec. 

   time_taken.update();   
}
//now you have a complete packet or a timeout. 

You can intersperse a callback if you like, or inject relevant portions in your processing loops.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM