Gtk+ 和串行输入 (UART)
Gtk+ and serial input (UART)
我有以下问题:
我通过 UART(每 10 毫秒)接收传感器数据,我想在 gtk 中处理这些数据。这个想法是在特定的时间间隔内处理这些数据(例如每 100 毫秒)。因此,我使用 g_timeout_add() 函数。
被调用的函数包含用于 UART 通信的通用集 -> 在我的例子中它会阻塞,直到我收到 10 个字符。
这就是问题所在 - 在我的 read()(-> 系统调用)函数中程序挂断了。
我的串口设置如下:
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
无论我如何更改 g_timeout_add 函数中的间隔参数,都没有任何反应。
我读了很多关于这个问题的资料,但我找不到对我的问题的任何解释。此外,我什至找不到处理 UART - GTK 组合的线程。
代码很长,所以我现在只想问你,你觉得这个想法基本上可行吗,或者我的概念是否存在根本问题?
仅供参考:我在没有 GUI (->GTK) 的情况下测试了这段代码,它运行良好。在纯 C 中,我可以读取数据并将其打印出来。
我很高兴得到任何答案。
GTK+ 与大多数工具包一样是事件驱动的。事件在主循环中分派和处理,这是您进程的主线程。因此,您不能进行阻塞调用(例如 sleep(5)
),也不能进行需要很长时间才能在回调中执行的处理。
想想两个工人(主循环和回调)只有洞要挖,只有一把铲子(CPU 时间)。主循环发现有一个超时事件要处理,并将铲子交给回调,以便它可以工作。如果回调拿走了 5 个小时的铲子,主循环就不能完成它的工作(比如画 UI windows),也不能把铲子交给任何其他等待它的回调。
在您的情况下,您正在以阻塞方式等待数据。您正在等待 10 个字节到达。如果他们需要 5 个小时才能到达,您就保留铲子。要解决此问题,您需要:
- 在单独的线程中执行阻塞调用(这样两个工人都有铲子并且可以并行工作)
- 或异步执行(也许使用 GTask?我自己从未使用过)
- 或(效率较低)进行一些轮询,这已经是您已经在做的事情,因为您每 100 毫秒查找一次数据
对于轮询解决方案,您可能会实施 VMIN = 0 and VTIME > 0
情况,但您需要处理这样一个事实,即您必须重建消息,因为您可能会一次收到比预期更多的字节.
对于线程解决方案,我将使用 GThread
来执行阻塞 read
调用,就像您现在所做的那样,然后删除 g_timeout_add
。您不需要它,因为您将从轮询模型("did I have received data?" 每秒 10 次)更改为通知模型("you have received data!")。相反,当您在线程中读取了一些字节时,使用 g_idle_add
将它们发送到主循环。在关联的回调中,您将收到您的数据并将能够将其呈现给用户。但是请记住,你不应该从主线程以外的线程调用 GTK+ 函数,因为该工具包不是线程安全的。这就是为什么我们 g_idle_add
(来自 GLib,没关系)。
我有以下问题:
我通过 UART(每 10 毫秒)接收传感器数据,我想在 gtk 中处理这些数据。这个想法是在特定的时间间隔内处理这些数据(例如每 100 毫秒)。因此,我使用 g_timeout_add() 函数。 被调用的函数包含用于 UART 通信的通用集 -> 在我的例子中它会阻塞,直到我收到 10 个字符。 这就是问题所在 - 在我的 read()(-> 系统调用)函数中程序挂断了。
我的串口设置如下:
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
无论我如何更改 g_timeout_add 函数中的间隔参数,都没有任何反应。
我读了很多关于这个问题的资料,但我找不到对我的问题的任何解释。此外,我什至找不到处理 UART - GTK 组合的线程。
代码很长,所以我现在只想问你,你觉得这个想法基本上可行吗,或者我的概念是否存在根本问题?
仅供参考:我在没有 GUI (->GTK) 的情况下测试了这段代码,它运行良好。在纯 C 中,我可以读取数据并将其打印出来。
我很高兴得到任何答案。
GTK+ 与大多数工具包一样是事件驱动的。事件在主循环中分派和处理,这是您进程的主线程。因此,您不能进行阻塞调用(例如 sleep(5)
),也不能进行需要很长时间才能在回调中执行的处理。
想想两个工人(主循环和回调)只有洞要挖,只有一把铲子(CPU 时间)。主循环发现有一个超时事件要处理,并将铲子交给回调,以便它可以工作。如果回调拿走了 5 个小时的铲子,主循环就不能完成它的工作(比如画 UI windows),也不能把铲子交给任何其他等待它的回调。
在您的情况下,您正在以阻塞方式等待数据。您正在等待 10 个字节到达。如果他们需要 5 个小时才能到达,您就保留铲子。要解决此问题,您需要:
- 在单独的线程中执行阻塞调用(这样两个工人都有铲子并且可以并行工作)
- 或异步执行(也许使用 GTask?我自己从未使用过)
- 或(效率较低)进行一些轮询,这已经是您已经在做的事情,因为您每 100 毫秒查找一次数据
对于轮询解决方案,您可能会实施 VMIN = 0 and VTIME > 0
情况,但您需要处理这样一个事实,即您必须重建消息,因为您可能会一次收到比预期更多的字节.
对于线程解决方案,我将使用 GThread
来执行阻塞 read
调用,就像您现在所做的那样,然后删除 g_timeout_add
。您不需要它,因为您将从轮询模型("did I have received data?" 每秒 10 次)更改为通知模型("you have received data!")。相反,当您在线程中读取了一些字节时,使用 g_idle_add
将它们发送到主循环。在关联的回调中,您将收到您的数据并将能够将其呈现给用户。但是请记住,你不应该从主线程以外的线程调用 GTK+ 函数,因为该工具包不是线程安全的。这就是为什么我们 g_idle_add
(来自 GLib,没关系)。