inux应用开发基础知识——串口应用编程(十一)

news2025/1/19 20:31:28

前言:

在Linux系统中,串口设备以文件的形式存在,通常位于/dev目录下,如ttyS0、ttyUSB0等。这些设备文件可以用于读取和写入数据。要使用串口设备,需要打开相应的设备文件。在打开串口时,可以使用O_RDWR选项标志进行读写操作,同时使用O_NOCTTY选项标志告诉Linux“本程序不作为串口的‘控制终端’”,以避免一些输入字符影响进程运行。下面让我们对串口应用编程进行一个简单的入门学习吧。

目录

 一、串口的作用

二、UART硬件介绍

1.使用串口

2. mini2440:

3. JZ2440: 

三、TTY体系中设备节点的差别

1.什么是TTY?

2.各类设备节点的差别

 四、TTY驱动程序的框架

五、回环 

1.串口API

2.串口收发实验

六、GPS 模块

1.GPS 模块硬件

2.GPS 模块数据格式


 一、串口的作用

UART:通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),简称串口。

        调试:移植u-boot、内核、应用程序时,主要使用串口查看打印信息

        外接各种模块

串口因为结构简单、稳定可靠,广受欢迎。

通过三根线即可,发送、接收、地线
 

二、UART硬件介绍

1.使用串口

(1)波特率

(2)格式:数据位,停止位,校验位,流量控制

怎么样发送1Byte,比如‘A’        

        'A' = 0x41 = 0100 0001

(1) 双方约定波特率: 每一位占据的时间,假设为1秒

(2) 逻辑电平

 如果TTL想转化为RS-232的话需要电平转化芯片

2. mini2440:

3. JZ2440: 

波特率为115200格式为8n1时每秒可以传输的字节为:11520 byte

三、TTY体系中设备节点的差别

/dev/ttySo、/dev/ttySACO、/dev/tty.ldev/tty0、/dev/tty1、/devlconsole,

它们有什么差别?

TTYTerminal/ConsolE/UART,

它们有什么差别?

1.什么是TTY?

        teletype,更准确地说是teleprinter,是一种通信设备,可以用来发送、接收文本信息

        teletype是一家公司的名字,它生产的teleprinter实在太有名,结果公司名变成了这类产品的名字:teleprinter都被称为teletype了。

teletype被用来传输商业电报,想象一下:

        把两台teletype的线缆接在一起,或者使用无线技术连接两台telety这边打字,另一边就可以接收到信息并诵过纸张打印出来。

将 teletype 的另一端直接与电脑连接起来,这样TTY就与计算机关联起来了。

        以前的计算机非常的昂贵,可能会有很多地方是共用一台电脑,通过终端进行操作计算机,一个系统存在多个终端。

Terminal和Console的差别
        Terminal含有远端的意思,中文为:终端。Console翻译为控制台,可以理解为权限更大、能查看更多信息。比如我们可以在Console上看到内核的打印信息,从这个角度上看:

        Console是某一个Terminal

        Terminal并不都是Console。

        我们可以从多个Terminal中选择某一个作为Console

        很多时候,两个概念混用,并无明确的、官方的定义

        随着时代的发展又出现了新的设备。

        随着时代的发展个人电脑和虚拟终端也进入了人们的生活中

在Ubuntu上演示:
按住键盘:Ctrl+Alt+F3启动一个虚拟终端,Ctrl+Alt+F4再启动一个虚拟终端。在里面切换为root用户:
sudo passwd root //如果su root不成功,就先设置root密码su root

2.各类设备节点的差别

 /devlconsole

比如: coisole=ttyS0 console=tty
不想去分辨这个设备是串口还是虚拟端,有没有办法得到这个设备?
有!通过/devlconsole!
console=ttyS0时: /devlconsole就是ttyso
console=tty时:/devlconsole就是前台程序的虚拟终端

console=tty0时: /devlconsole就是前台程序的虚拟终端

