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

初学51单片机之UART串口通信

CSDN其他博主的博文(自用)嵌入式学习笔记9-51单片机UART串口通信_51uart串口通讯-CSDN博客

CSDN其他博主的博文写的蛮好,如果你想了解51单片机UART串口可以点进去看看:

UART全称Universal Asynchronous Receiver/Transmitter即通用异步收发器

异步通信是指通信中两个字符(8位)之间的时间间隔是不固定的,而在一个字符内各位的时间间隔是固定的

UART串行通信是单片机最常用的的一种通信技术,通常用于单片机和计算机之间以及单片机和单片机之间的通信。

       通信按照基本类型可以分为并行通信串行通信,并行通信时数据的各个位同时传送,可以实现以字节为单位通信,但是通信线多占用资源多,成本高。 比如前边用到的P0 = 0xFF;一次给P0的8个IO口分别赋值,同时进行信号输出,类似于有8个车道同时可以过去8辆车。这种形式就是并行的,习惯上称P0 、P1、P2和P3为51单片机的4组并行总线。

    而串行通信就如同一条车道,一次只能过一辆车。如果一个0xFF这样一个字节的数据要传输过去的话,假设一辆车拉着1位的数据,那么它就需要8辆车以相同的时间间隔依次穿过同一车道。

同步通信与异步通信区别:

1、同步通信要求接收端时钟频率和发送端时钟频率一致,发送端发送连续的比特流;异步通信时不要求接收端时钟和发送端时钟同步,发送端发送完一个字节后,可经过任意长的时间间隔再发送下一个字节。

2、同步通信效率高,异步通信效率较低。

3、同步通信较复杂,双方时钟的允许误差较小;异步通信简单,双方时钟可允许一定误差。

4、同步通信只适用于点对多点,异步通信可用于点对点。

STC89C52单片机有两个引脚是专门用来做UART串行通信,1个是P3^0,一个是P3^1,它们还分别有另外的名字叫做RXD(Receive Data)和TXD(Transmit  Data),由他们组成的通信接口就叫作串行接口。如图:

单片机和单片机之间通信:

     图示中GND表示单片机系统电源的参考地。TXD叫串行发送引脚,RXD是串行接收引脚。

      两个单片机之间要通信,首先电源基准得一样,所以要把两个单片机的GND相互连接起来。然后是单片机1的TXD引脚接到单片机2的RXD引脚上,功能是单片机1向单片机2发送信息,(这个过程分为两部分,单片机1的发送信号过程 与单片机2的接收信号过程)同理单片机2的TXD引脚接到单片机1的RXD引脚上。

这个示意图就体现了两个单片机相互收发信息的过程。

当单片机1想给单片机2发送数据时,比如发送1个0xE4这个数据,用二进制形式表示就是

0b1110 0100,在传输开始前单片机1就面临几个问题:

假设我先传输了最低位的0,那么我需要把单片机1的TXD端口置为低电平。

单片机一般是TTL或者CMOS电平,对于TTL/CMOS电平标准 :

大于2点几伏的为高电平,低于0点几伏的为低电平,之间的既不是高电平也不是低电平,之间的逻辑会比较混乱它可能会被器件判断为高电平,也可能被判断为低电平。因此电路工作尽量不要处在这个区域。处在这个区域会让你的产品时好时坏。因此开发产品的时候要查看器件的数据手册,以及电路工作过程时的电压,使之处在一个合适的电压。

1、TTL电平:

输出高电平>2.4V,输出低电平<0.4V。在室温下,一般输出高电平是3.5V,输出低电平是0.2V。最小输入高电平和低电平:输入高电平>=2.0V,输入低电平<=0.8V,噪声容限是0.4V。

2、CMOS电平:

1逻辑电平电压接近于电源电压,0逻辑电平接近于0V。而且具有很宽的噪声容限。

那么问题来了:

          在不传送或者接收信息的时候,单片机1的传输端口(TXD)会处在高电平或者低电平亦或者端口的电压在此之间。单片机2的接收端口电压(RXD)又是跟随单片机1传输端口的电压。如何设定单片机1要开始传输信息了的信号,即单片机2怎么知道单片机1向他传输信息了?因此需要规范通信标准。

第1步:规定没发生通信的时候,单片机TXD和RXD端口电压保持高电平。

第2步:规范数据传输方式如下图

可以看到它定义了1个起始位,1个停止位,加上本身要传输的数据1个字节8位即共十位数据

