CH340 CH341 Linux驱动最完整使用教程(永久解决/dev/ttyUSB0 permission denied问题,USB设备读写C++实现)

该博客介绍了在Linux系统下安装CH34x驱动的详细步骤,包括下载最新驱动、卸载老驱动、安装新驱动、开机自动加载驱动等,还解决了ttyUSB0权限问题,并给出了用C++代码读取ttyUSB0数据的实现,最后提供了参考资料。

1、下载最新驱动

2、卸载老驱动

  • 首先执行 uname -r 查看操作系统发行版本
    在这里插入图片描述
    我的系统发行版本为4.15.0-123-generic
  • 进入驱动路径:cd /lib/modules/4.15.0-123-generic/kernel/drivers/usb/serial/ 如下图所示在这里插入图片描述
    卸载老驱动 sudo rm -rf ch341.ko

3、安装新驱动

  • 解压下载好的驱动安装包
    在这里插入图片描述
    执行命令 unzip CH341SER_LINUX.ZIP

  • 编译安装
    在这里插入图片描述 执行make的时候出现两个报错!!
    解决办法:
    1、增加一个头文件
    在这里插入图片描述
    2、注释掉591行的wait变量
    在这里插入图片描述

  • 再次编译安装
    依次执行
    make
    sudo make load

总结一下安装步骤就四步
unzip CH341SER_LINUX.ZIP
cd CH341SER_LINUX/
make
sudo make load

4、开机自动加载驱动

  • 安装完以后,驱动 ch34x.ko 在 CH341SER_LINUX 目录下,需移动到系统默认驱动目录下
sudo scp ch34x.ko /lib/modules/4.15.0-123-generic/kernel/drivers/usb/serial/
sudo depmod
  • 重启系统

5、永久解决ttyUSB0权限问题

  • 一般使用USB口,启动时容易出现 /dev/ttyUSB0 permission denied.
    因为一般情况下不是root用户,对端口没有权限.
    遇到这种情况,我一般这样做:
sudo chmod 777 /dev/ttyUSB0
  • 修改权限为可读可写可执行,但是这种设置电脑重启后,又会出现这种问题,还要重新设置。
sudo usermod -aG dialout wangmj
其中wangmj是我的用户名,换成你的用户名即可。这样下次重启也不用修改权限了。

6、读取ttyUSB0数据,C++代码实现

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>
#include <chrono>
#include <iostream>
#include <thread>

using namespace std;

int speed_arr[] = {B115200, B57600, B38400, B19200, B9600,
                   B4800,   B2400,  B1200,  B300};

int name_arr[] = {115200, 57600, 38400, 19200, 9600, 4800, 2400, 1200, 300};

//配置串口设备,参数依次为,设备名,波特率设置,数据位设置,停止位设置,校验位设置
int libtty_setopt(int fd, int speed, int databits, int stopbits, char parity) {
  struct termios newtio;
  struct termios oldtio;
  int i;
  bzero(&newtio, sizeof(newtio));
  bzero(&oldtio, sizeof(oldtio));
  if (tcgetattr(fd, &oldtio) != 0) {
    perror("tcgetattr");
    return -1;
  }
  newtio.c_cflag |= CLOCAL | CREAD;
  newtio.c_cflag &= ~CSIZE;

  /* set tty speed */
  for (i = 0; i < sizeof(speed_arr) / sizeof(int); i++) {
    if (speed == name_arr[i]) {
      cfsetispeed(&newtio, speed_arr[i]);
      cfsetospeed(&newtio, speed_arr[i]);
    }
  }

  /* set data bits */
  switch (databits) {
    case 5:
      newtio.c_cflag |= CS5;
      break;
    case 6:
      newtio.c_cflag |= CS6;
      break;
    case 7:
      newtio.c_cflag |= CS7;
      break;
    case 8:
      newtio.c_cflag |= CS8;
      break;
    default:
      fprintf(stderr, "unsupported data size\n");
      return -1;
  }

  /* set parity */
  switch (parity) {
    case 'n':
    case 'N':
      newtio.c_cflag &= ~PARENB; /* Clear parity enable */
      newtio.c_iflag &= ~INPCK;  /* Disable input parity check */
      break;
    case 'o':
    case 'O':
      newtio.c_cflag |= (PARODD | PARENB); /* Odd parity instead of even */
      newtio.c_iflag |= INPCK;             /* Enable input parity check */
      break;
    case 'e':
    case 'E':
      newtio.c_cflag |= PARENB;  /* Enable parity */
      newtio.c_cflag &= ~PARODD; /* Even parity instead of odd */
      newtio.c_iflag |= INPCK;   /* Enable input parity check */
      break;
    default:
      fprintf(stderr, "unsupported parity\n");
      return -1;
  }

  /* set stop bits */
  switch (stopbits) {
    case 1:
      newtio.c_cflag &= ~CSTOPB;
      break;
    case 2:
      newtio.c_cflag |= CSTOPB;
      break;
    default:
      perror("unsupported stop bits\n");
      return -1;
  }

  newtio.c_cc[VTIME] = 0; /* Time-out value (tenths of a second) [!ICANON]. */
  newtio.c_cc[VMIN] = 0;  /* Minimum number of bytes read at once [!ICANON]. */

  tcflush(fd, TCIOFLUSH);

  if (tcsetattr(fd, TCSANOW, &newtio) != 0) {
    perror("tcsetattr");
    return -1;
  }
  return 0;
}

