Linux纯C串口开发

news2024/11/25 21:48:17

为什么要用纯C语言

为了数据流动加速,实现低配CPU建立高速数据流而不用CPU干预,避免串口数据流多次反复上升到软件应用层又下降低到硬件协议层。

在这里插入图片描述

关于termios.h

麻烦的是,在 Linux 中使用串口并不是一件最简单的事情。在处理 termios.h 标头时,有许多挑剔的设置隐藏在多个字节的位字段中。本文将试图帮助解释这些设置并向您展示如何在 Linux 中正确配置串行端口。

在这里插入图片描述

一切都是文件

在典型的 UNIX 风格中,串行端口由操作系统中的文件表示。这些文件通常在 /dev/ 中弹出,并以名称 tty* 开头。常见的名称如下:

  1. /dev/ttyACM0: ACM 代表 USB 总线上的 ACM 调制解调器。 Arduino UNO(和类似的)将使用此名称出现。
  2. /dev/ttyPS0:运行基于 Yocto 的 Linux 版本的 Xilinx Zynq FPGA 将使用此名称作为 Getty 连接到的默认串行端口。
  3. /dev/ttyS0:通常情况下标准 COM 端口用的此名称。如今,由于较新的台式机和笔记本电脑没有实际的 COM 端口,这种情况已不太常见。
  4. /dev/ttyUSB0: 大多数 USB 转串口电缆将使用这样命名的文件显示。
  5. /dev/pts/0 - 伪终端。这些可以使用 socat 生成。

下图展示了一块常见的开发板提供的串口设备:

在这里插入图片描述

要写入串行端口,请写入文件。要从串行端口读取,请从文件中读取。当然,这允许您发送/接收数据,但是如何设置串口参数,例如波特率、奇偶校验等。这是由特殊的 tty 配置 struct 设置的。

开发C代码

在这里插入图片描述

首先需要包含一些头文件

// C library headers
#include <stdio.h>
#include <string.h>

// Linux headers
#include <fcntl.h> // Contains file controls like O_RDWR
#include <errno.h> // Error integer and strerror() function
#include <termios.h> // Contains POSIX terminal control definitions
#include <unistd.h> // write(), read(), close()

然后我们要打开串行端口设备(在 /dev/ 下显示为文件),保存 open() 返回的文件描述符:

int serial_port = open("/dev/ttyUSB0", O_RDWR);

// Check for errors
if (serial_port < 0) {
    printf("Error %i from open: %s\n", errno, strerror(errno));
}

您可能在此处看到的常见错误之一是 errno = 2 ,并且 strerror(errno) 返回 No such file or directory 。确保您拥有设备的正确路径并且该设备存在!

您可能在这里遇到的另一个常见错误是 errno = 13 ,即 Permission denied 。这通常是因为当前用户不属于dialout组的一部分而发生。使用以下命令将当前用户添加到 dialout 组:

sudo adduser $USER dialout

上述命令没有立即生效。您可以选择注销并重新登录,也可以使用其它工具让它立即生效。

此时,我们可以从技术上读取和写入串行端口,但它可能不起作用,因为默认配置设置不是为串行端口使用而设计的。所以现在我们将正确设置配置。

修改任何配置值时,最佳做法是仅修改您感兴趣的bit位,并保持字段的所有其它bit位不变。这就是为什么您会在下面看到设置位时使用 &=|= ,而不是 =

串口启动配置

我们需要访问 termios 结构才能配置串行端口。我们将创建一个新的 termios 结构体,然后使用 tcgetattr() 将串口的现有配置写入其中,然后根据需要修改参数并使用 tcsetattr()

// Create new termios struct, we call it 'tty' for convention
// No need for "= {0}" at the end as we'll immediately write the existing
// config to this struct
struct termios tty;

// Read in existing settings, and handle any error
// NOTE: This is important! POSIX states that the struct passed to tcsetattr()
// must have been initialized with a call to tcgetattr() overwise behaviour
// is undefined
if(tcgetattr(serial_port, &tty) != 0) {
    printf("Error %i from tcgetattr: %s\n", errno, strerror(errno));
}