上述传输一个0xE4的数据事实上是传输了0 1110 0100 1,单片机1向单片机2传递1字节的数据其实是传输了10位数据,把这个10位的整体叫作一个完整的串行数据帧。

     有了起始位和停止位,那单片机2 RXD 端口在检测到低电平的时候就开始准备接收数据了。同理单片机1要发送1个字节的数据,它就需要先发送1位起始位0(告诉单片机2让它准备接收),数据发送结束后再发送1位停止位1(告诉单片机2一个字节的数据传输完毕)。而且传输数据的时候是先低位后高位的顺序

第3步:需要统一设置一下传输1位数据所需要的时间,这个时间快慢用波特率(  baud)表示。1波特率表示1秒传输1位数据,9600波特率表示在1秒内传输9600位数据。

那么就得出传输1位的持续时间=1/baud.,那么就可求得传递1完整数据帧的时间是10/baud,数据帧之间的时间间隔是任意的。

对于单片机1和单片机2在传输信号的时候,它们的波特率必须设置成一致才能正确的实现通信。

RS232通信接口

在早先年台式计算机上,一般会有一个9针的串行接口,这个串行接口叫RS-232接口,它和UART通信有关联,但现在的笔记本都不带这种9针串口了,所以和单片机之间通信越来越趋向使用USB虚拟串口了。

RS232使用的电平逻辑是负逻辑它不同于TTL/CMOS逻辑电平

因此计算机的9针RS232串口是不能和单片机直接连接的,需要1个电平转换芯片MAX232完成如下图:

RS232针脚说明:

1、载波检测DCD(Data Carrier detection) 2、接收数据RXD 3、发送数据TXD

4、数据终端准备好DTR(Data Terminal Ready) 5、信号地线 6、数据准备好DSR(Data Set ready)

7、请求发送RTS(Request to Send) 8、清除发送CTS(Clear to Send) 9、振铃提示RI(Ringing)

如上图在串口接线中RS232只要2脚、3脚以及5脚与相应的MAX232的引脚相连就能完成通信。

USB转串口通信

   随着技术的发展,工业上还有RS-232串口通信的大量使用,但是商业技术的应用上,已经慢慢的使用USB转UART技术取代了RS232串口。

那么如何实现单片机和计算机之间的通信如图

这是本案开发板使用 USB转串口芯片CH340T实现这个功能。

单片机端口TXD用了跳线帽和USB-TX连接在一起并且是与CH340第4脚RXD端口相连,同时

单片机端口RXD用了跳线帽和USB-RX连接在一起并且是与CH340的第3脚TXD相连。

可以看到USB-RX与CH340的第三引脚TXD之间串着一个4148二极管,因为STC89C52单片机下载程序时需要冷启动。就是先点下载后上电。

 所谓冷启动是指单片机从断电到通电的这么一个启动过程;而热启动是单片机始终通电。冷启动与热启动的区别在于:冷启动时单片机内部RAM中的数值是一些随机量,而热启动时单片机内部RAM的值不会被改变,与启动前相同。

     上电瞬间单片机会先检测需不需要下载程序,虽然单片机的VCC是由开关来控制的,但是由于CH340T的3脚是输出引脚,如果没有此二极管,开关后级单片机在断电的情况下,CH340T的3脚和单片机的P3.0(RXD)引脚连在一起,有电流会通过这个引脚流入后级电路并且给后级的电容充电,造成后级有一定幅度的电压,这个电压值虽然只有2~3V左右,但是可能会影响正常的冷启动。加上二极管后,一方面不影响通信,另一方面可以消除这种不良影响。

通过单片机手册得知P3.0是准双向IO口

红框部分是准双向IO口, 当内部输出位为低电平的时候,经过非门,三极管基极为高电平,该三极管导通(饱和导通CE两端电压几乎相同),单片机IO口输出低电平,无论外部的按键开关按下还是弹开,IO口一直保持低电平。即不受外部信号的控制。

      当内部输出为高电平的时候,经过非门,三极管基极为低电平,三极管未导通(CE两端的阻值很大,电阻R与之相比可以忽略不计)这时IO口输出高电平。当按下按键,IO口就被接地,这时IO口输出低电平。

由此可知只有当IO口输出为高电平的时候,IO口的输出才受外部电路的控制

因此我们可知P3.0在作为RXD端口时内部输出一直是高电平的。而P3.1TXD端口作为发送端它的内部输出电平可以根据需要高低变化。

而且:对TTL电平

输出端:高电平>=2.4V,低电平<=0.4V;

接收端:高电平>=2.0V,低电平<=0.8V。

对于COMS电平

输出端:高电平=Vcc,低电平=GND(Vcc为电源电压);

接收端:高电平>=0.7Vcc,低电平<=0.2Vcc。