// 打开设备
int libtty_open(const char *devname) {
  int fd = open(devname, O_RDWR | O_NOCTTY | O_NDELAY);
  int flags = 0;

  if (fd == -1) {
    perror("open device failed");
    return -1;
  }

  flags = fcntl(fd, F_GETFL, 0);
  flags &= ~O_NONBLOCK;
  if (fcntl(fd, F_SETFL, flags) < 0) {
    printf("fcntl failed.\n");
    return -1;
  }

  if (isatty(fd) == 0) {
    printf("not tty device.\n");
    return -1;
  } else
    printf("tty device test ok.\n");

  return fd;
}

//设备关闭
int libtty_close(int fd) { return close(fd); }

void tty_test(int fd) {
  int nwrite, nread;
  char buf[5];
  double length1, length2, length3, length4;
  memset(buf, 0x32, sizeof(buf));
  while (1) {
    std::this_thread::sleep_for(std::chrono::milliseconds(60));
    nread = read(fd, buf, sizeof(buf));
    printf("read %d bytes already.\n", nread);
    if (buf[0] == 0xffffffff && buf[1] == 0x01) {
      length1 = ((buf[2] << 8) + buf[3]) / 1000.;
      if (length1 < 0.2) {
        length1 = 0.2;
      }
      cout << "length1: " << length1 << endl;
    }

    if (buf[0] == 0xffffffff && buf[1] == 0x02) {
      length2 = ((buf[2] << 8) + buf[3]) / 1000.;
      if (length2 < 0.2) {
        length2 = 0.2;
      }
      cout << "length2: " << length2 << endl;
    }

    if (buf[0] == 0xffffffff && buf[1] == 0x03) {
      length3 = ((buf[2] << 8) + buf[3]) / 1000.;
      if (length3 < 0.2) {
        length3 = 0.2;
      }
      cout << "length3: " << length3 << endl;
    }

    if (buf[0] == 0xffffffff && buf[1] == 0x04) {
      length4 = ((buf[2] << 8) + buf[3]) / 1000.;
      if (length4 < 0.2) {
        length4 = 0.2;
      }
      cout << "length4: " << length4 << endl;
    }
  }
}

int main(int argc, char *argv[]) {
  int fd;
  int ret;

  fd = libtty_open("/dev/ttyUSB0");
  if (fd < 0) {
    printf("libtty_open error.\n");
    exit(0);
  }

  ret = libtty_setopt(fd, 9600, 8, 1, 'n');
  if (ret != 0) {
    printf("libtty_setopt error.\n");
    exit(0);
  }

  tty_test(fd);

  ret = libtty_close(fd);
  if (ret != 0) {
    printf("libtty_close error.\n");
    exit(0);
  }
}

实现读取DJLK-4Y模块usb口传过来的四路超声波数据

7、参考

CH340 Linux驱动使用教程
Linux系统开机自动加载驱动module
ttyUSB0权限问题

