问题
I have following problem:
I receive sensor data via UART (every 10ms) and I want to process this data in gtk. The idea is to process this data in specific time intervalls (e.g. every 100ms). Due to that I use the g_timeout_add() function. The called function contains the common sets for UART communication -> in my case it blocks until I receive 10 chars. And here is the problem - at my read() (-> system call) function the program hang up.
My serial settings are as follows:
struct termios oldtio,newtio;
tcgetattr(fd,&oldtio);
bzero(&newtio, sizeof(newtio));
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
// set input mode (non-canonical, no echo,...)
newtio.c_lflag = 0;
newtio.c_cc[VTIME] = 0; // inter-character timer unused
newtio.c_cc[VMIN] = 10; // blocking read until 10 chars received
res = read(fd,buf,20); // returns after 10 chars have been input
Nothing happens, no matter how I change the interval parameter in the g_timeout_add function.
I read a lot about this issue, but I could not find any explanation for my problem. Further I could not even find threads dealing with UART - GTK combination.
The code is very long, due to that I just want to ask you for now if you think that this idea basically could work or if there is a fundamental problem in my concept?
FYI: I tested this code without GUI (->GTK) and it works perfect. In pure C I can read the data and printf it.
Im happy for any answer.
回答1:
GTK+ like most toolkits is event-driven. Events are dispatched and processed in a main loop, which is the main thread of your process. So you can't do blocking calls (for example a sleep(5)
) and you can't do processing that takes a long time to execute in a callback.
Think of two workers (the main loop and the callback) that only have holes to dig and only one shovel (CPU time). The main loop sees there's a timeout event to process and gives the shovel to the callback so it can work. If the callback takes the shovel for 5 hours, the main loop can't do its work (like painting the UI windows) nor give the shovel to any other callback waiting for it.
In your case, you're waiting for data in a blocking way. You're waiting for 10 bytes to arrive. Event if that takes them 5 hours to arrive, you keep the shovel. To fix that, you need to:
- do your blocking calls in a separate thread (so both workers have a shovel and can work in parallel)
- or do that asynchronously (maybe using GTask? Never used it myself)
- or (less efficient) do some polling, which is already what you are already doing since you are looking for data every 100ms
For the polling solution, you could probably implement the VMIN = 0 and VTIME > 0 case, but you'd need to process the fact that you'll have to rebuild the messages as you could receive more bytes than expected at once.
For the threading solution, I'd use a GThread
to perform the blocking read
calls like you're doing now, and remove g_timeout_add
. You won't need it as you'd change from a polling model ("did I have received data?" 10 times per second) to a notification model ("you have received data!"). Instead, when in your thread you have read some bytes, send them to the main loop with g_idle_add
. In the associated callback you'll receive your data and will be able to present it to the user. Please remember however that you should never call GTK+ functions from threads other than the main thread, as the toolkit is not-thread-safe. That's why we g_idle_add
(wich is from the GLib, which is ok).
来源:https://stackoverflow.com/questions/48330436/gtk-and-serial-input-uart