我们现在可以根据需要更改 tty 的设置,如以下部分所示。在我们开始之前,如果您感兴趣的话,这里是 termios 结构的定义(从 termbits.h 中提取):

struct termios {
	tcflag_t c_iflag;		/* input mode flags */
	tcflag_t c_oflag;		/* output mode flags */
	tcflag_t c_cflag;		/* control mode flags */
	tcflag_t c_lflag;		/* local mode flags */
	cc_t c_line;			/* line discipline */
	cc_t c_cc[NCCS];		/* control characters */
};

串口参数配置c_cflags

termios 结构的 c_cflag 成员包含控制参数字段。

PARENB (Parity)

如果设置该位,则启用奇偶校验位的生成和检测。大多数串行通信不使用奇偶校验位,因此如果您不确定,请清除该位。

tty.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common)
tty.c_cflag |= PARENB;  // Set parity bit, enabling parity

CSTOPB (停止位)

如果设置该位,则使用两个停止位。如果该位被清除,则仅使用一个停止位。大多数串行通信仅使用一个停止位。

tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common)
tty.c_cflag |= CSTOPB;  // Set stop field, two stop bits used in communication

字节的位数

CS<number> 字段设置通过串行端口每个字节传输多少数据位。这里最常见的设置是 8 ( CS8 )。如果你不确定的话,一定要使用这个,我以前从来没有使用过串口,之前没有使用过8(但它们确实存在)。在使用 &= ~CSIZE 设置任何大小位之前,您必须清除所有大小位。

tty.c_cflag &= ~CSIZE; // Clear all the size bits, then use one of the statements below
tty.c_cflag |= CS5; // 5 bits 每字节
tty.c_cflag |= CS6; // 6 bits 每字节
tty.c_cflag |= CS7; // 7 bits 每字节
tty.c_cflag |= CS8; // 8 bits 每字节 (most common)

CRTSCTS(硬件流控制)

如果设置了 CRTSCTS 字段,则启用硬件RTS/CTS流控制。这是当端点之间有两条额外的电线时,用于在数据准备好发送/接收时发出信号的情况。这里最常见的设置是禁用它。在应该禁用它的时候启用它可能会导致您的串行端口接收不到数据,因为发送者将无限期地缓冲它,等待您“准备好”。少于3根线的串口一定没有这个功能,应该禁用。

tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common)
tty.c_cflag |= CRTSCTS;  // Enable RTS/CTS hardware flow control

有关与流量控制相关的其他设置,请参阅的串口流量控制相关文章。

CREAD 和 CLOCAL

设置 CLOCAL 禁用调制解调器特定的信号线,例如载波检测。它还可以防止在检测到调制解调器断开连接时向控制进程发送 SIGHUP 信号,这通常是一件好事。设置 CREAD 允许我们读取数据(我们绝对想要这样!)。

tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1)

c_lflag

禁用规范模式

UNIX系统提供两种基本的输入模式:规范模式和非规范模式。在规范模式下,当收到新行字符时处理输入。接收应用程序逐行接收该数据。在处理串行端口时,这通常是不受欢迎的,因此我们通常希望禁用规范模式。禁用规范模式:

tty.c_lflag &= ~ICANON;

此外,在规范模式下,某些字符(例如退格键)会被特殊处理,用于编辑当前文本行(擦除)。同样,如果处理原始串行数据,我们不希望使用此功能,因为它会导致特定字节丢失!

回应(Echo)

如果设置了该位,发送的字符将被回显。因为我们禁用了规范模式,所以我认为这些位实际上没有做任何事情,但以防万一禁用它们也没有什么坏处!串口默认启用了这个模式,因为测试硬件的正确性经常需要TX/RX短接。

tty.c_lflag &= ~ECHO; // Disable echo
tty.c_lflag &= ~ECHOE; // Disable erasure
tty.c_lflag &= ~ECHONL; // Disable new-line echo

禁用信号字符

当设置 ISIG 位时,将解释 INTRQUITSUSP 字符。我们不希望使用串行端口,因此请清除此位:

tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP

输入模式(c_iflag

termios的输入流与输出流是分开配置的。因为大部分场景下输入流与输出流的配置相同,所以termios显得比较麻烦。termios 结构的 c_iflag 成员包含输入处理的低级设置。 c_iflag 成员是 int

软件流控制(IXOFFIXONIXANY

清除 IXOFF 、 IXON 和 IXANY 会禁用软件流控制,这是我们不想要的:

tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl

禁用接收时字节的特殊处理

在将字节传递给应用程序之前,清除以下所有位将禁用串行端口接收字节时对字节的任何特殊处理。我们只想要原始数据!

tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // Disable any special handling of received bytes

输出模式(c_oflag

termios 结构的 c_oflag 成员包含输出处理的低级设置。配置串行端口时,我们希望禁用对输出字符/字节的任何特殊处理,因此请执行以下操作:

tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars)
tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed
// tty.c_oflag &= ~OXTABS; // Prevent conversion of tabs to spaces (NOT PRESENT IN LINUX)
// tty.c_oflag &= ~ONOEOT; // Prevent removal of C-d chars (0x004) in output (NOT PRESENT IN LINUX)

OXTABSONOEOT 在 Linux 中都没有定义。然而,Linux 确实有似乎相关的 XTABS 字段。当针对 Linux 进行编译时,我只是排除这两个字段,串行端口仍然可以正常工作。

VMINVTIME (c_cc)

VMIN 和 VTIME 是许多程序员在尝试在 Linux 中配置串行端口时感到困惑的根源。需要注意的重要一点是, VTIME 的含义略有不同,具体取决于 VMIN 的含义。当 VMIN 为 0 时, VTIME 指定从 read() 调用开始时的超时。但当 VMIN > 0 时, VTIME 指定从第一个接收到的字符开始算起的超时时间。让我们探索不同的组合:

  1. VMIN = 0,VTIME = 0:无阻塞,立即返回可用内容
  2. VMIN > 0,VTIME = 0:这将使 read() 始终等待字节(具体多少由 VMIN 确定),因此 read() 可能无限期阻塞。
  3. VMIN = 0,VTIME > 0:这是对任意数量的字符的阻塞读取,具有最大超时(由 VTIME 给出)。 read() 将阻塞,直到有任意数量的数据可用或发生超时。这恰好是我最喜欢的模式(也是我使用最多的模式)。
  4. VMIN > 0、VTIME > 0:阻塞直至收到 VMIN 个字符,或在第一个字符过去后 VTIME 。请注意, VTIME 的超时直到收到第一个字符后才开始。

VMIN 和 VTIME 都定义为类型 cc_t ,我一直认为它是 unsigned char (1 字节)的别名。这将 VMIN 字符数的上限设置为 255,最大超时设置为 25.5 秒(255 分秒)。

收到数据后立即返回并不意味着一次只能获取 1 个字节。根据操作系统延迟、串行端口速度、硬件缓冲区和您无法直接控制的许多其他因素,您可能会收到任意数量的字节。例如,如果我们想等待最多 1 秒,一旦收到任何数据就返回,我们可以使用:

tty.c_cc[VTIME] = 10;    // Wait for up to 1s (10 deciseconds), returning as soon as any data is received.
tty.c_cc[VMIN] = 0;

波特率

串行端口波特率不是像所有其他设置那样使用位字段,而是通过调用函数 cfsetispeed()cfsetospeed() 并传入指向 tty :

// Set in/out baud rate to be 9600
cfsetispeed(&tty, B9600);
cfsetospeed(&tty, B9600);

如果您想保持 UNIX 兼容,则必须从以下选项之一中选择波特率:

B0,  B50,  B75,  B110,  B134,  B150,  B200, B300, B600, B1200, B1800, B2400, B4800, B9600, B19200, B38400, B57600, B115200, B230400, B460800

Linux 的某些实现提供了一个辅助函数 cfsetspeed() ,它同时设置输入和输出速度:

cfsetspeed(&tty, B9600);

自定义波特率

由于您现在完全意识到配置 Linux 串行端口并非小事,因此您可能不会因为设置自定义波特率同样困难而感到困惑。没有可移植的方法来执行此操作,因此请准备好尝试以下代码示例,以了解哪些内容适用于您的目标系统。

GNU/Linux 方法

如果您使用 GNU C 库进行编译,则可以放弃上面的标准枚举,只需直接为 cfsetispeed()cfsetospeed() 指定整数波特率,例如:

// Specifying a custom baud rate when using GNU C
cfsetispeed(&tty, 104560);
cfsetospeed(&tty, 104560);

termios2方法

此方法依赖于使用 termios2 结构,该结构类似于 termios 结构,但功能明显更多。我不确定 termios2 到底是在什么 UNIX 系统上定义的,但如果是的话,它通常是在 termbits.h 中定义的(它是在我正在做的带有 GCC 系统的 Xubuntu 18.04 上)这些测试):

struct termios2 {
	tcflag_t c_iflag;		/* input mode flags */
	tcflag_t c_oflag;		/* output mode flags */
	tcflag_t c_cflag;		/* control mode flags */
	tcflag_t c_lflag;		/* local mode flags */
	cc_t c_line;			/* line discipline */
	cc_t c_cc[NCCS];		/* control characters */
	speed_t c_ispeed;		/* input speed */
	speed_t c_ospeed;		/* output speed */
};

这与普通的旧 termios 非常相似,除了添加了 c_ispeed 和 c_ospeed 。我们可以使用这些来直接设置自定义波特率!我们几乎可以以与 termios 完全相同的方式设置除波特率之外的所有内容,除了从文件描述符读取/写入终端属性之外 - 而不是使用 tcgetattr() 和 tcsetattr() 我们必须使用 ioctl() 。

让我们首先更新我们的包含,我们必须删除 termios.h 并添加以下内容:

// #include <termios.h> This must be removed! 
// Otherwise we'll get "redefinition of ‘struct termios’" errors
#include <sys/ioctl.h> // Used for TCGETS2/TCSETS2, which is required for custom baud rates
struct termios2 tty;

// Read in the terminal settings using ioctl instead
// of tcsetattr (tcsetattr only works with termios, not termios2)
ioctl(fd, TCGETS2, &tty);

// Set everything but baud rate as usual
// ...
// ...

// Set custom baud rate
tty.c_cflag &= ~CBAUD;
tty.c_cflag |= CBAUDEX;
// On the internet there is also talk of using the "BOTHER" macro here:
// tty.c_cflag |= BOTHER;
// I never had any luck with it, so omitting in favour of using
// CBAUDEX
tty.c_ispeed = 123456; // What a custom baud rate!
tty.c_ospeed = 123456;

// Write terminal settings to file descriptor
ioctl(serial_port, TCSETS2, &tty);

请阅读上面关于 BOTHER 的评论。也许在你的系统上这个方法会起作用!

并非所有硬件都支持所有波特率,因此如果可以选择,最好坚持使用上述标准 BXXX 速率之一。如果您不知道波特率是多少,并且尝试与第三方系统通信,请尝试 B9600 ,然后 B57600 ,然后 B115200 因为它们是最常见的波特率。

使配置生效

更改这些设置后,我们可以使用 tcsetattr() 传递 tty termios 结构到硬件:

// Save tty settings, also checking for error
if (tcsetattr(serial_port, TCSANOW, &tty) != 0) {
    printf("Error %i from tcsetattr: %s\n", errno, strerror(errno));
}

读写串口数据

现在我们已经打开并配置了串口,我们可以对其进行读写了!

对Linux串口的写入是通过 write() 函数完成的。我们使用上面调用 open() 返回的 serial_port 文件描述符。

unsigned char msg[] = { 'H', 'e', 'l', 'l', 'o', '\r' };
write(serial_port, msg, sizeof(msg));

读取是通过 read() 函数完成的。你必须为 Linux 提供一个缓冲区来写入数据。

// Allocate memory for read buffer, set size according to your needs
char read_buf [256];

// Read bytes. The behaviour of read() (e.g. does it block?,
// how long does it block for?) depends on the configuration
// settings above, specifically VMIN and VTIME
int n = read(serial_port, &read_buf, sizeof(read_buf));

// n is the number of bytes read. n may be 0 if no bytes were received, and can also be negative to signal an error.

用完了记得要关闭

close(serial_port);

完整代码

// C library headers
#include <stdio.h>
#include <string.h>

// Linux headers
#include <fcntl.h> // Contains file controls like O_RDWR
#include <errno.h> // Error integer and strerror() function
#include <termios.h> // Contains POSIX terminal control definitions
#include <unistd.h> // write(), read(), close()

int main() {
  // Open the serial port. Change device path as needed (currently set to an standard FTDI USB-UART cable type device)
  int serial_port = open("/dev/ttyUSB0", O_RDWR);

  // Create new termios struct, we call it 'tty' for convention
  struct termios tty;

  // Read in existing settings, and handle any error
  if(tcgetattr(serial_port, &tty) != 0) {
      printf("Error %i from tcgetattr: %s\n", errno, strerror(errno));
      return 1;
  }

  tty.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common)
  tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common)
  tty.c_cflag &= ~CSIZE; // Clear all bits that set the data size 
  tty.c_cflag |= CS8; // 8 bits per byte (most common)
  tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common)
  tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1)

  tty.c_lflag &= ~ICANON;
  tty.c_lflag &= ~ECHO; // Disable echo
  tty.c_lflag &= ~ECHOE; // Disable erasure
  tty.c_lflag &= ~ECHONL; // Disable new-line echo
  tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP
  tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl
  tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // Disable any special handling of received bytes

  tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars)
  tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed
  // tty.c_oflag &= ~OXTABS; // Prevent conversion of tabs to spaces (NOT PRESENT ON LINUX)
  // tty.c_oflag &= ~ONOEOT; // Prevent removal of C-d chars (0x004) in output (NOT PRESENT ON LINUX)

  tty.c_cc[VTIME] = 10;    // Wait for up to 1s (10 deciseconds), returning as soon as any data is received.
  tty.c_cc[VMIN] = 0;

  // Set in/out baud rate to be 9600
  cfsetispeed(&tty, B9600);
  cfsetospeed(&tty, B9600);

  // Save tty settings, also checking for error
  if (tcsetattr(serial_port, TCSANOW, &tty) != 0) {
      printf("Error %i from tcsetattr: %s\n", errno, strerror(errno));
      return 1;
  }

  // Write to serial port
  unsigned char msg[] = { 'H', 'e', 'l', 'l', 'o', '\r' };
  write(serial_port, msg, sizeof(msg));

  // Allocate memory for read buffer, set size according to your needs
  char read_buf [256];

  // Normally you wouldn't do this memset() call, but since we will just receive
  // ASCII data for this example, we'll set everything to 0 so we can
  // call printf() easily.
  memset(&read_buf, '\0', sizeof(read_buf));

  // Read bytes. The behaviour of read() (e.g. does it block?,
  // how long does it block for?) depends on the configuration
  // settings above, specifically VMIN and VTIME
  int num_bytes = read(serial_port, &read_buf, sizeof(read_buf));

  // n is the number of bytes read. n may be 0 if no bytes were received, and can also be -1 to signal an error.
  if (num_bytes < 0) {
      printf("Error reading: %s", strerror(errno));
      return 1;
  }

  // Here we assume we received ASCII data, but you might be sending raw bytes (in that case, don't try and
  // print it to the screen like this!)
  printf("Read %i bytes. Received message: %s", num_bytes, read_buf);

  close(serial_port);
  return 0; // success
};

