C++ RS232/serial port - problem with signals or interrupts
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
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
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:
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
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.
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.
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
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.
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().
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.
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.
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
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."
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.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.