【北京迅为】《iTOP-3588开发板系统编程手册》-第16章 串口应用编程

RK3588是一款低功耗、高性能的处理器,适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用,RK3588支持8K视频编解码,内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP,内置NPU,支持INT4/INT8/INT16/FP16混合运算能力,支持安卓12和、Debian11、Build root、Ubuntu20和22版本登系统。了解更多信息可点击迅为官网   

【粉丝群】824412014

【实验平台】:迅为RK3588开发板

【内容来源】《iTOP-3588开发板系统编程手册》

【全套资料及网盘获取方式】联系淘宝客服加入售后技术支持群内下载

【视频介绍】:【强者之芯】 新一代AIOT高端应用芯片 iTOP -3588人工智能工业AI主板


第16章 串口应用编程

串口设备是嵌入式开发中最常用的外设之一,通过串口打印信息可以调试程序的运行,通过串口也可以链接很多种外设,比如串口打印机,蓝牙,wifi,GPS,GPRS 等等。

16.1串口介绍 

串口是一种通过串行通信传输数据的接口,与并行接口不同,它只使用一个信号线路传输数据,这样就可以避免并行接口由于多条线路的干扰和延迟等问题而导致的错误。在计算机和外部设备之间,通过串口可以传输数据、控制信号和时钟等信息,是计算机通信中最常用的接口之一。

下面对串口通信相关的知识进行简要介绍:

1.数据传输方式

串口通信使用的是串行传输方式,即将数据按照顺序一个一个地发送或接收。每个数据位都在一定的时间间隔内发送或接收。串口使用两条数据线(TXD,RXD)和一条地线(GND)进行传输。

2.数据格式

串口通信使用的是异步传输方式,数据被分成固定大小的数据帧,每个数据帧由起始位、数据位、校验位和停止位组成,通常称为“帧”。其中起始位和停止位用于标识数据帧的开始和结束,校验位用于检验数据传输的准确性。数据位的数量一般为7位或8位。

3.波特率

波特率是指数据传输的速度,也称为比特率,表示每秒钟可以传输多少个二进制数据位。波特率越高,数据传输的速度就越快。常见的波特率有9600bps、19200bps、38400bps、115200bps等。

4.硬件流控制和软件流控制

为了保证数据传输的准确性和稳定性,串口通信还需要使用流控制。硬件流控制是通过硬件电路控制数据的传输,软件流控制则是通过软件控制数据的传输。硬件流控制的主要方法是使用RTS和CTS两个信号线进行控制,软件流控制的主要方法是使用XON和XOFF两个控制字符进行控制。

5.错误检测

在串口通信过程中,常常会出现一些错误,例如校验错误、帧错误、溢出错误等。为了避免这些错误,通常使用校验位来检测数据的准确性,并使用缓冲区来避免数据溢出。

6.串口编程

为了控制串口通信,我们需要进行串口编程。在Linux系统中,可以使用文件I/O操作和ioctl操作来进行串口编程。文件I/O操作可以用于读取和写入串口数据,ioctl操作可以用于设置串口参数、控制流控制和获取串口状态等操作(关于ioctl相关的指令宏会在之后的小节进行讲解)。

16.2串口设备节点介绍 

在嵌入式设备中,串口设备通常以字符设备节点的形式出现在Linux系统中。在Linux系统中,每个设备都由一个设备节点(device node)来表示,设备节点是与设备相关联的一个文件,以/dev目录下的文件形式存在。

串口设备节点通常以tty开头,具体命名方式根据串口的类型和数量不同而不同。开发板系统启动之后,使用以下命令打印终端设备节点,如下图所示:

ls /dev/tty*

dev/ttyX(X 是一个数字编号,譬如 0、1、2、3 等)设备节点:tty是teletype 的简称,在 Linux 中,/dev/ttyX 代表的都是本地终端, Linux 内核在初始化时所生成的 63 个本地终端,包括/dev/tty1~/dev/tty63 一共63 个本地终端,可以是连接到开发板的LCD 显示器、键盘和鼠标等。

