STM32F1之OV7725摄像头·SCCB总线代码编写附带源码详解

STM32F1之OV7725摄像头-CSDN博客

STM32F1之OV7725摄像头·像素数据输出时序、FIFO 读写时序以及摄像头的驱动原理详解-CSDN博客

目录

1.  硬件设计

1.1  SCCB 控制相关

1.2  VGA 时序相关

1.3  FIFO 相关

1.4  XCLK 信号

2.  代码设计

2.1  SCCB总线软件实现

2.1.1  宏定义

2.1.2  SCCB管脚配置

2.1.3  延时函数

2.1.4  SCCB起始信号

2.1.5  SCCB终止信号

2.1.6  SCCB应答信号

2.1.7  SCCB非应答信号

2.1.8  SCCB等待应答信号

2.1.9  发送数据

2.1.10  SCCB总线返回的数据

2.1.11  SCCB写一个字节数据

2.1.12  SCCB读一串数据


1.  硬件设计

        摄像头与 STM32 连接关系中主要分为 SCCB 控制、VGA 时序控制、FIFO 数据读取部分,介绍如下:

1.1  SCCB 控制相关

        摄像头中的 SIO_C 和 SIO_D 引脚直接连接到 STM32 普通的 GPIO,它们不具有硬件I2C 的功能,所以在后面的代码中采用模拟 I2C 时序,实际上直接使用硬件 I2C 是完全可以实现 SCCB 协议的,本设计采用模拟 I2C 是芯片资源分配妥协的结果。

1.2  VGA 时序相关

        检测 VGA 时序的 HREF、VSYNC 引脚,它们与 STM32 连接的 GPIO 均设置为输入模式,其中 HREF 在本实验中并没有使用,它已经通过摄像头内部的与非门控制了 FIFO 的写使能;VSYNC 与 STM32 连接的 GPIO 引脚会在程序中配置成中断模式,STM32 利用该中断信号获知新的图像是否采集完成,从而控制 FIFO 是否写使能。

1.3  FIFO 相关

        与 FIFO 控制相关的 RCLK、RRST、WRST、WEN 及 OE 与 STM32 连接的引脚均直接配置成推挽输出,STM32 根据图像的采集情况利用这些引脚控制 FIFO;读取 FIFO 数据内容使用的数据引脚 DO[0:7]均连接到 STM32 同一个 GPIO 端口连续的高 8 位引脚 PB[8:15],这些引脚使用时均配置成输入,程序设计中直接读取 GPIO 端口的高 8 位状态直接获取一个字节的 FIFO 内容,建议在连接这部分数据信号时,参考本设计采用同一个 GPIO 端口连续的 8 位(高 8 位或低 8 位均可),否则会导致读取数据的程序非常复杂。

1.4  XCLK 信号

        本设计中 STM32 的摄像头接口还预留了 PA8 引脚用于与摄像头的 XCLK 连接,STM32的 PA8 可以对外输出时钟信号,所以在使用不带晶振的摄像头时,可以通过该引脚给摄像头提供时钟,秉火摄像头内部已自带晶振,在程序中没有使用 PA8 引脚。

2.  代码设计

2.1  SCCB总线软件实现

2.1.1  宏定义

