LinuxQuestions.org
Download your favorite Linux distribution at LQ ISO.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 07-21-2008, 06:30 AM   #1
muzic
LQ Newbie
 
Registered: Jul 2008
Posts: 7

Rep: Reputation: 0
C++ RS232/serial port - problem with signals or interrupts


I'm new in Linux and I have a strange problem with programing RS232.

I have to write program which should give signal or interrupt whenever data come from RS232. I have a program but:

- When I read normaly without using the signals, all data are read excelent.

For example:

103
203
0
104
150
52
151
130
15
20
- When I switch signals I have something like this:

signal received
103
203
0
signal received
104
signal received
150
52
signal received
151
130
15
signal received
20
but I should have "signal recived" before every data like this:

signal received
103
signal received
203
signal received
0
signal received
104
signal received
150
signal received
52
signal received
151
signal received
130
signal received
15
signal received
20
So I don't know why I lost so many signals. Maybe somone can halp me or give me soulution. Or maybe it's better to write it by using the interrupts? Thx anyway

Here is a code:

Quote:
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <iostream>
#include <sys/signal.h>
//----------------------------------------------------
#define BAUDRATE B57600
#define MODEMDEVICE "/dev/ttyS0"
#define _POSIX_SOURCE 1
#define O_BINARY 0x8000
#define FALSE 0
#define TRUE 1
//----------------------------------------------------
volatile int STOP=FALSE;
void signal_handler_IO (int status);
int wait_flag=TRUE;
int fd, res;
unsigned char buf[255];
int przer=0;

struct sigaction saio;
struct termios oldtio,newtio;
//----------------------------------------------------
int main()
{
std::cout<<"START"<<std::endl;

fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd <0) {perror(MODEMDEVICE); exit(-1); }


saio.sa_handler = signal_handler_IO;
sigemptyset(&saio.sa_mask);
saio.sa_flags = 0;
saio.sa_flags = SA_NODEFER;
saio.sa_restorer = NULL;
sigaction(SIGIO,&saio,NULL);


fcntl(fd, F_SETOWN, getpid());
fcntl(fd, F_SETFL, FASYNC);
tcgetattr(fd,&oldtio);
bzero(&newtio, sizeof(newtio));


newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR ;
newtio.c_oflag = 0;
newtio.c_lflag = 0;
newtio.c_cc[VINTR] = 0;
newtio.c_cc[VQUIT] = 0;
newtio.c_cc[VERASE] = 0;
newtio.c_cc[VKILL] = 0;
newtio.c_cc[VEOF] = 1;
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 1;
newtio.c_cc[VSWTC] = 0;
newtio.c_cc[VSTART] = 0;
newtio.c_cc[VSTOP] = 0;
newtio.c_cc[VSUSP] = 0;
newtio.c_cc[VEOL] = 0;
newtio.c_cc[VREPRINT] = 0;
newtio.c_cc[VDISCARD] = 0;
newtio.c_cc[VWERASE] = 0;
newtio.c_cc[VLNEXT] = 0;
newtio.c_cc[VEOL2] = 0;
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);

while (STOP==FALSE) {
res = read(fd,buf,1);
buf[res]=0;
std::cout << " Data: " << (int)buf[0]<< std::endl;
if (buf[0]=='z') STOP=TRUE;
}

tcsetattr(fd,TCSANOW,&oldtio);
std::cout<<"END"<<std::endl;

return 0;
}

void signal_handler_IO (int status)
{
wait_flag = FALSE;
printf("signal recived.\n");
przer++;
}
 
Old 07-21-2008, 07:00 AM   #2
pinniped
Senior Member
 
Registered: May 2008
Location: planet earth
Distribution: Debian
Posts: 1,732

Rep: Reputation: 50
Why do you think you lost interrupts?

Looking at the spec of an 8250-compatible chip that I just pulled out of a hat, I see that the device can be set to generate an interrupt when the receive buffer has 4,8, or 15 characters in it. An interrupt will also be generated if there is data in the receive buffer and the timeout has been exceeded.

Interrupts are also generated when the transmit buffer becomes empty; an interrupt may also be generated when the last character has actually finished transmission.

You never see these interrupts - they are processed by the device driver. The device driver may then post a signal to your process if you requested it.

You can edit the 8250.c code in your kernel and rebuild it with some 'printk' statements added so you can see when you receive an interrupt, whether it is related to transmit, receive, or status lines, and what data was received (if any) while processing that interrupt.


Also, your read() should be done in the signal handler (because you know there is data to be read), not in the main loop, and you should consume all the data available at that time. The main loop itself can just be:

while (1) sched_yield();

Last edited by pinniped; 07-21-2008 at 07:08 AM.
 
Old 07-21-2008, 07:29 AM   #3
muzic
LQ Newbie
 
Registered: Jul 2008
Posts: 7