串口终端设备节点:从开发板原理图可以了解到,在iTOP-3588开发板上UART2为串口调试终端,对应的设备节点为/dev/ttyFIQ0,

底板上485对应/dev/ttyS0。

基于USB的虚拟串口:ttyGS0以及ttyUSBX(X 是一个数字编号,譬如 0、1、2、3 等)都是USB的虚拟串口,其中ttyGS0为烧写usb虚拟出的串口,在系统启动之后可以在windows终端通过“adb shell”命令进入开发板控制台。ttyUSBX在这里为4G模块的虚拟串口。

16.3串口的使用步骤

步骤

解释

1

打开串口设备文件 open()

2

获取当前串口的配置参数 tcgetattr()

3

修改串口的配置参数 tcsetattr()

4

写入修改后的配置参数 tcsetattr()

5

对串口数据进行读写 read() /write()

6

关闭串口设备文件 close()

关于打开和关闭设备文件我们已经非常熟悉了,接下来将对上述重要步骤和函数进行讲解。

1获取当前串口的配置参数

使用tcgetattr()函数可以获取当前串口的配置参数,包括串口的波特率、数据位、停止位、校验位等信息。在对串口进行应用编程时,需要了解当前串口的配置参数,才能进行相应的操作,比如修改串口的波特率或其他配置参数。同时,获取当前串口属性还可以用于备份当前的配置参数,便于在操作完成后还原串口属性,以避免对其他程序或系统造成影响。因此,使用tcgetattr()函数获取串口属性是进行串口编程的基础。tcgetattr()函数所需头文件和函数原型如下所示:

所需头文件

函数原型

1 

2

#include <termios.h>

#include <unistd.h>

int tcgetattr(int fd, struct termios *termios_p);

该函数的作用是获取与指定终端相关的参数(也称为终端属性或终端设置),并将这些参数存储到termios结构体中。其中,fd参数是打开的串口设备文件的文件描述符,termios_p参数是一个指向termios结构体的指针,用于存储从终端读取的属性。

termios结构体包含了终端的所有属性信息,包括输入输出波特率、数据位、校验位、停止位等。其定义如下:

struct termios {
    tcflag_t c_iflag;  // 输入模式标志
    tcflag_t c_oflag;  // 输出模式标志
    tcflag_t c_cflag;  // 控制模式标志
    tcflag_t c_lflag;  // 本地模式标志
    cc_t c_cc[NCCS];   // 控制字符数组
};

其中,c_iflag、c_oflag、c_cflag和c_lflag分别表示输入模式标志、输出模式标志、控制模式标志和本地模式标志。c_cc数组是一组控制字符,包含了终端驱动程序使用的一些控制字符(会在下一小节对该结构体进行详细讲解)。

2修改和写入串口的配置参数

tcsetattr()函数是Linux中设置终端属性的函数,它用于设置指定终端设备的属性。

所需头文件

函数原型

1 

2

#include <termios.h>

#include <unistd.h>

 int tcsetattr(int fd, int optional_actions,

                     const struct termios *termios_p);

接下来对tcsetattr函数要用到的三个参数进行介绍: 

参数名称

参数含义

filename

打开的终端设备的文件描述符。

2

optional_actions

可选操作,决定何时应用改变的属性值。常见的可选操作包括以下三个值:

(1)TCSANOW:立即改变属性值。

(2)TCSADRAIN:等待所有输出数据传输完毕后改变属性值。

(3)TCSAFLUSH:清空输入和输出缓冲区,并等待数据传输完毕后改变属性值。

3

termios_p

一个指向终端属性结构体的指针,用于存储新的终端属性值

在调用tcsetattr()函数时,需要提供一个指向termios结构体的指针。这个结构体包含了串口的各种参数,如波特率、数据位数、停止位数、奇偶校验等(关于各种参数的修改会在17.5小节进行详细的讲解)。

16.4.1输入模式

输入模式控制输入数据(终端驱动程序从串口或键盘接收到的字符数据)在被传递给应用程序之前的处理方式。通过设置 struct termios 结构体中 c_iflag 成员的标志对它们进行控制。所有的标志都被定义为宏, 除 c_iflag 成员外,c_oflag、c_cflag 以及 c_lflag 成员也都采用这种方式进行配置。可用于 c_iflag 成员的宏如下所示:

成员

对应成员的含义

IGNBRK

忽略输入终止条件

BRKINT

当检测到输入终止条件时发送 SIGINT 信号

IGNPAR

忽略帧错误和奇偶校验错误

PARMRK

对奇偶校验错误做出标记

INPCK

对接收到的数据执行奇偶校验

ISTRIP

将所有接收到的数据裁剪为 7 比特位、也就是去除第八位

INLCR

将接收到的 NL(换行符)转换为 CR(回车符)

IGNCR

忽略接收到的 CR(回车符)

ICRNL

将接收到的 CR(回车符)转换为 NL(换行符)

IUCLC

将接收到的大写字符映射为小写字符

IXON

启动输出软件流控

16.4.2输出模式

输出模式控制输出字符的处理方式,即由应用程序发送出去的字符数据在传递到串口或屏幕之前是如何处理的。可用于 c_oflag 成员的宏如下所示:

成员

对应成员的含义

OPOST

启用输出处理功能,如果不设置该标志则其他标志都被忽略

OLCUC

将输出字符中的大写字符转换成小写字符

ONLCR

将输出中的换行符(NL '\n')转换成回车符(CR '\r')

OCRNL

将输出中的回车符(CR '\r')转换成换行符(NL '\n')

ONOCR

在第 0 列不输出回车符(CR

ONLRET

不输出回车符

OFILL

发送填充字符以提供延时

OFDEL

如果设置该标志,则表示填充字符为 DEL 字符,否则为 NULL字符

16.4.3控制模式

在这个结构体中,最终要的就是c_cflag ,可以控制模式控制终端设备的硬件特性,譬如对于串口来说,该字段比较重要,可设置串口波特率、数据位、校验位、停止位等硬件特性。通过设置 struct termios 结构中 c_cflag 成员的标志对控制模式进行配置。可用于 c_cflag 成员的标志如下所示:

c_cflag支持的常量名称

CBAUD

波特率的位掩码

B0

0波特率(放弃DTR)

B1800

1800波特率

B2400

2400波特率

B4800

4800波特率

B9600

9600波特率

B19200

19200波特率

B38400

38400波特率

B57600

57600波特率

B115200

115200波特率

CSIZE

数据位的位掩码

CS5

5个数据位

CS6

6个数据位

CS7

7个数据位

CS8

8个数据位

CSTOPB

2个停止位(不设则是1个停止位)

CREAD

接收使能

PARENB

校验位使能

PARODD

使用奇校验而不使用偶校验

HUPCL

最后关闭时挂线(放弃DTR)

CLOCAL

本地连接(不改变端口所有者)

LOBLK

块作业控制输出

CNET_CTSRTS

硬件流控制使能

16.4.4本地模式

本地模式用于控制终端的本地数据处理和工作模式。通过设置 struct termios 结构体中 c_lflag 成员的标 志对本地模式进行配置。可用于 c_lflag 成员的标志如下所示:

c_iflag支持的常量名称

INPCK

奇偶校验使能

IGNPAR

忽略奇偶校验错误

PARMRK

奇偶校验错误掩码

ISTRIP

除去奇偶校验位

IXON

启动出口硬件流控

IXOFF

启动入口软件流控

IXANY

允许字符重新启动流控

IGNBRK

忽略中断情况

BRKINT

当发生中断时发送SIGINT信号

INLCR

将NL映射到CR

IGNCR

忽略CR

ICRNL

将CR映射到NL

ICANON

启用规范模式

16.4.5特殊控制字符

特殊控制字符是一些字符组合,如 Ctrl+C、Ctrl+Z 等,当用户键入这样的组合键,终端会采取特殊处理方式。struct termios 结构体中 c_cc 数组将各种特殊字符映射到对应的支持函数。每个字符位置(数组下标)由对应的宏定义的,如下所示

c_cc 支持的常量名称

VKILL

删除行,对应键为CTRL+U

VEOF

位于文件结尾,对应键为CTRL+D

VEOL

 位于行尾,对应键为Carriage return(CR)

VEOL2

 位于第二行尾,对应键为Line feed(LF)

VMIN

指定了最少读取的字符数

VTIME

指定了读取每个字符的等待时间

VINTR

中断控制,对应键为CTRL+C

VQUIT

退出操作,对应键为CRTL+Z

VERASE

删除操作,对应键为Backspace(BS)

在以上所列举的这些宏定义中,TIME 和 MIN 值只能用于非规范模式,可用于控制非规范模式下 read()调用的一些行为特性,后面再向大家介绍。

16.5对串口进行配置

16.5.1设置串口的波特率 

在编写串口应用程序时,设置串口波特率是必须的步骤之一,它决定了数据传输的速率。在 Linux 中,设置波特率通常使用 cfsetspeed() 函数,cfsetspeed()函数所需头文件和函数原型如下所示:

所需头文件

函数原型

1

2

#include <termios.h>

#include <unistd.h>

int cfsetspeed(struct termios *termios_p, speed_t speed);

它接受一个 struct termios 结构体作为输入参数,同时返回一个整数值,表示操作是否成功。该函数实际上是对 c_cflag 字段中的波特率进行设置。例如,如果要将波特率设置为 115200,可以调用以下代码:

struct termios options;
// 获取当前终端配置
tcgetattr(fd, &options);

// 设置波特率为 115200
cfsetspeed(&options, B115200);

// 将新的终端配置写入终端
tcsetattr(fd, TCSANOW, &options);

需要注意以下几点:

(1)需要先打开串口设备文件并且获取串口的属性,包括波特率在内的其他属性。可以使用 tcgetattr() 函数获取属性值,并存储在一个 termios 结构体变量中。

(2)设置波特率时需要调用 cfsetspeed() 函数,并传入一个波特率常量作为参数。常用的波特率常量包括 B9600、B115200 等。这些常量可以在头文件 termios.h 中找到。

(3)设置完成后,需要使用 tcsetattr() 函数将属性值写回到串口设备中。

16.5.2设置数据位大小

在串口通讯中,数据位指的是每个字符(byte)中实际包含的数据位数。通常情况下,一个字符包含8个位(即8个0或1),但有时也可能为7个位或其他值。

在编写串口应用程序时,需要使用 struct termios 结构体中的 c_cflag 成员变量来设置数据位大小。具体来说,需要将 c_cflag 成员变量中与数据位相关的位清零,然后再根据需要设置相应的值。

清零操作通常使用按位与(&)运算符和位运算中的“非”运算符(~),具体步骤如下:

new_cfg.c_cflag &= ~CSIZE; // 将数据位相关的比特位清零

其中,CSIZE 是一个宏定义,表示数据位的位掩码。宏定义通常定义在 termios.h 头文件中,其取值如下:

#define CSIZE   0x00000300  /* 字符长度掩码 */

通过清零操作,将数据位相关的位全部置为0。接下来,可以使用按位或(|)运算符和宏定义来设置具体的数据位数,例如:

new_cfg.c_cflag |= CS8;  // 将数据位数设置为8位

此时,CS8 宏定义将会被解释为一个包含8位的位掩码,通过按位或运算,将其设置到 c_cflag 成员变量中,从而完成数据位的设置。

16.5.3设置奇偶校验位 

串口的奇偶校验位配置一共涉及到 struct termios 结构体中的两个成员变量:c_cflag 和 c_iflag。首先对于 c_cflag 成员,需要添加 PARENB 标志以使能串口的奇偶校验功能,只有使能奇偶校验功能之后才会对输出数据产生校验位,从而对输入数据进行校验检查;同时对于 c_iflag 成员来说,还需要添加 INPCK 标志,这样才能对接收到的数据执行奇偶校验,代码如下所示:

奇校验使能:

new_cfg.c_cflag |= (PARODD | PARENB); // 设置为奇校验
new_cfg.c_iflag |= INPCK; // 使能奇偶校验

偶校验使能:

new_cfg.c_cflag &= ~PARODD;  // 设置为偶校验
new_cfg.c_cflag |= PARENB;   // 使能奇偶校验
new_cfg.c_iflag |= INPCK;    // 对输入数据执行奇偶校验

无校验:

new_cfg.c_cflag &= ~PARENB;  // 禁用奇偶校验
new_cfg.c_iflag &= ~INPCK;   // 不执行奇偶校验

16.5.4设置停止位

在串口通信中,停止位用于指定每个数据帧的结束位置。在传输一个完整的数据字节后,通常需要一个或多个停止位,以便接收端能够确定一个数据帧的结束。停止位的数量通常为1位或2位,其中1位停止位被广泛使用,而2位停止位则较少使用。

在Linux中,通过在 struct termios 结构体中设置 c_cflag 成员变量的 CSTOPB 标志位来控制停止位的数量。当 CSTOPB 为0时,仅使用1位停止位;当 CSTOPB 为1时,则使用2位停止位。

例如,以下代码将串口的停止位设置为1位:

1

new_cfg.c_cflag &= ~CSTOPB; // 设置停止位为1位 

16.6串口实验

本小节代码在配套资料“iTOP-3588开发板\03_【iTOP-RK3588开发板】指南教程\03_系统编程配套程序\66”目录下,如下图所示:

实验要求:

编写串口应用程序,实验发送和接收两种模式的测试。设置波特率为115200、8位数据位、1位停止位,无校验。main函数传递的第一个参数用来表示串口的设备节点,第二个参数来设置串口的收发模式,如果参数为send表示发送模式,来发送固定的字符,如果参数为rece表示接收模式,用来接收发送过来的数据。

16.6.1编写应用程序

实验步骤:

首先进入到ubuntu的终端界面输入以下命令来创建 demo66_uart.c文件,如下图所示:

vim demo102_uart.c

然后向该文件中添加以下内容: 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>

#define BUFFER_SIZE 256  // 定义缓冲区大小为256字节

void set_serial_port(int fd, int baud_rate, int data_bits, int stop_bits, int parity) 
// 设置串口的波特率、数据位、停止位和校验位
{
    struct termios options;
    tcgetattr(fd, &options);  // 获取串口的设置选项
    options.c_cflag &= ~CSIZE;  // 设置数据位为8位
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);  // 关闭规范模式、回显和信号处理
    options.c_oflag &= ~OPOST;  // 关闭输出处理
    options.c_iflag &= ~(IXON | IXOFF | IXANY);  // 关闭输入处理

    options.c_cc[VTIME] = 0;  // 设置读取时的超时时间
    options.c_cc[VMIN] = 0;  // 设置读取时的最小字节数

    switch (baud_rate) 
    // 设置波特率
    {
        case 9600:
            cfsetispeed(&options, B9600);
            cfsetospeed(&options, B9600);
            break;
        case 19200:
            cfsetispeed(&options, B19200);
            cfsetospeed(&options, B19200);
            break;
        case 38400:
            cfsetispeed(&options, B38400);
            cfsetospeed(&options, B38400);
            break;
        case 57600:
            cfsetispeed(&options, B57600);
            cfsetospeed(&options, B57600);
            break;
        case 115200:
            cfsetispeed(&options, B115200);
            cfsetospeed(&options, B115200);
            break;
        default:
            cfsetispeed(&options, B115200);
            cfsetospeed(&options, B115200);
            break;
    }

    options.c_cflag |= CLOCAL | CREAD;  // 必须设置的标志,表示启用本地连接和接收数据
    options.c_cflag &= ~CRTSCTS;  // 关闭硬件流控制

    switch (parity) 
    // 设置校验位
    {
        case 'O':  // 奇校验
            options.c_cflag |= PARENB;
            options.c_cflag |= PARODD;
            options.c_iflag |= (INPCK | ISTRIP);
            break;
        case 'E':  // 偶校验
            options.c_cflag |= PARENB;
            options.c_cflag &= ~PARODD;
            options.c_iflag |= (INPCK | ISTRIP);
            break;
        case 'N':  // 无校验
            options.c_cflag &= ~PARENB;
            break;
        default:
            options.c_cflag &= ~PARENB;
            break;
    }

// 设置数据位
switch (data_bits) 
{
    case 7:
        options.c_cflag &= ~CSIZE; // 清除CSIZE位
        options.c_cflag |= CS7; // 设置CS7位,即设置数据位为7位
        break;
    case 8:
        options.c_cflag &= ~CSIZE; // 清除CSIZE位
        options.c_cflag |= CS8; // 设置CS8位,即设置数据位为8位
        break;
    default:
        options.c_cflag &= ~CSIZE; // 清除CSIZE位
        options.c_cflag |= CS8; // 默认设置数据位为8位
        break;
}
// 设置停止位
switch (stop_bits) 
{
    case 1:
        options.c_cflag &= ~CSTOPB; // 清除CSTOPB位,即设置停止位为1位
        break;
    case 2:
        options.c_cflag |= CSTOPB; // 设置CSTOPB位,即设置停止位为2位
        break;
    default:
        options.c_cflag &= ~CSTOPB; // 默认设置停止位为1位
        break;
}

tcsetattr(fd, TCSANOW, &options); // 把新的设置应用到串口上
}