#ifndef __SCCB_H
#define __SCCB_H#include "stm32f10x.h"/************************** OV7725 连接引脚定义********************************/
#define      OV7725_SIO_C_SCK_APBxClock_FUN              RCC_APB2PeriphClockCmd
#define      OV7725_SIO_C_GPIO_CLK                       RCC_APB2Periph_GPIOC
#define      OV7725_SIO_C_GPIO_PORT                      GPIOC
#define      OV7725_SIO_C_GPIO_PIN                       GPIO_Pin_6#define      OV7725_SIO_D_SCK_APBxClock_FUN              RCC_APB2PeriphClockCmd
#define      OV7725_SIO_D_GPIO_CLK                       RCC_APB2Periph_GPIOC
#define      OV7725_SIO_D_GPIO_PORT                      GPIOC
#define      OV7725_SIO_D_GPIO_PIN                       GPIO_Pin_7#define SCL_H         GPIO_SetBits(OV7725_SIO_C_GPIO_PORT , OV7725_SIO_C_GPIO_PIN) 
#define SCL_L         GPIO_ResetBits(OV7725_SIO_C_GPIO_PORT , OV7725_SIO_C_GPIO_PIN) #define SDA_H         GPIO_SetBits(OV7725_SIO_D_GPIO_PORT , OV7725_SIO_D_GPIO_PIN) 
#define SDA_L         GPIO_ResetBits(OV7725_SIO_D_GPIO_PORT , OV7725_SIO_D_GPIO_PIN) #define SCL_read      GPIO_ReadInputDataBit(OV7725_SIO_C_GPIO_PORT , OV7725_SIO_C_GPIO_PIN) 
#define SDA_read      GPIO_ReadInputDataBit(OV7725_SIO_D_GPIO_PORT , OV7725_SIO_D_GPIO_PIN) #define ADDR_OV7725   0x42void SCCB_GPIO_Config(void);
int SCCB_WriteByte( u16 WriteAddress , u8 SendByte);
int SCCB_ReadByte(u8* pBuffer,   u16 length,   u8 ReadAddress);#endif 

2.1.2  SCCB管脚配置

        前面我们说过,SCCB的引脚配置类似于IIC的引脚配置,这里我们对SCCB的引脚进行初始化,使用的类似软件IIC的协议,可以不用规定必须是IIC的引脚,进行配置为开漏输出模式:

void SCCB_GPIO_Config(void)
{GPIO_InitTypeDef  GPIO_InitStructure; 	/* SCL(PC6)、SDA(PC7)管脚配置 */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE );GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;  GPIO_Init(GPIOC, &GPIO_InitStructure);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE );GPIO_InitStructure.GPIO_Pin =GPIO_Pin_7;GPIO_Init(GPIOC, &GPIO_InitStructure);	
}

        按照宏定义进行转换:

void SCCB_GPIO_Config(void)
{GPIO_InitTypeDef  GPIO_InitStructure; /* SCL(PC6)、SDA(PC7)管脚配置 */OV7725_SIO_C_SCK_APBxClock_FUN ( OV7725_SIO_C_GPIO_CLK, ENABLE );GPIO_InitStructure.GPIO_Pin =  OV7725_SIO_C_GPIO_PIN ;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;  GPIO_Init(OV7725_SIO_C_GPIO_PORT, &GPIO_InitStructure);OV7725_SIO_D_SCK_APBxClock_FUN ( OV7725_SIO_D_GPIO_CLK, ENABLE );GPIO_InitStructure.GPIO_Pin =  OV7725_SIO_D_GPIO_PIN ;GPIO_Init(OV7725_SIO_D_GPIO_PORT, &GPIO_InitStructure);}

2.1.3  延时函数

        一个简单的循环等于0时跳出循环,起到延时的作用:

static void SCCB_delay(void)
{	uint16_t i = 400; while(i) { i--; } 
}

2.1.4  SCCB起始信号

        类比IIC协议,起始条件下,SCL高电平期间,SDA从高电平切换到低电平。

static int SCCB_Start(void)
{GPIO_SetBits(GPIOC, GPIO_Pin_7);SCCB_delay();GPIO_SetBits(GPIOC, GPIO_Pin_6);SCCB_delay();GPIO_ResetBits(GPIOC, GPIO_Pin_7);SCCB_delay();GPIO_ResetBits(GPIOC, GPIO_Pin_6);SCCB_delay();}

       为了提高程序的健壮性,可以加入:GPIO_ReadInputDataBit 函数,其作用是读取指定GPIO端口的指定引脚的输入状态,并返回该引脚的输入值(0 或 1) ,进行检测SDA线是否忙碌是否正常。

static int SCCB_Start(void)
{GPIO_SetBits(GPIOC, GPIO_Pin_7);GPIO_SetBits(GPIOC, GPIO_Pin_6);SCCB_delay();if(!GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_7))return DISABLE;	/* SDA线为低电平则总线忙,退出 */GPIO_ResetBits(GPIOC, GPIO_Pin_7);SCCB_delay();if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_7)) return DISABLE;	/* SDA线为高电平则总线出错,退出 */GPIO_ResetBits(GPIOC, GPIO_Pin_7);SCCB_delay();return ENABLE;
}

        按照宏定义进行转换:

