树莓派3B:桌面串口调试助手

# 实现桌面串口调试助手一般需要用到Linux下的GUI库,如GTK或Qt等。这里我们以GTK为例,介绍如何在树莓派3B上用C语言实现一个简单的串口调试助手。

1. 安装GTK库

        在终端中输入以下命令来安装GTK库:

sudo apt-get install libgtk-3-dev

2. 创建工程

        创建一个新文件夹,并在该文件夹下创建一个名为`main.c`的文件,该文件将包含整个程序的源代码。

mkdir gtk_serialport_debug_tool
cd gtk_serialport_debug_tool
touch main.c

3. 编写GUI界面在`main.c`文件中添加如下代码,创建一个基本的GUI界面:

#include <gtk/gtk.h>

int main(int argc, char *argv[]) {
  GtkWidget *window;

  // 初始化GTK库
  gtk_init(&argc, &argv);

  // 创建主窗口
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), "Serial Debug Tool");
  gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

  // 显示窗口
  gtk_widget_show(window);

  // 运行事件循环
  gtk_main();

  return 0;
}

4. 运行程序

        在终端中输入以下命令编译程序:

gcc main.c -o serial_debug_tool `pkg-config --cflags --libs gtk+-3.0`

        其中`pkg-config --cflags --libs gtk+-3.0`会自动帮你添加GTK库所需的编译参数。接着输入以下命令运行程序:

./serial_debug_tool

        这时将会弹出一个包含标题为"Serial Debug Tool"的空白窗口。

5. 添加串口连接功能

        在窗口中添加串口连接相关的控件,并编写串口连接的函数。

#include <gtk/gtk.h>
#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
#include <unistd.h>

#define BAUDRATE B115200
#define SERIAL_PORT "/dev/ttyUSB0"

int fd; // 串口文件描述符

// 串口连接函数
void connect_serial_port(GtkButton *button, gpointer data) {
  GtkWidget *port_entry = (GtkWidget *)data;
  const char *port_name = gtk_entry_get_text(GTK_ENTRY(port_entry));

  // 打开串口
  fd = open(port_name, O_RDWR | O_NOCTTY | O_NDELAY);
  if (fd == -1) {
    printf("unable to open serial port!\n");
    return;
  }

  // 配置串口参数
  struct termios options;
  tcgetattr(fd, &options);
  cfsetispeed(&options, BAUDRATE);
  cfsetospeed(&options, BAUDRATE);
  options.c_cflag |= (CLOCAL | CREAD);
  options.c_cflag &= ~PARENB;
  options.c_cflag &= ~CSTOPB;
  options.c_cflag &= ~CSIZE;
  options.c_cflag |= CS8;
  options.c_cflag &= ~CRTSCTS;
  tcsetattr(fd, TCSANOW, &options);
}

int main(int argc, char *argv[]) {
  GtkWidget *window;
  GtkWidget *port_entry;
  GtkWidget *connect_button;
  GtkWidget *hbox;

  // 初始化GTK库
  gtk_init(&argc, &argv);

  // 创建主窗口
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), "Serial Debug Tool");
  gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

  // 创建串口连接相关的控件
  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
  gtk_container_add(GTK_CONTAINER(window), hbox);
  port_entry = gtk_entry_new();
  gtk_entry_set_text(GTK_ENTRY(port_entry), SERIAL_PORT);
  gtk_box_pack_start(GTK_BOX(hbox), port_entry, TRUE, TRUE, 5);
  connect_button = gtk_button_new_with_label("Connect");
  g_signal_connect(connect_button, "clicked", G_CALLBACK(connect_serial_port), port_entry);
  gtk_box_pack_start(GTK_BOX(hbox), connect_button, FALSE, FALSE, 5);

  // 显示窗口
  gtk_widget_show_all(window);

  // 运行事件循环
  gtk_main();

  return 0;
}

        在窗口中添加了一个包含串口设备路径的文本输入框,一个“Connect”按钮,当单击“Connect”按钮时,调用`connect_serial_port`函数来打开和配置串口参数。