int main(int argc, char *argv[]) 
{
    if (argc != 3) 
    {
        printf("Usage: %s [serial device] [send|recv]\n", argv[0]);
        return -1;
    }

    int fd;
    char buffer[BUFFER_SIZE];

    if (strcmp(argv[2], "send") == 0) // 发送数据
    {
        fd = open(argv[1], O_WRONLY | O_NOCTTY | O_SYNC);
        if (fd < 0) 
        {
            printf("Error opening serial device\n");
            return -1;
        }

        set_serial_port(fd, 115200, 8, 1, 'N'); // 设置串口参数

        while (1) // 不停地读取用户输入,并写入串口
        {
            printf("Enter message: ");
            fgets(buffer, BUFFER_SIZE, stdin);
            write(fd, buffer, strlen(buffer));
        }
    }
    else if (strcmp(argv[2], "recv") == 0) // 接收数据
    {
        fd = open(argv[1], O_RDONLY | O_NOCTTY | O_SYNC);
        if (fd < 0) 
        {
            printf("Error opening serial device\n");
            return -1;
        }

        set_serial_port(fd, 115200, 8, 1, 'N'); // 设置串口参数

        while (1) // 不停地读取串口数据,并输出到屏幕上
        {
            int bytes_read = read(fd, buffer, BUFFER_SIZE);
            if (bytes_read > 0) 
            {
                buffer[bytes_read] = '\0';
                printf("Received message: %s", buffer);
            }
        }
    } 
    else // 参数不合法
    {
        printf("Invalid option\n");
        return -1;
    }

    return 0;
}

