Thread: Serial Port Issues (again!)

  1. #1
    Registered User HalNineThousand's Avatar
    Join Date
    Mar 2008
    Posts
    43

    Serial Port Issues (again!)

    I had a few problems before with writing to the serial port in Linux and everything worked for while, but now, after everything else is done and I'm checking over all the functions, I'm running into trouble again. I'm having 2 problems and since they involve most of the same code, they might be intermixed.

    I'm using this code as part of a program to control with a radio through the serial port. The radio is activated when the DTR line goes high and I can communicate with it to read and write data and when the DTR drops, the radio turns off. There are also on and off commands, but they seem to basically turn the output on and off. In other words, the DTR seems to be the master on/off and the commands for on/off seem to be more for a sleep mode.

    The first problem is with the DTR line. It raises when I open the port for reading, which it should do, but it is not dropping when I close the port. In other experiments (including one I mentioned before with Perl), when I killed the program, the DTR line dropped. That was happening before and isn't now. I even reverted to the earlier code I used to open the port and the DTR still doesn't drop when I exit the program. I can go into details if needed, but in short, it's important that the DTR drop when this program exits, crashes, or is killed. I know I can code it in, but I have no guarantee that code will be executed in case of a core dump or in other situations.

    The second problem is that I think I have the settings so when I try to read a byte, it'll block until one is available. That's not happening.

    I open the port twice. This has been working well for me. I'm reading with one descriptor and writing with the 2nd. I tried opening it only once, but it never seemed to work that way. (That was a few weeks ago, so I don't remember exactly why it didn't work.) When I close it at program exit, I close using both file descriptors.

    Here's the code to open the port:
    Code:
    bool LinuxPort::openport() { //public
    //isOpen, infd, outfd are global and of the appropriate type for what they do
    	if (isOpen) return true;
    	isOpen = true;
    	cout << "Opening port: " << serialdevice << endl;
    
    	infd = open(serialdevice.c_str(), O_RDWR | O_NOCTTY);
    	fcntl(infd, F_SETFL, 0);
    //code included for setportattr below
    	setportattr(infd);
    	outfd = open(serialdevice.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
    //Yeah, we just set it for input, but for some reason there are consistently better
    //results if we do it here, too.
    	setportattr(outfd);
    
    	if (infd == -1 || outfd == -1) {
    		if (infd == -1)
    			perror("\tUnable to open port for reading");
    		if (outfd == -1)
    			perror("\tUnable to open port for writing");
    // 		exit(1);
    		return false;
    	} else {
    		cout << "\tPort " << serialdevice << " has been opened. Input: " << infd << ", Output " << outfd << endl;
    	}
    	return true;
    }
    Here is the code I use to set the port attributes. I don't set the DTR because it goes high automatically:
    Code:
    void LinuxPort::setportattr(int portfd) { //public
    //BAUD is B115200
    	int status;
    //Should never change, set at 115200
    	tcgetattr(portfd, &options);
    	options.c_cflag = 0 | BAUD;
    	options.c_cflag &= ~PARENB;
    	options.c_cflag &= ~CSTOPB;
    	options.c_cflag &= ~CSIZE;
    	options.c_cflag |= CS8;
    	options.c_cflag |= (CLOCAL | CREAD);
    	options.c_cflag &= ~CRTSCTS;
    	options.c_iflag = 0 | (IGNBRK | IGNPAR);
    	options.c_iflag = (IXON | IXOFF | IXANY);
    	options.c_oflag = 0;
    	options.c_lflag = 0;
    	int rc = tcsetattr(portfd, TCSANOW, &options);
    	cout << "Attribute set return code: " << rc << endl;
    	return;
    }
    I've experimented with settings. For now this is on /dev/ttyS0, but it needs to also work through an adapter at /dev/ttyUSB0. These settings have worked before and using them I did have it working so when I killed the program or exited it, the DTR always dropped.

    And here's the code to read a byte from the port. I'm including both problems because I don't know if they spring from the same problems with opening the port.
    Code:
    //for now ilen is always 1 byte
    char* LinuxPort::hdreadbytes(int ilen) { //public
    	char* buff = new char[1024];
    	unsigned char cIn;
    	int i, rd = 0;
    	bool loopflag = false;
    
    	buff[0] = 0;
    	if (ilen > 1024)
    		return buff;
    	if (!isOpen) {
    		cout << "Having to re-open port\n";
    		openport();
    	}
    	while (rd < 1) {
    //In debugging this line prints over and over, showing it loops and doesn't block.
    // 		cout << "Waiting for byte...\n";
    		rd = read(infd, buff, ilen);
    		usleep(naptime);
    		loopflag = true;
    	}
    	lastread = rd;
    	return buff;
    }
    I don't think the close code is relevant, since my concern is that the DTR doesn't drop when the program is killed, but even when the close code is used, the DTR does not drop.
    Code:
    void LinuxPort::closeport() { //public
    //infd & outfd are file descriptors
    	int portfd, status;
    	isOpen = false;
    	if (outfd == 0 && infd == 0)
    		return;
    	if (verbose) printdtr("About to close ports");
    	if (outfd > 0) {
    		close(outfd);
    	}
    	if (infd > 0) {
    		close(infd);
    	}
    	return;
    }
    I am still new to C++ and I'm still learning about the serial port. At this point, this looks like it should work to me. I've done what one tutorial says that should make the port block while waiting to read a new byte, but it's not working. I'm not sure what I'm doing that holds the DTR line high when I quit. With other programs I've tried that use the serial port, if the program is killed the DTR drops automatically.

    I'm also not clear why I need to open the port to read from it and again to write to it. I think the problem was I wanted it to block on reads but not on writes.

    Since I have the usleep() statement in the read loop, I can get by without fixing that (although I'd like to, if for no other reason to understand what I'm doing wrong), but I do need to find out why the DTR is not dropping.

    Thanks for any help on this!

  2. #2
    Registered User HalNineThousand's Avatar
    Join Date
    Mar 2008
    Posts
    43
    The DTR problem is gone. I added:
    Code:
    	options.c_cflag |= HUPCL;
    in setportattr() and it behaves. I had added it earlier, with other settings and it didn't make a difference. Since I'm totally new to C++ and had never done much with the serial port, it's likely something else was causing other problems earlier so adding the HUPCL flag didn't deal with the other problem. With it in, it works like a charm. Whether the program exits or is killed, it still drops the DTR on exit.

    I have been able to make it work with only opening the port once, for reading and writing.

    I still can't get the port to block on the read until a byte comes in. I have to have a loop with a usleep() function for that.

  3. #3
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by HalNineThousand View Post
    I still can't get the port to block on the read until a byte comes in. I have to have a loop with a usleep() function for that.
    Can you post your current call to open() and the fcntl()'s you are using?

  4. #4
    Registered User HalNineThousand's Avatar
    Join Date
    Mar 2008
    Posts
    43
    Sure. Code to open:
    Code:
    bool LinuxPort::openport() { //public
    	if (isOpen) return true;
    	isOpen = true;
    	cout << "Opening port: " << serialdevice << endl;
    	portfd = open(serialdevice.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
    //Used to use this (supposedly makes read block if no data, doesn't work)
    //	fcntl(portfd, F_SETFL, 0);
    	setportattr(portfd);
    	if (portfd == 0) {
    		perror("\tUnable to open serial port.\n");
    		return false;
    	} else {
    		cout << "\tPort " << serialdevice << " has been opened.  Descriptor: " << portfd << endl;
    	}
    	return true;
    }
    Code to set serial port parms:
    Code:
    void LinuxPort::setportattr(int portfd) { //public
    	options.c_cflag = 0 | BAUD;
    	options.c_cflag &= ~PARENB;
    	options.c_cflag &= ~CSTOPB;
    	options.c_cflag &= ~CSIZE;
    	options.c_cflag |= CS8;
    	options.c_cflag |= (CLOCAL | CREAD);
    	options.c_cflag &= ~CRTSCTS;
    	options.c_cflag |= HUPCL;
    	options.c_iflag = 0 | (IGNBRK | IGNPAR);
    	options.c_iflag = (IXON | IXOFF | IXANY);
    	options.c_oflag = 0;
    	options.c_lflag = 0;
    	int rc = tcsetattr(portfd, TCSANOW, &options);
    	cout << "\tSerial port attribute set return code: " << rc << endl;
    	return;
    }
    I have the one call to fcntl() commented out because it made no difference. My guess is it normally does but I'm doing something else to cancel it out.

  5. #5
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    That O_NDELAY might be causing it.

  6. #6
    Registered User HalNineThousand's Avatar
    Join Date
    Mar 2008
    Posts
    43
    So if I want it to block, I would have to open it as two separate files, one for reading, one for writing, right? I need the O_NDELAY for writing, don't I?

    Even when I was opening it as two different files and not using the O_NDELAY on the one I used for reading, it still wouldn't block.

    At this point it's not a must, but it would be nice to have it set up that way.

    Thanks.

  7. #7
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by HalNineThousand View Post
    So if I want it to block, I would have to open it as two separate files, one for reading, one for writing, right? I need the O_NDELAY for writing, don't I?
    It's been a while since I wrote data to a serial port, but I can't think of a reason you'd need O_NDELAY for writing. Why do you think so?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. serial port to poll on request
    By infineonintern in forum C++ Programming
    Replies: 2
    Last Post: 06-11-2009, 06:52 AM
  2. Can't Read From Serial Port
    By HalNineThousand in forum Linux Programming
    Replies: 14
    Last Post: 03-20-2008, 05:56 PM
  3. brace-enclosed error
    By jdc18 in forum C++ Programming
    Replies: 53
    Last Post: 05-03-2007, 05:49 PM
  4. Reading and writing to a serial port
    By SwarfEye in forum C Programming
    Replies: 2
    Last Post: 08-18-2006, 12:28 AM
  5. DOS, Serial, and Touch Screen
    By jon_nc17 in forum A Brief History of Cprogramming.com
    Replies: 0
    Last Post: 01-08-2003, 04:59 PM