Original Poster
Rep: Reputation: 0
I know that I can put the read function inside the signal hendler but I wanted to see how many signals I lost.

The problem is that I recive signals when the machine which is connect to rs232 transmit data very slowly but if it's going byte after byte it gives a reasult like:

20 bytes was sent by machine and I recived 9 signal for example.

I was trying to find information about signals and interrupts but almost everywhere it's the same informations but not useful for my problem.

I need information that the 1byte has come and then read this byte


And thank You for Your Help!
 
Old 07-21-2008, 12:00 PM   #4
estabroo
Senior Member
 
Registered: Jun 2008
Distribution: debian, ubuntu, sidux
Posts: 1,126
Blog Entries: 2

Rep: Reputation: 124Reputation: 124
With regards to the code, there really isn't any relation to when signal received was printed and when you got characters. printf is buffered io, so unless you are excplicitly flushing you don't know when it'll appear relative to anything else since you are also using cout which is buffered but is using a different buffering then printf.

I disagree with pinniped, I don't think you should do any I/O in a signal handler in a userspace program (including printing), those operations tend to take way to long and usually involve a call into kernelspace.

http://reality.sgiweb.org/davea/signals.html
 
Old 07-22-2008, 02:00 AM   #5
muzic
LQ Newbie
 
Registered: Jul 2008
Posts: 7

Original Poster
Rep: Reputation: 0
thx 4 Your help and the link to interesting hints

Back to the code:

When I'll delate printf or cout from signal handler, and there will be only incrementation of one int ( przer++; - inside the code)

void signal_handler_IO (int status)
{
przer++;
}


at the end of the program U will see that this int contains for example: 9 and we sent 20 bytes so we should have 20 inside przer.???
Am I right?

Maybe it's something with queue?
 
Old 07-22-2008, 06:03 AM   #6
pinniped
Senior Member
 
Registered: May 2008
Location: planet earth
Distribution: Debian
Posts: 1,732

Rep: Reputation: 50
Quote:
Originally Posted by estabroo View Post
I don't think you should do any I/O in a signal handler in a userspace program (including printing), those operations tend to take way to long and usually involve a call into kernelspace.
This isn't true of the Linux kernel, nor is it true of the serial driver code. The serial driver responds to interrupts generated by the UART (in special and somewhat rare cases the code uses an internal timer, dubbed the 'soft interrupt'); in the case of incoming data, the data is read and given to the Line Discipline code for filtering and any special actions. The Line Discipline will process the data and then decide if a signal will be set for the process using that device. The scheduler is notified and it is the scheduler which delivers the signal whenever the process is scheduled to run. So the signals all happen in user space; there is no kernel processing at all involved. So from the signal handling routine you simply read the device as you normally would. Signal handling routines are NOT atomic; they can be interrupted by the scheduler at any time.

Now for the multiple data arriving for each signal received - the most likely explanation is that the specific setup of the UART is resulting in multiple characters received before an interrupt is generated. To check this theory, hack the serial driver code and print out when an interrupt is delivered for incoming data, and print out the data. This will make a mess of your kernel log, but you'll have a better idea of what's happening. Of course you can send data maybe once per second - that should be slow enough to ensure that you get one signal per byte received.
 
Old 07-22-2008, 06:43 AM   #7
muzic
LQ Newbie
 
Registered: Jul 2008
Posts: 7

Original Poster
Rep: Reputation: 0
Quote:
Originally Posted by pinniped View Post
Now for the multiple data arriving for each signal received - the most likely explanation is that the specific setup of the UART is resulting in multiple characters received before an interrupt is generated. To check this theory, hack the serial driver code and print out when an interrupt is delivered for incoming data, and print out the data. This will make a mess of your kernel log, but you'll have a better idea of what's happening

I will try do that, I'm new in Linux but I hope that I can make it

Quote:
Originally Posted by pinniped View Post
Of course you can send data maybe once per second - that should be slow enough to ensure that you get one signal per byte received.
It's working perfect when I send data once per second and even less (every 9ms). When it works like that I have a signal information after 1 byte (exactly how i want.

I need to have as fast data sending as it possible.
So it should be without any delay...


I need it to control OpenGL using RS232 from another machine.

Last edited by muzic; 07-22-2008 at 05:49 PM.
 
Old 07-22-2008, 09:18 AM   #8
estabroo
Senior Member
 
Registered: Jun 2008
Distribution: debian, ubuntu, sidux
Posts: 1,126
Blog Entries: 2

Rep: Reputation: 124Reputation: 124
pinniped - I still have to disagree, doing a call to read() will cause a context switch via a syscall into the kernel. The only way to avoid it is to be using dma into a memory mapped area and in that case you aren't using a read().
 
Old 07-29-2008, 06:07 AM   #9
muzic
LQ Newbie
 
Registered: Jul 2008
Posts: 7