console=ttyN时:/devlconsole就是/dev/ttyN
console有多个取值时,使用最后一个取值来判断

 四、TTY驱动程序的框架

        大多数用户都会在输入时犯错,所以退格键会很有用。这当然可以由应用程序本身来实现,但是根据UNIX设计"哲学",应用程序应尽可能保持简单。为了方便起见,操作系统提供了一个编辑缓冲区和一些基本的编辑命令(退格清除单个单词,清除行,重新打印),这些命令在行规范((line discipline)内默认启用。高级应用程序可以通过将行规范设置为原姓模式(raw mode)而不是默认的成熟或准则模式(cooked and canonical)来禁用这些功能。

五、回环 

1.串口API

        在Linux系统中,操作设备的统一接口就是: open/ioctl/read/write

        对于UART,又在ioctl之上封装了很多函数,主要是用来设置行规程。所以对于UART,编程的套路就是:
                 open
                设置行规程,比如波特率、数据位、停止位、检验位、RAW模式、一有数据就返回                        read/write

        怎么设置行规程? 

        行规程的参数用结构体 termios 来表示,可以参考 Linux 串口—struct termios 结构体:

https://blog.csdn.net/yemingzhu163/article/details/5897156

这些函数在名称上有一些惯例:

 tc:terminal contorl

 cf: control flag

        主要是需要设置好 termios 中的参数,这些参数很复杂,可以参考 Linux 串口—struct termios 结构体。 

   
  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <sys/types.h>
  4 #include <errno.h>
  5 #include <sys/stat.h>
  6 #include <fcntl.h>
  7 #include <unistd.h>
  8 #include <termios.h>
  9 #include <stdlib.h>
 10
 11 /* set_opt(fd,115200,8,'N',1) */
 12 int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
 13 {
 14     struct termios newtio,oldtio;
 15
 16     if ( tcgetattr( fd,&oldtio) != 0) {
 17         perror("SetupSerial 1");
 18         return -1;
 19     }
 20
 21     bzero( &newtio, sizeof( newtio ) );
 22     newtio.c_cflag |= CLOCAL | CREAD;
 23     newtio.c_cflag &= ~CSIZE;
 24
 25     newtio.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);  /*Input*/
 26     newtio.c_oflag  &= ~OPOST;   /*Output*/
 27
 28     switch( nBits )
 29     {
 30     case 7:
 31         newtio.c_cflag |= CS7;
 32     break;
 33     case 8:
 34         newtio.c_cflag |= CS8;
 35     break;
 36     }
 37
 38     switch( nEvent )
 39     {
 40     case 'O':
 41         newtio.c_cflag |= PARENB;
 42         newtio.c_cflag |= PARODD;
 43         newtio.c_iflag |= (INPCK | ISTRIP);
 44     break;
 45     case 'E':
 46         newtio.c_iflag |= (INPCK | ISTRIP);
 47         newtio.c_cflag |= PARENB;
 48         newtio.c_cflag &= ~PARODD;
 49     break;
 50     case 'N':
 51         newtio.c_cflag &= ~PARENB;
 52     break;
 53     }
 54
 55     switch( nSpeed )
 56     {
 57     case 2400:
 58         cfsetispeed(&newtio, B2400);
 59         cfsetospeed(&newtio, B2400);
 60     break;
 61     case 4800:
 62         cfsetispeed(&newtio, B4800);
 63         cfsetospeed(&newtio, B4800);
 64     break;
 65     case 9600:
 66         cfsetispeed(&newtio, B9600);
 67         cfsetospeed(&newtio, B9600);
 68     break;
 69     case 115200:
 70         cfsetispeed(&newtio, B115200);
 71         cfsetospeed(&newtio, B115200);
 72     break;
 73     default:
 74         cfsetispeed(&newtio, B9600);
 75         cfsetospeed(&newtio, B9600);
 76     break;
 77     }
 78
 79     if( nStop == 1 )
 80         newtio.c_cflag &= ~CSTOPB;
 81     else if ( nStop == 2 )
 82         newtio.c_cflag |= CSTOPB;
 83
 84     newtio.c_cc[VMIN]  = 1;  /* 读数据时的最小字节数: 没读到这些数据我就不返回! */
 85     newtio.c_cc[VTIME] = 0; /* 等待第1个数据的时间:
 86                              * 比如VMIN设为10表示至少读到10个数据才返回,
 87                              * 但是没有数据总不能一直等吧? 可以设置VTIME(单位是10秒)
 88                              * 假设VTIME=1,表示:
 89                              *    10秒内一个数据都没有的话就返回
 90                              *    如果10秒内至少读到了1个字节,那就继续等待,完全读到VMIN个数据再返回
 91                              */
 92
 93     tcflush(fd,TCIFLUSH);
 94
 95     if((tcsetattr(fd,TCSANOW,&newtio))!=0)
 96     {
 97         perror("com set error");
 98         return -1;
 99     }                                                                                                                                                                                                                                                                                                                                     
100     //printf("set done!\n");
101     return 0;
102 }
103
104 int open_port(char *com)
105 {
106     int fd;
107     //fd = open(com, O_RDWR|O_NOCTTY|O_NDELAY);
108     fd = open(com, O_RDWR|O_NOCTTY);
109     if (-1 == fd){
110         return(-1);
111     }
112
113       if(fcntl(fd, F_SETFL, 0)<0) /* 设置串口为阻塞状态*/
114       {
115             printf("fcntl failed!\n");
116             return -1;
117       }
118
119       return fd;
120 }
121
122
123 /*
124  * ./serial_send_recv <dev>
125  */
126 int main(int argc, char **argv)
127 {
128     int fd;
129     int iRet;
130     char c;
131
132     /* 1. open */
133
134     /* 2. setup
135      * 115200,8N1
136      * RAW mode
137      * return data immediately
138      */
139
140     /* 3. write and read */
141
142     if (argc != 2)
143     {
144         printf("Usage: \n");
145         printf("%s </dev/ttySAC1 or other>\n", argv[0]);
146         return -1;
147     }
148
149     fd = open_port(argv[1]);
150     if (fd < 0)
151     {
152         printf("open %s err!\n", argv[1]);
153         return -1;
154     }
155
156     iRet = set_opt(fd, 115200, 8, 'N', 1);
157     if (iRet)
158     {
159         printf("set port err!\n");
160         return -1;
161     }
162
163     printf("Enter a char: ");
164     while (1)
165     {
166         scanf("%c", &c);
167         iRet = write(fd, &c, 1);
168         iRet = read(fd, &c, 1);
169         if (iRet == 1)
170             printf("get: %02x %c\n", c, c);
171         else
172             printf("can not get data\n");
173     }
174
175     return 0;
176 }

第152行: 打开设备节点

第156行:设置波特率和格式

164     while (1)
165     {
166         scanf("%c", &c);
167         iRet = write(fd, &c, 1);
168         iRet = read(fd, &c, 1);
169         if (iRet == 1)
170             printf("get: %02x %c\n", c, c);
171         else
172             printf("can not get data\n");
173     }

第164~173行: 进入循环后等待用户输入数据,得到数据后发给指定的串口,再读取指定串口上的数据,读到后在命令行中打印出来  

104 int open_port(char *com)


 第104~120行:打开设备节点

 第113行:

               1.fcntl (fd,FSETFL,FNDELAY);读数据时不等待,没有数据就返回0

               2.fcntl (fd, F_SETFL,0);读数据时,没有数据阻塞

12 int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)

第12~102行: 设置波特率和格式

 

2.串口收发实验

        通过把串口的发送、接收引脚短接,实现自发自收:使用 write 函数 发出字符,使用 read 函数读取字符。

1. Ubuntu 上
arm-buildroot-linux-gnueabihf-gcc -o serial_send_recv serial_send_recv.c
2. 板子上
/mnt/serial_send_recv /dev/ttymxc5

六、GPS 模块

        全球定位系统(Global Positioning System,GPS)是一种以空中卫星为 基础的高精度无线电导航的定位系统,它在全球任何地方以及近地空间都能够提 供准确的地理位置、车行速度及精确的时间信息。GPS 主要由三大组成部分:空 间部分、地面监控部分和用户设备部分。GPS 系统具有高精度、全天候、用广泛 等特点。

1.GPS 模块硬件

        GPS 模块与外部控制器的通讯接口有多种方式,这里我们使用串口进行通讯, 波特率为 9600bps,1bit 停止位,无校验位,无流控,默认每秒输出一次标准格式数据。

2.GPS 模块数据格式

        GPS 使用多种标准数据格式,目前最通用的 GNSS 格式是 NMEA0183 格式。 NMEA0183 是最终定位格式,即将二进制定位格式转为统一标准定位格式,与卫星类型无关。这是一套定义接收机输出的标准信息,有几种不同的格式,每种都是独立相关的 ASCII 格式,逗点隔开数据流,数据流长度从 30-100 字符不等, 通常以每秒间隔持续输出。

        我们使用串口接收数据,收到的数据包含:$GPGGA(GPS 定位数据)、$GPGLL (地理定位信息)、$GPGSA(当前卫星信息)、$GPGSV(可见卫星状态信息)、 $GPRMC(推荐最小定位信息)、$GPVTG(地面速度信息)。

         这里我们只分析$GPGGA (Global Positioning System Fix Data)即可, 它包含了 GPS 定位经纬度、质量因子、HDOP、高程、参考站号等字段。其标准格式如下:

$XXGGA 语句各字段的含义和取值范围各字段的含义和取值范围见下表所示, XX 取值有: ◼         GPGGA:单 GPS

        BDGGA:单北斗

        GLGGA:单 GLONASS

        GNGGA:多星联合定位

例子:$GPGGA,074529.82,2429.6717,N,11804.6973,E,1,8,1.098, 42.110,,,M,,*76。

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <sys/types.h>
  4 #include <errno.h>
  5 #include <sys/stat.h>
  6 #include <fcntl.h>
  7 #include <unistd.h>
  8 #include <termios.h>
  9 #include <stdlib.h>
 10
 11 /* set_opt(fd,115200,8,'N',1) */
 12 int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
 13 {
 14     struct termios newtio,oldtio;
 15
 16     if ( tcgetattr( fd,&oldtio) != 0) {
 17         perror("SetupSerial 1");
 18         return -1;
 19     }
 20
 21     bzero( &newtio, sizeof( newtio ) );
 22     newtio.c_cflag |= CLOCAL | CREAD;
 23     newtio.c_cflag &= ~CSIZE;
 24
 25     newtio.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);  /*Input*/
 26     newtio.c_oflag  &= ~OPOST;   /*Output*/
 27
 28     switch( nBits )
 29     {
 30     case 7:
 31         newtio.c_cflag |= CS7;
 32     break;
 33     case 8:
 34         newtio.c_cflag |= CS8;
 35     break;
 36     }
 37
 38     switch( nEvent )
 39     {
 40     case 'O':
 41         newtio.c_cflag |= PARENB;
 42         newtio.c_cflag |= PARODD;
 43         newtio.c_iflag |= (INPCK | ISTRIP);
 44     break;
 45     case 'E':
 46         newtio.c_iflag |= (INPCK | ISTRIP);
 47         newtio.c_cflag |= PARENB;
 48         newtio.c_cflag &= ~PARODD;
 49     break;
 50     case 'N':
 51         newtio.c_cflag &= ~PARENB;
 52     break;
 53     }
 54
 55     switch( nSpeed )
 56     {
 57     case 2400:
 58         cfsetispeed(&newtio, B2400);
 59         cfsetospeed(&newtio, B2400);
 60     break;
 61     case 4800:
 62         cfsetispeed(&newtio, B4800);
 63         cfsetospeed(&newtio, B4800);
 64     break;
 65     case 9600:
 66         cfsetispeed(&newtio, B9600);
 67         cfsetospeed(&newtio, B9600);
 68     break;
 69     case 115200:
 70         cfsetispeed(&newtio, B115200);
 71         cfsetospeed(&newtio, B115200);
 72     break;
 73     default:
 74         cfsetispeed(&newtio, B9600);
 75         cfsetospeed(&newtio, B9600);
 76     break;
 77     }
 78
 79     if( nStop == 1 )
 80         newtio.c_cflag &= ~CSTOPB;
 81     else if ( nStop == 2 )
 82         newtio.c_cflag |= CSTOPB;
 83
 84     newtio.c_cc[VMIN]  = 1;  /* 读数据时的最小字节数: 没读到这些数据我就不返回! */
 85     newtio.c_cc[VTIME] = 0; /* 等待第1个数据的时间:
 86                              * 比如VMIN设为10表示至少读到10个数据才返回,
 87                              * 但是没有数据总不能一直等吧? 可以设置VTIME(单位是10秒)
 88                              * 假设VTIME=1,表示:
 89                              *    10秒内一个数据都没有的话就返回
 90                              *    如果10秒内至少读到了1个字节,那就继续等待,完全读到VMIN个数据再返回
 91                              */
 92
 93     tcflush(fd,TCIFLUSH);
 94
 95     if((tcsetattr(fd,TCSANOW,&newtio))!=0)
 96     {
 97         perror("com set error");
 98         return -1;
 99     }