保存退出之后,使用以下命令设置交叉编译器环境,并对demo102_uart.c进行交叉编译,编译完成如下图所示:

export PATH=/usr/local/arm64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin:$PATH

aarch64-none-linux-gnu-gcc -o demo66_uart demo66_uart.c

最后将交叉编译生成的demo66_uart文件拷贝到/home/nfs共享目录下即可。

16.6.2开发板测试

Buildroot系统启动之后,首先使用以下命令进行nfs共享目录的挂载(其中192.168.1.7为作者ubuntu的ip地址,需要根据自身ubuntu的ip来设置),如下图所示:

mount -t nfs -o nfsvers=3,nolock 192.168.1.7:/home/nfs /mnt

nfs共享目录挂载到了开发板的/mnt目录下,进入到/mnt目录下,如下图所示:

可以看到/mnt目录下的demo66_uart文件已经存在了,然后使用以下命令通过开发板的485(使用的uart0)向外发送数据(usb 转485已经被连接好了开发板和PC),如下图所示:

./demo66_uart /dev/ttyS0 send

在PC端打开串口助手,设置好相应的功能之后打开串口,如下图所示:

 

随后在开发板终端进行数据的发送,如下图所示:

串口助手接收成功之后如下所示:

串口发送测试完成之后,使用以下命令进行串口的接收测试,命令使用之后如下图所示:

./demo66_uart /dev/ttyS0 recv

然后在PC端使用串口助手,进行数据的发送,如下图所示: 

串口助手每发送一个字符串,开发板的串口终端就会将对应的字符串打印出来,如下图所示:

至此我们串口的接收功能也测试完成了。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

291个地级市资源错配指数、劳动和资本相对扭曲指数(2006-2021年)

01、数据介绍 资源错配指数&#xff08;Misallocation Index&#xff09;是一个用于衡量资源配置效率的指标&#xff0c;它衡量的是生产要素的配置是否合理&#xff0c;是否达到了最优的状态。资源错配指数越高&#xff0c;资源的利用效率越低。资源错配指数主要用于衡量各种生…

学习STM32第十七天

备份域详解 一、简介 在参考手册的电源控制章节&#xff0c;提到了备份域&#xff0c;BKPR是在RTC外设中用到&#xff0c;包含20个备份数据寄存器&#xff08;80字节&#xff09;&#xff0c;备份域包括4KB的备份SRAM&#xff0c;以32位、16位或8位模式寻址&#xff0c;在VBAT…

SpringCloud系列(9)--将服务消费者Consumer注册进Eureka Server

前言&#xff1a;上一章节我们介绍了如何将服务提供者注册进Eureka服务里&#xff0c;本章节则介绍如何将服务消费者Consumer注册进Eureka服务里 Eureka架构原理图 1、修改consumer-order80子模块的pom.xml文件&#xff0c;引入Eureka Clinet的依赖&#xff0c;然后reolad一下&…