static int SCCB_Start(void)
{SDA_H;SCL_H;SCCB_delay();if(!SDA_read)return DISABLE;	/* SDA线为低电平则总线忙,退出 */SDA_L;SCCB_delay();if(SDA_read) return DISABLE;	/* SDA线为高电平则总线出错,退出 */SDA_L;SCCB_delay();return ENABLE;
}

2.1.5  SCCB终止信号

        终止条件下,SCL高电平期间,SDA从低电平切换到高电平。

static void SCCB_Stop(void)
{GPIO_ResetBits(GPIOC, GPIO_Pin_6);SCCB_delay();GPIO_ResetBits(GPIOC, GPIO_Pin_7);SCCB_delay();GPIO_SetBits(GPIOC, GPIO_Pin_6);SCCB_delay();GPIO_SetBits(GPIOC, GPIO_Pin_7);SCCB_delay();
}

        按照宏定义进行转换:

static void SCCB_Stop(void)
{SCL_L;SCCB_delay();SDA_L;SCCB_delay();SCL_H;SCCB_delay();SDA_H;SCCB_delay();
}

2.1.6  SCCB应答信号

        与 I2C 时序类似,在 SCCB 时序也使用自由位(Don’t care bit )和非应答(NA)信号来保证正常通讯。自由位和非应答信号位于 SCCB 每个传输阶段中的第九位。

        在写数据的第一个传输阶段中,第 9 位为自由位,在一般的正常通讯中,第 9 位时,主机的 SDA 线输出高电平,而从机把 SDA 线拉低作为响应,只是传输的内容分别为目的寄存器地址和要写入的数据。

        应答信号的发送是 SCCB 协议中的一个重要步骤,用于确认数据传输的成功。

static void SCCB_Ack(void)
{GPIO_ResetBits(GPIOC, GPIO_Pin_6);SCCB_delay();GPIO_ResetBits(GPIOC, GPIO_Pin_7);SCCB_delay();GPIO_SetBits(GPIOC, GPIO_Pin_6);SCCB_delay();GPIO_ResetBits(GPIOC, GPIO_Pin_6);SCCB_delay();
}

        按照宏定义进行转换:

static void SCCB_Ack(void)
{	SCL_L;SCCB_delay();SDA_L;SCCB_delay();SCL_H;SCCB_delay();SCL_L;SCCB_delay();
}

2.1.7  SCCB非应答信号

        非应答信号的发送也是 SCCB 协议中的一部分,用于处理数据传输失败或其他错误情况。在某些情况下,如果从设备无法正确接收数据,主设备可能会发送非应答信号并采取相应的错误处理措施。

static void SCCB_NoAck(void)
{GPIO_ResetBits(GPIOC, GPIO_Pin_6);SCCB_delay();GPIO_SetBits(GPIOC, GPIO_Pin_7);SCCB_delay();GPIO_SetBits(GPIOC, GPIO_Pin_6);SCCB_delay();GPIO_ResetBits(GPIOC, GPIO_Pin_6);SCCB_delay();
}

        按照宏定义进行转换:

static void SCCB_NoAck(void)
{	SCL_L;SCCB_delay();SDA_H;SCCB_delay();SCL_H;SCCB_delay();SCL_L;SCCB_delay();
}

2.1.8  SCCB等待应答信号

        让主机把 SDA 线设为高电平,延时一段时间后再检测 SDA 线的电平,若为低则返回 ENABLE 表示接收到从机的应答,反之返回 DISABLE。(L代表低,H代表高,以下直接使用SCL和SDA的宏定义表示)

static int SCCB_WaitAck(void) 	
{SCL_L;SCCB_delay();SDA_H;			SCCB_delay();SCL_H;SCCB_delay();if(SDA_read){SCL_L;return DISABLE;}SCL_L;return ENABLE;
}

2.1.9  发送数据

        首先,我们先创建一个用于接收数据的参数SendByt,它接受一个 8 位的字节数据作为输入参数,使用一个循环逐位发送这个字节的数据。循环从最高位开始,依次发送每一位,直到最低位。在每一次循环中,首先将 SCL(时钟线)拉低,然后通过 SCCB_delay 函数产生一定延迟,接着,根据 SendByte 的当前最高位,控制 SDA(数据线)为高电平或低电平,以此来发送数据的当前位,然后将 SendByte 左移一位,准备发送下一位数据。通过循环8次来完成一位数据的发送。