100     //printf("set done!\n");
101     return 0;
102 }
103
104 int open_port(char *com)
105 {
106     int fd;
107     //fd = open(com, O_RDWR|O_NOCTTY|O_NDELAY);
108     fd = open(com, O_RDWR|O_NOCTTY);
109     if (-1 == fd){
110         return(-1);
111     }
112
113       if(fcntl(fd, F_SETFL, 0)<0) /* 设置串口为阻塞状态*/
114       {
115             printf("fcntl failed!\n");
116             return -1;
117       }
118
119       return fd;
120 }
121
122
123 int read_gps_raw_data(int fd, char *buf)
124 {
125     int i = 0;
126     int iRet;
127     char c;
128     int start = 0;
129
130     while (1)
131     {
132         iRet = read(fd, &c, 1);
133         if (iRet == 1)
134         {
135             if (c == '$')
136                 start = 1;
137             if (start)
138             {
139                 buf[i++] = c;
140             }
141             if (c == '\n' || c == '\r')
142                 return 0;
143         }
144         else
145         {
146             return -1;
147         }
148     }
149 }
150
151 /* eg. $GPGGA,082559.00,4005.22599,N,11632.58234,E,1,04,3.08,14.6,M,-5.6,M,,*76"<CR><LF> */
152 int parse_gps_raw_data(char *buf, char *time, char *lat, char *ns, char *lng, char *ew)
153 {
154     char tmp[10];
155
156     if (buf[0] != '$')
157         return -1;
158     else if (strncmp(buf+3, "GGA", 3) != 0)
159         return -1;
160     else if (strstr(buf, ",,,,,"))
161     {
162         printf("Place the GPS to open area\n");
163         return -1;
164     }
165     else {
166         //printf("raw data: %s\n", buf);
167         sscanf(buf, "%[^,],%[^,],%[^,],%[^,],%[^,],%[^,]", tmp, time, lat, ns, lng, ew);
168         return 0;
169     }
170 }
171
172
173 /*
174  * ./serial_send_recv <dev>
175  */
176 int main(int argc, char **argv)
177 {
178     int fd;
179     int iRet;
180     char c;
181     char buf[1000];
182     char time[100];
183     char Lat[100];
184     char ns[100];
185     char Lng[100];
186     char ew[100];
187
188     float fLat, fLng;
189
190     /* 1. open */
191
192     /* 2. setup
193      * 115200,8N1
194      * RAW mode
195      * return data immediately
196      */
197
198     /* 3. write and read */
199
200     if (argc != 2)
201     {
202         printf("Usage: \n");
203         printf("%s </dev/ttySAC1 or other>\n", argv[0]);
204         return -1;
205     }
206
207     fd = open_port(argv[1]);
208     if (fd < 0)
209     {
210         printf("open %s err!\n", argv[1]);
211         return -1;
212     }
213
214     iRet = set_opt(fd, 9600, 8, 'N', 1);
215     if (iRet)
216     {
217         printf("set port err!\n");
218         return -1;
219     }
220
221     while (1)
222     {
223         /* eg. $GPGGA,082559.00,4005.22599,N,11632.58234,E,1,04,3.08,14.6,M,-5.6,M,,*76"<CR><LF>*/
224         /* read line */
225         iRet = read_gps_raw_data(fd, buf);
226
227         /* parse line */
228         if (iRet == 0)
229         {
230             iRet = parse_gps_raw_data(buf, time, Lat, ns, Lng, ew);
231         }
232
233         /* printf */
234         if (iRet == 0)
235         {
236             printf("Time : %s\n", time);
237             printf("ns   : %s\n", ns);
238             printf("ew   : %s\n", ew);
239             printf("Lat  : %s\n", Lat);
240             printf("Lng  : %s\n", Lng);
241
242             /* 纬度格式: ddmm.mmmm */
243             sscanf(Lat+2, "%f", &fLat);
244             fLat = fLat / 60;
245             fLat += (Lat[0] - '0')*10 + (Lat[1] - '0');
246
247             /* 经度格式: dddmm.mmmm */
248             sscanf(Lng+3, "%f", &fLng);
249             fLng = fLng / 60;
250             fLng += (Lng[0] - '0')*100 + (Lng[1] - '0')*10 + (Lng[2] - '0');
251             printf("Lng,Lat: %.06f,%.06f\n", fLng, fLat);
252         }
253     }
254
255     return 0;
256 }
257
          
123 int read_gps_raw_data(int fd, char *buf)

 第123行对应到255行:GPS原始数据,读取一行数据