独占串口设备

谨慎的做法是尝试阻止其他进程同时读取/写入串行端口。实现此目的的一种方法是使用 flock() 系统调用

 if(flock(fd, LOCK_EX | LOCK_NB) == -1) {
    //输出错误消息
 }

获取RX有多少个字节可读取

您可以将 FIONREAD 与 ioctl() 一起使用来查看串行端口 1 的操作系统输入(接收)缓冲区中是否有任何可用字节。这在轮询式方法中非常有用,其中应用程序在尝试读取字节之前定期检查字节。

#include <unistd.h>
#include <termios.h>

int main() {

  // ... get file descriptor here

  // See if there are bytes available to read
  int bytes;
  ioctl(fd, FIONREAD, &bytes);
}

ioctl() 函数将提供的指向整数 bytes 的指针写入可从串行端口读取的字节数。尽管获取和设置终端设置是通过文件描述符完成的,但这些设置适用于终端设备本身,并将影响正在使用或将要使用该终端的所有其他系统应用程序。这也意味着在文件描述符关闭后,甚至在更改设置的应用程序终止后,终端设置更改仍然存在。

作者:岬淢箫声
日期:2023年11月1日
版本:1.0
链接:http://caowei.blog.csdn.net

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1161225.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Geth的进行合约部署和调用合约方法

环境 Ubuntu20 geth : 1.10.5-stable go: 1.17 前言 还未安装geth的读者可以参考这篇文章 Geth的安装并简单使用篇 我们需要进入geth交互式控制台进行操作 root192-168-19-133:~# geth --dev console INFO [10-03|22:25:29.918] Starting Geth in ephemeral dev mode... INFO…

list集合中的元素排序

目录 一、利用lambda对list集合排序 二、对对象集合操作&#xff0c;其实与基本类型集合操作类似 三、对 JSONArray 排序 一、利用lambda对list集合排序 先定义一个集合 List<Integer> list new ArrayList<>(); list.add(1); list.add(5); list.add(4); list…

基于SSM的酒店客房预定管理系统

基于SSM的酒店客房预定管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringSpringMVCMyBatis工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 前台主页 客房详情 登录界面 管理员界面 用户界面 摘要 基于SSM&#xff08;…

逻辑分析仪与示波器选择

一、简介 逻辑分析仪是利用时钟从测试设备上采集和显示数字信号的仪器&#xff0c;最主要的作用在于时序判定。逻辑分析仪与示波器不同&#xff0c;它不能显示连续的模拟量波形&#xff0c;而只显示高低两种电平状态&#xff08;逻辑1和0&#xff09;。在设置了参考电压后&…