static void SCCB_SendByte(uint8_t SendByte) 
{uint8_t i=8;while(i--){SCL_L;SCCB_delay();if(SendByte&0x80)SDA_H;  else SDA_L;   SendByte<<=1;SCCB_delay();SCL_H;SCCB_delay();}SCL_L;
}

2.1.10  SCCB总线返回的数据

        首先声明了一个变量 i 并初始化为 8,以及再声明了一个变量 ReceiveByte 并初始化为 0,用于存储接收到的字节数据,然后,将 SDA(数据线)拉高,准备接收数据,通过一个循环逐位接收一个字节的数据。循环从最高位开始,依次接收每一位,直到最低位,读取 SDA 线上的数据,并根据其值决定是否将 ReceiveByte 的当前最低位设置为 1,将 ReceiveByte 左移一位,准备接收下一位数据。

static int SCCB_ReceiveByte(void)  
{ uint8_t i=8;uint8_t ReceiveByte=0;SDA_H;				while(i--){ReceiveByte<<=1;      SCL_L;SCCB_delay();SCL_H;SCCB_delay();	if(SDA_read){ReceiveByte|=0x01;}}SCL_L;return ReceiveByte;
}

2.1.11  SCCB写一个字节数据

        首先,明确两个宏,其实OV7725的设备地址:

#define ADDR_OV7725 0x42
#define DEV_ADR ADDR_OV7725

        为了确保SCCB总线通信正常启动,我们可以先进行判断SCCB是否发送起始信号,若是发送继续,若是失败则返回DISABLE。

    if(!SCCB_Start()){return DISABLE;}

        然后,通过 SCCB_SendByte 函数发送设备地址 DEV_ADR,用于指定要通信的设备,此时指定要通信的设备会发送应答,通过判断本机时候接收到应答,来确定两个设备间是否能进行正常通信,若是不能则终止信号,并返回DISABLE。

    SCCB_SendByte( DEV_ADR );                   if( !SCCB_WaitAck() ){SCCB_Stop(); return DISABLE;}

        若是能则将要写入的寄存器地址的低八位(WriteAddress 的低八位)发送给设备,以确定写入数据的目标寄存器,再次等待指定要通信的设备的应答,然后发送要写入的数据 SendByte 给设备,再次等待设备的应答,,停止 SCCB 通信,并返回 ENABLE 表示写入操作成功。

    SCCB_SendByte((uint8_t)(WriteAddress & 0x00FF));  SCCB_SendByte(SendByte);SCCB_WaitAck();   SCCB_Stop(); return ENABLE;

完整代码:

#define ADDR_OV7725 0x42
#define DEV_ADR ADDR_OV7725int SCCB_WriteByte( uint16_t WriteAddress , uint8_t SendByte )
{		if(!SCCB_Start()){return DISABLE;}SCCB_SendByte( DEV_ADR );                    /* Æ÷¼þµØÖ· */if( !SCCB_WaitAck() ){SCCB_Stop(); return DISABLE;}SCCB_SendByte((uint8_t)(WriteAddress & 0x00FF));   /* ÉèÖõÍÆðʼµØÖ· */      SCCB_WaitAck();	SCCB_SendByte(SendByte);SCCB_WaitAck();   SCCB_Stop(); return ENABLE;
}

2.1.12  SCCB读一串数据

int SCCB_ReadByte(uint8_t* pBuffer, uint16_t length, uint8_t ReadAddress)
{	if(!SCCB_Start()){return DISABLE;}SCCB_SendByte( DEV_ADR );         /* Æ÷¼þµØÖ· */if( !SCCB_WaitAck() ){SCCB_Stop(); return DISABLE;}SCCB_SendByte( ReadAddress );     /* ÉèÖõÍÆðʼµØÖ· */      SCCB_WaitAck();	SCCB_Stop(); if(!SCCB_Start()){return DISABLE;}SCCB_SendByte( DEV_ADR + 1 );     /* Æ÷¼þµØÖ· */ if(!SCCB_WaitAck()){SCCB_Stop(); return DISABLE;}while(length){*pBuffer = SCCB_ReceiveByte();if(length == 1){SCCB_NoAck();}else{SCCB_Ack(); }pBuffer++;length--;}SCCB_Stop();return ENABLE;
}

