Winbond W25Qxx SPI FLASH 使用示例(基于沁恒CH32V307单片机)
文章目录
- 目的
- 基础说明
- 使用示例
- 总结
目的
Winbond(华邦)的 W25Qxx 系列 SPI FLASH 是比较常用的芯片,这篇文章将演示单片机中通过SPI使用该芯片的操作过程。
本文使用沁恒官方的开发板 (CH32V307-EVT-R1沁恒RISC-V模块MCU赤兔评估板) 进行演示。
基础说明
SPI的基础概念见下面文章:
SPI基础概念:https://blog.csdn.net/Naisu_kun/article/details/118102844
下面的例程中其实真正直接涉及到SPI的也就初始化、读取标志、收发数据几个操作。真正大头的都是根据芯片手册来操作。通常的操作就是主机向芯片写命令,然后根据命令类型紧接着写入或读取数据。
芯片的命令可以参考芯片手册中 INSTRUCTIONS 章节,比如我这里使用的 W25Q32FV 的手册:《W25Q32FV Datasheet》
需要注意的是FLASH写操作的时候,因为物理特性,通常bit可以写0,无法写1,所以对已经写过数据的地址上写入数据前需要先擦除。另外擦除只能按扇区(对于W25Qxx而已一个扇区为4k字节)、块或者全片擦除。写入一次最多可以写入一页(256字节)。
使用示例
下面代码源自沁恒CH32V307官方例程,这里调整了下代码次序和注释:
#include "debug.h"/*此例程通过CH32V307的SPI1与W25Q系列Flash进行通讯,引脚连接如下:MCU Flash 说明PA2 CS 片选引脚(低电平选中)PA5 CLK 时钟PA7(SPI1_MOSI) DI 主机发送从机接收PA6(SPI1_MISO) DO 从机发送主机接收GND GND 地3.3V VCC 电源3.3V HOLD(RESET) 复位*//******************** 以下为SPI1基本操作函数声明 ********************/
void SPI1_Init(void);
u8 SPI1_ReadWriteByte(u8 TxData);/******************** 以下为华邦Falsh ID ********************/
#define W25Q80 0XEF13
#define W25Q16 0XEF14
#define W25Q32 0XEF15
#define W25Q64 0XEF16
#define W25Q128 0XEF17/******************** 以下为华邦Falsh操作指令 ********************/
#define W25X_WriteEnable 0x06
#define W25X_WriteDisable 0x04
#define W25X_ReadStatusReg 0x05
#define W25X_WriteStatusReg 0x01
#define W25X_ReadData 0x03
#define W25X_FastReadData 0x0B
#define W25X_FastReadDual 0x3B
#define W25X_PageProgram 0x02
#define W25X_BlockErase 0xD8
#define W25X_SectorErase 0x20
#define W25X_ChipErase 0xC7
#define W25X_PowerDown 0xB9
#define W25X_ReleasePowerDown 0xAB
#define W25X_DeviceID 0xAB
#define W25X_ManufactDeviceID 0x90
#define W25X_JedecDeviceID 0x9F/******************** 以下为Falsh操作函数声明 ********************/
u8 SPI_Flash_ReadSR(void);
void SPI_FLASH_Write_SR(u8 sr);
void SPI_Flash_Wait_Busy(void);
void SPI_FLASH_Write_Enable(void);
void SPI_FLASH_Write_Disable(void);
u16 SPI_Flash_ReadID(void);
void SPI_Flash_Erase_Sector(u32 Dst_Addr);
void SPI_Flash_Read(u8 *pBuffer, u32 ReadAddr, u16 size);
void SPI_Flash_Write_Page(u8 *pBuffer, u32 WriteAddr, u16 size);
void SPI_Flash_Write_NoCheck(u8 *pBuffer, u32 WriteAddr, u16 size);
void SPI_Flash_Write(u8 *pBuffer, u32 WriteAddr, u16 size);
void SPI_Flash_Erase_Chip(void);
void SPI_Flash_PowerDown(void);
void SPI_Flash_WAKEUP(void);/******************** 以下为主函数 ********************/int main(void) {NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);SystemCoreClockUpdate();Delay_Init();USART_Printf_Init(115200);printf("SystemClk:%d\r\n", SystemCoreClock);printf("\r\n");SPI1_Init(); // 初始化SPI1u16 Flash_Model = SPI_Flash_ReadID(); // 读取Flash IDswitch(Flash_Model) {case W25Q80: printf("W25Q80 OK!\r\n"); break;case W25Q16: printf("W25Q16 OK!\r\n"); break;case W25Q32: printf("W25Q32 OK!\r\n"); break;case W25Q64: printf("W25Q64 OK!\r\n"); break;case W25Q128: printf("W25Q128 OK!\r\n"); break;default: printf("Fail!\r\n"); break;}const u8 msg[] = "CH32F103 SPI FLASH W25Qxx";const int size = sizeof(msg);u8 buffer[size];printf("Start Erase W25Qxx....\r\n");SPI_Flash_Erase_Sector(0); // 擦除扇区printf("W25Qxx Erase Finished!\r\n");Delay_Ms(500);printf("Start Read W25Qxx....\r\n");SPI_Flash_Read(buffer, 0x0, size); // 读取数据printf("%s\r\n", buffer);Delay_Ms(500);printf("Start Write W25Qxx....\r\n");SPI_Flash_Write((u8*)msg, 0, size); // 写入数据printf("W25Qxx Write Finished!\r\n");Delay_Ms(500);printf("Start Read W25Qxx....\r\n");SPI_Flash_Read(buffer, 0x0, size); // 再次读取数据printf("%s\r\n", buffer);while(1) {}
}/******************** 以下为SPI1基本操作函数实现 ********************/// 初始化SPI1
void SPI1_Init(void) {// 初始化时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);// 初始化GPIOGPIO_InitTypeDef GPIO_InitStructure = { 0 };GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_SetBits(GPIOA, GPIO_Pin_2); // CS引脚输出高电平,不选中Flash芯片GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(GPIOA, &GPIO_InitStructure);// 初始化SPI1SPI_InitTypeDef SPI_InitStructure = { 0 };SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // 全双工模式SPI_InitStructure.SPI_Mode = SPI_Mode_Master; // 作为主机SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 8bit数据SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; // 时钟不通讯时高电平SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; // 在第二个边缘进行采样(结合上条,这里相当于使用Mode3)SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // 片选引脚手动控制SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; // SPI外设时钟源4分频SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // 传输时高比特在前SPI_InitStructure.SPI_CRCPolynomial = 7;SPI_Init(SPI1, &SPI_InitStructure); // 初始化SPI1设置SPI_Cmd(SPI1, ENABLE); // 启动SPI1
}// 很多库中SPI的读和写是封装在一起的,根据需要选择写或读或读写
// 在SPI读数据时需要注意,这时候虽然数据是从机发送的,但时钟还是主机给出的
// 所以往往读数据时主机还需要通过写数据(任意均可)来给出时钟信号
// SPI1_Write_Read(0x00) 经常用来表示通过写一个字节来发送时钟从而接收从机一个字节数据
u8 SPI1_ReadWriteByte(u8 TxData) {u8 i = 0;while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) { // 等待SPI1可写i++; if(i > 200) return 0;}SPI_I2S_SendData(SPI1, TxData); // 写数据i = 0;while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET) { // 等待SPI1可读i++; if(i > 200) return 0;}return SPI_I2S_ReceiveData(SPI1); // 读数据
}/******************** 以下为Falsh操作函数实现 ********************/// 读状态寄存器
// BIT7 6 5 4 3 2 1 0
// SPR RV TB BP2 BP1 BP0 WEL BUSY
u8 SPI_Flash_ReadSR(void) {u8 byte = 0;GPIO_WriteBit(GPIOA, GPIO_Pin_2, 0); // 选中芯片SPI1_ReadWriteByte(W25X_ReadStatusReg); // 写命令byte = SPI1_ReadWriteByte(0x00); // 读数据GPIO_WriteBit(GPIOA, GPIO_Pin_2, 1); // 释放芯片return byte;
}// 写状态寄存器
void SPI_FLASH_Write_SR(u8 sr) {GPIO_WriteBit(GPIOA, GPIO_Pin_2, 0); // 选中芯片SPI1_ReadWriteByte(W25X_WriteStatusReg); // 写命令SPI1_ReadWriteByte(sr); // 写数据GPIO_WriteBit(GPIOA, GPIO_Pin_2, 1); // 释放芯片
}// 等待Flash空闲
void SPI_Flash_Wait_Busy(void) {while((SPI_Flash_ReadSR() & 0x01) == 0x01) { }
}// 使能写入
void SPI_FLASH_Write_Enable(void) {GPIO_WriteBit(GPIOA, GPIO_Pin_2, 0); // 选中芯片SPI1_ReadWriteByte(W25X_WriteEnable); // 写命令GPIO_WriteBit(GPIOA, GPIO_Pin_2, 1); // 释放芯片
}// 失能写入
void SPI_FLASH_Write_Disable(void) {GPIO_WriteBit(GPIOA, GPIO_Pin_2, 0); // 选中芯片SPI1_ReadWriteByte(W25X_WriteDisable); // 写命令GPIO_WriteBit(GPIOA, GPIO_Pin_2, 1); // 释放芯片
}// 读取芯片ID
u16 SPI_Flash_ReadID(void) {GPIO_WriteBit(GPIOA, GPIO_Pin_2, 0); // 选中芯片SPI1_ReadWriteByte(W25X_ManufactDeviceID); // 写命令SPI1_ReadWriteByte(0x00); // 通过发送数据产生时钟等待数据可读SPI1_ReadWriteByte(0x00); // 同上SPI1_ReadWriteByte(0x00); // 同上u16 Temp = 0;Temp |= SPI1_ReadWriteByte(0xFF) << 8; // 读数据Temp |= SPI1_ReadWriteByte(0xFF); // 同上GPIO_WriteBit(GPIOA, GPIO_Pin_2, 1); // 释放芯片return Temp;
}// 擦除扇区
void SPI_Flash_Erase_Sector(u32 Dst_Addr) {SPI_FLASH_Write_Enable(); // 使能写入SPI_Flash_Wait_Busy(); // 等待操作完成GPIO_WriteBit(GPIOA, GPIO_Pin_2, 0); // 选中芯片SPI1_ReadWriteByte(W25X_SectorErase); // 发送命令Dst_Addr *= 4096; // 每个扇区大小为4kSPI1_ReadWriteByte((u8)((Dst_Addr) >> 16)); // 写扇区地址SPI1_ReadWriteByte((u8)((Dst_Addr) >> 8)); // 同上SPI1_ReadWriteByte((u8)Dst_Addr); // 同上GPIO_WriteBit(GPIOA, GPIO_Pin_2, 1); // 释放芯片SPI_Flash_Wait_Busy(); // 等待操作完成
}// 读取数据
void SPI_Flash_Read(u8 *pBuffer, u32 ReadAddr, u16 size) {GPIO_WriteBit(GPIOA, GPIO_Pin_2, 0); // 选中芯片SPI1_ReadWriteByte(W25X_ReadData); // 写命令SPI1_ReadWriteByte((u8)((ReadAddr) >> 16)); // 写地址SPI1_ReadWriteByte((u8)((ReadAddr) >> 8)); // 同上SPI1_ReadWriteByte((u8)ReadAddr);for(u16 i = 0; i < size; i++) {pBuffer[i] = SPI1_ReadWriteByte(0x00); // 读数据}GPIO_WriteBit(GPIOA, GPIO_Pin_2, 1); // 释放芯片
}// 按页方式写入
void SPI_Flash_Write_Page(u8 *pBuffer, u32 WriteAddr, u16 size) {SPI_FLASH_Write_Enable(); // 使能写入GPIO_WriteBit(GPIOA, GPIO_Pin_2, 0); // 选中芯片SPI1_ReadWriteByte(W25X_PageProgram); // 写命令SPI1_ReadWriteByte((u8)((WriteAddr) >> 16)); // 写地址SPI1_ReadWriteByte((u8)((WriteAddr) >> 8)); // 同上SPI1_ReadWriteByte((u8)WriteAddr); // 同上for(u16 i = 0; i < size; i++) {SPI1_ReadWriteByte(pBuffer[i]); // 写数据}GPIO_WriteBit(GPIOA, GPIO_Pin_2, 1); // 释放芯片SPI_Flash_Wait_Busy(); // 等待操作完成
}// 写入数据(写入前需要先擦除)
void SPI_Flash_Write_NoCheck(u8 *pBuffer, u32 WriteAddr, u16 size) {// 一次最多只能写一页(256字节u16 pageremain = 256 - WriteAddr % 256;if(size <= pageremain) pageremain = size;while(1) {SPI_Flash_Write_Page(pBuffer, WriteAddr, pageremain);if(size == pageremain) { break; }else {pBuffer += pageremain;WriteAddr += pageremain;size -= pageremain;if(size > 256) pageremain = 256;else pageremain = size;}}
}// 写入数据
void SPI_Flash_Write(u8 *pBuffer, u32 WriteAddr, u16 size) {u8 buffer[4096];u32 secpos;u16 secoff;u16 secremain;u16 i;secpos = WriteAddr / 4096;secoff = WriteAddr % 4096;secremain = 4096 - secoff;if(size <= secremain) secremain = size;while(1) {SPI_Flash_Read(buffer, secpos * 4096, 4096); // 读取数据for(i = 0; i < secremain; i++) {if(buffer[secoff + i] != 0XFF) break; // 检查数据}if(i < secremain) { // 需要先擦除SPI_Flash_Erase_Sector(secpos);for(i = 0; i < secremain; i++) { // 擦除数据buffer[i + secoff] = pBuffer[i]; // 将要写入数据填充到缓存中}SPI_Flash_Write_NoCheck(buffer, secpos * 4096, 4096); // 写数据} else { // 不需要擦除SPI_Flash_Write_NoCheck(pBuffer, WriteAddr, secremain); // 直接写数据}if(size == secremain) { break; }else {secpos++;secoff = 0;pBuffer += secremain;WriteAddr += secremain;size -= secremain;if(size > 4096) { secremain = 4096; }else { secremain = size; }}}
}// 全片擦除
void SPI_Flash_Erase_Chip(void) {SPI_FLASH_Write_Enable(); // 使能写入SPI_Flash_Wait_Busy(); // 等待操作完成GPIO_WriteBit(GPIOA, GPIO_Pin_2, 0); // 选中芯片SPI1_ReadWriteByte(W25X_ChipErase); // 写命令GPIO_WriteBit(GPIOA, GPIO_Pin_2, 1); // 释放芯片SPI_Flash_Wait_Busy(); // 等待操作完成
}// 进入省电模式
void SPI_Flash_PowerDown(void) {GPIO_WriteBit(GPIOA, GPIO_Pin_2, 0); // 选中芯片SPI1_ReadWriteByte(W25X_PowerDown); // 写命令GPIO_WriteBit(GPIOA, GPIO_Pin_2, 1); // 释放芯片Delay_Us(3); // 等待操作完成
}// 从省电模式唤醒
void SPI_Flash_WAKEUP(void) {GPIO_WriteBit(GPIOA, GPIO_Pin_2, 0); // 选中芯片SPI1_ReadWriteByte(W25X_ReleasePowerDown); // 写命令GPIO_WriteBit(GPIOA, GPIO_Pin_2, 1); // 释放芯片Delay_Us(3); // 等待操作完成
}
上面代码演示结果如下:
总结
Winbond(华邦)的 W25Qxx 系列 SPI FLASH 芯片使用上是比较简单的。上面的代码只是测试使用的代码,可能存在很多不严谨或不完善的地方。实际使用中可以自行调整并抽象出相关功能使之可以适用各类平台。
相关文章:

Winbond W25Qxx SPI FLASH 使用示例(基于沁恒CH32V307单片机)
文章目录目的基础说明使用示例总结目的 Winbond(华邦)的 W25Qxx 系列 SPI FLASH 是比较常用的芯片,这篇文章将演示单片机中通过SPI使用该芯片的操作过程。 本文使用沁恒官方的开发板 (CH32V307-EVT-R1沁恒RISC-V模块MCU赤兔评估…...

贪心算法的原理以及应用
文章目录0、概念0.1.定义0.2.特征0.3.步骤0.4.适用1、与动态规划的联系1.1.区别1.2.联系2、例子3、总结4、引用0、概念 0.1.定义 贪心算法(greedy algorithm ,又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是…...

WebRTC拥塞控制原理之一基本介绍
1 基本原理 WebRTC的拥塞控制模块使用的是基于TCP的拥塞控制算法。它是根据网络带宽和延迟等信息来自适应地调整传输速率的。 具体来说,该模块采用的是基于RFC 3550中的延迟抖动调整算法的改进版本。该算法实施的基本原理是在传输的过程中定期探测网络的质量和延迟…...

选择 .NET 的 n 个理由
自从我们启动快速发展的 .NET 开源和跨平台项目以来,.NET 发生了很大变化。我们重新思考并完善了该平台,添加了专为性能和安全性而设计的新低级功能,以及以生产力为中心的高级功能。Span<T>、硬件内在函数和可为空的引用类型都是示例。…...

spark第三章:工程化代码
系列文章目录 spark第一章:环境安装 spark第二章:sparkcore实例 spark第三章:工程化代码 文章目录系列文章目录前言一、三层架构二、拆分WordCount1.三层拆分2.代码抽取总结前言 我们上一次博客,完成了一些案例的练习࿰…...

Vue实战【封装一个简单的列表组件,实现增删改查】
文章目录🌟前言🌟table组件封装🌟父组件(展示表格的页面)🌟控制台查看父子组件通信是否成功🌟Vue2父子组件传递参数🌟写在最后🌟JSON包里写函数,关注博主不迷…...

微前端(无界)
前言:微前端已经是一个非常成熟的领域了,但开发者不管采用哪个现有方案,在适配成本、样式隔离、运行性能、页面白屏、子应用通信、子应用保活、多应用激活、vite 框架支持、应用共享等用户核心诉求都或存在问题,或无法提供支持。本…...
强烈推荐:0基础入门网安必备《网络安全知识图谱》
蚁景网安学院一直专注于网安实战技能培养,提供全方位的网安安全学习解决方案。我们集聚专业网安技术大佬资源,倾力打造了这本更全面更系统的“网络安全知识图谱”,让大家在网络安全学习路上不迷茫。 在这份网安技能地图册里,我们对…...

网络技术与应用概论(上)——“计算机网络”
各位CSDN的uu们你们好呀,今天,小雅兰的内容依旧是计算机网络的一些知识点噢,下面,让我们进入计算机网络的世界吧 网络内涵 网络特征 网络定义 互联网发展过程 从ARPA网络到Internet 从低速互联网到高速互联网 从数据结构到统一网…...

JAVASE/封装、继承、多态
博客制作不易,欢迎各位点赞👍收藏⭐关注前言在学习面向对象编程语言时,封装、继承、多态则是我们必须学习和使用的三大特征。本文通过举例,说明了该三大特征的基本权限特点。一、访问限定符范围private默认权限protectedpublic同一…...

SpringBoot ElasticSearch 【SpringBoot系列16】
SpringCloud 大型系列课程正在制作中,欢迎大家关注与提意见。 程序员每天的CV 与 板砖,也要知其所以然,本系列课程可以帮助初学者学习 SpringBooot 项目开发 与 SpringCloud 微服务系列项目开发 elasticsearch是一款非常强大的开源搜索引擎&a…...

Virtual box磁盘大小调整操作
Virtual box磁盘大小调整操作环境说明思路操作1、挂载要压缩的硬盘到 ~/data2、填充 0 文件3、删除 全是0空文件4、虚拟机关机5、在windows环境下用VBoxManage.exe 进行压缩硬盘加大环境说明 主机 windows 虚拟机 ubuntu 分配了 80G 的硬盘,现在已经占用 80 G 了。…...

MySQL注入秘籍【上篇】
MySQL注入秘籍【上篇】1.数据库敏感信息常用语句2.联合(UNION)查询注入3.报错注入原理常见报错注入函数1.数据库敏感信息常用语句 获取数据库版本信息 select version(); select innodb_version;获取当前用户 select user();获取当前数据库 select database();数…...

简单三步解决动态规划难题,记好这三步,动态规划就不难
目录一、简单的一维DP剑指 Offer 10- I. 斐波那契数列1、三板斧解决问题2、优雅的解决问题剑指 Offer 63 股票的最大利润1、三板斧解决问题2、优雅的解决问题二、进阶的二维DP剑指offer47 礼物的最大价值1、三板斧解决问题2、优雅的解决问题编辑距离1、三板斧解决问题2、优雅的…...

算法进阶指南打卡
文章目录 基本算法 位运算递推与递归前缀和与差分二分排序倍增贪心总结与练习基本数据结构 栈队列链表与邻接表Hash字符串Tire二叉堆总结与练习搜索 树与图的遍历深度优先搜索剪枝迭代加深广度优先搜索广度变形A*IDA*总结与练习数学知识 质数约数同余矩阵乘法高斯消元与线性空…...

Chapter6.2:其他根轨迹及综合实例分析
该系列博客主要讲述Matlab软件在自动控制方面的应用,如无自动控制理论基础,请先学习自动控制系列博文,该系列博客不再详细讲解自动控制理论知识。 自动控制理论基础相关链接:https://blog.csdn.net/qq_39032096/category_10287468…...

3. 无重复字符的最长子串——滑动窗口
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。 示例 2: 输入: s "bbbbb" 输出: 1 解释: 因为无…...

ChatGPT研究分享:机器第一次开始理解人类世界
0、为什么会对ChatGPT感兴趣一开始,我对ChatGPT是没什么关注的,无非就是有更大的数据集,完成了更大规模的计算,所以能够回答更多的问题。但后来了解到几个案例,开始觉得这个事情并不简单。我先分别列举出来,…...

可换皮肤的Qt登录界面
⭐️我叫忆_恒心,一名喜欢书写博客的在读研究生👨🎓。 如果觉得本文能帮到您,麻烦点个赞👍呗! 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧,喜欢的小伙伴给个三连支持一下呗。👍⭐️❤️ 可换皮肤的Qt登录界面 QSS的学习笔记 快…...

Spring的常见问题汇总
一、bean实例化1、构造方法底层是无参构造方法来new的对象。2、静态工厂实例化Bean实质上就是:创建一个静态工厂类,然后调用静态工厂类的静态方法,来创建对象。3、实例工厂与FactoryBean实质上就是:创建一个工厂类,工厂…...

yolov8训练筷子点数数据集
序言 yolov8发布这么久了,一直没有机会尝试一下,今天用之前自己制作的筷子点数数据集进行训练,并且记录一下使用过程以及一些常见的操作方式,供以后翻阅。 一、环境准备 yolov8的训练相对于之前的yolov5简单了很多,…...

使用 Python 从点云生成 3D 网格
从点云生成 3D 网格的最快方法 已经用 Python 编写了几个实现来从点云中获取网格。它们中的大多数的问题在于它们意味着设置许多难以调整的参数,尤其是在不是 3D 数据处理专家的情况下。在这个简短的指南中,我想展示从点云生成网格的最快和最简单的过程。…...

vue使用split()将字符串分割数组join()将数组转字符串reverse()将数组反转
1.split() 将字符串切割成数组 const str Hello Vue2 Vue3 console.log(str.split()) console.log(str.split()) console.log(str.split( )) console.log(str.split( , 2)) console.log(str.split( , 6))输出如下 1.split()不传参数默认整个字符串作为数组的一个元素…...

队列实现及leetcode相关OJ题
上一篇写的是栈这一篇分享队列实现及其与队列相关OJ题 文章目录一、队列概念及实现二、队列源码三、leetcode相关OJ一、队列概念及实现 1、队列概念 队列同栈一样也是一种特殊的数据结构,遵循先进先出的原则,例如:想象在独木桥上走着的人&am…...

【Log4j2远程命令执行复现CVE-2021-12-09】
目录 一、前言 二、漏洞环境构建 三、复现过程 一、前言 Log4j2是基于log4j这个java日志处理组件进行二次开发和改进而来的。也是目前最常用的日志框架之一,在之前的博客中(http://t.csdn.cn/z9um4)我们阐述了漏洞的原理和大致的利用方…...

Jenkins 平台搭建 | 为 Jenkins 配置 nginx 反向代理
以 Centos7 系统为例,详细记录一下 Jenkins 搭建流程。 参考官网:https://www.jenkins.io/doc/book/installing/linux/#red-hat-centos Install Jenkins 从 redhat-stable yum 存储库中安装 LTS(长期支持) 版本,该版…...

【云原生】Docker 架构及工作原理
一、Docker 概述二、Client 客户端三、Docker 引擎四、Image 镜像五、Container 容器六、镜像分层可写的容器层七、Volume 数据卷八、Registry 注册中心九、总结一、Docker 概述 Docker 是一个开发、发布和运行应用程序的开放平台。Docker使您能够将应用程序与基础架构分离&am…...

【Java 】Java NIO 底层原理
文章目录1、 Java IO读写原理1.1 内核缓冲与进程缓冲区1.2 java IO读写的底层流程2、 四种主要的IO模型3、 同步阻塞IO(Blocking IO)4、 同步非阻塞NIO(None Blocking IO)5、 IO多路复用模型(I/O multiplexing)6、 异步…...

Vue基础27之VueUI组件
Vue基础27Vue UI组件库移动端常用 UI 组件库PC 端常用 UI 组件库Element-ui插件基本使用安装引入并使用main.jsApp.vue按需引入安装 babel-plugin-componentbabel.config.jsmain.jsApp.vueVue UI组件库 移动端常用 UI 组件库 Vant https://youzan.github.io/vant Cube UI htt…...

第35篇:Java代码规范全面总结
编程规范目的是帮助我们编写出简洁、可维护、可靠、可测试、高效、可移植的代码,提高产品代码的质量。 适当的规范和标准绝不是消灭代码内容的创造性、优雅性,而是限制过度个性化, 以一种普遍认可的统一方式一起做事,提升协作效率,降低沟通成本。 代码的字里行间流淌的是软…...