当前位置: 首页 > news >正文

初学51单片机之指针基础与串口通信应用

开始之前推荐一个电路学习软件,这个软件笔者也刚接触。名字是Circuit有在线版本和不在线版本,这是笔者在B站看视频翻到的。

Paul Falstadicon-default.png?t=N7T8https://www.falstad.com/这是地址。

离线版本在网站内点这个进去

根据你的系统下载你需要的版本红线的是windows版本。它的使用说明都是英文,如果是英文苦手,可以使用QQ浏览器自带网页翻译或者使用翻译工具百度或者搜狗,它有个文件翻译功能可以翻译整个文件。虽然有些翻译的不正确,总比没有的好。

      这期浅述下C语言指针与串口通信的一些功能。在学C之前笔者就听过C指针的恶名,这次写程序的时候也确实狠狠的体验了一把。笔者的C也是初学,目前是51特攻。除了之前博文使用的语句外,笔者对C的其他用语句没有过多涉及或者说基本没有涉及。因此为了写这个程序笔者又在B站大学突击了一下,最终是完成了。程序是简单的,但是完成这个程序花了笔者不少时间。

言归正转:

变量的地址:

      要研究指针,得先来深入理解内存地址这个概念。打个比方:整个内存就相当于一个拥有很多房间的大楼,每个房间都有房间号,比如从101、102、103一直到NNN。房间号就是房间的地址,相对应的内存中的每个单元也都有自己的编号,比如从0x00,0x01,0x02一直到0xNN,同样可以说这些编号就是内存单元的地址。房间里可以住人,对应的的内存单元里就可以“住进”变量了。

      假如1位名叫A先生的顾客住进101的房间,就可以说101就是A先生的住址。同样一个名为x的变量住在编号为0x00的内存单元中,就可以说0x00是变量x的地址。

基本的内存单元是字节,英文单词为Byte。(通俗的讲是1个地址单元只能存储8位数据,就好比一间客房只能住1个人,而这个房间号或者地址名可以是8位、16位的或者32位的这个一般受限于地址总线,51单片机的地址总线是16根因此它的取值范围是0x0000-0xFFFF因此它的寻址空间最大是64KB,51单片机的内存RAM的地址范围是0x00-0xFF,也就是它只需8根地址总线寻址,且可拓展的外部RAM最大空间为64KB)

看图不同类型元素在地址中存储:

      假设要存储char型的变量 a和b,因为a和b只有1个字节的大小,因此它只需要一个存储单元就能存下数据。 定义1个unsigned int型的变量c = 0x0A0B,它的大小有两个字节因此需要两个存储单元。那么变量C的低字节序内容0B,是存储在内存空间中的低地址还是高地址即如图是0x02还0x03?

        这个问题是由所用C编译器与单片机构架共同决定的,单片机类型不同就有可能不同。Keil+51单片机的环境下,0x02存的是高字节内容,0x03存的是低字节内容0B。同理long型的d储存需要4个存储单元。这种存储方式在C语言中叫大端字节序。与之相反常用的VS编程软件储存方式是小端字节序。

标准的Big-Endian和Little-Endian的定义如下:
  a) Little-Endian 小端
  就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
  
  b) Big-Endian 大端
  就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。                                                 

        实际生活中,要寻找1个人有两种方式,一种是通过它的名字来找人,还有第二种方式就是通过它的住址来找人。在派出所的户籍管理系统中输入小明的家庭住址,系统就会自动指向小明的相关信息。那么在C语言中,要访问一个变量同样有两种方式:一种是通过变量名来访问,另一种自然就是通过变量的地址来访问。

    在C语言,地址就等同于指针,变量的地址就是变量的指针。举个例子:

 unsigned char a = 0;//定义了变量a

unsigned char* ptr = &a;  //定义1个char型的指针变量,这个指针变量ptr的值是变量a的地址

虽然这个ptr指针指向的是变量a的地址,作为全局变量a程序开始运行后 编译系统会自动分配变量的地址,对于变量a来说这个地址是固定,但是对于指向变量的指针变量(地址)ptr它是可以被操作的,即左移或者右移。那么它移动一次的距离由指针类型决定的。比如上述的char*就是代表指针移动一次跨过一个存储单元,int*就代表指针移动一次跨过两个储存单元。注:这只在51单片机中是这样的,电脑端VS软件int型是4个字节的。

        定义一个int型的变量如上图c=0x0A0B,它的存储单元有两个。那么它的指针变量是指向哪个存储单元?它指向的一定是该变量的低地址这是由C语言本身所决定的规则即上图的0x02,变量c低地址0x02中存放是高字节内容0x0A。这和你是哪种指针类型没有关系,它不会因为你是int型的指针就指向两个地址(注:这里仅只描述地址的位置,它操作1次的范围还是两个存储空间,它执行的内容是地址本身的内容和地址+1后指向的内容),上图long型的变量d它的(ulong*p)指针指向的也是低地址0x04。

举个例子:变量B = 0x12345678 一个long型的变量

如果我们定义1个char*的指针变量ptr。

即 unsigned char * ptr = &B; 我们用printf打印这个 *ptr指向的内容,在小端序的主机中它的值是78H,如果用51单片机它是大端序它的值是12H.

如果我们定义1个int*的指针变量ptr

即 unsigned int * ptr = &B 我们用printf打印这个 *ptr指向的内容,在小端序的主机中它的值是5678H,如果用51单片机它是大端序它的值是1234H.

如果我们定义1个long*的指针变量ptr

即 unsigned int * ptr = &B 我们用printf打印这个 *ptr指向的内容,在小端序的主机中它的值是12345678H,如果用51单片机它是大端序它的值是12345678H.

如果我们把变量B=0x12345678直接赋值给SBUF缓冲寄存器,不做其他的操作即只传输1次。问这个操作在串口助手STC-ISP接收缓冲区最后显示的值是什么?后续笔者会给出完整的测试程序。

void main()
{unsigned long B = 0x12345678;unsigned char* p = &B;unsigned char* ptr = &B;unsigned long* ptr1 = &B;SBUF = 0x12345678; SBUF = B;	SBUF = *P;SBUF = *ptr;SBUF = *ptr1;
}void interruptUART() interrupt 4{if(RI){RI = 0;}if(TI){TI = 0;}}

     从题目上看着好像是个信息传递的问题?即对于一个多字节的的变量来说,它在传输的过程中是先传递低字节的内容呢,还是高字节的内容呢或者是先传输低地址的内容,还是高地址的内容呢?如果你涉及过着方面的内容肯定知道一些相关的内容可能在考虑什么网络传输字节序和主机传输字节序什么什么得。

先说结果:

SBUF = 0x12345678;  它最后在接收缓冲区的值是 78H

SBUF = B;它最后在接收缓冲区的值是 78H

从上面两个结果上来看它好像是先传输了低字节(即高地址)的内容。

char型指针 SBUF = *P;    它最后在接收缓冲区的值是 12H
int型指针    SBUF = *ptr;  它最后在接收缓冲区的值是  34H
long型指针 SBUF = *ptr1;它最后在接收缓冲区的值是  78H

可以看到用三种不同的指针它发送的结果都不一样。笔者陈述下这个问题,可能不是非常正确,只是笔者目前的理解。

1:对于串口软件STC-ISP,它的工作原则是我先接收到什么数据我就先显示什么数据,并且显示的数据在接收缓冲区里从左往右依次排序。即左边的数据接收到的时间是早于右边的数据:,它显示的最小单元是1个字节(即8为数据)。

2:SBUF它只是1个8位缓冲寄存器,因此对于51单片机如果你1次要传输多字节的内容,它仅会传输8位数据,后面的数据它就中止传输了抛掉了。而且传输的是该数据的低8位,有点像强制类型转换。如果说它是1个16位的缓冲寄存器对于变量B,SBUF = B,他的结果就会是5678H。

3:对于char型指针,它的操作权限只有一个字节,鉴于是大端序,因此该指针操作一次的内容是12H,刚好SBUF是8位寄存器可以完全的显示出来。

      对于int型指针它的操作权限是两个字节,鉴于是大端序,因此该指针操作一次的内容是1234H,应用陈述2的结论它结果就34H。

     对于longt型指针它的操作权限是4个字节,鉴于是大端序,因此该指针操作一次的内容是12345678H,应用陈述2的结论它结果就78H。

     因此对于51单片机我们在传输的时候要控制每次传输的内容是1个字节,不要超出了。而且无论它是用什么协议的,程序是我们自己设计,作为程序员的我们自己知道先发送的是哪些内容。

指向数组元素的指针

    所谓指向数组元素的指针,其本质还是变量的指针,因为数组中的每个元素,其实都可以直接看成是1个变量 ,所以指向数组元素的指针,也就是变量的指针。

定义1个数组元素

unsigned char number[10]  ={0,1,2,3,4,5,6,7,8,9};  unsigned char *p = &number[0];它也可以写成

unsigned char* p = number; 数组元素名其实就代表了数组元素的首地址。整个数组元素是在内存空间中连续存储的,那么首元素是存在高位第地址还是低位地址呢?对于数组、字符串来说首元素就相当于高字节。

      数组、字符串或者文本它们的首元素都是存在低位地址的,无论是大端序还是小端序,它和单纯的变量存储是不一样的的。(这个是笔者暴论可能也存在不一样的,PC主机VS软件是小端序但它的首地址也是放在低位的,因为大端序是符合我们这种自然人阅读习惯的)

  • 1:定义1个Int型变量L=0x1134;

定义1个int的指针指向这个变量unsigned int*p = &l;

然后再定义1个char型的变量K ,最后把指针P指向的内容存入K中 k = *p,问K的值是多少?

结果 K = 0x34;这其实是强制类型转换了把int的变量存入char型的变量地址,它只存入低字节的内容和大小端序没什么关系。

  • 2:还是这个变量,把这指针变量改成char型unsigned char*p = &l; 问现在K的值是多少?

结果K = 0x11;char型指针指向的是变量L的低位地址,低位地址存储的是0x11,加上char型指针的权限因此结果是0x11。

  • 3还是这个变量指针变量依然是char型,现在把K定义为1个Int型的数,问现在K的值是多少?

结果K=0x0011;11H存放在高位地址,因为51单片机是大端序。

那么定义1个数组unsigned char array4[8] ={0x1,0x23,0x123,0x1234,0x12345,6,7,8};首先可以看到很多数组元素已经不符合数组定义的类型了。看下代码:

#include <reg52.h>bit cmdArrived = 0;   //命令到达标志,即接收到上位机下发的命令
unsigned char cmdIndex = 0;
unsigned char *ptrTxd;
unsigned char cntTxd = 0;unsigned char array1[1] = {1};
unsigned char array2[2] = {1,2};
unsigned char array3[4] = {0x11,0x21,0x31,0x41};
unsigned char array4[8] ={0x1,0x23,0x123,0x1234,0x12345,6,7,8};
unsigned int l =0x1134;
unsigned char*p = &l;
unsigned int k ;
void ConfigUART(unsigned int baud);
void main()
{EA = 1;  //开总中断k = *p;ConfigUART(9600);  //配置波特率为9600while (1){if(cmdArrived){cmdArrived = 0;switch(cmdIndex){case 1:ptrTxd = array1;cntTxd = sizeof(array1);TI = 1;break;case 2:ptrTxd = array2;cntTxd = sizeof(array2);TI = 1;break;case 3:ptrTxd = array3;cntTxd = sizeof(array3);TI = 1;break;case 4:ptrTxd = array4;cntTxd = sizeof(array4);TI = 1;break;default:break;	}	}}
}void ConfigUART(unsigned int baud)
{SCON  = 0x50;  //配置串口为模式1TMOD &= 0x0F;  //清零T1的控制位TMOD |= 0x20;  //配置T1为模式2TH1 = 256 - (11059200/12/32)/baud;  //计算T1重载值TL1 = TH1;     //初值等于重载值ET1 = 0;       //禁止T1中断ES  = 1;       //使能串口中断TR1 = 1;       //启动T1
}void InterruptUART() interrupt 4
{if (RI)  //接收到字节{RI = 0;  //清零接收中断标志位cmdIndex = SBUF;cmdArrived = 1;       }if (TI)  //字节发送完毕{TI = 0;  //清零发送中断标志位if(cntTxd > 0){SBUF = *ptrTxd;cntTxd--;ptrTxd++;}}
}

这个代码的功能就是自动计算数组元素的长度,并把数组元素1个个发送到串口助手的接收区显示出来。操作方式就是在单字符串发送区用16进制发送数字2,对函数来说就是使能了case 2:

因此显示出来的数据是数组2的元素。sizeof() 是一个判断数据类型或者表达式长度的运算符。

如果发送数字4显示的数组元素是什么

原数组 :unsigned char array4[8] ={0x1,0x23,0x123,0x1234,0x12345,6,7,8};结果是:

不知道这个结果是否符合读者你们的计算结果。首先呢这是char类型的数组,如果它存入的数据所占的内存空间大于char的内存空间,它会先发生强制类型转换,对于这个数组来说它就只存8位数多余的就会被抛掉,然后再传输数据因此这个强制类型转换后的结果是:

unsigned char array4[8] ={0x1,0x23,0x23,0x34,0x45,6,7,8}这就是串口助手显示出来的数据。

问:如果把数组的类型改成int型,即                                                                                                unsigned int array4[8] ={0x1,0x23,0x123,0x1234,0x12345,6,7,8}那么最后显示出来的结果是什么?

首先它会报警,提示指向不同对象的指针,不过不影响工作。

        结果是这个,我们分析下为什么会是这个结果。首先数组类型从char变成了int型,代表着它的存储空间扩大了1倍,因此sizeof()函数计数的结果翻倍了,即cntTxd的值是原先的2倍。所以它发送的值变成了16个,又因为是char型的指针因此它操作1次的内容是1个字节,操作1次越过的地址也是1个,因此它的结果就是把数组里的元素按照地址从低到高全部发出来了,因为数组所占的地址刚好是16个。并且对于long型的0x12345它显示的数是23H,45H,这个转换结果也符合之前的陈述。

问:如果把ptrTxd指针改成int型,数组也是Int型,它显示的结果会是什么?

        不知道这个结果是不是符合你的推论,显然前8个的显示结果和第一次的显示结果是一样的。我们分析一下,因为该数组还是int型的,因此数组求出的cntTxd值依然是16是之前两倍.int型的指针1次操作的内容是两个字节且跨过的地址也是两个,前面例子已经展示过SBUF它是个8位的寄存器,因此它只传输了低字节的内容。而操作8次就到达了数组的边界,因而后面8次的内容就都是数组之外连续的高位地址存储的内容。这就越界了,指针也变成了野指针。从这里你就可以看到指针是非常的自由,同样的用好它你就需要注意每个细节。如果我们不越界计数这里除以2,它就不会越界了,即对char类型的元素sizeof()函数求出的值刚好是元素个数,对与其他类型的元素的值的应用,应该要配合指针的类型来确定。

字符数组和字符指针

       在程序运行过程中,其值不能被改变的量称之为常量。常量分为不同类型,有整型常量如1、2、3;浮点型常量3.14、0.56、-4.8;字符型常量‘a’,‘b’,‘1’用单引号括起来,字符串常量"abcd","1234","abc123",用单引号括起来的表示1个字符,用双引号括起来的是1个字符串。

字符串常量是常量不能被修改的,因此如果你要对字符串处理应该开辟字符数组。

    字符串常量在内存中按顺序逐个储存字符串中字符的ASCII码值,并且特别注意,最后1个字符‘\0’,它是字符串的结束标志,它是系统自动添的。也就是说"abcd"这个字符串有5个字符,但是对于字符数组char arr[] = {'a','c','c','d'};系统是不会自动添加‘\0’的。在字符串中空格也是字符,因此要注意。

        笔者前面的博文有一个关于电子密码锁的功能,密码是2024 键码(区别长短按键)是1010。如果想要通过串口通信交互改掉密码,必然面对一些问题:如何识别输入的数据是用于修改密码的。对此程序内要有明确的规定。

因此使能这个功能的格式是 :先输入指令码    再输入数据码   

       因此必然要对指令码进行区别,这个指令码是干什么的呢?正常状态下程序是锁定密码修改的,为此必须给他一个指令告诉它打开修改密码的功能。这个指令可以是数字,字母,字符串只要和程序约定好都行。单个数字,字母这些虽然都可以但是不太符合自然人使用习惯,而且容易误触。因此使用字符串会是一个比较好的方式。 

   比如我就设定1个字符串"setkey"作为密码锁开启的指令,那么怎么在串口通信中实现这个功能呢?:

  • 1:需要开辟一个内存空间用于存储6个字节,因此可以开辟1个char的数组arrbuf[6],这个可以用于存放通过串口通信传输过来的数据。
  • 2:程序怎么辨别接收的字符串已经发送完毕了?C语言在操作字符串的时候它会自动加字符串结束码'\0',很多库函数在操作字符串的时候都是以这个字符来判断的。但是串口通信助手STC-ISP软件,在“单字符串发送区”向单片机发送字符串的时候它是不会自动加‘\0’,因此需要手动添加字符串结束码,本案添加的是!。即设定的开启指令是"setkey!",当然你也可以不添加,直接以字符串最后的字符y作为结束码判断,但是这个代码扩展性就低了,也不怎么规范。
  • 3:很多字符串的库函数工作方式都是通过判断字符串结束码‘\0’来实现功能的,因此一开始开辟的存储空间需要扩大,最少7个,多开辟几个也是没问题的。本案采用的strcmp(),字符串比较函数。这样就能确定输入的字符串是不是预设的指令了。这就完成了指令部分
  • 4:字符串怎么变成整数,如“2024”字符串变成整数2024,本案采用的还是库函数atoi()来实现,当然这个也是要加入结束码!。如此就是实现了密码修改的最初要求,数据的正确的获得。获得这些值后,应该是可以通过修改下程序功能实现串口通信修改密码的。

    看代码:

# include<reg52.h>
# include<string.h>
# include<stdlib.h>bit cmdArrived = 0;
unsigned char cmdindex = 0;    //命令索引,即与上位机约定好的数组编号
unsigned char cntTxd = 0;     //串口发送计数(数组字节个数)
unsigned char *ptrTxd = 0;    //串口发送指针unsigned char array1[] = "1-Hello!\r\n";
unsigned char array2[] ={'2','-','H','e','l','l','o','!','\r','\n'};
unsigned char array3[] = {51,45,72,101,108,108,111,33,13,10};
unsigned char array4[] = "4-Hello!\r\n";
unsigned char ErroyRpt[] = "Error!Please re-enter\r\n";
pdata unsigned char CorrectRpt[] = "Correct!Please enter a 4-digit password";
unsigned char WordSet[] = "setkey";
unsigned char* wd_set = WordSet;pdata unsigned char Word[7] = {'2','0','2','4','\0','s','b'};
pdata unsigned long RxdByte = 0x12345678;
pdata unsigned int* y = &RxdByte;
unsigned char *ptrRxd = Word;   //串口接收指针
bit Rxd_Fin_Mark = 0; //接收接收位标志
unsigned int vaule = 0;//atoi(Word);
unsigned char* p = &vaule;
unsigned char  x = 0;
code unsigned char* k = &vaule;//备份指针地址,用于指针初始化
pdata unsigned long l = 0x010203;
unsigned char* s = &l;void ConfigUART(unsigned int baud); void main(){EA = 1;ConfigUART(9600);//strcpy(array2,"2-Hello!");while(1){if(strcmp(WordSet,Word) == 0 && Rxd_Fin_Mark == 1){Rxd_Fin_Mark = 0;ptrRxd = &Word[0];cmdArrived = 1;cmdindex = 8;}if(cmdArrived){cmdArrived = 0;switch(cmdindex){case 1:ptrTxd = array1;   //数组1的首地址赋值给发送指针cntTxd = sizeof(array1); //数组1的长度赋值给发送计数器TI = 1;             //手动方式启动发送中断,处理数据发生break;case 2:ptrTxd = array2;cntTxd = sizeof(array2);TI = 1;break;case 3:ptrTxd = array3;cntTxd = sizeof(array3);TI = 1;break;case 4:ptrTxd = array4;cntTxd = sizeof(array4) -1 ;TI = 1;break;case 5:// ptrTxd = ErroyRpt;//cntTxd = sizeof(ErroyRpt) ;SBUF = *y;// TI = 1;break;case 6:ptrTxd = Word;cntTxd = sizeof(Word);TI = 1;break;case 7:vaule = atoi(Word);	cntTxd = sizeof(vaule);					 ptrTxd = k;//指针初始化              					 					    TI = 1;break;case 8:ptrTxd =CorrectRpt;cntTxd = sizeof(CorrectRpt) ;TI = 1;					  					 					    					 break;case 9:Rxd_Fin_Mark = 1;default:break;}}}} /*串口配置函数  buad为通信波特率  */void ConfigUART(unsigned int baud){SCON = 0x50;TMOD &= 0x0F;//定时器控制寄存器,T1区域清零,T0区域不动TMOD |=0x20;//工作在模式2TH1 = 256 - (11059200/12/32)/baud;TL1 = TH1;ET1 = 0;      //禁止使能T1中断ES = 1;      //使能串口中断TR1 = 1;}/*UART中断服务函数   */void interruptUART() interrupt 4{static unsigned char i = 0; //定义数组指针在数组的索引if(RI){	   		 				 if(SBUF>= 0 &&  SBUF<=9) //输入0-9输出相应的语句{cmdindex = SBUF;    //把数据赋值给命令索引cmdArrived = 1;			//使能命令语句后后续的数据传输就停止使能}if(SBUF == 32)  //如果输入的是空格键则指针回到数组的首地址{i = 0;          //初始化ptrRxd = Word;}else             //不是上述的输入都存入word数组,数组最大储存6个字节的数据{if(cmdArrived == 0){//if((SBUF >='0' && SBUF <= '9' ) | SBUF == '!')if(SBUF == '!'){*ptrRxd ='\0'; //把字符串结束位符号发给相应的数组地址ptrRxd = Word;//初始化i = 0;					Rxd_Fin_Mark = 1;																						}else{* ptrRxd = SBUF; //把数据存入指针指向的地址区域ptrRxd++; //指针数加1i++;     //指针索引加1								 }if(i >= 7)    //这里不能用ptrRxd >=6判断因为这是数组地址它的值不是从0开始的{              //这里i的值等于Word数组的长度,数组内存扩充了注意更改。i = 0;ptrRxd = Word;//初始化}}							}RI = 0;}if(TI){if(cntTxd > 0)    //发送的数据判断{SBUF = *ptrTxd; //把相应地址的数据发送cntTxd--;  //每发送一个数据计数减1ptrTxd++;//每发送1个数据相应的地址移动1位		 }TI = 0;	 		 }}

写的有点乱,不过这本身就是个测试程序,有些变量可能无用了,是之前笔者测试别的用的。如果有兴趣可以拷贝过去自己测试一下,这个程序使用的时候有几个注意点。

1:程序烧录完成后,必须要关机再开机,测试程序才正常,因为在烧录过程中,就会向串口写入一堆其他的数据,因此要想正确使用,必须关机再开机就正常了。

2:

1:输入16进制的1-9 根据你想要的结果,用16进制显示或者字符显示。

2:要想输入字符串就用字符格式发送。

3:字符串结尾要加上!。

4:如果在使用过程中找不到指针位置,可以输入字符‘!’或者字符‘空格’来使指针初始化位置,

主意:输入空格的结果只是让光标移了一位但是确实是输入了空格字符,然后点发送字符/数据按钮指针就初始化,执向数组的首元素了。

5:这个函数的指令长度不一定要7位的,它是不固定的。你预设设置为set!这也是可以实现的,因为库函数在使用的时候遇到‘\0’就结束后,后面是什么字符它不管的。所以这就很方便扩展移植。

然后向分享1个关于kei1,memery窗口的相关情况。如果有小伙伴知道这个原因,能否留言告诉我一下。keil5memery内容查找_哔哩哔哩_bilibili

测试程序操作示意:

测试程序操作_哔哩哔哩_bilibili

对于这次程序笔者总结了一点心德:

1:在使用指针的时候要注意是否要初始化,要时刻注意指针的边界问题,以及类型问题。

2:对于串口通信发送端:在发送数据前要指针初始化。

3:对于串口通信接收端:在接收完字符串数据指针要初始化

4:字符串结束码是个好标志。

 
 

相关文章:

初学51单片机之指针基础与串口通信应用

开始之前推荐一个电路学习软件&#xff0c;这个软件笔者也刚接触。名字是Circuit有在线版本和不在线版本&#xff0c;这是笔者在B站看视频翻到的。 Paul Falstadhttps://www.falstad.com/这是地址。 离线版本在网站内点这个进去 根据你的系统下载你需要的版本红线的是windows…...

【启明智显分享】甲醛检测仪HMI方案:ESP32-S3方案4.3寸触摸串口屏,RS485、WIFI/蓝牙可选

今年&#xff0c;“串串房”一词频繁引发广大网友关注。“串串房”&#xff0c;也被称为“陷阱房”“贩子房”——炒房客以低价收购旧房子或者毛坯房&#xff0c;用极度节省成本的方式对房子进行装修&#xff0c;之后作为精修房高价租售&#xff0c;因甲醛等有害物质含量极高&a…...

Linux 驱动学习笔记

1、驱动程序分为几类&#xff1f; • 内核驱动程序&#xff08;Kernel Drivers&#xff09;&#xff1a;这些是运行在操作系统内核空间的驱动程序&#xff0c;用于直接访问和控制硬件设备。它们提供了与硬件交互的底层功能&#xff0c;如处理中断、访问寄存器、数据传输等。 •…...

ip地址设置了重启又改变了怎么回事

在数字世界的浩瀚星海中&#xff0c;IP地址就如同每个设备的“身份证”&#xff0c;确保它们在网络中准确无误地定位与通信。然而&#xff0c;当我们精心为设备配置好IP地址后&#xff0c;却时常遭遇一个令人费解的现象&#xff1a;一旦设备重启&#xff0c;原本设定的IP地址竟…...

layui table 浮动操作内容收缩,展开

layui table 隐藏浮动操作内容 fixed: right, style:, title: 操作,align:left, minWidth: 450, toolbar:#id分析&#xff1a; 浮动一块新增一个class layui-table-fixed-r 可以隐藏整块内容进行&#xff0c;新增一个按钮点击时间&#xff0c;然后进行收缩和展开 $(‘.layui-…...

Ubuntu24.04 NFS 服务配置

1、NFS 介绍 NFS 是 Network FileSystem 的缩写&#xff0c;顾名思义就是网络文件存储系统&#xff0c;它允许网络中的计算机之间通过 TCP/IP 网络共享资源。通过 NFS&#xff0c;我们本地 NFS 的客户端应用可以透明地读写位于服务端 NFS 服务器上的文件&#xff0c;就像访问本…...

vue3使用html2canvas

安装 yarn add html2canvas 代码 <template><div class"container" ref"container"><div class"left"><img :src"logo" alt"" class"logo"><h2>Contractors pass/承包商通行证&l…...

OpenCV分水岭算法watershed函数的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 描述 我们将学会使用基于标记的分水岭算法来进行图像分割。我们将看到&#xff1a;watershed()函数的用法。 任何灰度图像都可以被视为一个地形表…...

laravel为Model设置全局作用域

如果一个项目中存在这么一个sql条件在任何情况下或大多数情况都会被使用&#xff0c;同时很容易被开发者遗忘&#xff0c;那么就非常适用于今天要提到的这个功能&#xff0c;Eloquent\Model的全局作用域。 首先看一个示例&#xff0c;有个数据表&#xff0c;结构如下&#xff1…...

Leetcode之string

目录 前言1. 字符串相加2. 仅仅反转字母3. 字符串中的第一个唯一字符4. 字符串最后一个单词的长度5. 验证回文串6. 反转字符串Ⅱ7. 反转字符串的单词Ⅲ8. 字符串相乘9. 打印日期 前言 本篇整理了一些关于string类题目的练习, 希望能够学以巩固. 博客主页: 酷酷学!!! 点击关注…...

OS:处理机进程调度

1.BackGround&#xff1a;为什么要进行进程调度&#xff1f; 在多进程环境下&#xff0c;内存中存在着多个进程&#xff0c;其数目往往多于处理机核心数目。这就要求系统可以按照某种算法&#xff0c;动态的将处理机CPU资源分配给处于就绪状态的进程。调度算法的实质其实是一种…...

【车辆轨迹处理】python实现轨迹点的聚类(一)——DBSCAN算法

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、单辆车轨迹的聚类与分析1.引入库2.聚类3.聚类评价 二、整个数据集多辆车聚类1.聚类2.整体评价 前言 空间聚类是基于一定的相似性度量对空间大数据集进行分组…...

Apache Kylin

Apache Kylin 是一个开源的分布式分析引擎&#xff0c;提供 SQL 查询接口及多维分析&#xff08;OLAP&#xff09;能力以支持超大规模数据集。它能在亚秒级的时间内提供 PB 级数据的查询能力&#xff0c;非常适合大数据分析和报表系统。 ### 入门指南 #### 1. 环境准备 首先…...

为何Vue3比Vue2快

Proxy响应式 PatchFlag 编译模板时&#xff0c;动态节点做标记标记&#xff0c;分为不同的类型&#xff0c;如TEXT PROPSdiff算法时&#xff0c;可以区分静态节点&#xff0c;以及不同类型的动态节点 <div>Hello World</div> <span>{{ msg }}</span>…...

人工智能与社交变革:探索Facebook如何领导智能化社交平台

在过去十年中&#xff0c;人工智能&#xff08;AI&#xff09;技术迅猛发展&#xff0c;彻底改变了我们与数字世界互动的方式。Facebook作为全球最大的社交媒体平台之一&#xff0c;充分利用AI技术&#xff0c;不断推动社交平台的智能化&#xff0c;提升用户体验。本文将深入探…...

八股文之java基础

jdk9中对字符串进行了一个什么优化&#xff1f; jdk9之前 字符串的拼接通常都是使用进行拼接 但是的实现我们是基于stringbuilder进行的 这个过程通常比较低效 包含了创建stringbuilder对象 通过append方法去将stringbuilder对象进行拼接 最后使用tostring方法去转换成最终的…...

深度挖掘行情接口:股票市场中的关键金融数据API接口解析

在股票市场里&#xff0c;存在若干常见的股票行情数据接口&#xff0c;每一种接口皆具备独特的功能与用途。以下为一些常见的金融数据 API 接口&#xff0c;其涵盖了广泛的金融数据内容&#xff0c;其中就包含股票行情数据&#xff1a; 实时行情接口 实时行情接口&#xff1a…...

逆向破解 对汇编的 简单思考

逆向破解汇编非常之简单 只是一些反逆向技术非常让人难受 但网络里都有方法破解 申请变量 &#xff1a; int a 0; 00007FF645D617FB mov dword ptr [a],0 char b b; 00007FF645D61802 mov byte ptr [b],62h double c 0.345; 00007FF645D61…...

搜维尔科技:人机交互学术应用概览

人机交互学术应用概览 搜维尔科技&#xff1a;人机交互学术应用概览...

植物遗传转化相关介绍【卡梅德生物】

植物的遗传转化是指以植物器官、组织、细胞或原生质体作为受体&#xff0c;应用重组DNA技术&#xff0c;将外源基因导入植物基因组&#xff0c;以获得转基因植物的技术。目前应用最普遍的植物基因的遗传转化方法主要有农杆菌介导法和DNA直接转入法。 一&#xff0e;植物遗传转化…...

0711springNews新闻系统管理 实现多级评论

0611springmvc新闻系统管理-CSDN博客 0711springNews新闻系统管理项目包 实现多级评论-CSDN博客 数据库字段 需要添加父节点id&#xff0c;通过该字段实现父评论和子评论的关联关系。 对象属性 实现链表&#xff0c;通过一个父评论可以找到它对应的所有子孙评论。 业务层 实现…...

如何在Ubuntu上安装并启动SSH服务(Windows连接)

在日常的开发和管理工作中&#xff0c;通过SSH&#xff08;Secure Shell&#xff09;连接到远程服务器是一个非常常见的需求。如果你在尝试通过SSH连接到你的Ubuntu系统时遇到了问题&#xff0c;可能是因为SSH服务未安装或未正确配置。本文将介绍如何在Ubuntu上安装并启动SSH服…...

docker build时的网络问题

docker build时无法yum安装包&#xff0c;因为无法访问外网&#xff0c;无法ping通外网。 解决办法&#xff1a; systemctl stop NetworkManager.service firewall-cmd --permanent --zonetrusted --change-interfacedocker0 systemctl start NetworkManager.service systemct…...

Vue的安全性:防范XSS攻击与安全最佳实践

引言 随着Web应用的普及,前端安全问题日益受到重视。Vue作为当下流行的前端框架,其安全性也成为开发者关注的焦点。跨站脚本攻击(XSS)是常见的Web安全漏洞之一,本文将讨论如何在使用Vue时防范XSS攻击,并分享其他Vue中的安全最佳实践。 什么是XSS攻击? XSS攻击是一种将…...

ARM架构(一)—— ARMV8V9基础概念

目录 1.ARMCore的时间线2.ARM术语小结2.1 A64和arrch642.2ARM架构现在的5个系列2.3 微架构2.4 PE2.5 Banked2.6 ARM文档术语2.7 IMPLEMENTATION DEFINFD 和 DEPRECATED2.8 EL1t和EL1h 3 ARMv7的软件架构4 安全状态切换模型4.1 Secure state和Non-secure state介绍 5 Interproce…...

如何使用Python进行数据分析

Python是一种广泛应用于数据科学和机器学习领域的编程语言。本文将介绍如何使用Python进行数据分析&#xff0c;包括Python在数据分析中的应用场景、常用库和工具&#xff0c;以及实际案例分析。 一、Python在数据分析中的应用场景 数据清洗&#xff1a;处理缺失值、异常值&a…...

Python学习笔记40:游戏篇之外星人入侵(一)

前言 入门知识已经学完&#xff0c;常用标准库也了解了,pygame入门知识也学了&#xff0c;那么开始尝试小游戏的开发。 当然这个小游戏属于比较简单的小游戏&#xff0c;复杂的游戏需要长时间的编写累计开发经验&#xff0c;同时也需要一定的时间才能编写出来。现在的话还是嫩…...

R的数据集读取和利用,如何高效地直接复制黏贴数据到R

​​​​​​R语言自带了许多内部数据集,这些数据集不仅为初学者提供了丰富的练习资源,还为研究人员和数据分析师提供了方便的数据测试和模型验证工具。在这篇文章中,我们将详细探讨如何读取和使用数据集。 一、认识数据集 1、数据和数据集 数据(Data)是指以某种形式表示…...

@JsonProperty 踩坑

JsonProperty 在fastjson 和 hutooljson 中是不会生效的。 在 fastjson 中&#xff0c;对应的注解是 JSONField。如果你正在使用 fastjson 进行 JSON 的序列化和反序列化&#xff0c;并且想要改变字段的 JSON 属性名&#xff0c;你应该使用 JSONField 注解&#xff0c;而不是 …...

业务架构、数据架构、应用架构和技术架构分析

一文看懂&#xff1a;什么是业务架构、数据架构、应用架构和技术架构 TOGAF&#xff08;开放集团架构框架&#xff09;是企业广泛应用的架构设计和管理利器。其核心在于四大架构领域&#xff1a;业务、数据、应用和技术&#xff0c;助力组织高效运作。TOGAF&#xff0c;让架构设…...

android studio中svn的使用

第一步&#xff0c;建立一个项目。 第二步&#xff0c;share project。 第三步&#xff0c;选择存放的位置&#xff0c;然后添加提交信息&#xff0c;最后点击share。这样就可以在svn上面看到一个空的项目名称。 第四步&#xff0c;看到文件变成了绿色&#xff0c;点击commit图…...

敏捷CSM认证:精通敏捷Scum估算方法,高效完成项目!

咱们做项目的时候可能都遇到过这种情况&#xff1a;项目一开始信心满满&#xff0c;觉得 deadline 稳了。结果呢&#xff1f;各种意外状况频出&#xff0c;时间好像怎么都不够用了&#xff0c;最后项目只能无奈延期&#xff0c;整个团队都像霜打的茄子。 说到底&#xff0c;还…...

三、建造者模式

文章目录 1 基本介绍2 案例2.1 Car 类2.2 CarBuilder 抽象类2.3 EconomyCarBuilder 类2.4 LuxuryCarBuilder 类2.5 CarDirector 类2.6 测试程序2.7 测试结果2.8 总结 3 各角色之间的关系3.1 角色3.1.1 Product ( 产品 )3.1.2 Builder ( 抽象建造者 )3.1.3 ConcreteBuilder ( 具…...

MySQL-----索引

一、什么是索引 存储引擎用于快速找到记录的一种数据结构。 索引类似于目录。就比如我们要找书里的一段话&#xff0c;我们先按目录找&#xff0c;然后再具体定位&#xff0c;这样速度会很快。 二、索引的作用 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的…...

Webpack 5 Tree Shaking与Module Federation

Webpack是一个流行的JavaScript模块打包器&#xff0c;它在前端工程化中扮演着核心角色。Webpack 5引入了许多新特性&#xff0c;其中两个最值得关注的是Tree Shaking和Module Federation。这两个特性分别解决了代码体积优化和微前端架构的问题。接下来&#xff0c;我们将深入探…...

免费分享一套微信小程序图书馆座位预约管理系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】,帅呆了~~

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序图书馆座位预约管理系统(SpringBoot后端Vue管理端)&#xff0c;分享下哈。 项目介绍 随着移动互联网技术的飞速发展和智能设备的普及&#xff0c;图书馆服务模式正在经历深刻的变革。本论文旨在…...

k8s入门:从安装到实际应用

Kubernetes (K8s) 入门指南&#xff1a;从安装到实际应用 Kubernetes 是一个开源的容器编排平台&#xff0c;用于自动化容器化应用程序的部署、扩展和管理。它能帮助你管理多个容器化应用程序&#xff0c;并确保它们在不同环境下的一致性和可用性。本文将介绍如何在本地环境安…...

基于Qt的上位机通用框架

0.前言 最近一年多的时间一直在开发设备控制相关的软件&#xff0c;加上之前在聚光的两年时间&#xff0c;前前后后开发这种设备控制类型的上位机软件也有三年的时间了。总结出了一套基于Qt的上位机编程框架&#xff0c;核心思想类似于C#的依赖注入&#xff0c;对象的初始化都…...

Vulnhub靶场DC-7练习

目录 0x00 准备0x01 主机信息收集0x02 站点信息收集1. 获取用户名/密码2. ssh连接目标主机3. drush命令修改Drupal密码 0x03 漏洞查找与利用1. Drupal写入php木马2. 连接shell3. 反弹shell并提权 0x04 总结 0x00 准备 下载链接&#xff1a;https://download.vulnhub.com/dc/DC-…...

吴恩达深度学习笔记1 Neural Networks and Deep Learning

参考视频&#xff1a;(超爽中英!) 2024公认最好的【吴恩达深度学习】教程&#xff01;附课件代码 Professionalization of Deep Learning_哔哩哔哩_bilibili Neural Networks and Deep Learning 1. 深度学习引言(Introduction to Deep Learning) 2. 神 经 网 络 的 编 程 基 础…...

(十)Spring教程——Spring配置概述

目录 前言 1.Spring容器高层视图 2.基于XML的配置 前言 在使用Spring所提供的各项丰富而神奇的功能之前&#xff0c;必须在Spring IoC容器中装配好Bean&#xff0c;并建立好Bean和Bean之间的关联关系。Spring的配置文件已经很精简了&#xff0c;但是广大的开发者希望它做得更…...

飞书群聊机器人自定义机器人接入,并实现艾特@群成员功能

飞书群聊机器人还是比钉钉的要麻烦一点&#xff0c;钉钉的直接通过手机号就可以艾特群里面的人&#xff0c;但是飞书的要想艾特群里面的人&#xff0c;需要使用用户的 Open ID 或 User ID。这两个ID怎么获取呢&#xff1f;还需要在飞书的开放平台上创建一个应用&#xff0c;然后…...

CrowdStrike更新致850万Windows设备宕机,微软紧急救火!

7月18日&#xff0c;网络安全公司CrowdStrike发布了一次软件更新&#xff0c;导致全球大范围Windows系统宕机。 预估CrowdStrike的更新影响了将近850万台Windows设备&#xff0c;多行业服务因此停滞&#xff0c;全球打工人原地放假&#xff0c;坐等吃瓜&#xff0c;网络上爆梗…...

银行黄金交易流程

银行黄金交易流程 银行黄金交易流程通常包括以下几个步骤&#xff1a; 咨询和开户&#xff1a; 首先&#xff0c;客户需要到银行的贵金属交易柜台或在线平台咨询黄金交易的相关规定和手续&#xff0c;然后进行开户&#xff0c;在银行开立有关黄金交易的账户。这可能需要提供个…...

MATLAB实验五:MATLAB数据分析

1. 某线路上不同时间对应的电压如下表所示&#xff1a; 1&#xff09;用 3 次多项式拟合(polyfit)该实验曲线&#xff0c;要求绘制 2 原始采样 点&#xff0c;并在 1~8 范围内&#xff0c;使用时间间隔为 0.2 的数据绘制拟合曲线。 建立一个脚本文件&#xff1a;text5_1.m 如下…...

Cannot perform upm operation: connect ETIMEDOUT 34.36.199.114:443 [NotFound]

版本&#xff1a;Unity 2018 Windows 问题&#xff1a;打开 Package Manager&#xff0c;加载报错 尝试解决&#xff1a; 删除项目文件里的Packages下的mainfest.json文件&#xff0c;然后重新打开项目&#xff08;X&#xff09;重新登录 Unity 账号&#xff08;X&#xff09…...

Docusaurus VS VuePress:哪一个更适合你的技术文档?

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…...

昇思25天学习打卡营第25天|MindNLP ChatGLM-6B StreamChat

配置环节 %%capture captured_output !pip uninstall mindspore -y !pip install -i https://pypi.mirrors.ustc.edu.cn/simple mindspore2.2.14 !pip install mindnlp !pip install mdtex2html配置国内镜像 !export HF_ENDPOINThttps://hf-mirror.com下载与加载模型 from m…...

海康威视综合安防管理平台 detection 前台RCE漏洞复现

0x01 产品简介 海康威视综合安防管理平台是一套“集成化”、“智能化”的平台,通过接入视频监控、一卡通、停车场、报警检测等系统的设备。海康威视集成化综合管理软件平台,可以对接入的视频监控点集中管理,实现统一部署、统一配置、统一管理和统一调度。 0x02 漏洞概述 海康…...

【BUG】已解决:ModuleNotFoundError: No module named ‘PIL‘

已解决&#xff1a;ModuleNotFoundError: No module named ‘PIL‘ 目录 已解决&#xff1a;ModuleNotFoundError: No module named ‘PIL‘ 【常见模块错误】 错误原因&#xff1a; 解决办法&#xff1a; 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我…...