OV7725摄像头_时光の尘的博客-CSDN博客

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

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

相关文章

AI图书推荐:ChatGPT解码—人工智能增强生活指南

《ChatGPT解码—人工智能增强生活指南》&#xff08;ChatGPT Decoded. A Beginners Guide to AI-Enhanced Living &#xff09;是一本由 大卫维恩斯&#xff08;David Wiens &#xff09;所著的书籍&#xff0c;旨在帮助读者了解并有效利用GPT-4语言模型这一强大工具来提升日常…

springmvc Web上下文初始化

Web上下文初始化 web上下文与SerlvetContext的生命周期应该是相同的&#xff0c;springmvc中的web上下文初始化是由ContextLoaderListener来启动的 web上下文初始化流程 在web.xml中配置ContextLoaderListener <listener> <listener-class>org.springframework.…

ncnn 优化量化

问题&#xff1a;发现推理时间过长&#xff0c;需要优化 当前正在做人脸检测部署&#xff0c;发现检测速度有点吓人&#xff0c;以下监测的时间 gpu&#xff1a; cpu&#xff1a; gpu推理大概整体时间200多毫秒&#xff0c;cpu推理时间300多毫秒&#xff0c;这里暂时没去考虑…

Oracle实践|内置函数之字符串函数

&#x1f4eb; 作者简介&#xff1a;「六月暴雪飞梨花」&#xff0c;专注于研究Java&#xff0c;就职于科技型公司后端工程师 &#x1f3c6; 近期荣誉&#xff1a;华为云云享专家、阿里云专家博主、腾讯云优秀创作者、ACDU成员 &#x1f525; 三连支持&#xff1a;欢迎 ❤️关注…

C++模板——函数模板和类模板

目录 泛型编程 函数模板 函数模板概念 函数模板的定义和语法 函数模板的工作原理 函数模板的实例化 隐式实例化 显示实例化 函数模板的匹配原则 类模板 类模板的定义格式 类模板的实例化 泛型编程 什么是泛型编程&#xff1f; 泛型编程&#xff08;Generic Pr…

【Linux-并发与竞争】

Linux-并发与竞争 ■ 原子操作■ 原子操作简介■ 原子整形操作 API 函数■ 原子位操作 API 函数■ 示例一&#xff1a;原子操作实验&#xff0c;使用原子变量来实现对实现设备的互斥访问 ■ 自旋锁■ 自旋锁 API 函数■ 死锁■ 最好的解决死锁方法就是获取锁之前关闭本地中断&a…

LeetCode 124 —— 二叉树中的最大路径和

阅读目录 1. 题目2. 解题思路3. 代码实现 1. 题目 2. 解题思路 二叉树的问题首先我们要想想是否能用递归来解决&#xff0c;本题也不例外&#xff0c;而递归的关键是找到子问题。 我们首先来看看一棵最简单的树&#xff0c;也就是示例 1。这样的一棵树总共有六条路径&#xf…

docker如何拉取nginx最新镜像并运行

要拉取Docker Hub上的最新Nginx镜像&#xff0c;您可以使用以下命令&#xff1a; docker pull nginx 这个命令会从Docker Hub下载最新版本的Nginx镜像。如果您想要拉取特定版本的Nginx镜像&#xff0c;可以指定版本号&#xff0c;例如&#xff1a; docker pull nginx:1.18.0 拉…

详细分析tcping的基本知识以及用法

目录 前言1. 安装配置2. 基本知识3. Demo 前言 针对ping的基本知识推荐阅读&#xff1a;详细分析ping的基本知识以及常见网络故障的诊断&#xff08;图文解析&#xff09; 1. 安装配置 针对Window的下载如下&#xff1a; 安装路径&#xff1a;tcping官网 下载tcping.exe&a…

《微服务王国的守护者:Spring Cloud Dubbo的奇幻冒险》