他们接收端的逻辑相较于输出端的逻辑电平还是有有些电压差的,这可能是考虑到信号传输过程中产生的压降干扰亦或者保护电路的存在,所以加1个二极管并不会导致它所传输后的电压处在一个混乱的逻辑区。

用IO口模拟UART串口通信

上程序:

# include<reg52.h>sbit PIN_RXD = P3^0;      //接受引脚定义
sbit PIN_TXD = P3^1;     //发送引脚定义bit RxdorTxd = 0;       //指示当前状态为接受还是发送
bit RxdEnd = 0;         //接受结束标志
bit TxdEnd = 0;         //发送结束标志
unsigned char RxdBuf = 0;  //接收缓冲区
unsigned char TxdBuf = 0;  //发送缓冲器void ConfigUART(unsigned int baud);
void StartTXD(unsigned char dat);
void StartRXD();void main()
{EA = 1;           //打开总中断ConfigUART(9600); //配置波特率为9600while(1){while(PIN_RXD); //等待接收引脚出现低电平,即起始位StartRXD();     //启动接收while(!RxdEnd);  //等待接受完成StartTXD(RxdBuf +1);//接受到的整数+1后,发送回去while(!TxdEnd); //等待发送结束}
}/* 串口配置函数,baud-通信波特率  */
void ConfigUART(unsigned int baud)
{TMOD &= 0xF0;  //清零T0的控制位TMOD |= 0x02;  //配置T0为模式2TH0 = 256 - (11059200/12)/baud; //计数T0的重载值
}/* 启动串行接受   */
void StartRXD()
{TL0 = 256 - ((256-TH0) >> 1);//接受启动时T0定时为半个波特率周期ET0 = 1;       //使能T0中断TR0 = 1;       //启动T0RxdEnd = 0;    //清零接受结束标志RxdorTxd = 0; //设置当前状态为接受  1位发送}/* 启动串行发送,dat-待发送字节数据  */
void StartTXD(unsigned char dat)
{TxdBuf = dat;  //待发送数据保存到发送缓冲器TL0 = TH0;    //T0计算初值为重载值ET0 = 1;      //使能T0中断TR0 = 1;      //启动T0PIN_TXD = 0;   //发送起始位TxdEnd = 0;    //清零发送结束标志RxdorTxd = 1;  //设置当前状态为发送
}/*T0中断服务函数,处理串行发送和接收 */
void interruptTimer0() interrupt 1
{static unsigned char cnt = 0;if(RxdorTxd){cnt++;if(cnt <= 8)  //低位在先一次发送8bit数据位{PIN_TXD = TxdBuf & 0x01;TxdBuf >>= 1;}else if(cnt == 9) //发送停止位{PIN_TXD = 1;}else           //发送结束{cnt = 0;    //复位bit计数器TR0 = 0;    //关闭T0TxdEnd = 1; //置发送结束标志}}else                 //串行接收处理{if(cnt == 0)   // 处理起始位{if(!PIN_RXD)   //起始位为0时,清零接收缓冲器,准备接受数据位{RxdBuf = 0;cnt++;}else         //起始位为1(不为0)时,中止接收{TR0 = 0;  //关闭T0}}else if(cnt <= 8)  //处理8位数据位{RxdBuf >>= 1;       //低位在先,所以将之前接收的位向右移if(PIN_RXD)        //接收脚为1时,缓冲器最高位置1{                  //而为0时不处理即仍保持位移后的0RxdBuf |= 0x80; }cnt++;}else               //停止位处理{cnt = 0;        //复位bit计数器TR0 = 0;       //关闭T0if(PIN_RXD)    //停止位为1时,方认为数据有效{RxdEnd = 1;  //置接收结束标志}}}}

工作逻辑导图

该程序是用定时器0模拟UART串口通信,注意这只是个模拟程序。该程序实现了计算机与单片机之间的通信。它的通信结果是把计算机传输的数据传输给单片机,单片机把数据加1后回传给计算机。使用STC-ISP自带的串口调试助手来演示这个结果。

 首先确保端口是一样的上图都是COM3,波特率是9600 校验位是NO 数据位是8 停止位是1.(这里设置的是计算机的串口参数)

看下结果:可以正常通信定时器0模拟串口通信_哔哩哔哩_bilibili

简易说明该程序的工作逻辑:对于发送模块比如发送0xAA =1010 1010 从最低位开始发送

即TxdBuf = 1010 1010看下程序

 if(RxdorTxd){cnt++;if(cnt <= 8)  //低位在先一次发送8bit数据位{PIN_TXD = TxdBuf & 0x01;TxdBuf >>= 1;}else if(cnt == 9) //发送停止位{PIN_TXD = 1;}else           //发送结束  {cnt = 0;    //复位bit计数器TR0 = 0;    //关闭T0TxdEnd = 1; //置发送结束标志}}

第一次进入中断cnt = 1 

PIN_TXD = TxdBuf & 0x01 即1010 1010    0000 0001相与的结果就是把0xAA的最低位赋值给发送端口让其使能相应的电平。可知第一次进入中断TXD端口的电压是低电平,这个过程要持续到竟然第二次中断。

接着TxdBuff右移了一位即TxdBuf = 0101 0101

然后第二次中断

cnt = 2

PIN_TXD = 1这次发送端口的电平是高电平

TxdBuf = 0010 1010

接着是第3次第4次直到第8次

cnt = 8

PIN_TXD = 1设置的电平是高电平,是该传输数据的最高位1

这时候TxdBuf = 0x00

然后是第9次进入中断,第9次进入中断代表着数据位刚好发送完毕,要准备发送停止位了

因此PIN_TXD = 1发送端口直接赋值为高电平

最后第十次中断代表着停止位也发送结束了。因此cnt复位赋值为0,TR0赋值为0关闭定时器0,TXDEnd = 1,发送结束标志置1,到此一个完整的数据帧发送结束

然后是接收模块:前文我们发送了0xAA,对于本函数来说它的值是来自于接收的数据加1后的结果,因此接收到的数据是 :0xA9 =1010 1001

void StartRXD()
{TL0 = 256 - ((256-TH0) >> 1);//接受启动时T0定时为半个波特率周期ET0 = 1;       //使能T0中断TR0 = 1;       //启动T0RxdEnd = 0;    //清零接受结束标志RxdorTxd = 0; //设置当前状态为0接收  1为发送}
else                 //串行接收处理{if(cnt == 0)   // 处理起始位{if(!PIN_RXD)   //起始位为0时,清零接收缓冲器,准备接受数据位{RxdBuf = 0;cnt++;}else         //起始位为1(不为0)时,中止接收{TR0 = 0;  //关闭T0}}else if(cnt <= 8)  //处理8位数据位{RxdBuf >>= 1;       //低位在先,所以将之前接收的位向右移if(PIN_RXD)        //接收脚为1时,缓冲器最高位置1{                  //而为0时不处理即仍保持位移后的0RxdBuf |= 0x80; }cnt++;}else               //停止位处理{cnt = 0;        //复位bit计数器TR0 = 0;       //关闭T0if(PIN_RXD)    //停止位为1时,方认为数据有效{RxdEnd = 1;  //置接收结束标志}}}

可以看到发送和接收过程其实是差不多的,对于接收数据,我把第1次中断标为从第0次开始主要是为了和cnt配合便于理解。

如图可以看到它第0次进入中断经过的时间是预设中断时间的一半,这是因为在确认一个电平信号是0还是1的时候,如果采样最开始时间的电平有可能会产生误差或者干扰,因此一般都会把采样点设置在信号传递时间的中点位置,把该处的电平信号认为这个时间段传达的信号。所以对于接收模块只需在第一次接收起始位的时候,把它的中断时间设置为原先的一半,这样就能保证后续所有的信号采集点都处在中心点位置。

看下程序是怎么设置的:TL0 = 256 - ((256-TH0) >> 1);//接受启动时T0定时为半个波特率周期

256-TH0就是预设中断时间,前文是我们提到希望中断时间变为之前的一半,程序操作是右移了1位,我们举个例子 8的二进制 0000 1000,右移1为是0000 0100 = 4,可见右移1位的操作就是把数变成原先的一半。 同理左移1位的结果是0001 0000 =16 是把原先的数乘以2

开始程序过程分析 

当RxdorTxd为0时进入else函数,即第0次进入中断 ,接收模块在时域上的起点就是开始位的中点位置。首先确定端口电压是否真的是低电平,如果不是就关闭中断。为什么要这么干呢?

因为这是接收模块,RXD的电压不是由单片机控制的,对于本案来说它是由计算机控制CH340T的TXD端电压控制的。单片机RXD的电压是跟随CH340TXD端的,对于接收端来说接收到的信息可能受到干扰因此需要经过再次确认。

如果是,cnt++,则cnt=1。RxdBuf = 0;接收缓冲区清0

然后是第1(其实是第二次)进入中断cnt = 1进入else if()函数一步步的把数据传输到缓冲区。后续过程不在赘述,如果有兴趣的读者可以自行按照程序逻辑走一遍。看是否能真的把数据先低位后高位的方式存入缓冲变量。因为之前的博文,函数通过左移的方法都是先存入数据的高位。这种方法相当补充了另一种工作方式。

至此程序用定时器0模拟了UART串口通信。

通信的三种基本类型

单工通信:只允许一方向另外一方传送信息,另一方不能回传信息,比如电视遥控器、收音机广播等。    

半双工通信:数据可以在双方之间传播,同一时刻只能其中一方发给另外一方,比如对讲机就是典型半双工。    上文的串口模拟程序也可以理解为半双工通信

全双工通信:发送数据的同时也能够接收数据,两者同步进行,比如我们的电话通信。    

      IO口模拟串口通信基本展现的串口通信的本质,但是单片机程序却需要不停的检测扫描单片机IO口收到的数据,大量占用了单片机的运行时间。因此51单片机内部有一个UART模块,能够自动接收数据,接收完了通知一下就可以了,要正确使用它,需要正确的配置特殊功能寄存器。

SCON串行控制寄存器

      本案只介绍模式1,即设置SM1 = 1、SM0 = 0就是模式1,该模式就是前文模拟串口通信所使用的数据帧格式:1位起始位,8位数据位,1位停止位。

   模拟串口通信是采用定时器0来表现波特率,STC89C52 UART模块波特率发送器只能由定时器T1或者定时器T2产生,而不能由定时器T0产生,这和模拟通信是完全不同的概念。

在使用定时器T2需要额外的配置寄存器默认使用定时T1。

上程序代码:

# include<reg52.h>void ConfigUART(unsigned int baud);void main()
{  EA = 1;ConfigUART(9600); //配置波特率为9600while(1);}
/* 串口配置函数,baud-通信波特率 */void ConfigUART(unsigned int baud){SCON = 0x50;  //0x50= 0101 0000 配置串口为模式1TMOD &= 0x0F; //清零T1的控制位TMOD |= 0x20; //0x20 = 0010 0000 配置T1的为模式2自动重载模式TH1 = 256 - (11059200/12/32)/baud; //计算T1的重载值TL1 = TH1; //设置初值ET1 = 0;  //禁止T1中断ES = 1;  //启动串口中断TR1 = 1; //启动T1定时器}/*UART中断服务函数   */void interruptUART() interrupt 4{if(RI)      //接收到字节{RI = 0;   //软件清0接收中断标志SBUF = SBUF+1;//接收的数据+1,左边是发送SBUF,右边是接收SBUF}if(TI){TI = 0;  //软件清0发送中断标志位}}

看一下结果视频:自带模块串口通信_哔哩哔哩_bilibili

可以看到可以正常通信没有问题,然后对程序里面相关内容进行简单讲解:

定时器T1的重载值计算公式为:

TH1 = TL1 = 256 - 晶振值/12/2/16/波特率 那么它的

中断间隔时间 = 晶振值/12/2/16/波特率 =晶振值/12*(1/波特率)*(1/32)

晶振值/12含义是1秒内的机器周期数,(1/波特率)含义是传递1位数据所需要的时间

晶振值/12*(1/波特率)含义是传递1位数据需要多少机器周器

同理晶振值/12*(1/波特率)*(1/32)这个中断的时间=32分之1传递1位数据的时间

即把传递1位数据分成了32次时间间隔。

计算下结果是3即 中断间隔时间 = 3个机器周期这个时间是很短的

      因为串口模块它的信号采样方式是把一位信号采集16次,把其中的7、8、9次的信号电平取出来,如果这三次中其中两次是高电平,就认定这个数据是1.如果两次电平是0,就认定这位为0,这样一旦受到意外的干扰读错一次 数据,依然能保证最终数据的正确性。

下图来自推荐博文的图片主要是为了方便说明:波特率的计算公式

SMOD由电源寄存器PCON控制,默认下SMOD为0,代入上式中就是之前的初值求解公式

TH1 = TL1 = 256 - 晶振值/12/2/16/波特率

当把寄存器最高置1 PCON |= 0x80;即把SMOD置1,那就可以把波特率提高1倍,如上图的公式。

这时T1的初值就变成了:TH1 = TL1 = 256 - 晶振值/12/16/波特率

这里必须要注意的一点是:如果你想要使用PCON寄存器控制波率翻倍它的初值公式依然要写成

TH1 = TL1 = 256 - 晶振值/12/2/16/波特率 只不过

现在的重载值是 TH1=256 - (11059200/12/32)/baud =256-(11059200/12/16)/(2*buad)baud*2=4800*2=9600,9600就是现在程序的波特率

如果你这里的初值重载公司写成TH1 = TL1 = 256 - 晶振值/12/16/波特率,那么它现在的波特率就依然是4800不是9600。你用串口通信软件通信波特率设置成9600它传输后的结果出错

看视频:波特率相关_哔哩哔哩_bilibili

       我们可以看到9600的波特率它的时间间隔只有3个机器周期,14400的时间间隔是2个机器周期。可见如果它的波特率再高一点的话它的时间间隔可能不满1个机器周期。因此有了另外一种工作模式即PCON最高位置1后 ConfigUART(9600);它现在的波特率是19200,但是它的时间间隔是原9600波特率的时间间隔即3个机器周期。

       必须要提醒一下,定时器0模拟串口通信和串口通信模块工作上还是有不少差异的,串口通信模块完成一次输入输出,它只进入两次串口通信中断。一次是RI置1响应,一次是TI置1响应,然后软件清零。不再关注传输过程只关注是否传输完成,传输完成就发个信号。

      串口通信的发送接收电路在物理上有两个相同的SBUF寄存器,它们的地址也都是0x99,但是一个是用来发送缓冲,一个是用来接收缓冲。意思就是说,有两个房间,两个房间的门牌号是一样的,其中一个许进不出另一个许出不许进。这样的话就可以实现UART的全双工通信,相互之间不会产生干扰。但是在逻辑上,每次只操作SBUF,单片机会自动根据对它的执行的是“读”还是“写”操作来选择是接收SBUF还是发送SBUF。

串口通信程序的基本步骤:

1、配置串口为模式1。

2、配置定时器T1为模式2,即自动重装模式。  

3、根据波特率计算TH1和TL1的初值,如果有需要可以使用PCON进行波特率加倍。

4、打开定时器控制寄存器TR1,让定时器跑起来。注意:在使用串口中断的时候,定时器1中断就不能再使能了,除非你用的是定时器2的串口中断。

相关文章:

初学51单片机之UART串口通信

CSDN其他博主的博文&#xff08;自用&#xff09;嵌入式学习笔记9-51单片机UART串口通信_51uart串口通讯-CSDN博客 CSDN其他博主的博文写的蛮好&#xff0c;如果你想了解51单片机UART串口可以点进去看看&#xff1a; UART全称Universal Asynchronous Receiver/Transmitter即通…...

数据结构——查找(线性表的查找与树表的查找)

目录 1.查找 1.查找的基本概念 1.在哪里找&#xff1f; 2.什么查找&#xff1f; 3.查找成功与否&#xff1f; 4.查找的目的是什么&#xff1f; 5.查找表怎么分类&#xff1f; 6.如何评价查找算法&#xff1f; 7.查找的过程中我们要研究什么&#xff1f; 2.线性表…...

MySQL入门学习-深入索引.组合索引

在 MySQL 中&#xff0c;组合索引&#xff08;也称为复合索引&#xff09;是在多个列上创建的索引。以下是关于组合索引的详细信息&#xff1a; 一、组合索引的概念&#xff1a; - 组合索引是基于多个列创建的索引结构。它可以提高在这些列上进行查询的效率。 二、深入理解组…...

RABBITMQ的本地测试证书生成脚本

由于小程序要求必须访问wss的接口&#xff0c;因此需要将测试环境也切换到https&#xff0c;看了下官方的文档 RabbitMQ Web STOMP Plugin | RabbitMQ里面有这个信息 然后敲打GPT一阵子&#xff0c;把要求输入几个来回&#xff0c;得到这样一个脚本&#xff1a; generate_cer…...

记录些Redis题集(4)

Redis 通讯协议(RESP) Redis 通讯协议&#xff08;Redis Serialization Protocol&#xff0c;RESP&#xff09;是 Redis 服务端与客户端之间进行通信的协议。它是一种二进制安全的文本协议&#xff0c;设计简洁且易于实现。RESP 主要用于支持客户端和服务器之间的请求响应交互…...

JVM:垃圾回收器

文章目录 一、介绍二、年轻代-Serial垃圾回收器三、老年代-SerialOld垃圾回收器四、年轻代-ParNew垃圾回收器五、老年代-CMS&#xff08;Concurrent Mark Sweep&#xff09;垃圾回收器六、年轻代-Parllel Scavenge垃圾回收器七、Parallel Old垃圾回收器八、G1垃圾回收器 一、介…...

Golang | Leetcode Golang题解之第228题汇总区间

题目&#xff1a; 题解&#xff1a; func summaryRanges(nums []int) (ans []string) {for i, n : 0, len(nums); i < n; {left : ifor i; i < n && nums[i-1]1 nums[i]; i {}s : strconv.Itoa(nums[left])if left < i-1 {s "->" strconv.It…...

单目3D和bev综述

文章目录 SOTA2D 检测单目3d检测3d bev cam范式1 Transformer attention is all you need 20172 ViT vision transformer ICLR 2021google3 swin transformer 2021 ICCV bestpaper MS4 DETR 20205 DETR3D 20216 PETR 20227 bevformerLSSbevdetcaddn指标 mAP NDS标注&#xff1a…...

每日Attention学习11——Lightweight Dilated Bottleneck

模块出处 [TITS 23] [link] [code] Lightweight Real-Time Semantic Segmentation Network With Efficient Transformer and CNN 模块名称 Lightweight Dilated Bottleneck (LDB) 模块作用 改进的编码器块 模块结构 模块代码 import torch import torch.nn as nn import to…...

EM32DX-E4 IO 扩展模块

输入&#xff1a;0x6000-01 // 输入 0-15 6020H——00H IN0 计数【0~7】 ——01H IN0_SetCountMode S32 r/w 初始值默认为 0 设置 IN0 的计数方式&#xff1a;0 电平下 降沿&#xff0c;1 电平上升沿&#xff0c; 2 电平任意沿 ——02H IN0_Set…...

【数据结构与算法】选择排序篇----详解直接插入排序和哈希排序【图文讲解】

欢迎来到CILMY23的博客 &#x1f3c6;本篇主题为&#xff1a;【数据结构与算法】选择排序篇----详解直接插入排序和哈希排序 &#x1f3c6;个人主页&#xff1a;CILMY23-CSDN博客 &#x1f3c6;系列专栏&#xff1a;Python | C | C语言 | 数据结构与算法 | 贪心算法 | Linux…...

SpringBoot实战:多表联查

1. 保存和更新公寓信息 请求数据的结构 Schema(description "公寓信息") Data public class ApartmentSubmitVo extends ApartmentInfo {Schema(description"公寓配套id")private List<Long> facilityInfoIds;Schema(description"公寓标签i…...

解决mysql,Navicat for MySQL,IntelliJ IDEA之间中文乱码

使用软件版本 jdk-8u171-windows-x64 ideaIU-2021.1.3 mysql-essential-5.0.87-win32 navicat8_mysql_cs 这个问题我调试了好久&#xff0c;网上的方法基本上都试过了&#xff0c;终于是解决了。 三个地方结果都不一样。 方法一 首先大家可以尝试下面这种方法&#xff1a…...

虚拟环境操作

1、对虚拟环境的操作 查看虚拟环境列表 conda env list 创建虚拟环境 conda create -n 虚拟环境名称 python3.x 激活虚拟环境 conda activate 虚拟环境名称 退出虚拟环境 conda deactivate 删除虚拟环境 conda remove -n 虚拟环境名称 all 2、对虚拟环境下的包的操作…...

企业网三层架构

企业网三层架构&#xff1a;是一种层次化模型设计&#xff0c;旨在将复杂的网络设计分成三个层次&#xff0c;每个层次都着重于某些特定的功能&#xff0c;以提高效率和稳定性。 企业网三层架构层次&#xff1a; 接入层&#xff1a;使终端设备接入到网络中来&#xff0c;提供…...

node.js的安装及学习(node/nvm/npm的区别)

一、什么是node、nvm和npm 1.Node.js node.js 一种Javascript编程语言的运行环境&#xff0c;能够使得javascript能够脱离浏览器运行。以前js只能在浏览器&#xff08;也就是客户端&#xff09;上运行&#xff0c;node.js将浏览器中的javascript运行环境进行封装的&#xff0c;…...

性能优化篇:用WebSocket替代传统的http轮循

当我还是初级菜鸟时,我只会写定时器定时调用接口,发起http请求,定时轮训请求接口,返回最新数据,定时器开启的多了还会引起页面卡顿的性能问题,虽然及时销了但还是会影响流畅问题。然后技术leader一声令下说改成websoket的请求方式,为什么这么做呢?下面来谈谈WebSocket相…...

virtualbox的ubuntu默认ipv4地址为10.0.2.15的修改以及xshell和xftp的连接

virtualbox安装Ubuntu后&#xff0c;默认的地址为10.0.2.15 我们查看virtualbox的设置发现是NAT 学过计算机网络的应该了解NAT技术&#xff0c;为了安全以及缓解ip使用&#xff0c;我们留了部分私有ip地址。 私有IP地址网段如下&#xff1a; A类&#xff1a;1个A类网段&…...

Codeforces Round 957 (Div. 3)(A~D题)

A. Only Pluses 思路: 优先增加最小的数&#xff0c;它们的乘积会是最优,假如只有两个数a和b&#xff0c;b>a&#xff0c;那么a 1&#xff0c;就增加一份b。如果b 1&#xff0c;只能增加1份a。因为 b > a&#xff0c;所以增加小的数是最优的。 代码: #include<bi…...

fedora 40 安装拼音输入法

仅做参考&#xff0c;一般主流linux版本在安装完成后&#xff0c;都会自带中文输入法。而需要配置中文输入法的小众发行版往往软件仓库自带的依赖不全。 1,sudo dnf install ibus 2,sudo dnf install im-chooser 3,sudo dnf install ibus-libpinyin 4,在终端输入im-choose…...

Chromium CI/CD 之Jenkins实用指南2024-如何创建新节点(三)

1. 前言 在前一篇《Jenkins实用指南2024-系统基本配置&#xff08;二&#xff09;》中&#xff0c;我们详细介绍了如何对Jenkins进行基本配置&#xff0c;包括系统设置、安全配置、插件管理以及创建第一个Job。通过这些配置&#xff0c;您的Jenkins环境已经具备了基本的功能和…...

Git代码管理工具 — 3 Git基本操作指令详解

目录 1 获取本地仓库 2 基础操作指令 2.1 基础操作指令框架 2.2 git status查看修改的状态 2.3 git add添加工作区到暂存区 2.4 提交暂存区到本地仓库 2.5 git log查看提交日志 2.6 git reflog查看已经删除的记录 2.7 git reset版本回退 2.8 添加文件至忽略列表 1 获…...

Linux——多线程(五)

1.线程池 1.1初期框架 thread.hpp #include<iostream> #include <string> #include <unistd.h> #include <functional> #include <pthread.h>namespace ThreadModule {using func_t std::function<void()>;class Thread{public:void E…...

张量分解(4)——SVD奇异值分解

&#x1f345; 写在前面 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;这里是hyk写算法了吗&#xff0c;一枚致力于学习算法和人工智能领域的小菜鸟。 &#x1f50e;个人主页&#xff1a;主页链接&#xff08;欢迎各位大佬光临指导&#xff09; ⭐️近…...

第三方配件也能适配苹果了,iOS 18与iPadOS 18将支持快速配对

苹果公司以其对用户体验的不懈追求和对创新技术的不断探索而闻名。随着iOS 18和iPadOS 18的发布&#xff0c;苹果再次证明了其在移动操作系统领域的领先地位。 最新系统版本中的一项引人注目的功能&#xff0c;便是对蓝牙和Wi-Fi配件的配对方式进行了重大改进&#xff0c;不仅…...

Docker 部署 Nginx 并在容器内配置申请免费 SSL 证书

文章目录 dockerdocker-compose.yml申请免费 SSL 证书请求头参数带下划线 docker https://hub.docker.com/_/nginx docker pull nginx:1.27注&#xff1a; 国内网络原因无法下载镜像&#xff0c;nginx 镜像文件下载链接 https://pan.baidu.com/s/1O35cPbx6AHWUJL1v5-REzA?pw…...

模型评估与选择

2.1 经验误差与过拟合 错误率&#xff08;error rate&#xff09;&#xff1a; 分类错误的样本数占样本总数的比例 精度&#xff08;accuracy&#xff09;&#xff1a;1- 错误率 训练误差 / 经验误差&#xff1a;在训练集上的误差 泛化误差&#xff1a;在新样本上的误差 过…...

有必要把共享服务器升级到VPS吗?

根据自己的需求来选择是否升级&#xff0c;虚拟专用服务器 (VPS) 是一种托管解决方案&#xff0c;它以低得多的成本提供专用服务器的大部分功能。使用 VPS&#xff0c;您的虚拟服务器将与在其上运行的其他虚拟服务器共享硬件服务器的资源。但是&#xff0c;与传统的共享托管&am…...

LLM代理应用实战:构建Plotly数据可视化代理

如果你尝试过像ChatGPT这样的LLM&#xff0c;就会知道它们几乎可以为任何语言或包生成代码。但是仅仅依靠LLM是有局限的。对于数据可视化的问题我们需要提供一下的内容 描述数据:模型本身并不知道数据集的细节&#xff0c;比如列名和行细节。手动提供这些信息可能很麻烦&#…...

大模型系列3--pytorch dataloader的原理

pytorch dataloader运行原理 1. 背景2. 环境搭建2.1. 安装WSL & vscode2.2. 安装conda & pytorch_gpu环境 & pytorch 2.112.3 命令行验证python环境2.4. vscode启用pytorch_cpu虚拟环境 3. 调试工具3.1. vscode 断点调试3.2. py-spy代码栈探测3.3. gdb attach3.4. …...