SVD奇异值分解原理及应用

-------------------------------------------------------------------------------------------------------------------------------- 首先说明&#xff1a;本文的内容来自百家号“人工智能遇见磐创”大佬的整理&#xff0c;感谢原作者&#xff08;本文在原作者的基础上按…

找不到msvcp140dll,无法继续执行代码的详细解决方法

在我们日常使用计算机进行各类工作任务的过程中&#xff0c;时常会遭遇一些突发的技术问题。比如&#xff0c;有时在运行某个重要程序或应用软件时&#xff0c;系统会突然弹出一个令人困扰的错误提示&#xff1a;“电脑提示找不到msvcp140.dll文件&#xff0c;因此无法继续执行…

Mysql基础(二)数据类型和约束

一 数据类型 讲解主要的数据类型,不面面俱到,后续遇到具体问题再查询补充扩展&#xff1a; 知识点的深度和广度以工作为导向 ① int float M : 表示显示宽度&#xff0c;M的取值范围是(0, 255)例如: int(5),当数据宽度小于5位的时候在数字前面需要用字符填满宽度说明&…

双击复制elementui表格某个单元格的数据

提示:记录工作中遇到的需求及解决办法 文章目录 前言一、代码前言 在使用elementui的表格将数据展示出来时,我们想复制该表格区域对应的内容,但因为想复制的列不想太宽而数据太长导致数据省略,无法使用鼠标选择来全部复制到,所以想能不能实现一个双击该内容达到复制效果;…

VSCode 配置 C/C++ 环境