Original Poster
Rep: Reputation: 0
still have a problem
i don't know how to hack this serial code
 
Old 07-31-2008, 04:32 AM   #10
muzic
LQ Newbie
 
Registered: Jul 2008
Posts: 7

Original Poster
Rep: Reputation: 0
Could someone help me with this code!
Please

I need an interrupt or signal or something else wich will give me the information that 1 byte was send via RS232!
 
Old 07-31-2008, 09:09 AM   #11
theNbomr
LQ 5k Club
 
Registered: Aug 2005
Distribution: OpenSuse, Fedora, Redhat, Debian
Posts: 5,399
Blog Entries: 2

Rep: Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908
In termios.c_lflag, the ICANON flag seems to be appropriate.
From man termios:
Quote:
ICANON Enable canonical mode. This enables the special characters EOF, EOL, EOL2, ERASE, KILL, LNEXT, REPRINT, STATUS, and WERASE, and buffers by lines.
--- rod.
 
Old 08-02-2008, 07:59 PM   #12
pinniped
Senior Member
 
Registered: May 2008
Location: planet earth
Distribution: Debian
Posts: 1,732

Rep: Reputation: 50
The serial code will use the UART's FIFO if it exists - since your code works as you expect when you send at a low data rate, the multiple bytes in per interrupt is probably due to a timeout related to the setup of the UART and its FIFOs. I really don't see why this is a problem with your program; data will be transmitted/received, and if the FIFO isn't full and a certain time exceeded, an interrupt will be generated so that the FIFO can be read out. No data is lost, everything is working, and you don't generate a large number of unnecessary interrupts.
 
Old 08-11-2008, 04:13 AM   #13
muzic
LQ Newbie
 
Registered: Jul 2008
Posts: 7

Original Poster
Rep: Reputation: 0
Quote:
Originally Posted by pinniped View Post
The serial code will use the UART's FIFO if it exists - since your code works as you expect when you send at a low data rate, the multiple bytes in per interrupt is probably due to a timeout related to the setup of the UART and its FIFOs. I really don't see why this is a problem with your program; data will be transmitted/received, and if the FIFO isn't full and a certain time exceeded, an interrupt will be generated so that the FIFO can be read out. No data is lost, everything is working, and you don't generate a large number of unnecessary interrupts.

It's important to have interrupt exactly at same time that data has been recived.
I have machine connected to the PC by RS232, and on the PC i have some graphic objects in OpenGL which are controlled by that machine.

Need some help how to do that.
It can't work with FIFO, I must have realtime
 
Old 08-11-2008, 07:05 AM   #14
sundialsvcs
LQ Guru
 
Registered: Feb 2004
Location: SE Tennessee, USA
Distribution: Gentoo, LFS
Posts: 10,659
Blog Entries: 4

Rep: Reputation: 3941Reputation: 3941Reputation: 3941Reputation: 3941Reputation: 3941Reputation: 3941Reputation: 3941Reputation: 3941Reputation: 3941Reputation: 3941Reputation: 3941
A signal is designed to notify a process that it "should not be asleep right now."

Notice the subtle difference between this and "you should wake up now."

Device-drivers like serial ports can perform either synchronous I/O (the process goes to sleep when there's no data) or asynchronous (the process does not wait). In the latter case, you have to code for no timing-dependencies: anything can happen at any order at any time.

So... the process should consume as many bytes as it can, then wait for a signal. It's entirely possible that the signal arrived between the time that you decided "there are no more bytes" and the time that you waited for a signal. If you wait for a signal that has already arrived, the process does not sleep.

You have to code so that "the process might make extra loops with nothing to do," but "it will not miss a signal and sleep-forever."
 
Old 08-12-2008, 01:56 AM   #15
pinniped
Senior Member
 
Registered: May 2008
Location: planet earth
Distribution: Debian
Posts: 1,732

Rep: Reputation: 50
Did you try setting the "min" parameter to 0? According to the termios docs:

"If neither is set, the read will return immediately, only giving the currently already available characters."

So if there are characters present, respond to them - otherwise do other things. If you're still not happy because the UARTS don't generate interrupts frequently enough for you, you will need to hack the serial driver yourself to control the settings (generate interrupt when at least one character in FIFO). This is not trivial and you cannot expect this to work on all systems because other people simply will not install those hacked drivers.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
USB to RS232 (serial port)? - no I need to configure anything? Ultrus Linux - Hardware 12 08-16-2007 06:00 PM
serial port interrupts in user space pierre-luc Programming 2 04-24-2007 09:48 PM
console login through RS232/serial port ? Yalla-One Linux - Software 3 02-24-2005 04:12 PM
RS232 serial port programing blackzone Programming 2 08-11-2004 12:54 AM
serial port: RS232 RS574? blackzone Linux - Hardware 1 08-10-2004 04:15 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 05:34 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration