最近在Jetson Nano上发现个问题,插多个USB-TTL后,串口号会发生变化。看来笔者之前
C/C++语言读写UART或USB串口数据https://blog.csdn.net/qq_25662827/article/details/122581819的同学应该都知道,笔者在代码中是将端口号写死了的。
面对多变的串口,我们应该怎么办呢?本篇笔者就分享一下,使用C/C++获取可用串口的方法。
之前也说过,LIUNX下万物皆文件,每个串口的注册实际上就会生成一个文件,我们要获取可用串口,也就是要查找当前的文件名。废话不多说,还是先献上代码:
#include <stdlib.h>
#include <dirent.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <linux/serial.h>
#include <iostream>
#include <list>
using namespace std;
static string get_driver(const string& tty);
static void register_comport( list<string>& comList, list<string>& comList8250, const string& dir);
static void probe_serial8250_comports(list<string>& comList, list<string> comList8250);
list<string> getComList();
int main() {
list<string> l = getComList();
list<string>::iterator it = l.begin();
while (it != l.end()) {
cout << *it << endl;
it++;
}
return 0;
}
static string get_driver(const string& tty) {
struct stat st;
string devicedir = tty;
// Append '/device' to the tty-path
devicedir += "/device";
// Stat the devicedir and handle it if it is a symlink
if (lstat(devicedir.c_str(), &st)==0 && S_ISLNK(st.st_mode)) {
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
// Append '/driver' and return basename of the target
devicedir += "/driver";
if (readlink(devicedir.c_str(), buffer, sizeof(buffer)) > 0)
return basename(buffer);
}
return "";
}
static void register_comport( list<string>& comList, list<string>& comList8250, const string& dir) {
// Get the driver the device is using
string driver = get_driver(dir);
// Skip devices without a driver
if (driver.size() > 0) {
string devfile = string("/dev/") + basename(dir.c_str());
// Put serial8250-devices in a seperate list
if (driver == "serial8250") {
comList8250.push_back(devfile);
} else
comList.push_back(devfile);
}
}
static void probe_serial8250_comports(list<string>& comList, list<string> comList8250) {
struct serial_struct serinfo;
list<string>::iterator it = comList8250.begin();
// Iterate over all serial8250-devices
while (it != comList8250.end()) {
// Try to open the device
int fd = open((*it).c_str(), O_RDWR | O_NONBLOCK | O_NOCTTY);
if (fd >= 0) {
// Get serial_info
if (ioctl(fd, TIOCGSERIAL, &serinfo)==0) {
// If device type is no PORT_UNKNOWN we accept the port
if (serinfo.type != PORT_UNKNOWN)
comList.push_back(*it);
}
close(fd);
}
it ++;
}
}
list<string> getComList() {
int n;
struct dirent **namelist;
list<string> comList;
list<string> comList8250;
const char* sysdir = "/sys/class/tty/";
// Scan through /sys/class/tty - it contains all tty-devices in the system
n = scandir(sysdir, &namelist, NULL, NULL);
if (n < 0)
perror("scandir");
else {
while (n--) {
if (strcmp(namelist[n]->d_name,"..") && strcmp(namelist[n]->d_name,".")) {
// Construct full absolute file path
string devicedir = sysdir;
devicedir += namelist[n]->d_name;
// Register the device
register_comport(comList, comList8250, devicedir);
}
free(namelist[n]);
}
free(namelist);
}
// Only non-serial8250 has been added to comList without any further testing
// serial8250-devices must be probe to check for validity
probe_serial8250_comports(comList, comList8250);
// Return the lsit of detected comports
return comList;
}
笔者将代码文件命名为find.cpp,因此用命令行编译为
g++ -o find.o find.cpp
测试
在Jetson Nano B01下进行测试。
在没有插入设备的时候,由于板子自带了RX TX引脚,因此能获得三个串口设备
然后,插上了USB-TTL后,可以得到
多了个ttyUSB1。
优化
这个方法在单端口设备的时候挺有用,我们的目标仅仅是获取ttyUSB*,怎么办呢?我们只需要在main里面做改动。
while (it != l.end()) {
// cout << *it << endl;
string temp = *it;
cout << temp << endl;
if(temp.find("/dev/ttyUSB") != string::npos)
cout << "1" << endl;
it++;
}
这段代码,实现了当发现ttyUSB*的时候就打个1出来。测试情况如下:
其他改动,就留给读者结合自己的项目情况自行展开了吧。
闲谈
那么多端口设备怎么办?笔者的方法的话,会再加个端口信息校验。当连接上串口后,通过接收到设备信息来进行设备身份识别。具体的实现方法就读者自己完善吧,自己动手,丰衣足食。