152 int parse_gps_raw_data(char *buf, char *time, char *lat, char *ns, char *lng, char *ew)

 第151~170行:进行数据解析

 

167 sscanf(buf, "%[^,],%[^,],%[^,],%[^,],%[^,],%[^,]", tmp, time, lat, ns, lng, ew);

 注:%[^] 剔除不包含符合

 第181~186 :定义这五个数据 

 第228行:iRet == 0表明读到数据

 第234~252行:读到数据并进行数据解析,如果解析成功则将这些信息打印出来

第181~186行 :定义这五个数据。

第242~251行:将经纬度转变并且打印出来。

1. Ubuntu 上
arm-buildroot-linux-gnueabihf-gcc -o gps_read gps_read.c
2. 板子上
/mnt/gps_read /dev/ttymxc5

 

 

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

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

相关文章

YOLOv8-seg改进:重新思考轻量化视觉Transformer中的局部感知CloFormer,提升上下文感知权重来增强局部特征 |2023清华

🚀🚀🚀本文改进:CloFormertAttention利用共享权重和上下文感知权重有效地提取高频局部特征表示 🚀🚀🚀SEAM、MultiSEAM分割物与物相互遮挡、分割小目标性能 🚀🚀🚀YOLOv8-seg创新专栏:http://t.csdnimg.cn/KLSdv 学姐带你学习YOLOv8,从入门到创新,轻轻…

年底了,我劝大家真别轻易离职...

年底了&#xff0c;一些不满现状&#xff0c;被外界的“高薪”“好福利”吸引的人&#xff0c;一般就在这时候毅然决然地跳槽了。 在此展示一套学习笔记 / 面试手册&#xff0c;年后跳槽的朋友可以好好刷一刷&#xff0c;还是挺有必要的&#xff0c;它几乎涵盖了所有的软件测试…

局域网协议:VLAN技术介绍

文章目录 VLAN概述VLAN的优点VLAN的原理VLAN的配置推荐阅读 VLAN概述 VLAN&#xff08;Virtual Local Area Network虚拟局域网&#xff09;是一种在物理网络基础上划分逻辑上独立的局域网的技术。它允许将网络设备按照逻辑上的需求而非物理位置进行分组&#xff0c;提供更好的…

解决 VSCode 配置远程连接,过程试图写入的管道不存在

解决 VSCode 配置远程连接&#xff0c;过程试图写入的管道不存在

vxe编辑保存表格

业务需求&#xff1a; 1、需要点击编辑时&#xff0c;全部表格显示编辑框&#xff0c;点击保存&#xff0c;全部保存。 2、因为位置问题&#xff0c;产品经理把24小时分成了两行&#xff0c;开发就得分两个表格。列标题是写死的&#xff0c;文字偏移也是写死的&#xff0c;其他…

部署你的第一个应用

&#x1f5d3;️实验环境 OS名称Microsoft Windows 11 家庭中文版系统类型x64-based PCDocker版本Docker version 24.0.6, build ed223bcminikube版本v1.32.0 &#x1f913;FastAPI 构建应用 #基于fastapi快速创建一个项目 rkun1LAPTOP-TUS5FU0D MINGW64 / $ mkdir k8s-appr…

Java引用和内部类

引用 引用变量 引用相当于一个 “别名”, 也可以理解成一个指针. 创建一个引用只是相当于创建了一个很小的变量, 这个变量保存了一个整数, 这个整数表示内存中的一个地址. new 出来的数组肯定是在堆上开辟的空间,那么在栈中存放的就是引用,引用存放的的就是一个对象的地址,代表…

ORB-SLAM3在windows11下的编译使用

01 写在前面 近期在学习SLAM&#xff0c;想部署一下ORB-SLAM3&#xff0c;但是自己电脑是win11系统&#xff0c;因此就想着在win11上部署一下。但是网上看了一些教程&#xff0c;有一些博客&#xff0c;但是可能不适合我这种情况把&#xff0c;就很纠结。先说下结果&#xff0…

写单元测试,没你想得那么简单!

前言 单元测试是什么我们就简单介绍一下&#xff1a; 单元测试是针对程序模块&#xff08;软件设计的最小单位&#xff09;来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。 接下来是本人对单元测试的理解和实践。里面没有废话&#xff0c;希望每句话能说到你心…

【数据库】数据库中的检查点Checkpoint,数据落盘的重要时刻

