STM32+W5500实现以太网通信
STM32系列32位微控制器基于Arm® Cortex®-M处理器,旨在为MCU用户提供新的开发自由度。它包括一系列产品,集高性能、实时功能、数字信号处理、低功耗/低电压操作、连接性等特性于一身,同时还保持了集成度高和易于开发的特点。本例采用STM32作为MCU。
W5500是一款全硬件TCP/IP嵌入式以太网控制器,为嵌入式系统提供了更加简洁的互联网方案。W5500集成了TCP/IP协议栈,10/100M以太网数据链路层(MAC)以及物理层(PHY)。全硬件实现的TCP/IP协议栈支持TCP,UDP,IPv4,ICMP,ARP,IGMP以及PPPoE协议。W5500内嵌32K字节片上缓存以供以太网包处理,用户可以同时使用8个硬件Socket独立通信。W5500使用了高效的SPI协议支持80MHz速率,解决系统通信瓶颈,更好地实现高速网络通信。
文章目录
概念说明
实现原理
嵌入式程序
底层通用接口
W5500抽象接口
官网例程补充说明
概念说明
- SPI:Serial Peripheral interface是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线。SPI分为主、从两种模式,一个SPI通讯系统需要包含一个(且只能是一个)主设备,一个或多个从设备。这里STM32为主设备(Master)提供时钟,W5500为从设备(Slave)。SPI接口一般使用四条信号线通信:SDI(数据输入),SDO(数据输出),SCK(时钟),CS(片选)
- MISO:主设备输入/从设备输出引脚。该引脚在从模式下发送数据,在主模式下接收数据。
- MOSI:主设备输出/从设备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据。
- SCLK:主设备输出/从设备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据。
- CS/SS:主设备输出/从设备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据。从设备片选信号,由主设备控制。它的功能是用来作为“片选引脚”,也就是选择指定的从设备,让主设备可以单独地与特定从设备通讯,避免数据线上的冲突。
- TCP/IP协议:TCP/IP协议是Internet互联网最基本的协议,其在一定程度上参考了七层ISO模型。OSI模型共有七层,在TCP/IP协议中,七层被简化为了四个层次。TCP/IP模型中的各种协议,依其功能不同,被分别归属到这四层之中,常被视为是简化过后的七层OSI模型。 W5500实现的部分为红框部分:
- Socket:在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信的一个虚拟通道描述符,一个socket对应着网络通信的一扇门。
- 网络参数:网络通信需要的参数有很多,我这里介绍一下我们常见的:
- IP地址:Internet Protocol Address是指互联网协议地址,两台通过IP协议通信的机器通过IP地址寻找对方,子网掩码配合IP地址能得到对端IP地址是在本地子网,还是需要发送至网关进行路由发送至广域网。
- MAC地址:Media Access Control 地址是制造商为网络硬件 (如无线网卡或以太网网卡)分配的唯一代码。MAC地址作为数据链路设备的地址标识符,ARP协议局域网寻址使用MAC地址。
- 网络端口号:网络中的计算机是通过IP地址来代表其身份的,它只能表示某台特定的计算机,但是一台计算机上可以同时提供很多服务,比如常见的端口号21表示的是FTP服务。端口号是socket的属性之一。
- DNS/DHCP/网关:网关作为局域网的一个出口可以将报文转发至广域网中,DNS服务器提供域名解析功能,DHCP服务器提供局域网IP地址管理功能。
实现原理
嵌入式程序跑在STM32微控制器,通过片上SPI控制器与W5500进行通信,配置所需网络参数并与远端服务器建立链接(TCP客户端)之后发送接收数据;又或者是建立服务器(TCP服务器)等待建立链接之后收发数据。抑或是不用建立连接直接发送报文(UDP)。PHY信号变压之后通过RJ45接口收发至网线。原理示意图如下:
嵌入式程序
嵌入式程序分两个部分,第一部分是一些底层通用接口。第二部分W5500抽象供上层调用的接口。
底层通用接口
这部分实现主要包括IO口控制器初始化,以及MCU不同位宽配置芯片接口和Socket批量发送接收数据接口,代码片如下(中文注释帮助您更好的理解实现):
//IO口控制器初始化
uint8 w55_IoInit(void)
{GPIO_InitTypeDef GPIO_InitStructure;SPI_InitTypeDef SPI_InitStructure;RCC_APB2PeriphClockCmd(SPI_CS_RCC|SPI_SCLK_RCC|SPI_SO_RCC|SPI_SI_RCC|W5500_RESET_RCC|LINK_RCC|RCC_APB2Periph_AFIO,ENABLE);/*使能AFIO时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); /*只保留SWD模式*/GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); RCC_APB1PeriphClockCmd(W5500_SPI_CLK ,ENABLE);GPIO_StructInit(&GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = W5500_RESET_pin;GPIO_Init(W5500_RESET_GPIO, &GPIO_InitStructure);GPIO_StructInit(&GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin =SPI_CS_pin;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(SPI_CS_GPIO, &GPIO_InitStructure);GPIO_SetBits(SPI_CS_GPIO,SPI_CS_pin);GPIO_StructInit(&GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin =SPI_SCLK_pin|SPI_SI_pin|SPI_SO_pin;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(SPI_SCLK_GPIO, &GPIO_InitStructure);GPIO_SetBits(SPI_SCLK_GPIO,SPI_SCLK_pin|SPI_SI_pin|SPI_SO_pin);RCC_APB1PeriphClockCmd(LINK_RCC ,ENABLE);GPIO_StructInit(&GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = LINK_pin;GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(LINK_GPIO, &GPIO_InitStructure);SPI_InitStructure.SPI_Direction=SPI_Direction_2Lines_FullDuplex;SPI_InitStructure.SPI_Mode=SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL=SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA=SPI_CPHA_1Edge; SPI_InitStructure.SPI_NSS=SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_2; SPI_InitStructure.SPI_FirstBit=SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial=7; SPI_Init(W5500_SPI, &SPI_InitStructure); SPI_Cmd(W5500_SPI,ENABLE);return 1;
}//spi发送一个字节
void SPI_Send_Byte(unsigned char dat)
{while (SPI_I2S_GetFlagStatus(W5500_SPI, SPI_I2S_FLAG_TXE) == RESET); SPI_I2S_SendData(W5500_SPI, dat);
}
//spi接收一个字节
unsigned char SPI_Recv_Byte(void)
{return (uint8)(SPI_I2S_ReceiveData(W5500_SPI));
}
//spi发送两个字节
void SPI_Send_Short(unsigned short dat)
{SPI_Send_Byte(dat/256);SPI_Send_Byte(dat);
}
//通过spi向指定地址寄存器写n个字节数据
uint8 w55_WritenByte(uint16 reg, uint8 *dat_ptr, uint16 size)
{unsigned short i;GPIO_ResetBits(W5500_SCS_PORT, W5500_SCS); SPI_Send_Short(reg);SPI_Send_Byte(VDM|RWB_WRITE|COMMON_R);for(i=0;i<size;i++){SPI_Send_Byte(*dat_ptr++);}GPIO_SetBits(W5500_SCS_PORT, W5500_SCS); return 1;
}
//读取W5500指定地址1Byte数据
uint8 w55_Read1Byte(uint16 reg)
{unsigned char i;GPIO_ResetBits(W5500_SCS_PORT, W5500_SCS);SPI_Send_Short(reg);SPI_Send_Byte(FDM1|RWB_READ|COMMON_R);i=SPI_Recv_Byte();SPI_Send_Byte(0x00);i=SPI_Recv_Byte();GPIO_SetBits(W5500_SCS_PORT, W5500_SCS);return i;
}
///
//华丽的分割线
///
//单字节配置socket
uint8 w55_WriteSock1Byte(SOCKET s, uint16 reg, uint8 dat)
{GPIO_ResetBits(W5500_SCS_PORT, W5500_SCS);SPI_Send_Short(reg);SPI_Send_Byte(FDM1|RWB_WRITE|(s*0x20+0x08));SPI_Send_Byte(dat);GPIO_SetBits(W5500_SCS_PORT, W5500_SCS);return 1;
}
//两字节配置socket
uint8 w55_WriteSock2Byte(SOCKET s, uint16 reg, uint16 dat)
{GPIO_ResetBits(W5500_SCS_PORT, W5500_SCS);SPI_Send_Short(reg);SPI_Send_Byte(FDM2|RWB_WRITE|(s*0x20+0x08));SPI_Send_Short(dat);GPIO_SetBits(W5500_SCS_PORT, W5500_SCS);return 1;
}
//四字节配置socket
uint8 w55_WriteSock4Byte(SOCKET s, uint16 reg, uint8 *dat_ptr)
{GPIO_ResetBits(W5500_SCS_PORT, W5500_SCS);//ÖÃW5500µÄSCSΪµÍµçƽSPI_Send_Short(reg);SPI_Send_Byte(FDM4|RWB_WRITE|(s*0x20+0x08));SPI_Send_Byte(*dat_ptr++);SPI_Send_Byte(*dat_ptr++);SPI_Send_Byte(*dat_ptr++);SPI_Send_Byte(*dat_ptr++);GPIO_SetBits(W5500_SCS_PORT, W5500_SCS); return 1;
}
//读取Socket1字节信息
uint8 w55_ReadSock1Byte(SOCKET s, uint16 reg)
{unsigned char i;GPIO_ResetBits(W5500_SCS_PORT, W5500_SCS);SPI_Send_Short(reg);SPI_Send_Byte(FDM1|RWB_READ|(s*0x20+0x08));//i=SPI_I2S_ReceiveData(W5500_SPI);i=SPI_Recv_Byte();SPI_Send_Byte(0x00);//i=SPI_I2S_ReceiveData(W5500_SPI);i=SPI_Recv_Byte();GPIO_SetBits(W5500_SCS_PORT, W5500_SCS);return i;
}
//读取2字节socket信息
uint16 w55_ReadSock2Byte(SOCKET s, uint16 reg)
{unsigned short i;GPIO_ResetBits(W5500_SCS_PORT, W5500_SCS);SPI_Send_Short(reg);SPI_Send_Byte(FDM2|RWB_READ|(s*0x20+0x08));i=SPI_Recv_Byte();SPI_Send_Byte(0x00);i=SPI_Recv_Byte();SPI_Send_Byte(0x00);i*=256;i+=SPI_Recv_Byte();GPIO_SetBits(W5500_SCS_PORT, W5500_SCS);return i;
}
W5500抽象接口
这部分主要实现了芯片以及Socket初始化,模拟中断处理循环函数等,示例使用Socket0实现TCP客户端,您可以在此代码基础上增加配置别的Socket实现TCP服务器以及UDP等。代码片(会有中文注释帮助您更好地理解程序)如下,你会通过这部分代码片体会到C51单片机程序的编程风格(尤其是开始的两个接口):
//从Socket接收数据缓存区读取数据
uint16 w55_ReadSockToBuffer(SOCKET s, uint8 *dat_ptr)
{unsigned short rx_size;unsigned short offset, offset1;unsigned short i;unsigned char j;rx_size=w55_ReadSock2Byte(s,W5500_Sn_RX_RSR);if(rx_size==0) return 0;//没接收到数据则返回if(rx_size>S_RX_SIZE) rx_size=S_RX_SIZE; offset=w55_ReadSock2Byte(s,W5500_Sn_RX_RD);offset1=offset;offset&=(S_RX_SIZE-1);//计算实际的物理地址GPIO_ResetBits(W5500_SCS_PORT, W5500_SCS);SPI_Send_Short(offset);//写16位地址SPI_Send_Byte(VDM|RWB_READ|(s*0x20+0x18));//写控制字节,N字节数据长度,读数据,选择端口j=SPI_Recv_Byte();if((offset+rx_size)<S_RX_SIZE)//如果最大地址未超过w5500接收缓冲区寄存器的最大地址{for(i=0;i<rx_size;i++)//循环读取rx_size个字节数据{SPI_Send_Byte(0x00);//发送一个哑数据j=SPI_Recv_Byte();*dat_ptr=j;dat_ptr++;}}else//如果最大地址超过w5500接收缓冲区寄存器最大地址{offset=S_RX_SIZE-offset;for(i=0;i<offset;i++)//循环读出前offset个字节数据{SPI_Send_Byte(0x00); j=SPI_Recv_Byte();*dat_ptr=j;dat_ptr++;}GPIO_SetBits(W5500_SCS_PORT, W5500_SCS); GPIO_ResetBits(W5500_SCS_PORT, W5500_SCS);SPI_Send_Short(0x00);//写16位地址SPI_Send_Byte(VDM|RWB_READ|(s*0x20+0x18));//写控制字j=SPI_Recv_Byte();for(;i<rx_size;i++)//循环读取后rx_size-offset个字节数据{SPI_Send_Byte(0x00);j=SPI_Recv_Byte();*dat_ptr=j;dat_ptr++;}}GPIO_SetBits(W5500_SCS_PORT, W5500_SCS); offset1+=rx_size;//更新实际物理地址,即下次读取接收到的数据的起始地址w55_WriteSock2Byte(s, W5500_Sn_RX_RD, offset1);w55_WriteSock1Byte(s, W5500_Sn_CR, RECV);//发送启动接收命令return rx_size;//返回接收到的数据长度
}
//将数据写入W5500的数据发送缓冲区
uint8 w55_WriteSockToBuffer(SOCKET s, uint8 *dat_ptr, uint16 size)
{unsigned short offset,offset1;unsigned short i;offset=w55_ReadSock2Byte(s,W5500_Sn_TX_WR);offset1=offset;offset&=(S_TX_SIZE-1);//计算实际物理地址GPIO_ResetBits(W5500_SCS_PORT, W5500_SCS);SPI_Send_Short(offset);SPI_Send_Byte(VDM|RWB_WRITE|(s*0x20+0x10));if((offset+size)<S_TX_SIZE)//如果最大地址未超过W5500发送缓冲区寄存器的最大地址{for(i=0;i<size;i++){SPI_Send_Byte(*dat_ptr++); }}else//如果最大地址超过w5500发送缓冲区寄存器的最大地址{offset=S_TX_SIZE-offset;for(i=0;i<offset;i++){SPI_Send_Byte(*dat_ptr++);}GPIO_SetBits(W5500_SCS_PORT, W5500_SCS); GPIO_ResetBits(W5500_SCS_PORT, W5500_SCS);SPI_Send_Short(0x00);SPI_Send_Byte(VDM|RWB_WRITE|(s*0x20+0x10));for(;i<size;i++){SPI_Send_Byte(*dat_ptr++);}}GPIO_SetBits(W5500_SCS_PORT, W5500_SCS); offset1+=size;//更新实际物理地址,即下次写待发送数据到发送数据缓冲区起始地址w55_WriteSock2Byte(s, W5500_Sn_TX_WR, offset1);w55_WriteSock1Byte(s, W5500_Sn_CR, SEND);//发送启动发送指令
}
//芯片初始化
uint8 w55_StartUp(void)
{uint8 i;w55_IoInit();w55_HardwareReset();w55_Write1Byte(W5500_MODE,MODE_RST); //软复位delay(1000);//设置网关的IP地址w55_WritenByte(W5500_GAR, s_app_w55_para.u8Gateway, 4);//设置子网掩码值w55_WritenByte(W5500_SUBR,s_app_w55_para.u8SubMask,4);//设置MAC地址w55_WritenByte(W5500_SHAR,s_app_w55_para.u8MacAddr,6); //设置w5500的IP地址w55_WritenByte(W5500_SIPR,s_app_w55_para.u8LocalIp,4); //设置发送缓冲区以及接收缓冲区大小for(i=0;i<8;i++){w55_WriteSock1Byte(i,W5500_Sn_RXBUF_SIZE, 0x02);//Socket Rx memory size=2kw55_WriteSock1Byte(i,W5500_Sn_TXBUF_SIZE, 0x02);//Socket Tx mempry size=2k } //设置重连时间为200msw55_Write2Byte(W5500_RTR, 0x07d0); //设置重连次数为8次w55_Write1Byte(W5500_RCR,8);w55_Write1Byte(W5500_IMR,IM_IR7|IM_IR6);w55_WriteSock1Byte(W55_SOCKETSERVER,W5500_Sn_IMR,(IMR_SENDOK|IMR_TIMEOUT|IMR_RECV|IMR_DISCON|IMR_CON));w55_WriteSock1Byte(W55_SOCKETCLIENT,W5500_Sn_IMR,(IMR_SENDOK|IMR_TIMEOUT|IMR_RECV|IMR_DISCON|IMR_CON));w55_WriteSock1Byte(W55_SOCKETUDP,W5500_Sn_IMR,(IMR_SENDOK|IMR_TIMEOUT|IMR_RECV|IMR_DISCON|IMR_CON));w55_WriteSock1Byte(W55_SOCKETLISTEN,W5500_Sn_IMR,(IMR_SENDOK|IMR_TIMEOUT|IMR_RECV|IMR_DISCON|IMR_CON));
}//Socket初始化
uint8 w55_SocketInit(SOCKET s)
{switch(s) {case 0: //将socket0用作Tcp客户端w55_WriteSock2Byte(s, W5500_Sn_MSSR, 1460);//设置最大字节分片数//设置源端口号w55_WriteSock2Byte(s, W5500_Sn_PORT, s_app_w55_para.u16UpPort);//设置目标(远程)端口号w55_WriteSock2Byte(1, W5500_Sn_DPORTR, s_app_w55_para.u16UpPort);//设置目标(远程)IP地址w55_WriteSock4Byte(s, W5500_Sn_DIPR, s_app_w55_para.u8UpIp); break;case 4:break;case 5:break;case 6:break;case 7:break;default:break;}
}//Tcp客户端连接
uint8 u8_app_w55_SocketConnect(SOCKET s)
{u8_drv_w55_WriteSock1Byte(s,W5500_Sn_MR,MR_TCP);u8_drv_w55_WriteSock1Byte(s,W5500_Sn_CR,OPEN);//打开Socketdelay(5);//ÑÓʱ5msif(u8_drv_w55_ReadSock1Byte(s,W5500_Sn_SR)!=SOCK_INIT){u8_drv_w55_WriteSock1Byte(s,W5500_Sn_CR,CLOSE);return FALSE;//·µ»ØFALSE(0x00)}u8_drv_w55_WriteSock1Byte(s,W5500_Sn_CR,CONNECT);//设置Socket尝试连接return TRUE;
} //循环模拟中断处理函数
uint8 w55_InterruptProcess(void)
{uint8 i,j;uint8 u8Data[50];uint16 u16Length;uint16 k;while(1){//读取当前中断标志i = w55_Read1Byte(W5500_IR);//清除中断标志 w55_Write1Byte(W5500_IR, (i&0xf0)); if((i & IR_CONFLICT) == IR_CONFLICT) {//IP冲突处理代码添加return 0;}if((i & IR_UNREACH) == IR_UNREACH) {//报文无法到达return 0;}i=w55_Read1Byte(W5500_SIR); if((i & S1_INT) == S1_INT) //Socket1用作TCP客户端 {//读取socket1中断标志j=w55_ReadSock1Byte(W55_SOCKETCLIENT,W5500_Sn_IR); //清除socket1中断标志 w55_WriteSock1Byte(W55_SOCKETCLIENT,W5500_Sn_IR,j); if(j&IR_CON)//TCP客户端模式下,成功建立连接 {//一般添加发送设备信息}if(j&IR_DISCON) //TCP模式下socket断开连接处理 {w55_WriteSock1Byte(W55_SOCKETCLIENT,W5500_Sn_CR,CLOSE);//关闭w55_SocketInit(W55_SOCKETCLIENT);// 初始化TCP客户端socket w55_SocketConnect(W55_SOCKETCLIENT);//尝试建立连接}if(j&IR_SEND_OK) //数据包发送成功{//添加发送成功相关代码}if(j&IR_RECV) //数据包接收 { memset(w55_DataTmpRC,'\0',sizeof(MAX_CIRCBUFF_SIZE_1+10)); u16Length = w55_ReadSockToBuffer(W55_SOCKETCLIENT,w55_DataTmpRC); //通知处理接收数据}if(j&IR_TIMEOUT)//Socket连接或数据传输超时处理 {w55_WriteSock1Byte(W55_SOCKETCLIENT,W5500_Sn_CR,CLOSE);//关闭w55_SocketInit(W55_SOCKETCLIENT);//初始化 w55_SocketConnect(W55_SOCKETCLIENT); //重连 }}w55_WriteSockToBuffer(7,u8test1,2);if(w55_Read1Byte(W5500_SIR) != 0) {continue;}else{break;}}
}
官网例程补充说明
W5500官网提供了包括开发板,原理图,以及参考代码和常见问题与技术服务等。参考代码又针对STM32,DSP,C8051以及基于操作系统分别提供了作为TCP客户端/服务器,UDP收发等例程。下面我们对STM32F407+W5500例程中的W5500作为TCP客户端工程进行简单的分析,具体代码可以去官网下载,我这边就不贴了。
工程中包含了DHCP/DNS客户端的实现代码,示例中并没有使用,我也就不详述了,有兴趣的可以去看看。W5500.c包含了主要的驱动实现,包含所有寄存器的定义以及用以上层调用的初始化,建立连接,发送接收等接口。考虑到MCU可能不通过SPI控制器与W5500进行通信,W5500.c实现了注册平台提供的MCU与W5500直接数据交互的函数。下面是示例工程的流程图:
十六宿舍 原创作品,转载必须标注原文链接。
©2023 Yang Li. All rights reserved.
欢迎关注 『十六宿舍』,大家喜欢的话,给个👍,更多关于嵌入式相关技术的内容持续更新中。
相关文章:

STM32+W5500实现以太网通信
STM32系列32位微控制器基于Arm Cortex-M处理器,旨在为MCU用户提供新的开发自由度。它包括一系列产品,集高性能、实时功能、数字信号处理、低功耗/低电压操作、连接性等特性于一身,同时还保持了集成度高和易于开发的特点。本例采用STM32作为MC…...

全网最详细,Jmeter性能测试-性能基础详解,终成测试卷王(一)
目录:导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜)前言 发起请求 发起HTTP…...

人工智能概述
一、人工智能发展必备三要素 算法 数据 算力 CPU、GPU、TPU 计算力之CPU、GPU对比: CPU主要适合I\O密集型任务GPU主要适合计算密集型任务 什么样的程序适合在GPU上运行? 计算密集型的程序 所谓计算密集型(Compute-intensive)的程序,就是…...

API接口安全—webservice、Swagger、WEBpack
API接口安全—webservice、Swagger、WEBpack1. API接口介绍1.1. 常用的API接口类1.1.1. API接口分类1.1.1.1. 类库型API1.1.1.2. 操作系统型API1.1.1.3. 远程应用型API1.1.1.4. WEB应用型API1.1.1.5. 总结1.1.2. API接口类型1.1.2.1. HTTP类接口1.1.2.2. RPC类接口1.1.2.3. web…...
从前M个字母中取N个的无重复排列 [2*+]
目录 从前M个字母中取N个的无重复排列 [2*+] 程序设计 程序分析 从前M个字母中取N个的无重复排列 [2*+] 输出从前M个字母中取N个的无重复字母排列 Input 输入M N 1<=M=10, N<=M Output 按字典序输出排列 Sample Input 4 2 Sample Output A B A C A D B A B C B …...
ES forceMerge 强制段合并为什么会提升检索性能?
根据以前的测试,forceMerge段合并,将段的个数合并成一个。带来了将近一倍的性能提升,测试过程文档(请参考我的另外一篇文章):ES优化实战- forceMerge搜索提升测试报告_es forcemerge_水的精神的博客-CSDN博…...

macOS Ventura 13.3.1 (22E261) Boot ISO 原版可引导镜像
本站下载的 macOS 软件包,既可以拖拽到 Applications(应用程序)下直接安装,也可以制作启动 U 盘安装,或者在虚拟机中启动安装。另外也支持在 Windows 和 Linux 中创建可引导介质。 macOS Ventura 13.3.1 为 Mac 提供下…...

html+css+JavaScript+json+servlet的社区系统(手把手教学)
目录 课前导读: 一、系统前期准备 二、前端代码的编写 三、登陆页面简介 四、注册页面 五、社区列表页 六、社区详情页 七、社区发帖页 八、注销 九、访问链接 登陆页面http://175.178.20.77:8080/java106_blog_system/login.html 总结: 课前…...

UI Toolkit(1)
UI ToolkitUI Toolkit界面画布设置背景制作UI布局UI Toolkit界面 在Unity 2021LTS版本之后UI Toolkit也被内置在Unity中,Unity有意的想让UI Toolkit 成为UI的主要搭建方式,当然与UGUI相比还是有一定的差别。他们各有有点,这次我们就开始介绍…...

vLive带你走进虚拟直播世界
虚拟直播是什么? 虚拟直播是基于5G实时渲染技术,在绿幕环境下拍摄画面,通过实时抠像、渲染与合成,再推流到直播平台的一种直播技术。尽管这种技术早已被影视工业所采用,但在全民化进程中却是困难重重,面临…...

初谈 ChatGPT
引子 最近,小编发现互联网中的大 V 突然都在用 ChatGPT 做宣传:“ChatGPT不会淘汰你,能驾驭ChatGPT的人会淘汰你”、“带领一小部分人先驾驭ChatGPT”。 确实,ChatGPT这个新生事物,如今被视为蒸汽机、电脑、iPhone 般的…...
JAVA练习103-螺旋矩阵
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 前言 提示:这里可以添加本文要记录的大概内容: 4月9日练习内容 提示:以下是本篇文章正文内容,下面案例可供参考 一、题目-螺…...

RecvByteBufAllocator内存分配计算
虽然了解了整个内存池管理的细节,包括它的内存分配的具体逻辑,但是每次从NioSocketChannel中读取数据时,应该分配多少内存去读呢? 例如,客户端发送的数据为1KB , 应该分配多少内存去读呢? 例如:…...

图数据结构与算法
什么是图数据的结构 图是由顶点和边组成的非线性数据结构。顶点有时也称为节点,边是连接图中任意两个节点的线或弧。更正式地说,图由一组顶点 ( V ) 和一组边 ( E ) 组成。该图由 G(E, V) 表示。 图的组成部分 顶点:顶点是图的基本单位。有时,顶点也称为顶点或节点。每个节…...
科普:c语言与C++的区别
C语言和C语言是两种广泛使用的编程语言,尽管它们非常相似,但它们在某些方面也存在不同之处。本文将详细介绍C语言和C语言的区别。 1. 编程范式 C语言是一种过程式编程语言,它的设计目标是为了编写操作系统和其他系统级编程。C语言是一种面向…...

流量整形(GTS和LR)
Generic Traffic Shaping通用流量整形 通用流量整形(简称GTS)可以对不规则或不符合预定流量特性的流量进行整形,以保证网络上下游之间的带宽匹配,避免拥塞发生。 GTS与CAR一样,都采用了令牌桶技术来控制流量。GTS与CAR的主要区别在于:利用CAR进行报文流量控制时,…...
Java接口详细讲解
目录 Java接口概念 Java接口主要有以下特点 Java接口的具体作用 定义接口 实现接口 接口继承 默认方法 静态方法 Java接口概念 Java编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。 …...

元宇宙地产暴跌,林俊杰亏麻了
文/章鱼哥出品/陀螺财经随着元宇宙的兴起,元宇宙地产曾一度被寄予厚望,成为各大投资者追捧的对象。然而,最近的一次元宇宙地产价值暴跌再次提醒我们,高收益背后可能伴随着高风险。根据元宇宙分析平台WeMeta的数据显示,…...

什么是瀑布流布局?瀑布流式布局的优缺点
瀑布流又称瀑布流式布局,是一种多列等宽不等高的一种页面布局方式。 视觉表现为参差不齐的多栏布局。随着页面滚动条向下滚动,这种布局会不断加载数据并附加至当前的尾部。 是一种多列等宽不等高的一种页面布局方式,用于图片比较复杂&#…...

给您的 MongoDB 定期做个体检:MongoDB 诊断
新钛云服已累计为您分享739篇技术干货接下来的一些列文章会为大家介绍日常工作中常用的 NoSQL 产品 MongoDB。主要涉及到:MongoDB 的安装及基本使用 MongoDB 文档查询 MongoDB 复制集 MongoDB 分片集群的介绍及搭建 MongoDB 安全加密 MongoDB 诊断我们会用…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...

Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

基于Java+MySQL实现(GUI)客户管理系统
客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息,对客户进行统一管理,可以把所有客户信息录入系统,进行维护和统计功能。可通过文件的方式保存相关录入数据,对…...