5. 经典问题与解决方案 5.3 服务追踪与链路监控 在微服务架构的广袤宇宙中&#xff0c;服务间的调用关系错综复杂&#xff0c;如同一张庞大的星系网络。当一个请求穿越这个星系&#xff0c;经过多个服务节点时&#xff0c;如何追踪它的路径&#xff0c;如何监控整个链路的健康…

VUE3 学习笔记(3):VUE模板理念、属性绑定、条件渲染、列表渲染

准备 1.清空不必要的项目文件 项目/src/assets/ 目录文件清空 项目/src/components/ 目录文件清空 删除main.js 的css引用 App.vue 代码如下 <template> </template> <script>//注意这里默认有一个setup 去掉 </script> 运行一下无错误提示就可以了…

Cohere继Command-R+之后发布大模型Aya-23,性能超越 Gemma、Mistral 等,支持中文

前言 近年来&#xff0c;多语言大模型&#xff08;MLLM&#xff09;发展迅速&#xff0c;但大多数模型的性能依然存在显著差距&#xff0c;尤其是在非英语语言方面表现不佳。为了推动多语言自然语言处理技术的发展&#xff0c;Cohere团队发布了新的多语言指令微调模型家族——…

机器学习预测-CNN手写字识别

介绍 这段代码是使用PyTorch实现的卷积神经网络&#xff08;CNN&#xff09;&#xff0c;用于在MNIST数据集上进行图像分类。让我一步步解释&#xff1a; 导入库&#xff1a;代码导入了必要的库&#xff0c;包括PyTorch&#xff08;torch&#xff09;、神经网络模块&#xff0…

shell脚本实战--批量修改文件名

字符串截取 先来了解一下shell字符串相关操作的变量 # 从开头删除匹配最短 ## 从开头删除匹配最长 % 从结尾削除匹配最短 %% 从结尾删除匹配最长#指定字符内容截取 a*c 匹配开头为a&#xff0c;中间任意个字符&#xff0c;结尾为c的字符串 a*C 匹配…

Java—集合Collection(一)

Java—集合Collection&#xff08;一&#xff09; 一、Collection集合1、方法add、addAll2、声明集合特别注意1&#xff1a;添加对象时需要创建对象类 3、总结4、判断方法4.1、总结 5、删除6、总结7、集合的其他方法8、所有代码演练 存放单个数据内容&#xff0c;声明一个变量&…

netcat一键开始瑞士军刀模式(KALI工具系列六)

目录 1、KALI LINUX简介 2、netcat工具简介 3、在KALI中使用netcat 3.1 目标主机IP&#xff08;win&#xff09; 3.2 KALI的IP 4、命令示例 4.1 测试某IP的端口是否打开 4.2 TCP扫描 4.3 UDP扫描 4.4 端口刺探 4.5 直接扫描 5、即时通信 5.1 单击对话互联 5.2 传…

新能源锂电池行业创业的财富方案,锂电池回收高阶课

课程下载&#xff1a;https://download.csdn.net/download/m0_66047725/89292234 更多资源下载&#xff1a;关注我。 实战攻略 12年锂电池回收行业经验与坑全收录 课程内容&#xff1a; 001-课程介绍.mp4 002-锂电池的全种类认识.mp4 003-废品锂电池到级片粉末价值估算,mp…

Go微服务: Http服务注册在Consul的示例(非Go-Micro)

概述 现在&#xff0c;我们使用consul客户端的api来把Http服务注册到consul上&#xff0c;非Go-Micro的形式其实&#xff0c;consul官方提供了对应的接口调用来实现&#xff0c;golang中的consul/api包对其进行了封装我们使用consul/api来进行展示 目录结构 gitee.com/go-mi…

设计模式7——建造者模式

写文章的初心主要是用来帮助自己快速的回忆这个模式该怎么用&#xff0c;主要是下面的UML图可以起到大作用&#xff0c;在你学习过一遍以后可能会遗忘&#xff0c;忘记了不要紧&#xff0c;只要看一眼UML图就能想起来了。同时也请大家多多指教。 建造者模式&#xff08;Builde…

VMware ESXi 7.0 U3q 发布 - 领先的裸机 Hypervisor

VMware ESXi 7.0 U3q 发布 - 领先的裸机 Hypervisor VMware ESXi 7.0 Update 3 Standard & All Custom Image for ESXi 7.0U3 Install CD 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-esxi-7-u3/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出…