1 安装 VSCode 直接去官网(https://code.visualstudio.com/)下载并安装即可。 2 配置C/C编译环境 方案一 如果是在Windows&#xff0c;需要安装 MingW&#xff0c;可以去官网(https://sourceforge.net/projects/mingw-w64/)下载安装包。 注意安装路径不要出现中文。 打开 w…

声明式事务

文章目录 1.事务分类1.传统方式解决事务2.声明式事务 2.声明式事务案例1.需求分析2.解决方案分析3.数据表创建4.编写GoodsDao.java1.编写配置文件JdbcTemplate_ioc.xml2.单元测试 5.编写GoodsService.java6.配置事务管理器JdbcTemplate_ioc.xml7.进行测试 3.debug事务管理器Dat…

HubSpot流量转化:从访客到客户的转化策略

在当今数字化时代&#xff0c;企业营销获客的关键在于如何将网站访客转化为实际客户。作为HubSpot的合作伙伴&#xff0c;我们深知HubSpot软件在流量转化方面的强大功能。今天运营坛将带领大家深入探讨HubSpot流量转化的核心原理&#xff0c;并介绍如何利用个性化营销策略、构建…

实验2 NFS部署和配置

一、实训目的 1.了解NFS基本概念 2.实现NFS的配置和部署 二、实训准备 1.准备一台能够安装OpenStack的实验用计算机&#xff0c;建议使用VMware虚拟机。 2.该计算机应安装CentOS 7&#xff0c;建议采用CentOS 7.8版本。 3.准备两台虚拟机机&#xff08;客户机和服务器机&…

在React Router 6中使用useRouteLoaderData钩子获取自定义路由信息

在 React Router 6 中怎么像vueRouter一样&#xff0c;可以在配置路由的时候&#xff0c;定义路由的元信息(附加信息)&#xff1f;答案是可以的。稍有些复杂。核心是通过为每个路由定义了一个 loader 函数,用于返回自定义的路由信息&#xff0c;然后通过useRouteLoaderData 钩子…

机器人实验室LAAS-CNRS介绍

一、LAAS-CNRS介绍 1、缩写介绍 同样的&#xff0c;给出英文缩写的全称&#xff0c;以便理解。这里的LAAS&#xff08;Laboratory for Analysis and Architecture of Systems&#xff09;指法国的系统分析与架构实验室&#xff0c;CNRS&#xff08;Centre National de la Rec…

docker容器内ping外网能通,curl不通

排查原因是因为&#xff0c;在服务器上查看ifconfig&#xff0c;显示docker0的mtu是1500&#xff0c;网卡的mtu是1450。 mtu是指在网络通信中能够承载的最大数据包大小。一般情况下&#xff0c;docker的mtu默认为1500字节。 然而&#xff0c;不同的网络设备和网络配置可能会导…

Web3安全性:保护去中心化应用和用户的最佳实践

引言 随着Web3和去中心化应用&#xff08;DApps&#xff09;的迅速发展&#xff0c;我们进入了一个充满无限可能性的新世界。然而&#xff0c;这个数字天堂也伴随着一系列复杂的安全挑战。本文将深入探讨这些挑战&#xff0c;并提供一系列实用的安全建议&#xff0c;帮助你在W…

C++初阶学习第二弹——C++入门(下)

C入门&#xff08;上&#xff09;&#xff1a;C初阶学习第一弹——C入门&#xff08;上&#xff09;-CSDN博客 目录 一、引用 1.1 引用的实质 1.2 引用的用法 二、函数重载 三、内敛函数 四、auto关键字 五、总结 前言&#xff1a; 在上面一章我们已经讲解了C的一些基本…

深度剖析图像处理—边缘检测

什么是边缘检测 边缘检测(Edge Detection)就是提取图像中的边缘点(Edge Point)。边缘点是与周围像素相比灰度值有阶跃变化或屋顶状变化的像素。边缘常存在于目标与背景之间、目标与目标之间、目标与其影子之间。 ​ 在图像处理和图像分析中&#xff0c;经常要用到边缘(Edge)、边…

【学习】对于加密接口、签名接口如何进行性能测试

随着科技的飞速发展&#xff0c;加密接口和签名接口在我们的日常生活中扮演着越来越重要的角色。从在线支付到信息安全&#xff0c;它们始终默默地守护着我们的数字世界。然而&#xff0c;随着应用场景的不断扩展&#xff0c;性能测试变得尤为重要。今天&#xff0c;让我们一起…

单例模式与反射创建对象

单例模式 饿汉式单例模式 单例模式&#xff0c;就是自己先把自己创建了&#xff0c;整个程序都只有这一个实例&#xff0c;别人都没有办法创建实例&#xff0c;因为他的构造方法是private的 一次性把全部都创建了 public class HungryMan {private static int [][] s new …

[lesson48]同名覆盖引发的问题

同名覆盖引发的问题 父子间的赋值兼容 子类对象可以当做父类对象使用(兼容性) 子类对象可以直接赋值给父类对象(<font color>兼容性)子类对象可以直接初始化父类对象父类指针可以直接指向子类对象父类引用可以直接引用子类对象 当使用父类指针(引用)指向子类对象时 子类…