JDK环境变量配置

windows系统 1. win建 r 输入sysdm.cpl打开系统属性界面。选择高级栏目&#xff0c;点击环境变量菜单。 2. 选择系统变量中的新建&#xff0c;变量名输入JAVA_HOME&#xff0c;变量值输入jdk安装目录&#xff0c;例如&#xff1a;D:\Java\jdk1.8.0_91。 3. 选择编辑系统变量中…

容斥原理,多步容斥

容斥意义法 设计状态表示容斥的过程。比较简单的容斥题目一般可以容斥意义。 如果我们要求方案数的话&#xff0c;通常情况下我们的把限制视为两个方面&#xff0c;一方面是总限制&#xff0c;一方面是对于每个物品的限制&#xff0c;这样设集合 S i S_i Si​表示满足总限制以及…

VSCode 如何设置背景图片

VSCode 设置背景图片 1.打开应用商店&#xff0c;搜索 background &#xff0c;选择第一个&#xff0c;点击安装。 2. 安装完成后点击设置&#xff0c;点击扩展设置。 3.点击在 settings.json 中编辑。 4.将原代码注释后&#xff0c;加入以下代码。 // { // "workben…

中海达守护电力人员作业安全

近日&#xff0c;中海达为电网某换流站作业人员提供的160余套北斗高精度定位产品顺利完成交付。通过使用北斗高精度定位技术&#xff0c;帮助换流站实现了人员&#xff08;车辆&#xff09;位置实时定位、电子围栏实时预警、远程作业指导等应用效果&#xff0c;用高科技保障电网…

红米redmi note7救砖/线刷/ROOT教程

一直循环开机界面&#xff0c;需要刷机 准备 使用windows电脑安装如下 MiUsbDriver.exe电脑连接小米手机驱动。可以去https://www.miui.com/unlock/index.html下载压缩包&#xff0c;里面有这个程序小米ROM固件包找一个适合自己手机型号的的即可&#xff0c;官网也可以下载但…

3D RPG Course | Core 学习日记三:Navigation智能导航地图烘焙

前言 前面我们已经绘制好了一个简单的地图场景&#xff0c;现在我们需要使用Navigation给地图做智能导航&#xff0c;以实现AI自动寻路&#xff0c;以及设置地图的可行走区域以及不可行走区域&#xff0c;Navigation的基础知识、原理、用法在Unity的官方文档&#xff0c;以及网…