6. 添加读取串口数据功能

        在窗口中添加一个显示串口收到数据的文本框,并在读取串口数据的线程中更新该文本框的内容。

#include <gtk/gtk.h>
#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

#define BAUDRATE B115200
#define SERIAL_PORT "/dev/ttyUSB0"

int fd; // 串口文件描述符
pthread_t read_thread_id; // 读取串口数据的线程ID
GtkWidget *recv_text_view; // 显示串口收到的数据的文本框

// 串口连接函数
void connect_serial_port(GtkButton *button, gpointer data) {
  GtkWidget *port_entry = (GtkWidget *)data;
  const char *port_name = gtk_entry_get_text(GTK_ENTRY(port_entry));

  // 打开串口
  fd = open(port_name, O_RDWR | O_NOCTTY | O_NDELAY);
  if (fd == -1) {
    printf("unable to open serial port!\n");
    return;
  }

  // 配置串口参数
  struct termios options;
  tcgetattr(fd, &options);
  cfsetispeed(&options, BAUDRATE);
  cfsetospeed(&options, BAUDRATE);
  options.c_cflag |= (CLOCAL | CREAD);
  options.c_cflag &= ~PARENB;
  options.c_cflag &= ~CSTOPB;
  options.c_cflag &= ~CSIZE;
  options.c_cflag |= CS8;
  options.c_cflag &= ~CRTSCTS;
  tcsetattr(fd, TCSANOW, &options);

  // 创建读取串口数据的线程
  if (pthread_create(&read_thread_id, NULL, (void *(*)(void *))read_serial_port, (void *)recv_text_view) != 0) {
    printf("unable to start read thread!\n");
    return;
  }
}

// 读取串口数据的函数
void *read_serial_port(GtkWidget *text_view) {
  char buf[1024];
  int len = 0;

  GtkTextIter end;
  GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
  gtk_text_buffer_get_end_iter(buffer, &end);

  // 一直读取串口数据直到程序结束
  while (1) {
    len = read(fd, buf, sizeof(buf));
    if (len > 0) {
      // 将收到的数据显示在文本框中
      buf[len] = '\0';
      gtk_text_buffer_insert(buffer, &end, buf, -1);
      gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(text_view), gtk_text_buffer_get_insert(buffer));
    }
    usleep(10000);
  }
}

int main(int argc, char *argv[]) {
  GtkWidget *window;
  GtkWidget *port_entry;
  GtkWidget *connect_button;
  GtkWidget *hbox;
  GtkWidget *scroll_win;

  // 初始化GTK库
  gtk_init(&argc, &argv);

  // 创建主窗口
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), "Serial Debug Tool");
  gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

  // 创建串口连接相关的控件
  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
  gtk_container_add(GTK_CONTAINER(window), hbox);
  port_entry = gtk_entry_new();
  gtk_entry_set_text(GTK_ENTRY(port_entry), SERIAL_PORT);
  gtk_box_pack_start(GTK_BOX(hbox), port_entry, TRUE, TRUE, 5);
  connect_button = gtk_button_new_with_label("Connect");
  g_signal_connect(connect_button, "clicked", G_CALLBACK(connect_serial_port), port_entry);
  gtk_box_pack_start(GTK_BOX(hbox), connect_button, FALSE, FALSE, 5);

  // 创建显示串口收到数据的文本框
  recv_text_view = gtk_text_view_new();
  gtk_text_view_set_editable(GTK_TEXT_VIEW(recv_text_view), FALSE);
  gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(recv_text_view), FALSE);
  scroll_win = gtk_scrolled_window_new(NULL, NULL);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_win), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll_win), recv_text_view);
  gtk_box_pack_start(GTK_BOX(GTK_BOX(window)), scroll_win, TRUE, TRUE, 0);

  // 显示窗口
  gtk_widget_show_all(window);

  // 运行事件循环
  gtk_main();

  return 0;
}

        在程序中,将`recv_text_view`定义为全局变量,并在连接串口时创建读取串口数据的线程。在读取串口数据的线程中,一直循环读取串口数据,并将收到的数据显示在`recv_text_view`中。

7. 完整代码