检查点(checkpoint) ​专栏内容&#xff1a; 手写数据库toadb 本专栏主要介绍如何从零开发&#xff0c;开发的步骤&#xff0c;以及开发过程中的涉及的原理&#xff0c;遇到的问题等&#xff0c;让大家能跟上并且可以一起开发&#xff0c;让每个需要的人成为参与者。 本专栏会定…

天空分割技术解决方案

图像处理技术已经成为企业提升用户体验、优化产品和服务的重要工具。美摄科技&#xff0c;作为全球领先的AI图像处理技术提供商&#xff0c;一直致力于研发和应用最先进的技术&#xff0c;以满足企业的各种需求。今天&#xff0c;我们很高兴地向大家介绍我们的新一代产品——美…

git中的分支管理:git branch,git checkout,解决git中的分支冲突的方法【Git学习三】

&#x1f601; 作者简介&#xff1a;一名大四的学生&#xff0c;致力学习前端开发技术 ⭐️个人主页&#xff1a;夜宵饽饽的主页 ❔ 系列专栏&#xff1a;Git等软件工具技术的使用 &#x1f450;学习格言&#xff1a;成功不是终点&#xff0c;失败也并非末日&#xff0c;最重要…

2023感恩节大促:跨境卖家如何借助海外网红营销赢得市场关注

随着全球贸易的日益发展&#xff0c;跨境电商行业变得愈发竞争激烈&#xff0c;各家卖家纷纷寻找新的营销策略以在大促期间脱颖而出。在2023年感恩节即将来临之际&#xff0c;海外网红营销成为许多卖家关注的热点。本文Nox聚星将和大家探讨跨境卖家如何充分利用海外网红营销&am…

《C++ Primer》第9章 顺序容器(三)

参考资料&#xff1a; 《C Primer》第5版《C Primer 习题集》第5版 9.5 额外的string操作&#xff08;P320&#xff09; 9.5.1 构造string的其他方法 const char *cp "hello, world!"; char arr[] { h,\0,i,\0 }; string s1(cp); // s1 "hello, world!…

什么是索引下推

索引下推介绍 索引下推&#xff08;INDEX CONDITION PUSHDOWN&#xff0c;简称 ICP&#xff09;是在 MySQL 5.6 针对扫描二级索引的一项优化改进。总的来说是通过把索引过滤条件下推到存储引擎&#xff0c;来减少 MySQL 存储引擎访问基表的次数以及 MySQL 服务层访问存储引擎的…

CSS伪类选择器详细讲解

前言 伪类选择器在CSS中起到的作用可以说是至关重要的&#xff0c;如果CSS没有伪类选择器&#xff0c;有很多效果都要借助js来完成&#xff0c;这样不仅代码量增加&#xff0c;维护起来你难度也大。这样程序员的工作量大&#xff0c;也违背了CSS诞生的作用&#xff0c;就是提高…

在Spring Boot中使用ECharts绘制数据图表

使用ECharts来完成一些花里胡哨的图表吧&#xff0c;一般这种需求我们在我们的客户端不太常见&#xff0c;但是&#xff0c;我们在后端进行各种数据统计的时候就会发现ECharts的优点了&#xff0c;比如我们常常做的柱状图&#xff0c;折线图&#xff0c;雷达图等可视化形式&…

阿里云发送短信

官方代码如下&#xff1a; // This file is auto-generated, dont edit it. Thanks. package com.aliyun.sample;import com.aliyun.tea.*;public class Sample {/*** 使用AK&SK初始化账号Client* param accessKeyId* param accessKeySecret* return Client* throws Excep…

windows系统安装ubuntu22.04虚拟机

镜像文件准备 镜像文件 官网 企业开源和Linux | Ubuntu 镜像下载地址 https://cn.ubuntu.com/download/server/step1 选择合适的版本下载 虚拟机安装 文件-- 新建虚拟机 选择镜像 修改安装路径 修改大小&#xff0c;最好60g&#xff0c;大一点 设置用户信息 设置虚拟机网络…

【NLP】培训LLM的不同方式

在大型语言模型&#xff08;LLM&#xff09;领域&#xff0c;存在多种具有不同手段、要求和目标的培训机制。由于它们有不同的用途&#xff0c;因此重要的是不要将它们相互混淆并了解它们适用的不同场景。 在本文中&#xff0c;我想概述一些最重要的训练机制&#xff0c;包括预…