Linux 使用C++读取串口数据并且显示16进制

本文主要分享在Linux系统中(Ubutun),如何使用C++语言实现串口的数据读取,并以16进制的格式显示。

串口调试

在不知道串口是否正常工作的情况下,可用使用minicom进系调试

安装minicom

 sudo apt-get install minicom 

使用minicom打开串口,查看数据:

minicom -D /dev/ttyUSB0

确保串口是正常工作后,就可以开始测试代码了。

程序代码

创建一个文件,并且输入以下代码:

// 加载 打开串口并读取显示 用到的库
#include <iostream>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>

// 加载 实现显示16进制 用到的库
#include <sstream>
#include <iomanip>

// 加载 使用延时时会 用到的库
#include <thread>
#include <chrono>

int main()
{
    int fd;
    struct termios tty;
    char buffer[1024];
    ssize_t bytesRead;

    // 打开 /dev/ttyUSB0 串口端口
    fd = open("/dev/ttyUSB0", O_RDONLY | O_NOCTTY);

    if (fd == -1)
    {
        std::cerr << "Error opening serial port." << std::endl;
        return 1;
    }

    // 配置串口参数
    if (tcgetattr(fd, &tty) != 0)
    {
        std::cerr << "Error getting serial port attributes." << std::endl;
        close(fd);
        return 1;
    }

    // 配置波特率
    cfsetospeed(&tty, B115200);
    cfsetispeed(&tty, B115200);

    tty.c_cflag |= (CLOCAL | CREAD);
    tty.c_cflag &= ~PARENB;
    tty.c_cflag &= ~CSTOPB;
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;

    tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    tty.c_iflag &= ~(INPCK | ISTRIP);
    tty.c_oflag &= ~OPOST;

    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 0;

    if (tcsetattr(fd, TCSANOW, &tty) != 0)
    {
        std::cerr << "Error setting serial port attributes." << std::endl;
        close(fd);
        return 1;
    }

    // 读取串口数据并输出到控制台
    while (true)
    {
        bytesRead = read(fd, buffer, sizeof(buffer));

        if (bytesRead == -1)
        {
            std::cerr << "Error reading serial port." << std::endl;
            close(fd);
            return 1;
        }

        if (bytesRead > 0)
        {
            std::stringstream ss;
            ss << std::hex;

            for(int i=0; i<bytesRead; i++) {
              ss << std::setw(2) << std::setfill('0') << (int)buffer[i];
              std::cout << (int)buffer[i];  // 原始数据显示
            }
                // 16进制显示
            std::string hex_str = ss.str();

            std::cout << "Hex: " << hex_str << std::endl;
            std::cout << '\n';
        }

        std::this_thread::sleep_for(std::chrono::milliseconds(500));

    }

    // 关闭串口
    close(fd);

    return 0;
}

保存文件,使用g++编译文件:

g++ <file_name.cpp> -o <function_name>

之后在当前目录会生成一个编译好的执行文件:<function_name>

运行该程序:

./<function_name>

会看到串口的输出:

image-20230914181904244

错误排除

Error reading serial port.

出现 Error reading serial port. 可能是串口设备的权属问题。

方法一:更改设备属主

1,使用以下命令,查看设备的属主:

ls -l /dev/ttyUSB0

image-20230914180645114

默认属主通常是root。

2,改变串口设备的属主为当前用户:

sudo chown $USER /dev/ttyUSB0

可以更改root用户为当前用户,再运行代码测试。

3,改变串口设备的属组为 dialout 或 tty :

sudo chgrp dialout /dev/ttyUSB0

4,设置串口设备的权限为660:

sudo chmod 660 /dev/ttyUSB0

这样ttyUSB0的属主就改为了bbot,属组为dialout,bbot用户就可以直接访问串口设备了。

5,重启可插拔设备服务更新规则:

sudo udevadm trigger

每次改变串口设备权限后,都需要重启udev服务使改动生效。

这样通过改变属主和属组,可以方便地将ttyUSB0的访问权限转移给当前用户。

方法二:把当前用户添加到dialout用户组

在Linux中,可以通过改变串口设备文件的用户组和权限来修改ttyUSB0的用户访问权限。

  1. 查看当前串口用户组和权限:
ls -l /dev/ttyUSB0

默认是root用户和dialout组拥有读写权限。

  1. 将当前用户加入dialout组:
sudo usermod -a -G dialout $USER

这会将当前用户加入dialout组,无需重启即可生效。

  1. 修改设备文件的组:
sudo chgrp dialout /dev/ttyUSB0

将串口设备文件的组改为dialout。

  1. 修改设备文件权限:
sudo chmod 660 /dev/ttyUSB0

设置为dialout组用户可读写,其他用户不可访问。

这样当前用户就可以通过dialout组访问该串口设备了。

也可以通过添加用户到tty组,或直接修改设备文件的用户等方式改变串口的访问权限。