#include <gtk/gtk.h>
#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

#define BAUDRATE B115200
#define SERIAL_PORT "/dev/ttyUSB0"

int fd; // 串口文件描述符
pthread_t read_thread_id; // 读取串口数据的线程ID
GtkWidget *recv_text_view; // 显示串口收到的数据的文本框
void *read_serial_port(GtkWidget *text_view);
// 串口连接函数
void connect_serial_port(GtkButton *button, gpointer data) {
  GtkWidget *port_entry = (GtkWidget *)data;
  const char *port_name = gtk_entry_get_text(GTK_ENTRY(port_entry));

  // 打开串口
  fd = open(port_name, O_RDWR | O_NOCTTY | O_NDELAY);
  if (fd == -1) {
    printf("unable to open serial port!\n");
    return;
  }

  // 配置串口参数
  struct termios options;
  tcgetattr(fd, &options);
  cfsetispeed(&options, BAUDRATE);
  cfsetospeed(&options, BAUDRATE);
  options.c_cflag |= (CLOCAL | CREAD);
  options.c_cflag &= ~PARENB;
  options.c_cflag &= ~CSTOPB;
  options.c_cflag &= ~CSIZE;
  options.c_cflag |= CS8;
  options.c_cflag &= ~CRTSCTS;
  tcsetattr(fd, TCSANOW, &options);

  // 创建读取串口数据的线程
  if (pthread_create(&read_thread_id, NULL, (void *(*)(void *))read_serial_port, (void *)recv_text_view) != 0) {
    printf("unable to start read thread!\n");
    return;
  }
}

// 读取串口数据的函数
void *read_serial_port(GtkWidget *text_view) {
  char buf[1024];
  int len = 0;

  GtkTextIter end;
  GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
  gtk_text_buffer_get_end_iter(buffer, &end);

  // 一直读取串口数据直到程序结束
  while (1) {
    len = read(fd, buf, sizeof(buf));
    if (len > 0) {
      // 将收到的数据显示在文本框中
      buf[len] = '\0';
      gtk_text_buffer_insert(buffer, &end, buf, -1);
      gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(text_view), gtk_text_buffer_get_insert(buffer));
    }
    usleep(10000);
  }
}

int main(int argc, char *argv[]) {
  GtkWidget *window;
  GtkWidget *port_entry;
  GtkWidget *connect_button;
  GtkWidget *hbox;
  GtkWidget *scroll_win;

  // 初始化GTK库
  gtk_init(&argc, &argv);

  // 创建主窗口
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), "Serial Debug Tool");
  gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

  // 创建串口连接相关的控件
  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
  gtk_container_add(GTK_CONTAINER(window), hbox);
  port_entry = gtk_entry_new();
  gtk_entry_set_text(GTK_ENTRY(port_entry), SERIAL_PORT);
  gtk_box_pack_start(GTK_BOX(hbox), port_entry, TRUE, TRUE, 5);
  connect_button = gtk_button_new_with_label("Connect");
  g_signal_connect(connect_button, "clicked", G_CALLBACK(connect_serial_port), port_entry);
  gtk_box_pack_start(GTK_BOX(hbox), connect_button, FALSE, FALSE, 5);

  // 创建显示串口收到数据的文本框
  recv_text_view = gtk_text_view_new();
  gtk_text_view_set_editable(GTK_TEXT_VIEW(recv_text_view), FALSE);
  gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(recv_text_view), FALSE);
  scroll_win = gtk_scrolled_window_new(NULL, NULL);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_win), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  //gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll_win), recv_text_view);   //报错:用gtk_container_add()替代
  gtk_container_add(GTK_CONTAINER(scroll_win), recv_text_view);
  gtk_box_pack_start(GTK_BOX(GTK_BOX(window)), scroll_win, TRUE, TRUE, 0);

  // 显示窗口
  gtk_widget_show_all(window);

  // 运行事件循环
  gtk_main();

  return 0;
}

        这个程序可能还有许多需要完善的地方,例如界面的美化、串口参数的设置等等,但这个程序足以完成基本的串口调试功能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值