Modelsim 使用教程(2)——Basic Simulation

一、概述 在本文中&#xff0c;我们将介绍Modelsim基本的仿真流程&#xff0c;包括有&#xff1a; Create the Working Design Library&#xff08;创建工具库&#xff09; Compile the Design Units&#xff08;编译设计单元&#xff09; Optimize the Design&#xff08;优化…

单工通信、半双工通信、全双工通信分别是什么?

单工通信&#xff1a; 单工通信是一种单向传输数据的通信方式&#xff0c;只允许信息在一个方向上流动。在单工通信中&#xff0c;通信的两个节点中的一个节点只能作为发送方&#xff0c;而另一个节点只能作为接收方。发送方只负责发送数据&#xff0c;而接收方只负责接收数据&…

LLMs之Agent之AutoGen:AutoGen的简介、安装、使用方法之详细攻略

LLMs之Agent之AutoGen&#xff1a;AutoGen的简介、安装、使用方法之详细攻略 目录 AutoGen的简介、安装、使用方法 1、多Agent对话框架 2、AutoGen的对话流程 3、增强的LLM推理 AutoGen的安装 1、安装 2、快速测试 AutoGen的使用方法 1、基础用法 (1)、启动两个代理之…

C++:map和set的基本使用

文章目录 搜索模型关联式容器setset的基本使用set的其他使用 multisetmapmap的基本使用map中的[] multimap 搜索模型 在实际搜索中有两个搜索模型&#xff1a;Key的搜索模型和Key/Value的搜索模型 Key的搜索模型&#xff1a; 简单来说就是在一个搜索树&#xff0c;搜索树中的…

大桌子安装笔记

单位弄了个服务器&#xff0c;里面全是虚拟机&#xff0c;自己也学着建了一个测试用的虚拟机&#xff0c;不知道干点啥好&#xff0c;朋友说做个网盘吧&#xff0c;就用大桌子。 于是&#xff0c;干起来&#xff0c;不会配制&#xff0c;就跟朋友一步一步学起来。 他说&#…

服务熔断保护实践--Sentinal

目录 概述 环境说明 步骤 Sentinel服务端 Sentinel客户端 依赖 在客户端配置sentinel参数 测试 保护规则设置 设置资源名 设置默认的熔断规则 RestTemplate的流控规则 Feign的流控规则 概述 微服务有很多互相调用的服务&#xff0c;构成一系列的调用链路&#xf…

LangChain介绍及代码实践

一、简介 LangChian 作为一个大语言模型开发框架&#xff0c;是 LLM 应用架构的重要一环。那什么是 LLM 应用架构呢&#xff1f;其实就是指基于语言模型的应用程序设计和开发的架构。 LangChian 可以将 LLM 模型、向量数据库、交互层 Prompt、外部知识、外部工具整合到一起&a…

如何看待将本增效?

如何看待将本增效&#xff1f; 么是降本增效&#xff1f;就是公司里&#xff0c;增进收益的动作要多做&#xff0c;无效的动作要少做&#xff0c;甚至不做。什么叫有效&#xff1f;回到公司的经营目标上去&#xff0c;企业的管理就是目标管理。降本&#xff0c;需要卓越运营&a…

NLP学习笔记:使用 Python 进行NLTK

一、说明 本文和接下来的几篇文章将介绍 Python NLTK 库。NLTK — 自然语言工具包 — NLTK 是一个强大的开源库&#xff0c;用于 NLP 的研究和开发。它内置了 50 多个文本语料库和词汇资源。它支持文本标记化、词性标记、词干提取、词形还原、命名实体提取、分割、分类、语义推…

【计算系统】5分钟了解超算,高性能计算,并行计算,分布式计算,网格计算,集群计算以及云计算的区别

5分钟了解超算&#xff0c;高性能计算&#xff0c;并行计算&#xff0c;分布式计算&#xff0c;网格计算&#xff0c;集群计算以及云计算的区别 1. 超算2. 高性能计算3. 并行计算4. 分布式计算5. 网格计算6. 集群计算7. 云计算小结相关资料 1. 超算 超级计算机&#xff08;Sup…