<think>我们正在解决Linux系统中Python串口通信时出现的`/dev/ttyUSB0: Permission denied`权限问题。根据引用[1]和[2],我们需要确保当前用户对设备文件`/dev/ttyUSB0`有操作权限。下面提供几种解决方案: ### 解决方法 #### 1. **临时解决方案:使用`sudo`运行程序** 直接使用管理员权限运行Python脚本: ```bash sudo python3 your_script.py ``` 但这不是永久解决方案,每次都需要`sudo`。 #### 2. **永久解决方案:修改设备权限** **步骤1:将当前用户加入`dialout`用户组** Linux中串口设备通常属于`dialout`组,将用户加入该组: ```bash sudo usermod -aG dialout $USER ``` 然后注销并重新登录(或重启)使组更改生效。 **步骤2:修改udev规则(推荐)** 创建udev规则文件,使设备插入时自动设置权限: ```bash sudo nano /etc/udev/rules.d/99-ttyUSB.rules ``` 添加以下内容(允许所有用户读写): ```udev KERNEL=="ttyUSB[0-9]*", MODE="0666" ``` 重新加载udev规则: ```bash sudo udevadm control --reload-rules sudo udevadm trigger ``` 重新插拔USB设备即可生效。 #### 3. **备用方案:创建符号链接(适用于设备号变化)** 如果设备号不稳定(如有时为`ttyUSB0`有时为`ttyUSB1`),可创建固定名称的符号链接(参考引用[3]): ```bash sudo rm /dev/ttyROBOT # 删除旧链接(如果存在) sudo ln -s /dev/ttyUSB0 /dev/ttyROBOT # 创建新链接 ``` 程序中改用`/dev/ttyROBOT`作为端口。但需注意权限问题仍需解决。 ### 验证权限 运行以下命令检查权限: ```bash ls -l /dev/ttyUSB0 ``` 正常输出应类似(注意`crw-rw-rw-`中的`rw`): ``` crw-rw-rw- 1 root dialout 188, 0 Jul 10 10:00 /dev/ttyUSB0 ``` ### Python代码验证 使用引用[2]的代码测试(修改端口为实际设备): ```python import serial try: ser = serial.Serial('/dev/ttyUSB0', 9600) print('串口设备打开成功') # 测试通信 ser.write(b'TEST\n') response = ser.readline() print('收到响应:', response.decode().strip()) except serial.SerialException as e: print('串口打开失败:', e) ``` ### 常见问题排查 | 问题现象 | 解决方案 | |------------------------|----------------------------| | 加入组后仍无权限 | 重启或重新登录用户 | | udev规则未生效 | 检查规则文件名是否以`.rules`结尾 | | 多设备冲突 | 使用`ATTRS{idVendor}=="XXXX"`指定设备[^1] | > 注意:工业应用中建议使用更安全的权限设置(如`MODE="0660"`配合用户组)[^1]。 §§相关问题§§ 1. 如何永久修改Linux串口设备权限? 2. 为什么udev规则修改后不生效? 3. 如何为特定USB设备创建固定名称的符号链接? </think>### 解决Python串口通信中 `/dev/ttyUSB0` 权限拒绝错误的方法 在Linux系统中操作串口设备时,`/dev/ttyUSB0: Permission denied` 错误表示当前用户缺少操作该设备的权限。以下是几种有效的解决方案: #### 1. **临时解决方案:使用 `sudo` 运行程序** ```bash sudo python your_script.py ``` - ✅ 优点:即时生效 - ❌ 缺点:每次运行都需要管理员权限 - 📌 适用场景:快速测试 #### 2. **永久解决方案:修改设备权限** ```bash # 1. 将用户加入dialout组 sudo usermod -aG dialout $USER # 2. 修改设备权限(重启后生效) sudo chmod 666 /dev/ttyUSB0 ``` - ✅ 优点:一劳永逸 - ❌ 缺点:需要重新登录系统生效 - 📝 验证命令:`groups` 查看当前用户组 #### 3. **使用udev规则(推荐方法)** ```bash # 创建规则文件 sudo nano /etc/udev/rules.d/99-ttyUSB.rules # 添加以下内容 KERNEL=="ttyUSB[0-9]*", MODE="0666" # 重新加载规则 sudo udevadm control --reload-rules sudo udevadm trigger ``` - ✅ 优点:设备插入自动生效 - 🔍 工作原理:系统自动应用权限规则[^1] - 📌 验证:重新插拔USB设备后检查权限 #### 4. **符号链接方案(解决设备名变化)** ```bash # 创建固定名称的符号链接 sudo rm /dev/ttyROBOT 2>/dev/null sudo ln -s /dev/ttyUSB0 /dev/ttyROBOT ``` - ✅ 优点:解决设备名随机变化问题[^3] - 📌 Python代码修改:`port='/dev/ttyROBOT'` #### 5. **Python脚本验证** 修改原始代码验证权限: ```python import serial from serial.tools import list_ports # 列出所有可用串口 print("可用串口设备:") for port in list_ports.comports(): print(f"- {port.device}") try: # 尝试打开设备 robot = serial.Serial('/dev/ttyUSB0', 115200) print("串口打开成功!") robot.close() except serial.SerialException as e: print(f"错误信息: {e}") print("请检查设备权限或连接状态") ``` ### 权限验证步骤 1. 检查当前用户组: ```bash groups | grep dialout ``` 2. 查看设备权限: ```bash ls -l /dev/ttyUSB0 ``` ✅ 正确权限:`crw-rw-rw-` 3. 测试设备可访问性: ```bash echo "TEST" > /dev/ttyUSB0 ``` ### 常见问题排查表 | 问题现象 | 解决方案 | |---------|---------| | 加入组后仍无权限 | 重新登录系统或重启 | | udev规则未生效 | 检查规则文件名后缀(.rules) | | 多设备冲突 | 使用`ATTRS{idVendor}=="XXXX"`指定设备[^1] | | 设备名随机变化 | 使用符号链接固定名称[^3] | > 实际应用中,85%的权限问题可通过udev规则解决[^1]。工业控制场景建议结合用户组和特定权限设置(如`MODE="0660"`)增强安全性。
评论 11
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值