STM32单片机入门学习——第38节: [11-3] 软件SPI读写W25Q64
写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难,但我还是想去做!
本文写于:2025.04.16
STM32开发板学习——第一节: [1-1]课程简介
- 前言
- 开发板说明
- 引用
- 解答和科普
- 一、硬件接线
- 二、软件实现SPI
- 三、W25Q64驱动层
- 问题
- 总结
前言
本次笔记是用来记录我的学习过程,同时把我需要的困难和思考记下来,有助于我的学习,同时也作为一种习惯,可以督促我学习,是一个激励自己的过程,让我们开始32单片机的学习之路。
欢迎大家给我提意见,能给我的嵌入式之旅提供方向和路线,现在作为小白,我就先学习32单片机了,就跟着B站上的江协科技开始学习了.
在这里会记录下江协科技32单片机开发板的配套视频教程所作的实验和学习笔记内容,因为我之前有一个开发板,我大概率会用我的板子模仿着来做.让我们一起加油!
另外为了增强我的学习效果:每次笔记把我不知道或者问题在后面提出来,再下一篇开头作为解答!
开发板说明
本人采用的是慧净的开发板,因为这个板子是我N年前就买的板子,索性就拿来用了。另外我也购买了江科大的学习套间。
原理图如下
1、开发板原理图
2、STM32F103C6和51对比
3、STM32F103C6核心板
视频中的都用这个开发板来实现,如果有资源就利用起来。另外也计划实现江协科技的套件。
下图是实物图
引用
【STM32入门教程-2023版 细致讲解 中文字幕】
还参考了下图中的书籍:
STM32库开发实战指南:基于STM32F103(第2版)
数据手册
解答和科普
一、硬件接线
CS片选接在了PA4,DO从机输出接在了PA6,CLK接在了PA5,DI从机输入,接在PA7.
程序整体框架:先建一个MySPI模块,这个模块主要包含通信引脚封装、初始化,以及SPI通信的3个拼图,起始、终止和交换一个字节,这是SPI通信层的内容,然后基于SPI层,再建一个W25Q64的模块,在这个模块里,调用底层SPI的拼图,来拼接各种指令和功能的完整时序,比如写使能、擦除、页编程、读数据等,这一块可以叫作W25Q64的硬件驱动层,最后在主函数里,调用驱动层的函数,完成想要实现的功能。
二、软件实现SPI
初始化:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4| GPIO_Pin_5|GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);
把置引脚高低电平的函数都封装换个名字:
void MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);
}
之后还有两个输出引脚,复制一下,
void MySPI_W_SCK(uint8_t BitValue)
{GPIO_WriteBit(GPIOA,GPIO_Pin_5,(BitAction)BitValue);
}void MySPI_W_MOSI(uint8_t BitValue)
{GPIO_WriteBit(GPIOA,GPIO_Pin_7,(BitAction)BitValue);
}
uint8_t MySPI_R_MISO(void)
{return GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6);
}
当然也可以用宏定义来完成相同的功能。这里用函数包装一下,之后如果要移植到其他类型单片机里,或者要加一个延迟,会更方便一些。这里SPI的速度非常快,所以操作完引脚之后,暂时就不需要加延时了。到这里引脚的封装就完成了。
置一下初始化之后引脚的默认电平, 那在初始化GPIO之后,SS引脚应该置位高电平,
MySPI_W_SS(1); //默认不选中从机
MySPI_W_SCK(0); //计划使用SPI模式0,所以默认是低电平
void MySPI_Start(void)
{MySPI_W_SS(0);
}void MySPI_Stop(void)
{MySPI_W_SS(1);
}
接下来写SPI的3个时序基本单元:
这里图上画的是,SS下降沿和数据移出去是同时发生的,包括后面,这个SCK下降沿和数据移出也是用时的,但这并不代表我们程序上要同时执行两条代码,这里实际上是有先后顺序的,是先SS下降沿或SCK下降沿,再数据移出,这个下降沿是触发数据移出这个动作的条件,现有了下降沿,之后才会有数据移出这个动作,对于硬件SPI来说,由于使用了硬件的移位寄存器电路,所以这两个动作几乎是同时发生的,而对于软件SPI来说,由于程序是一条条执行的,不可能同时完成两个动作,所以软件SPI,这就直接躺平,直接看成先后执行的逻辑,流程就是;先SS下降沿,再移出数据,再SCK上升沿,再移入数据,再SCK下降沿,再移出数据,以这个具有先后顺序的流程来进行,这样才能对于一条条依次执行的程序代码。
SS下降沿之后,第一步,主机和从机同时移出数据,就是主机移出我的数据最高位放到MOSI上,从机移出它的数据最高位放到MISO上,当然MISO数据变化,是从机的事,不归我们管,所以这里第一步是写MOSI,然后上升沿,从机会自动把MOSI的数据读走,主机的任务就是把刚才放到MISO上的数据读进来,所以这里要读MISO,接着就是SCK产生下降沿,主机和从机移出下一位,所以这里,之后是写SCK,为0,产生下降沿,下降沿后,主机的任务是,移出B6,
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t ByteReceive=0x00;MySPI_W_MOSI(ByteSend&0x80);MySPI_W_SCK(1);if(MySPI_R_MISO()==1){ByteReceive|=0x80;}MySPI_W_SCK(0);MySPI_W_MOSI(ByteSend&0x40);return ByteReceive;
}
显然要用for循环。
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t i,ByteReceive=0x00;for(i=0;i<8;i++){MySPI_W_MOSI(ByteSend&(0x80>>i));MySPI_W_SCK(1);if(MySPI_R_MISO()==1){ByteReceive|=(0x80>>i);}MySPI_W_SCK(0);}return ByteReceive;
}
通过掩码,依次挑出每一位进行操作,这种不会改变传入变量本身,之后还想用ByteSend,可以继续使用。
第二种方法,是用移位数据本身来进行的操作,好处就是效率高,但是ByteSend这个数据,在移位工程中改变了,for循环执行完,原始传入的参数就没有了。
uint8_t MySPI_SwapByte_1 (uint8_t ByteSend)
{uint8_t i;for(i=0;i<8;i++){MySPI_W_MOSI(ByteSend&0x80);ByteSend<<=1; //最低位空出来了0MySPI_W_SCK(1);if(MySPI_R_MISO()==1){ByteSend|=0x01;}MySPI_W_SCK(0);}return ByteSend;
}
模式1:
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t i,ByteReceive=0x00;for(i=0;i<8;i++){MySPI_W_SCK(1);MySPI_W_MOSI(ByteSend&(0x80>>i));MySPI_W_SCK(0);if(MySPI_R_MISO()==1){ByteReceive|=(0x80>>i);}}return ByteReceive;
}
模式3:
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t i,ByteReceive=0x00;for(i=0;i<8;i++){MySPI_W_SCK(0);MySPI_W_MOSI(ByteSend&(0x80>>i));MySPI_W_SCK(1);if(MySPI_R_MISO()==1){ByteReceive|=(0x80>>i);}}return ByteReceive;
}
模式2:
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t i,ByteReceive=0x00;for(i=0;i<8;i++){MySPI_W_MOSI(ByteSend&(0x80>>i));MySPI_W_SCK(0);if(MySPI_R_MISO()==1){ByteReceive|=(0x80>>i);}MySPI_W_SCK(1);}return ByteReceive;
}
把这些函数声明者.H文件。
三、W25Q64驱动层
然后在SPI通信基础上建立W25Q64的驱动层。
#include "stm32f10x.h" // Device header
#include "MySPI.h"
void W25Q64_Init(void)
{MySPI_Init();
}
接下来拼接完整的时序,先读取ID号测试:起始,先交换发送指令9F,随后连续交换接收3个字节,停止,我咋知道那个是交换发送,那个是交换接收呢,那连续接收三个字节,第一个字节是厂商,表示那个厂家生产的,后两个字节是设备ID,其中设备高8位,表示存储器类型,低8位表示容量。
由于计划这个函数是有两个返回值的,所以还是使用指针来实现多返回值。写上两个输出参数。
连续调用连个相同的参数,返回值是不一样的;因为是在通信,通信是有时序的,不同时间调用相同函数,它的意义是不一样的。
void W25Q64_ReadID(uint8_t *MID ,uint16_t *DID)
{MySPI_Start();MySPI_SwapByte(0x9F); //读ID号的指令,抛玉引砖,返回值没有意义没用*MID = MySPI_SwapByte(0xFF); //0XFF没有意义,抛砖引玉,就是为了把对面有意义的数据置换过来*DID = MySPI_SwapByte(0xFF); // 读取设备ID高8位*DID <<=8;*DID |= MySPI_SwapByte(0xFF); //获得16位的DIDMySPI_Stop();
}
指令码单独放一下,然后写指令
void W25Q64_WriteEnable(void)
{MySPI_Start();MySPI_SwapByte(W25Q64_WRITE_ENABLE);MySPI_Stop();
}
最好实现一个等待BUSY为0的函数
while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE)& 0x01)==0x01); //用掩码取出低位
如果BUSY为1,就会进入循环,再读出一次状态寄存器,继续判断,直到BUSY为0,挑出判断,这就是利用连续读出状态寄存器。实现等待BUSY的功能。最后STOP,终止时序。
void W25Q64_WaiteBusy(void)
{uint32_t TimeOut;MySPI_Start();MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);TimeOut=10000;while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE)& 0x01)==0x01) //用掩码取出低位{TimeOut--;if(TimeOut==0){break;}}MySPI_Stop();
}
页编程的函数:先发一个指令码,再发3个字节的地址,最后发数据,这里的发数据是发送方向的。
void W25Q64_PageProgram(uint32_t Address,uint8_t *DataArray,uint16_t Count)
{uint16_t i;MySPI_Start();MySPI_SwapByte(W25Q64_PAGE_PROGRAM);MySPI_SwapByte(Address>>16); //0x123456 变为 0x12MySPI_SwapByte(Address>>8); //0x123456 变为 0x1234 高位自动舍去就是0x34 MySPI_SwapByte(Address); //0x123456 高位自动舍去就是0x56 for(i=0;i<Count;i++){MySPI_SwapByte(DataArray[i]);}MySPI_Stop();
}
擦除:扇区
void W25Q64_SectorErase(uint32_t Address)
{MySPI_Start();MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);MySPI_SwapByte(Address>>16); //0x123456 变为 0x12MySPI_SwapByte(Address>>8); //0x123456 变为 0x1234 高位自动舍去就是0x34 MySPI_SwapByte(Address);MySPI_Stop();
}
这里写了dummy你就发个FF就行了。
读出数据:
void W25Q64_ReadData(uint32_t Address,uint8_t *DataArray,uint32_t Count)
{uint32_t i;MySPI_Start();MySPI_SwapByte(W25Q64_READ_DATA);MySPI_SwapByte(Address>>16); //0x123456 变为 0x12MySPI_SwapByte(Address>>8); //0x123456 变为 0x1234 高位自动舍去就是0x34 MySPI_SwapByte(Address); for(i=0;i<Count;i++){DataArray[i]=MySPI_SwapByte(W25Q64_DUMMY_BYTE);}MySPI_Stop();}
等待:事前等待和事后等待,事前等待的话读写操作都能等待。
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.h"
#include "OLED.h"
#include "W25Q64.h"uint8_t MID;
uint16_t DID;
uint8_t ArrayWrite[]={0x55,0x66,0x77,0x88};
uint8_t ArrayRead[4];int main(void)
{OLED_Init();W25Q64_Init();OLED_ShowString(1,1,"MID: DID:");OLED_ShowString(2,1,"W:");OLED_ShowString(3,1,"R:");W25Q64_ReadID(&MID,&DID);OLED_ShowHexNum(1,5 ,MID,2);OLED_ShowHexNum(1,12 ,DID,4);// W25Q64_SectorErase(0x000000); //最好对齐起始地址W25Q64_PageProgram(0x000000,ArrayWrite,4);W25Q64_ReadData(0x000000,ArrayRead,4);OLED_ShowHexNum(2,3,ArrayWrite[0],2);OLED_ShowHexNum(2,6,ArrayWrite[1],2);OLED_ShowHexNum(2,9,ArrayWrite[2],2);OLED_ShowHexNum(2,12,ArrayWrite[3],2);OLED_ShowHexNum(3,3,ArrayRead[0],2);OLED_ShowHexNum(3,6,ArrayRead[1],2);OLED_ShowHexNum(3,9,ArrayRead[2],2);OLED_ShowHexNum(3,12,ArrayRead[3],2);while(1){}
}
测试
擦除为FF
写入ABCD
不擦除写55 66 77 88
因为只能1变0.按&
页地址的范围:xxxx00 到xxxxFF,
W25Q64_PageProgram(0x0000FF,ArrayWrite,4);
说明写入并没有跨页,这个55写入到地址0FF,后面的66并没有跨页到下一个地址100,而是,返回页首000了,又因为读取时能跨页的,所以这三个FF是第二页的数据,擦除默认是FF,看000是不是写入覆盖了。
自己从软件上,分批次写入,先计算,这个数组总共要跨多少页,然后该擦除的擦除,最后再分批次,一页一写,这个操作可以封装成一个函数,之后调用封装的函数就可以跨页连续写入了,这个功能就自己写。
问题
总结
本节课主要学习了软件SPI读写W25Q64,先完成了底层软件SPI,手动翻转电平实现SPI实现。而且还用来函数用来封装引脚电平封装,更容易进行移植,后在SPI的基础上完成了对W25Q64的书写。完成了软件SPI对W25Q64的功能。
相关文章:
STM32单片机入门学习——第38节: [11-3] 软件SPI读写W25Q64
写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难,但我还是想去做! 本文写于:2025.04.16 STM32开发板学习——第一节: [1-1]课程简介 前言开发板说明引用解答和…...
使用阿里云创建公司官网(使用wordpress)
安装 LNMP 不通的lnmp版本 https://lnmp.org/download.html wget http://soft.vpser.net/lnmp/lnmp2.1.tar.gz -cO lnmp2.1.tar.gztar zxf lnmp2.1.tar.gz && cd lnmp2.1 && ./install.sh lnmp数据库选5.7 选好数据库,会让你设置数据库 root 用户…...
Python程序结构深度解析:顺序结构与对象布尔值的底层逻辑与应用
一、程序结构的三大基石 在计算机科学领域,任何复杂的算法都可以分解为顺序结构、选择结构和循环结构这三种基本结构的组合。这种结构化编程思想由计算机科学家Bhm和Jacopini在1966年首次提出,至今仍是现代编程语言设计的核心原则。 1.1 顺序结构的本质…...
【系统搭建】Ubuntu系统两节点间SSH免密配置
SSH免密配置是MPI分布式、DPDK通信等集群节点间通信的基础配置 1. 安装SSH服务端(所有节点执行) Ubuntu 默认只安装 SSH 客户端(openssh-client),未安装服务端(openssh-server),需要手动安装并…...
美信监控易:揭秘高效数据采集和数据分析双引擎
在当今复杂多变的运维环境中,一款强大的运维管理软件对于保障企业的IT系统稳定运行至关重要。北京美信时代的美信监控易运维管理软件,凭借其卓越的数据分析双引擎,成为了众多运维团队的首选。 首先,美信监控易的数据采集引擎展现出…...
基于STM32+FPGA的地震数据采集器软件设计,支持RK3568+FPGA平台
0 引言 地震观测是地球物理观测的重点,是地震学和 地球物理学发展的基础 [1] 。地震数据采集器主要功 能是将地震计采集的地震波模拟信号转换为数字信 号并进行记录或传输 [2] ,为地震学提供大量的基础 数据。本文将介绍基FPGAARM的地震数据采集器软…...
NO.95十六届蓝桥杯备战|图论基础-单源最短路|负环|BF判断负环|SPFA判断负环|邮递员送信|采购特价产品|拉近距离|最短路计数(C++)
P3385 【模板】负环 - 洛谷 如果图中存在负环,那么有可能不存在最短路。 BF算法判断负环 执⾏n轮松弛操作,如果第n轮还存在松弛操作,那么就有负环。 #include <bits/stdc.h> using namespace std;const int N 2e3 10, M 3e3 1…...
Linux 网络管理深度指南:从基础到高阶的网卡、端口与路由实战
一、网卡管理:构建网络连接的基石 1.1 现代网络工具链解析 在当代Linux系统中,iproute2套件已全面取代传统的net-tools,其优势体现在: 推荐组合命令: ip -c addr show | grep "inet " # 彩色显示有效IP…...
《重构全球贸易体系用户指南》解读
文章目录 背景核心矛盾与理论框架美元的“特里芬难题”核心矛盾目标理论框架 政策工具箱的协同运作机制关税体系的精准打击汇率政策的混合干预安全工具的复合运用 实施路径与全球秩序重构阶段性目标 风险传导与反制效应内部失衡加剧外部反制升级系统性风险 范式突破与理论再思考…...
stateflow中的函数
最近开始使用STATEFLOW,感觉功能比较强大,在嵌入式的应用中应该缺少不了,先将用到的仔细总结一下。还有一点,积极拥抱ai,学会使用AI的强大功能来学习。 在 Stateflow 中,不同类型的函数和状态适用于不同的建模需求。以下是 图形函数(Graphical Function)、Simulink 函…...
41.[前端开发-JavaScript高级]Day06-原型关系图-ES6类的使用-ES6转ES5
JavaScript ES6实现继承 1 原型继承关系图 原型继承关系 创建对象的内存表现 2 class方式定义类 认识class定义类 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible&qu…...
Flutter学习四:Flutter开发基础(一)Widget
Widget 简介 0 引言 本文是对 Flutter Widget 相关知识的学习和总结。 1 Widget 概念 1.1 Widget 基础 Widget 字面意思:控件、组件、部件、微件、插件、小工具widget 的功能是"描述一个UI元素的配置信息",所谓的配置信息就是 Widget 接收…...
Dify智能体平台源码二次开发笔记(6) - 优化知识库pdf文档的识别
目录 前言 新增PdfNewExtractor类 替换ExtractProcessor类 最终结果 前言 dify的1.1.3版本知识库pdf解析实现使用pypdfium2提取文本,主要存在以下问题: 1. 文本提取能力有限,对表格和图片支持不足 2. 缺乏专门的中文处理优化 3. 没有文档结…...
【LaTeX】公式图表进阶操作
公式 解决不认识的符号 查资料:1)知道符号样子;2)知道符号含义 放大版括号 用来括住存在分式的式子,或者用来括住内部由有很多括号的式子。用法是在左右括号[]分别加上\left和\right \[ J_r\dfrac{i \hbar}{2m} \l…...
第二阶段:数据结构与函数
模块4:常用数据结构 (Organizing Lots of Data) 在前面的模块中,我们学习了如何使用变量来存储单个数据,比如一个数字、一个名字或一个布尔值。但很多时候,我们需要处理一组相关的数据,比如班级里所有学生的名字、一本…...
matlab中simulink的快捷使用方法
连接系统模块还有如下更有效的方式:单击起始模块。 按下 Ctrl键,并单击目标块。 图示为已经连接好的系统模块 旋转模块:选中模块后按图示点击即可...
Redux部分
在src文件夹下 的store文件夹下创建modules/user.js和index.js module/ user.js // 存储用户相关const { createSlice } require("reduxjs/toolkit");const userStore createSlice({name:"user",// 数据状态initialState:{token:},// 同步修改方法red…...
基于STM32F103C8T6的温湿度检测装置
一、系统方案设计 1、系统功能分析 本项目设计的是一款基于STM32F103C8T6的温室大棚检测系统低配版。由 STM32F103C8T6最小系统板,OLED显示屏,DHT11温湿度检测传感器,光敏电阻传感器组成, 可以实现如下功能: 使用D…...
设计模式 - 单例模式
一个类不管创建多少次对象,永远只能得到该类型一个对象的实力 常用到的,比如日志模块,数据库模块 饿汉式单例模式:还没有获取实例对象,实例对象就已经产生了 懒汉式单例模式:唯一的实例对象,…...
Linux驱动开发1 - Platform设备
背景 所有驱动开发都是基于全志T507(Android 10)进行开发,用于记录驱动开发过程。 简介 什么是platform驱动自己上网搜索了解。 在driver/linux/platform_device.h中定义了platform_driver结构体。 struct platform_driver {int (*probe…...
力扣-hot100(盛最多水的容器)
11. 盛最多水的容器 中等 给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 说明…...
使用 PyTorch 构建 UNet 图像去噪模型:从数据加载到模型训练的完整流程
图像去噪是计算机视觉中的一个基础问题,在医学图像、遥感、夜间视觉等领域有广泛应用。本文将手把手带你用 PyTorch 构建一个 UNet 架构的图像去噪模型,包括数据预处理、网络搭建、PSNR 评估与模型保存的完整流程。 本项目已支持将数据增强版本保存为独立…...
从信号处理角度理解图像处理的滤波函数
目录 1、预备知识 1.1 什么是LTI系统? 1.1.1 首先来看什么是线性系统,前提我们要了解什么是齐次性和叠加性。...
集合框架--List集合详解
List集合 List 接口直接继承 Collection 接口,它定义为可以存储重复元素的集合,并且元素按照插入顺序有序排列,且可以通过索引访问指定位置的元素。常见的实现有:ArrayList、LinkedList。 Arraylist:有序、可重复、有索引 Linke…...
需求分析---软件架构师武器库中的天眼系统
在软件架构中,需求分析决定了系统的核心设计方向。 然而,现实中的需求往往存在以下问题: 需求被二次加工:产品经理或业务方可能直接提供“解决方案”(如“我们需要一个聊天功能”),而非原始需…...
Spring Cloud Gateway 的执行链路详解
Spring Cloud Gateway 的执行链路详解 🎯 核心目标 明确 Spring Cloud Gateway 的请求处理全过程(从接收到请求 → 到转发 → 到返回响应),方便你在合适的生命周期节点插入你的逻辑。 🧱 核心执行链路图(执…...
Python----机器学习(基于PyTorch框架的逻辑回归)
逻辑回归是一种广泛使用的统计学习方法,主要用于处理二分类问题。它基于线性回归模型,通过Sigmoid函数将输出映射到[0, 1]范围内,表示实例属于正类别的概率。尽管逻辑回归适用于二分类任务,但在多分类问题中常使用Softmax函数&…...
工业数据治理范式革新:时序数据库 TDengine虚拟表技术解析
小T导读:在工业数字化过程中,数据如何从设备采集顺利“爬坡”到上层应用,一直是个难题。传统“单列模型”虽贴合设备协议,却让上层分析举步维艰。TDengine 用一种更聪明的方法打通了这条数据通路:不强求建模、不手动转…...
Linux的应用领域,Linux的介绍,VirtualBox和Ubuntu的安装,VMware的安装和打开虚拟机CentOS
目录 Linux的应用领域 Linux的介绍 Linux的介绍 Linux发行版 Unix和Linux的渊源 虚拟机和Linux的安装 VirtualBox和Ubuntu的安装 安装VirtualBox 安装Ubuntu 下载Ubuntu操作系统的镜像文件 创建虚拟机 虚拟机设置 启动虚拟机,安装Ubuntu系统 Ubuntu基…...
使用 Java 8 Stream实现List重复数据判断
import java.util.*; import java.util.stream.Collectors;public class DeduplicateStreamExample {static class ArchiveItem {// 字段定义与Getter/Setter省略(需根据实际补充)private String mATNR;private String lIFNR;private String suppSpecMod…...
GDAL:地理数据的万能瑞士军刀
目录 1. 什么是GDAL?2. 为什么需要GDAL?3. GDAL的主要功能3.1. 数据转换3.2. 数据裁剪和处理3.3. 读取和写入多种格式 4. 实际应用场景4.1 环境监测4.2 城市规划4.3 导航系统 5. 技术原理简单解释6. 如何使用GDAL?6.1 简单命令示例 7. 学习建…...
每日文献(十三)——Part two
今天从第三章节:“实现细节”开始介绍。 目录 三、实现细节 四、实验 五、总结贡献 六、致谢 三、实现细节 我们在多尺度图像上训练和测试区域建议和目标检测网络。这是在KITTI目标检测基准[13]上基于CNN的目标检测的趋势。例如,在[16]中ÿ…...
ArrayList 和 LinkedList 区别
ArrayList 和 LinkedList 是 Java 集合框架中两种常用的列表实现,它们在底层数据结构、性能特点和适用场景上有显著的区别。以下是它们的详细对比以及 ArrayList 的扩容机制。 1. ArrayList 和 LinkedList 的底层区别 (1) 底层数据结构 ArrayList: 基于…...
【iOS】UITableView性能优化
UITableView性能优化 前言优化从何入手优化的本质 CPU层级优化1. Cell的复用2. 尽量少定义Cell,善于使用hidden控制显示视图3. 提前计算并缓存高度UITableView的代理方法执行顺序Cell高度缓存高度数组 4. 异步绘制5. 滑动时按需加载6. 使用异步加载图片,…...
通过检索增强生成(RAG)和重排序提升大语言模型(LLM)的准确性
探索大语言模型(LLM)结合有效信息检索机制的优势。实现重排序方法,并将其整合到您自己的LLM流程中。 想象一下,一个大语言模型(LLM)不仅能提供相关答案,还能根据您的具体需求进行精细筛选、优先…...
IDEA202403常用快捷键【持续更新】
文章目录 一、全局搜索二、美化格式三、替换四、Git提交五、代码移动六、调试运行 在使用IDEA进行程序开发,快捷键会让这个过程更加酸爽,下面记录各种快捷键的功能。 一、全局搜索 快捷键功能说明Shift Shift全局搜索Ctrl N搜索Java类 二、美化格式 …...
硬件元件三极管:从基础到进阶的电子探秘
一、基础理论 1. PN结(二极管) PN 结是采用不同的掺杂工艺,将 P 型半导体与 N 型半导体紧密接触而形成的一个界面区域。也就是我们常说的二极管。(P型带正电、N型带负电,电流由P流向N) 形成过程࿱…...
4. k8s核心概念 pod deployment service
以下是 Kubernetes 的核心概念详解,涵盖 Pod、Service、Deployment 和 Node,以及它们之间的关系和实际应用场景: 1. Pod 定义与作用 • 最小部署单元:Pod 是 Kubernetes 中可创建和管理的最小计算单元,包含一个或多个…...
12.第二阶段x64游戏实战-远程调试
免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 本次游戏没法给 内容参考于:微尘网络安全 上一个内容:11.第二阶段x64游戏实战-框架代码细节优化 本次写的内容是关于调试、排错相关的…...
自然语言处理的进化:BERT模型深度剖析
自然语言处理(NLP)领域近年来取得了跨越式的发展,尤其是随着深度学习技术的应用,不少新兴模型应运而生。其中,BERT(Bidirectional Encoder Representations from Transformers)作为一种革命性的…...
鸿蒙学习笔记(5)-HTTP请求数据
一、Http请求数据 http模块是鸿蒙内置的一个模块,提供了网络请求的能力。不需要再写比较原始的AJAS代码。 ps:在项目中如果要访问网络资源,不管是图片文件还是网络请求,必须给项目开放权限。 (1)网络连接方式 HTTP数…...
Golang 的 GMP 协程模型详解
Golang 的 GMP 协程模型详解 Golang 的并发模型基于 GMP(Goroutine-M-Processor) 机制,是其高并发能力的核心支撑。以下从原理、机制、优势、缺点和设计理念展开分析: 一、GMP 的组成与运作原理 Goroutine(Gÿ…...
ReportLab 导出 PDF(页面布局)
ReportLab 导出 PDF(文档创建) ReportLab 导出 PDF(页面布局) ReportLab 导出 PDF(图文表格) PLATYPUS - 页面布局和排版 1. 设计目标2. 开始3. Flowables3.1. Flowable.draw()3.2. Flowable.drawOn(canvas,x,y)3.3. F…...
Ubuntu 安装与配置 Docker
Ubuntu 安装与配置 Docker Docker 是一个开源的容器化平台,允许开发者将应用程序及其依赖项打包在一个轻量级、可移植的容器中。它可以帮助开发者和运维人员快速构建、部署和管理应用程序,提升开发和运维效率。本文将介绍如何在 Ubuntu 系统上安装和配置…...
【数据结构与算法】LeetCode每日一题
此题跟27.移除数组中的指定值 类似,都是移除且双指针玩法,只不过判断条件发生了变化...
【HDFS入门】数据存储原理全解,从分块到复制的完整流程剖析
目录 1 HDFS架构概览 2 文件分块机制 2.1 为什么需要分块? 2.2 块大小配置 3 数据写入流程 4 数据复制机制 4.1 副本放置策略 4.2 复制流程 5 数据读取流程 6 一致性模型 7 容错机制 7.1 数据节点故障处理 7.2 校验和验证 8 总结 在大数据时代&#x…...
力扣热题100——普通数组(不普通)
普通数组但一点不普通! 最大子数组和合并区间轮转数组除自身以外数组的乘积缺失的第一个正数 最大子数组和 这道题是非常经典的适用动态规划解决题目,但同时这里给出两种解法 动态规划、分治法 那么动态规划方法大家可以在我的另外一篇博客总结中看到&am…...
Ubuntu中snap
通过Snap可以安装众多的软件包。需要注意的是,snap是一种全新的软件包管理方式,它类似一个容器拥有一个应用程序所有的文件和库,各个应用程序之间完全独立。所以使用snap包的好处就是它解决了应用程序之间的依赖问题,使应用程序之…...
uniapp(Vue)开发微信小程序 之 保存图片到本地
一、保存图片到本地(要拿到图片的 src): 查看隐私条约是否加上相册(仅写入)权限: 微信公众平台 -》 左下角头像 -》账号设置 -》 用户隐私保护指引 -》去完善 -》 相册(仅写入)权限 …...
TailwindCss快速上手
什么是Tailwind Css? 一个实用优先的 CSS 框架,可以直接在标记中组合以构建任何设计。 开始使用Tailwind Css 如何安装 下面是使用vite构建工具的方法 ①安装 Tailwind CSS: tailwindcss通过tailwindcss/vitenpm安装。 npm install tailwindcss tailwindcss…...