stm32之SPI
目录
- 1.SPI通信协议
- 1.1 简介
- 1.2 硬件电路
- 1.3 移位示意图
- 1.4 SPI时序基本单元
- 1.5 SPI时序
- 2.W25Q64
- 2.1 简介
- 2.2 硬件电路
- 2.3 框图
- 2.3.1 结构介绍
- 2.3.2 混淆
- 2.4 Flash操作注意事项
- 2.4.1 写操作
- 2.4.2 读取操作
- 2.5 芯片手册补充
- 2.5.1 状态寄存器
- 2.5.2 指令集
- 3.软件操作W25Q64
- 4.SPI外设
- 4.1 简介
- 4.2 框图
- 4.3 基本结构
- 4.4 传输时序图
- 4.4.1 主模式全双工连续传输
- 4.4.2 非连续传输
- 4.5 软件/硬件波形对比
- 5.结构体和相关API
- 5.1 结构体
- 5.2 API
- **1.** `void SPI_I2S_DeInit(SPI_TypeDef* SPIx)`
- **2.** `void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct)`
- **3.** `void SPI_StructInit(SPI_InitTypeDef* SPI_InitStruct)`
- **4.** `void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState)`
- **5.** `void SPI_I2S_ITConfig(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT, FunctionalState NewState)`
- **6.** `void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState)`
- **7.** `void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data)`
- **8.** `uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx)`
- **9.** `void SPI_NSSInternalSoftwareConfig(SPI_TypeDef* SPIx, uint16_t SPI_NSSInternalSoft)`
- **10.** `FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG)`
- **11.** `void SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG)`
- **12.** `ITStatus SPI_I2S_GetITStatus(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT)`
- 6.硬件操作W25Q64
1.SPI通信协议
1.1 简介
- SPI(Serial Peripheral Interface)是由Motorola公司开发的一种通用数据总线
- 四根通信线:SCK(Serial Clock)、MOSI(Master Output Slave Input)、MISO(Master Input Slave Output)、SS(Slave Select)
- 同步,全双工
- 支持总线挂载多设备(一主多从)
相比较于I2C:
- SPI协议的传输速度没有严格规定的最大传输速度,取决于芯片厂商的设计需求。
- SPI的设计比较简单粗暴,实现的功能没有I2C的那么多
- SPI的硬件开销比较答,通信线的个数比较多,并且通信过程中经常会有资源浪费的现象。
1.2 硬件电路
所有SPI设备的SCK、MOSI、MISO分别连在一起
主机另外引出多条SS控制线,分别接到各从机的SS引脚
输出引脚配置为推挽输出,输入引脚配置为浮空或上拉输入
- MOSI:设置为推挽输出,高低电平都有很强的电平驱动能力,使得电平下降沿和上升沿都非常迅速,不像I2C那样,需要通过外部上拉电阻来拉高电平,这个过程并不是一下子完成的,因此可能会影响到数据传输。
- MISO:从机是有多个的,但是主机只有一个输入口,如果三个从机的MISO都一直是推挽输出,就会导致冲突,因此如果从机想要使用推挽输出的模式,就必须在未被SS选中的时候将MISO设置为高阻态模式,防止干扰到别的从设备
1.3 移位示意图
- SPI的移位操作:
-
- 在每个时钟(SCK)的上升沿,主机的移位寄存器会左移一位,将高位的数据通过
MOSI
发送给从机。 - 同时,从机的移位寄存器也会左移一位,将高位的数据通过
MISO
发送给主机。 - 这种操作可以实现主机和从机之间的同步数据交换。
- 在每个时钟(SCK)的上升沿,主机的移位寄存器会左移一位,将高位的数据通过
- 单向数据传输的资源浪费:
-
- 如果主机仅希望从从机接收数据,但并不关心发送给从机的内容,可以将移位寄存器的值预先设置为
0x00
或0xFF
(即发送无效数据)。 - 对于从机来说,它依然会接收主机发送的无效数据,但这些数据对实际应用无意义,因而这部分通信会造成一定的资源浪费。
- 这种资源浪费是SPI协议本身的特点,因为SPI是全双工通信协议,发送与接收同时发生。
- 如果主机仅希望从从机接收数据,但并不关心发送给从机的内容,可以将移位寄存器的值预先设置为
- 数据置换的本质:
-
- SPI通信本质是通过移位寄存器的左右移动,在时钟信号的驱动下实现主机与从机之间数据的字节级交换。
- 在通信过程中,主机和从机在时钟同步的条件下会同时完成一次字节的交换操作,这样主机的高位数据进入从机的低位,而从机的高位数据则进入主机的低位。完成置换后就会发现其实低位的数据就跑到最高位,因此没交换移位是需要左移一次的。如下图是交换后的数据:
- 优化方式:
-
- 如果单纯需要单向数据传输,可以考虑改用其他协议,比如I²C或UART,这些协议支持明确的主从通信方向,能避免资源浪费。
- 如果使用SPI协议,尽量在通信中利用双向数据交换,减少单向传输场景下的无效数据发送。
1.4 SPI时序基本单元
起始条件:SS从高电平切换到低电平
终止条件:SS从低电平切换到高电平
在SPI协议中,有两个值来确定SPI的模式, 定义了时钟信号如何与数据同步 。
CPOLL (时钟极性) :决定时钟信号在空闲状态下的电平。0表示时钟空闲时为低电平,1表示时钟空闲时为高电平。
CPHA (时钟相位) :表示相位, 决定数据采样的时刻。0表示数据在第一个时钟沿采样,1表示在第二个时钟沿采样。
CPOL | CPHA | 模式 | 描述 |
---|---|---|---|
0 | 0 | 0 | SPICLK初始电平为低电平,数据在第一个时钟沿(上升沿)采样(数据移入移位寄存器) |
0 | 1 | 1 | SPICLK初始电平为低电平,数据在第二个时钟沿(下降沿)采样 |
1 | 0 | 2 | SPICLK初始电平为高电平,数据在第一个时钟沿(下降沿)采样 |
1 | 1 | 3 | SPICLK初始电平为高电平,数据在第二个时钟沿(上升沿)采样 |
- MOSI → 从设备采样;
- MISO → 主设备采样。
交换一个字节(模式0)
-
CPOL=0:空闲状态时,SCK为低电平
-
CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据
-
注意:这里所谓的移入和移出是相对于从设备来说的!!!!!
-
- 第一个上升沿,MOSI的数据移入到从设备的寄存器,也就是说主设备在此之前已经将数据移出到MOSI。于此同时,MISO的数据移入到了主设备的寄存器,也就是从设备在此之前已经将数据移出到MISO
- 第二个上升沿同理。
交换一个字节(模式1)
- CPOL=0:空闲状态时,SCK为低电平
- CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据
交换一个字节(模式2)
- CPOL=1:空闲状态时,SCK为高电平
- CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据
交换一个字节(模式3)
- CPOL=1:空闲状态时,SCK为高电平
- CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据
上面说过,在从机未没选中的时候,其MISO必须置为高阻态模式:
红色标中的就是被置为了高阻态,不过一般不需要去关心,这个从机一般都会自己去做的。
MOSI,来自从设备,可以理解为是从设备引申到主设备的,主设备输出给从设备数据,从设备在该MOSI上采样来自主设备的数据。
1.5 SPI时序
发送指令:
- 向SS指定的设备,发送指令(0x06)
指定地址写:
- 向SS指定的设备,发送写指令(0x02),随后在指定地址(Address[23:0])下,写入指定数据(Data)
指定地址读:
- 向SS指定的设备,发送读指令(0x03),随后在指定地址(Address[23:0])下,读取从机数据(Data)
2.W25Q64
参考资料
- 📎W25Q256FV.PDF
- 📎W25Q16BV.PDF
- 📎W25Q32BV.PDF
- 📎W25Q40BV.PDF
- 📎W25Q64BV.PDF
- 📎W25Q80BV.PDF
- 📎W25Q128BV.PDF
原理图:
- 📎W25Qxx.pdf
2.1 简介
W25Qxx系列是一种低成本、小型化、使用简单的非易失性存储器,常应用于数据存储、字库存储、固件程序存储等场景
存储介质:Nor Flash(闪存)
时钟频率:80MHz / 160MHz (Dual SPI) / 320MHz (Quad SPI)存储容量(24位地址):
型号 | 容量 |
---|---|
W25Q40 | 4Mbit / 512KByte |
W25Q80 | 8Mbit / 1MByte |
W25Q16 | 16Mbit / 2MByte |
W25Q32 | 32Mbit / 4MByte |
W25Q64 | 64Mbit / 8MByte |
W25Q128 | 128Mbit / 16MByte |
W25Q256 | 256Mbit / 32MByte |
2.2 硬件电路
引脚 | 功能 |
---|---|
VCC、GND | 电源(2.7~3.6V) |
CS(SS) | SPI片选 |
CLK(SCK) | SPI时钟 |
DI(MOSI) | SPI主机输出从机输入 |
DO(MISO) | SPI主机输入从机输出 |
WP | 写保护 |
HOLD | 数据保持 |
2.3 框图
2.3.1 结构介绍
24位的地址,最大的寻址范围是16MB(224➗1024➗1024),一个24位的地址就对应1个字节(Byte)的存储数据(这个具体后面讲)
但是对于这个芯片只有8MB,也就是地址空间只用了一半。
\1. Block Segmentation(分块结构)
-
- 存储器被分成多个块(Block)和扇区(Sector)。
- 每个块大小为64KB,每个扇区大小为4KB。
- 通过这种分层结构,便于在不同应用场景中对存储器进行擦除、读取和写入操作。
- 支持按**扇区(4KB)或块(64KB)**擦除操作,提供灵活的存储管理。
- 每个块由16个扇区组成,每个扇区的地址范围为0x??0000到0x??0FFF。块的地址范围是0x00???~-0x7F???(127)
- 总存储容量为64M位(8MB:64Mbit➗8=8MByte — 8bit=1Byte),由128个块组成((8✖1024) KB ➗64KB=128)。
\2. Write Protect Logic and Row Decode(写保护逻辑和行解码)
-
- 用于控制写保护功能,并将输入的地址映射到特定的存储行。
- 提供硬件或软件的写保护,防止存储数据被意外修改。
- 配合页地址和列地址实现对存储器的定位。
\3. SPI Command & Control Logic(SPI命令和控制逻辑)
-
- 通过SPI接口与主机通信,接收命令并执行相应的存储器操作。
- 输入引脚包括:
-
-
- CLK:时钟信号,用于同步数据传输。
- CS:片选信号,低电平激活。
- DI(IO0):数据输入引脚,用于接收主机发送的指令或数据。
- DO(IO1):数据输出引脚,用于输出存储器中的数据到主机。
- /HOLD(IO3):暂停信号,用于中断或暂停当前操作。
-
-
- 提供命令解释功能,如读取、写入、擦除、状态寄存器操作等。
- 控制整个存储器的操作。
\4. Write Control Logic(写入控制逻辑)
-
- 负责管理写入操作,配合SPI命令实现数据的写入。
- 检查写保护状态,确保只有在写保护解除的情况下才能进行写操作。
- 管理写入时需要的高电压生成器。
\5. Status Register(状态寄存器)
-
- 存储芯片的当前状态信息,包括忙碌状态、写保护状态、错误状态等。
- 主机可以通过读取状态寄存器,判断芯片是否处于忙碌状态(Busy)或者是否已写保护。
- 也可用于设置写保护等功能。
\6. High Voltage Generators(高压发生器)
-
- 用于生成执行擦除和写入操作所需的高电压。
- 在写入或擦除操作时提供必要的高电压,确保存储单元的可靠修改。
- 实现掉电不丢失
7. Page Address Latch/Counter 和 Byte Address Latch/Counter(页地址锁存器/计数器 和 字节地址锁存器/计数器)
-
- 用于管理地址的存储和递增。
- 页地址和字节地址配合使用,实现对存储器的逐页(256字节/Byte)或逐字节访问。
- 页地址锁存器/计数器:用于定位目标页(256字节)。
- 字节地址锁存器/计数器:用于定位目标页内的具体字节。
8. Column Decode and 256-Byte Page Buffer(列解码和256字节页缓冲区)
-
- 列解码:用于从存储阵列中选取目标列。与行解码配合,实现完整的存储单元地址选择。
- 页缓冲区:用于暂存数据,支持一次最多256字节的数据写入。提升写入效率,可以一次性将一页数据写入存储阵列。为什么要有缓冲区RAM??因为这个模块的写入是“刻苦铭心”的,也就是永久性存储,写入是需要时间的,而SPI的写入是非常快的,所以需要个缓冲区暂存
- 一个扇区4KB,可以划分16页。1页的地址范围就是0x???00~0x???FF(256字节就说明有256个地址,也就是FF),这个也就是数据的地址范围。
\9. Block Diagram总体工作流程
-
存储分层:
-
- 整个存储空间按块(64KB)和扇区(4KB)划分,便于擦除和管理。
-
SPI通信:
-
- 主机通过SPI接口与芯片通信,发送操作命令和数据。
-
地址管理:
-
- 通过页地址和字节地址锁存器,定位到特定的存储单元。
-
数据传输:
-
- 数据先存入页缓冲区,再通过高压发生器写入存储阵列。
-
状态反馈:
-
- 状态寄存器记录当前芯片的状态,主机可以查询写入或擦除的完成情况。
2.3.2 混淆
注意这个模块的地址空间范围只有一半。
因此当需要写入数据的时候,就需要发送3字节的地址,高两位字节用于找到对应块的对应扇区的对应页,最后一位字节确定页内的地址
- 块号地址确定范围:0x00 ?? ?? ~ 0x7F ?? ??(128块)
- 扇号地址确定范围:0x## 0? ?? ~ 0x## F? ??(16个扇区)
- 扇区内页的地方确定范围:0x## #0 ?? ~ 0x## #F ??(16页)
- **页内的地址确定范围:**0x## ## 00 ~ 0x## ## FF(一页对应256字节)
往哪里存储,这肯定是要有地址去指向的。地址通过层级结构(块 > 扇区 > 页 > 字节)来定位存储空间。而一个地址指向的存储空间,其容量是1字节。
8MB的容量又是怎么算出来的?
24位的地址,不就有224个地址么,而1个地址又对应指向1个字节(Byte)的存储空间,那也就是说存储容量有224Byte,而224➗1024=16348KB、16348➗1024=16MB,即24位的地址其存储容量为16MB
而这里介绍的W25Q64,它只用了一半的地址空间,从块号的地址范围为:00~7F就可以知道,所以这个模块的存储容量就是8MB
将数据 0xAB
写入地址 0x123456
,流程如下:
- 主机发送写命令。
- 主机发送 3 字节地址
0x123456
:
-
12h
:块选择。34h
:页选择。56h
:页内的具体字节位置。
- 主机发送数据
0xAB
。 - 存储器将
0xAB
写入到地址0x123456
对应的存储单元。
2.4 Flash操作注意事项
2.4.1 写操作
- 写入前需要“写使能”
-
- 在进行写入操作之前,必须先发送 写使能指令(Write Enable, WREN),这是一种保护机制,防止意外写入。
- 通过写使能,芯片的状态寄存器会设置一个“写使能位”(WEL, Write Enable Latch)。
- 如果没有先进行写使能,芯片将拒绝任何写入操作。
- 只能从1改写为0,不能从0改写为1
-
- 闪存的基本存储单元是浮动栅极晶体管,写入操作的本质是改变存储单元的电荷状态:
-
-
- 1 → 0:可以通过编程直接完成。
- 0 → 1:需要通过擦除来恢复为1(清空浮动栅的电荷)。
-
-
- 因此,在写入数据前,必须确保目标存储区域已经擦除。
- 写入数据前必须先擦除
-
- 擦除操作会将存储区域中的所有位恢复为1。
- 擦除只能按最小擦除单元(如扇区、块)进行,不能只擦除单个字节。
- 这一步是为了保证存储器可以正确写入新的数据。
- 擦除的最小单元
-
- W25Q64支持不同级别的擦除操作:
-
-
- 扇区擦除(4KB):最小的擦除单位。
- 块擦除(64KB):擦除更大的存储区域。
- 芯片擦除:清空整个存储器。
-
-
- 擦除操作需要一定时间,在擦除完成之前,芯片处于“忙状态”。
- 最多写入一页数据(256字节)
-
- 写入操作是按页(Page)为单位管理的:
-
-
- 一次写入最多 256 字节(1 页)。
- 如果超出页尾地址(例如写入 257 字节),后续数据会回绕到页首,从而覆盖已有数据。
-
-
- 因此,应用程序必须确保每次写入操作不会超过页边界。
- 写入后芯片进入忙状态
-
- 写入数据需要通过内部的高压操作完成,期间芯片会进入忙状态(Busy)。
- 在忙状态下,芯片不会响应新的命令(包括读写操作)。
- 主机可以通过读取状态寄存器中的“忙位”(BUSY)来判断写入操作是否完成。
2.4.2 读取操作
- 直接调用读取时序
-
- 与写入不同,读取操作不需要执行写使能等额外步骤。
- 主机只需发送读取命令和目标地址,存储器即可返回指定地址处的数据。
- 没有页限制
-
- 读取操作不受页的限制,主机可以连续读取任意长度的数据。
- 存储器内部会自动按地址顺序输出数据,直到达到主机指定的读取长度。
- 读取操作结束后不会进入忙状态
-
- 读取操作不会修改存储器的内容,因此不需要涉及复杂的高压操作。
- 芯片在读取完成后立即可用,不会进入忙状态。
- 忙状态时不能读取
-
- 如果芯片正在执行写入或擦除操作,此时是忙状态,无法响应读取请求。
- 主机需要轮询状态寄存器的忙位(BUSY)来确认芯片是否空闲。
操作 | 特点 | 注意事项 |
---|---|---|
写入 | 需先写使能 数据位只能从1改为0 写入前需擦除 最多写入1页 | 写入后芯片进入忙状态,不可立即响应其他命令。 |
擦除 | 必须按最小擦除单元进行 所有位恢复为1 | 擦除速度较慢,应用时需考虑优化操作顺序。 |
读取 | 无需写使能 无页限制,连续读取任意长度 读取结束后不会忙状态 | 忙状态下不能读取,主机需先检查芯片是否空闲。 |
2.5 芯片手册补充
W25Q64BV.PDF
2.5.1 状态寄存器
BUSY是状态寄存器(S0)中的只读位,当设备执行页面程序、扇区擦除、块擦除、芯片擦除或写入状态寄存器指令时,该位被设置为1状态。在此期间,设备将忽略除读取状态寄存器和擦除挂起指令之外的其他指令(请参阅AC特性中的tW、tPP、tSE、tBE和tCE)。当程序、擦除或写入状态寄存器指令完成时,BUSY位将被清除为0状态,表示设备已准备好执行进一步的指令。
写启用锁存器(WEL)是状态寄存器(S1)中的只读位,在执行写启用指令后设置为1。当设备被写禁用时,WEL状态位被清除为0。通电或执行以下任何指令后,会出现写禁用状态:写禁用、页面程序、扇区擦除、块擦除、芯片擦除和写状态寄存器。
暂时最低两位就行了
2.5.2 指令集
可以通过输入表中的相关Instruction给设备来确认设备。
1. Write Enable (06h)
- 写使能指令,设置“写使能锁存器”(WEL,Write Enable Latch)位为 1,允许执行写入或擦除操作。
- 在每次写入或擦除之前必须先执行此指令,否则写入/擦除命令会被忽略。
\2. Write Disable (04h)
- 禁止写操作,清除 WEL 位,防止意外写入或擦除。
- 在完成写入或擦除后,可以通过此指令禁用写功能,增加安全性。
3. Read Status Register-1 (05h)
- 读取状态寄存器 1 的内容,主要包括 Busy 位(芯片是否忙碌)和 WEL 位(是否已启用写入)。
- Busy 位:1 表示芯片忙,0 表示空闲。
- WEL 位:1 表示允许写入,0 表示写入被禁止。
\4. Read Status Register-2 (35h)
- 读取状态寄存器 2,可能包含保护位或其他状态信息(具体参见芯片手册)。
- 用于检测写保护状态等信息。
\5. Write Status Register (01h)
- 写入状态寄存器的内容,用于设置写保护等功能。
- 配置状态寄存器,如启用或禁用某些扇区的写保护。
6. Page Program (02h)
- 写入数据到一页(256 字节)。
- 3 字节地址(A23-A0)用于定位页。
- 后续数据字节(D7-D0)是要写入的实际内容。
- 如果写入的数据超出页边界,多余的数据会覆盖页首。
\7. Quad Page Program (32h)
- 在四线模式下写入一页(256 字节)。
- 相比普通写入,四线模式可加快数据传输速度。
\8. Block Erase (64KB, D8h
- 擦除一个 64KB 的块。
- 3 字节地址(A23-A0),地址指定块的起始位置。
- 在写入新数据前清空整个块。
\9. Block Erase (32KB, 52h)
- 擦除一个 32KB 的块。
- 比 64KB 擦除更小粒度的操作。
10. Sector Erase (4KB, 20h)
- 擦除一个扇区(4KB)。
- 最小擦除单位,适用于需要频繁更新的存储区域。
\11. Chip Erase (C7h/60h)
- 擦除整个芯片,将所有数据位恢复为 1。
- 用于清空整个存储器,耗时最长。
\12. Erase Suspend (75h) / Erase Resume (7Ah)
- 暂停擦除操作或恢复擦除操作。
- 在擦除过程中插入读取操作,灵活调度任务。
\13. Power-down (B9h)
- 将芯片置于省电模式。
- 降低功耗,适用于低功耗应用场景。
\14. High Performance Mode (A3h)
- 启用高性能模式,提升芯片运行速度。
- 满足高速度需求的场景。
\15. Continuous Read Mode Reset (FFh)
- 重置连续读取模式。
- 退出连续读取模式,恢复到普通模式。
\16. Release Power-down or HPM / Device ID (ABh):
- 从省电模式中唤醒芯片,同时返回芯片的设备 ID。
- 唤醒后获取芯片标识信息。
\17. Manufacturer / Device ID (90h)
- 读取制造商 ID 和设备 ID。
- 用于验证芯片型号。
\18. Read Unique ID (4Bh)
- 读取芯片的唯一 ID。
- 适用于设备识别或防伪。
19. JEDEC ID (9Fh)
- 读取 JEDEC 标准的 ID 信息。
- 获取制造商信息、存储器类型和容量。
20. Read Data (03h)
- 普通读取模式,按字节读取数据。
- Byte 2-4 (A23-A0):3 字节地址,指定读取的起始地址。
- Byte 5+ (D7-D0):返回的数据字节。
- 无需“Dummy Byte”。
- 数据传输速度较慢,适用于非时间敏感的读取任务。
- 用于简单的逐字节数据读取。
\21. Fast Read (0Bh)
- 快速读取模式,通过插入一个“Dummy Byte”加速读取。
- Byte 2-4 (A23-A0):3 字节地址。
- Byte 5 (Dummy Byte):1 个无意义的占位字节。
- Byte 6+ (D7-D0):返回的数据字节。
- 相比普通读取,数据传输速度更快。
- 使用更高的 SPI 时钟频率(如 104 MHz)。
- 大容量数据读取场景,注重传输效率。
\22. Fast Read Dual Output (3Bh)
- 双线输出模式,利用
IO0
和IO1
两根数据线同时输出数据,提高速度。 - Byte 2-4 (A23-A0):3 字节地址。
- Byte 5 (Dummy Byte):占位字节。
- Byte 6+ (D7-D0):通过双线传输的数据字节。
- 数据传输效率比普通模式更高。
- 数据量较大且需要较快读取速度的场景。
\23. Fast Read Dual I/O (BBh)
- 双线输入输出模式,地址和数据都通过双线传输。
- Byte 2-3 (A23-A0):3 字节地址。
- Byte 4 (Dummy Byte):占位字节。
- Byte 5+ (D7-D0):通过双线传输的数据字节。
- 地址和数据均通过双线传输,大幅提升数据吞吐能力。
- 用于高性能需求的嵌入式系统。
\24. Fast Read Quad Output (6Bh)
- 四线输出模式,数据通过
IO0
、IO1
、IO2
和IO3
同时输出。 - Byte 2-4 (A23-A0):3 字节地址。
- Byte 5 (Dummy Byte):占位字节。
- Byte 6+ (D7-D0):通过四线传输的数据字节。
- 数据输出速度极高。
- 用于大数据量、高速读取的应用场景。
\25. Fast Read Quad I/O (EBh)
- 四线输入输出模式,地址和数据均通过四线传输。
- Byte 2-3 (A23-A0):地址。
- Byte 4 (Dummy Byte):占位字节。
- Byte 5+ (D7-D0):通过四线传输的数据字节。
- 支持最高速的读写操作。
- 用于存储芯片和处理器之间的高速通信场景。
\26. Octal Word Read Quad I/O (E3h)
- 八线模式读取。
- 最快的数据传输速度。
- 用于存储访问速度要求极高的嵌入式系统。
剩下的一些时序图去看手册就行了。
3.软件操作W25Q64
📎11-1 软件SPI读写W25Q64.zip
User:
- 📎main.c
Hardware:
- 📎MySPI.c
- 📎MySPI.h
- 📎OLED.c
- 📎OLED.h
- 📎OLED_Font.h
- 📎W25Q64.c
- 📎W25Q64.h
- 📎W25Q64_Ins.h
/*** 函 数:SPI交换传输一个字节,使用SPI模式0* 参 数:ByteSend 要发送的一个字节* 返 回 值:接收的一个字节*/
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t i, ByteReceive = 0x00; //定义接收的数据,并赋初值0x00,此处必须赋初值0x00,后面会用到for (i = 0; i < 8; i ++) //循环8次,依次交换每一位数据{/*两个!可以对数据进行两次逻辑取反,作用是把非0值统一转换为1,即:!!(0) = 0,!!(非0) = 1*/MySPI_W_MOSI(!!(ByteSend & (0x80 >> i))); //使用掩码的方式取出ByteSend的指定一位数据并写入到MOSI线MySPI_W_SCK(1); //拉高SCK,主设备上升沿移出数据(上面已经调用了),从设备从MOSI移入设备if (MySPI_R_MISO()){ByteReceive |= (0x80 >> i);} //读取MISO数据,并存储到Byte变量//当MISO为1时,置变量指定位为1,当MISO为0时,不做处理,指定位为默认的初值0MySPI_W_SCK(0); //拉低SCK,下降沿移入数据}return ByteReceive; //返回接收到的一个字节数据
}
需要注意的是不要混淆
移入:
-
MOSI:调用
MySPI_W_MOSI
就相当于将高位移出移位寄存器放入MOSI管,之后MySPI_W_SCK(1);
就是将SCK电平拉高,表明我主设备已经将数据移出放入到MOSI了(同时从设备也已经将数据移出到MISO),这时候从设备就可以移入数据,所以上图时序图中讲到的第一个SCK边沿移入数据是针对于MOSI上的从设备来说的。 -
MISO:而此时对于主设备,讲道理第一个SCK上升沿,从设备移入数据了,主设备应该同步也从MISO将数据移入寄存器,但由于是模拟实现的SPI,程序无法同步实现。
MySPI_W_SCK(1);
所以只能后续紧跟着调用MySPI_R_MISO
将MISO的数据移入进主设备的寄存器,所以上图时序图的第一个SCK边沿移入数据也是中是MISO上的主设备来说。 -
- 只不过,对于MISO,从设备会自己将数据移出到MISO,不需要像主设备这样手动调用一下
MySPI_W_MOSI
将数据移出到MOSI
- 只不过,对于MISO,从设备会自己将数据移出到MISO,不需要像主设备这样手动调用一下
移出:
- 从设备的移出;
MySPI_W_SCK(0);
拉低SCK,其实就是告诉从设备可以将数据移出到MISO - 至于主设备,会在下次循环调用
MySPI_W_MOSI
将数据移出到MOSI。
也就是说对于SCK的上升沿和下降沿其实主要是告诉从设备可以进行移入(MOSI)和移出(MISO)了,至于主设备是需要自己紧跟着在上升沿代码调用后进行移入(MISO),调用前将数据移出(MOSI);在下降沿代码前将数据进行移出(MOSI)
由于是软件实现,主设备无法说做到上升沿时刻同步移出数据,所以只能先移出数据,再将SCK电平置高。
下面来看看模式1的:
/*** 函 数:SPI交换传输一个字节,使用SPI模式1* 参 数:ByteSend 要发送的一个字节* 返 回 值:接收的一个字节*/
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t i, ByteReceive = 0x00; //定义接收的数据,并赋初值0x00,此处必须赋初值0x00,后面会用到for (i = 0; i < 8; i ++) //循环8次,依次交换每一位数据{MySPI_W_SCK(1); //拉高SCK,主设备将数据移出到MOSI(下面调用MySPI_W_MOSI),从设备也会将数据移出到MIS/*两个!可以对数据进行两次逻辑取反,作用是把非0值统一转换为1,即:!!(0) = 0,!!(非0) = 1*/MySPI_W_MOSI(!!(ByteSend & (0x80 >> i))); //使用掩码的方式取出ByteSend的指定一位数据并写入到MOSI线MySPI_W_SCK(0); //拉低SCK,主设备下降沿从MISO移入数据(下面调用MySPI_R_MISO),从设备从MOSI移出数据if (MySPI_R_MISO()){ByteReceive |= (0x80 >> i);} //读取MISO数据,并存储到Byte变量//当MISO为1时,置变量指定位为1,当MISO为0时,不做处理,指定位为默认的初值0}return ByteReceive; //返回接收到的一个字节数据
}
也就是说在上升沿和下降沿代码调用后主设备紧跟着调用相应的函数完成相应的移出移入操作
MySPI_W_SCK(1);
—>MySPI_W_MOSI
移出MySPI_W_SCK(0);
—>MySPI_R_MISO
移入
这个就比较好理解一点。
4.SPI外设
4.1 简介
-
STM32内部集成了硬件SPI收发电路,可以由硬件自动执行时钟生成、数据收发等功能,减轻CPU的负担
-
可配置8位/16位数据帧、高位先行/低位先行。一般都是用8位数据帧,也就是一个字节,以及高位先行
-
- 串口是低位先行
-
时钟频率: fPCLK / (2, 4, 8, 16, 32, 64, 128, 256)。SPI外设的时钟,其实就是由外设时钟分频而来的,PCLK就是Peripheral Clock外设时钟,APB2的是72MHz,APB1的是36MHz。所以SPI的时钟频率需要看挂载哪个总线上
-
支持多主机模型、主或从操作
-
可精简为半双工/单工通信
-
支持DMA
-
兼容I2S协议
STM32F103C8T6 硬件SPI资源:SPI1(APB1)、SPI2(APB2)
4.2 框图
主机SPI外设框图
1. MOSI、MISO、SCK、NSS 引脚
这些是 SPI 外设的主要硬件引脚:
- MOSI(Master Out Slave In): 主设备发送数据到从设备。
- MISO(Master In Slave Out): 从设备发送数据到主设备。
- SCK(Serial Clock): 时钟信号,由主设备产生,控制数据传输节奏。
- NSS(Slave Select): 片选信号,选择从设备的使能引脚,低电平表示选中。
其中实现了交叉,也就是如果主机为从设备,数据变成从MISO口输出,从MOSI输入
2. 移位寄存器
移位寄存器是 SPI 的核心,用于实现数据的串行传输:
-
数据在 MOSI 和 MISO 之间传输,通过 SCK 时钟信号逐位移入或移出。
-
控制位 LSBFIRST: 控制数据的传输顺序,决定是先传输低位(LSB)还是高位(MSB)。
-
3. 接收缓冲区
用于暂存接收到的数据:
- 当 SPI 接收到数据时,先存入此缓冲区,然后通过程序读取。
- 和移位寄存器配合,实现数据从 MISO 进入。
4. 发送缓冲区
用于暂存需要发送的数据:
- 程序将数据写入此缓冲区后,SPI 会通过 MOSI 按位发送数据。
- 和移位寄存器配合,实现数据的输出。
5. 主控制电路
主控制电路是 SPI 的控制逻辑核心,管理通信模式和数据传输:
- 控制主从模式(通过 MSTR 位)。
- 决定 SPI 的使能状态(通过 SPE 位)。
- 配置单向或双向数据模式(通过 BIDIMODE 位)。
- CRC 校验、帧格式、数据帧大小等参数由此电路管理。
6. 波特率发生器
控制 SPI 的时钟分频,产生通信所需的时钟信号:
- 通过 BR[2:0] 配置时钟分频比,从而控制 SPI 的通信速度。
SPI_CR1寄存器
7. 通信电路
通信电路负责协调数据的发送和接收,包括:
- 检测数据是否接收完成(RXNE)。
- 检测数据是否发送完成(TXE)。
- 判断总线状态(如忙碌状态 BSY)。
8. SPI 控制寄存器(SPI_CR1 和 SPI_CR2)
这些寄存器用于配置 SPI 外设的工作模式:
- SPI_CR1: 配置 SPI 的基本模式,比如主从模式、数据格式(LSB/MSB)、时钟极性(CPOL)和相位(CPHA)。
- SPI_CR2: 控制中断使能(如 RXNEIE、TXEIE),并配置硬件片选功能(SSOE)。
9. 状态寄存器(SPI_SR)
状态寄存器反映 SPI 当前的运行状态:
- TXE(发送缓冲区空): 表示可以写入新的数据。
- RXNE(接收缓冲区非空): 表示接收到新数据。
- BSY(忙): 表示 SPI 正在进行通信。
10. 地址和数据总线
连接 SPI 和系统总线,用于数据的读写和地址的映射:
- 数据通过此总线从 CPU 传输到 SPI 寄存器,或者从 SPI 传输到 CPU。
4.3 基本结构
4.4 传输时序图
4.4.1 主模式全双工连续传输
4.4.2 非连续传输
4.5 软件/硬件波形对比
5.结构体和相关API
5.1 结构体
/** * @brief SPI Init structure definition */typedef struct
{uint16_t SPI_Direction; /*!< Specifies the SPI unidirectional or bidirectional data mode.This parameter can be a value of @ref SPI_data_direction */uint16_t SPI_Mode; /*!< Specifies the SPI operating mode.This parameter can be a value of @ref SPI_mode */uint16_t SPI_DataSize; /*!< Specifies the SPI data size.This parameter can be a value of @ref SPI_data_size */uint16_t SPI_CPOL; /*!< Specifies the serial clock steady state.This parameter can be a value of @ref SPI_Clock_Polarity */uint16_t SPI_CPHA; /*!< Specifies the clock active edge for the bit capture.This parameter can be a value of @ref SPI_Clock_Phase */uint16_t SPI_NSS; /*!< Specifies whether the NSS signal is managed byhardware (NSS pin) or by software using the SSI bit.This parameter can be a value of @ref SPI_Slave_Select_management */uint16_t SPI_BaudRatePrescaler; /*!< Specifies the Baud Rate prescaler value which will beused to configure the transmit and receive SCK clock.This parameter can be a value of @ref SPI_BaudRate_Prescaler.@note The communication clock is derived from the masterclock. The slave clock does not need to be set. */uint16_t SPI_FirstBit; /*!< Specifies whether data transfers start from MSB or LSB bit.This parameter can be a value of @ref SPI_MSB_LSB_transmission */uint16_t SPI_CRCPolynomial; /*!< Specifies the polynomial used for the CRC calculation. */
}SPI_InitTypeDef;
SPI_Direction
-
描述:
指定SPI的数据传输模式,是单向或双向通信。 -
取值范围:
可取以下值(具体定义在@ref SPI_data_direction
中): -
SPI_Direction_2Lines_FullDuplex
:全双工模式(两条线用于发送和接收)。SPI_Direction_2Lines_RxOnly
:仅接收模式(两条线,只有接收)。SPI_Direction_1Line_Rx
:单线接收模式(单独一条线)。SPI_Direction_1Line_Tx
:单线发送模式(单独一条线)。
SPI_Mode
-
描述:
指定SPI的工作模式,是主机模式(Master)还是从机模式(Slave)。 -
取值范围:
可取以下值(具体定义在@ref SPI_mode
中): -
SPI_Mode_Master
:主机模式。SPI_Mode_Slave
:从机模式。
SPI_DataSize
-
描述:
指定SPI传输的数据帧大小。 -
取值范围:
可取以下值(具体定义在@ref SPI_data_size
中): -
SPI_DataSize_8b
:8位数据帧。SPI_DataSize_16b
:16位数据帧。
SPI_CPOL
-
描述:
指定串行时钟的空闲状态(极性),即SCK引脚的电平状态。 -
取值范围:
可取以下值(具体定义在@ref SPI_Clock_Polarity
中): -
SPI_CPOL_Low
:空闲时SCK保持低电平。SPI_CPOL_High
:空闲时SCK保持高电平。
SPI_CPHA
-
描述:
指定串行时钟的采样边沿,即数据采集发生在哪一个时钟边沿。 -
取值范围:
可取以下值(具体定义在@ref SPI_Clock_Phase
中): -
SPI_CPHA_1Edge
:第一个时钟跳变沿采样数据。SPI_CPHA_2Edge
:第二个时钟跳变沿采样数据。
SPI_NSS
-
描述:
指定从属选择(NSS)信号的管理方式,是否由硬件(NSS引脚)管理或通过软件(SSI位)模拟管理。 -
取值范围:
可取以下值(具体定义在@ref SPI_Slave_Select_management
中): -
SPI_NSS_Soft
:NSS由软件管理(使用SSI位)。SPI_NSS_Hard
:NSS由硬件管理(NSS引脚)。
SPI_BaudRatePrescaler
-
描述:
指定波特率分频系数,用于配置发送和接收的SCK时钟。 -
取值范围:
可取以下值(具体定义在@ref SPI_BaudRate_Prescaler
中): -
SPI_BaudRatePrescaler_2
:SCK = fPCLK/2。SPI_BaudRatePrescaler_4
:SCK = fPCLK/4。SPI_BaudRatePrescaler_8
:SCK = fPCLK/8。SPI_BaudRatePrescaler_16
:SCK = fPCLK/16。SPI_BaudRatePrescaler_32
:SCK = fPCLK/32。SPI_BaudRatePrescaler_64
:SCK = fPCLK/64。SPI_BaudRatePrescaler_128
:SCK = fPCLK/128。SPI_BaudRatePrescaler_256
:SCK = fPCLK/256。
SPI_FirstBit
-
描述:
指定数据传输的起始位方向,是从最高有效位(MSB)还是最低有效位(LSB)开始。 -
取值范围:
可取以下值(具体定义在@ref SPI_MSB_LSB_transmission
中): -
SPI_FirstBit_MSB
:从MSB(最高有效位)开始传输。SPI_FirstBit_LSB
:从LSB(最低有效位)开始传输。
SPI_CRCPolynomial
-
描述:
指定用于循环冗余校验(CRC)计算的多项式值。 -
取值范围:
-
- 任意16位无符号整数值(用户根据需求设置)。常用的CRC多项式是标准化的,例如
0x07
、0x1021
等。
- 任意16位无符号整数值(用户根据需求设置)。常用的CRC多项式是标准化的,例如
5.2 API
1. void SPI_I2S_DeInit(SPI_TypeDef* SPIx)
-
作用:
将指定的SPI/I2S外设复位,清除所有配置,恢复到默认状态。 -
参数:
-
SPIx
:需要复位的SPI外设(如SPI1
或SPI2
)。
-
使用实例:
SPI_I2S_DeInit(SPI1); // 复位SPI1外设
2. void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct)
-
作用:
初始化SPI外设,并配置其通信参数。 -
参数:
-
SPIx
:需要初始化的SPI外设(如SPI1
)。SPI_InitStruct
:包含SPI初始化参数的结构体。
-
使用实例:
SPI_InitTypeDef SPI_InitStruct;
SPI_StructInit(&SPI_InitStruct); // 初始化结构体为默认值
SPI_InitStruct.SPI_Mode = SPI_Mode_Master; // 设置为主机模式
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; // 波特率分频
SPI_Init(SPI1, &SPI_InitStruct); // 初始化SPI1
3. void SPI_StructInit(SPI_InitTypeDef* SPI_InitStruct)
-
作用:
将SPI_InitTypeDef
结构体填充为默认值。 -
参数:
-
SPI_InitStruct
:指向需要初始化的SPI_InitTypeDef
结构体。
-
使用实例:
SPI_InitTypeDef SPI_InitStruct;
SPI_StructInit(&SPI_InitStruct); // 填充为默认值
4. void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState)
-
作用:
启用或禁用SPI外设。 -
参数:
-
SPIx
:需要控制的SPI外设(如SPI1
)。NewState
:设置状态,ENABLE
表示启用,DISABLE
表示禁用。
-
使用实例:
SPI_Cmd(SPI1, ENABLE); // 启用SPI1
5. void SPI_I2S_ITConfig(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT, FunctionalState NewState)
-
作用:
启用或禁用SPI/I2S的中断。 -
参数:
-
SPIx
:需要控制的SPI/I2S外设(如SPI1
)。SPI_I2S_IT
:指定中断类型,例如SPI_I2S_IT_TXE
(发送缓冲区空中断)。NewState
:设置状态,ENABLE
表示启用,DISABLE
表示禁用。
-
使用实例:
SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_TXE, ENABLE); // 启用发送缓冲区空中断
6. void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState)
-
作用:
启用或禁用SPI/I2S的DMA功能。 -
参数:
-
SPIx
:需要控制的SPI/I2S外设(如SPI1
)。SPI_I2S_DMAReq
:指定DMA请求类型,例如SPI_I2S_DMAReq_Tx
(DMA发送请求)。NewState
:设置状态,ENABLE
表示启用,DISABLE
表示禁用。
-
使用实例:
SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE); // 启用SPI1的DMA发送请求
7. void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data)
-
作用:
通过SPI/I2S外设发送数据。 -
参数:
-
SPIx
:需要操作的SPI/I2S外设(如SPI1
)。Data
:要发送的数据。
-
使用实例:
SPI_I2S_SendData(SPI1, 0x55AA); // 发送数据0x55AA
8. uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx)
-
作用:
从SPI/I2S外设读取接收到的数据。 -
参数:
-
SPIx
:需要操作的SPI/I2S外设(如SPI1
)。
-
使用实例:
uint16_t receivedData = SPI_I2S_ReceiveData(SPI1); // 读取接收到的数据
9. void SPI_NSSInternalSoftwareConfig(SPI_TypeDef* SPIx, uint16_t SPI_NSSInternalSoft)
-
作用:
在软件管理模式下配置NSS信号。 -
参数:
-
SPIx
:需要操作的SPI外设(如SPI1
)。SPI_NSSInternalSoft
:设置NSS信号为高电平或低电平。
-
-
SPI_NSSInternalSoft_Set
:设置NSS为高。SPI_NSSInternalSoft_Reset
:设置NSS为低。
-
-
使用实例:
SPI_NSSInternalSoftwareConfig(SPI1, SPI_NSSInternalSoft_Set); // 设置NSS信号为高电平
10. FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG)
-
作用:
检查指定的SPI/I2S标志位状态。 -
参数:
-
SPIx
:需要操作的SPI/I2S外设(如SPI1
)。SPI_I2S_FLAG
:指定要检查的标志位,例如SPI_I2S_FLAG_TXE
(发送缓冲区空标志)。
-
使用实例:
if (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == SET) {// 发送缓冲区为空
}
11. void SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG)
-
作用:
清除指定的SPI/I2S标志位。 -
参数:
-
SPIx
:需要操作的SPI/I2S外设(如SPI1
)。SPI_I2S_FLAG
:指定要清除的标志位。
-
使用实例:
SPI_I2S_ClearFlag(SPI1, SPI_I2S_FLAG_OVR); // 清除溢出错误标志
12. ITStatus SPI_I2S_GetITStatus(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT)
-
作用:
检查指定的SPI/I2S中断状态。 -
参数:
-
SPIx
:需要操作的SPI/I2S外设(如SPI1
)。SPI_I2S_IT
:指定要检查的中断类型,例如SPI_I2S_IT_RXNE
(接收缓冲区非空中断)。
-
使用实例:
if (SPI_I2S_GetITStatus(SPI1, SPI_I2S_IT_RXNE) == SET) {// 接收缓冲区非空
}
6.硬件操作W25Q64
📎11-2 硬件SPI读写W25Q64.zip
User:
- 📎main.c
Hardware:
- 📎MySPI.c📎MySPI.h📎OLED.c📎OLED.h📎OLED_Font.h📎W25Q64.c📎W25Q64.h📎W25Q64_Ins.h
相关文章:
stm32之SPI
目录 1.SPI通信协议1.1 简介1.2 硬件电路1.3 移位示意图1.4 SPI时序基本单元1.5 SPI时序 2.W25Q642.1 简介2.2 硬件电路2.3 框图2.3.1 结构介绍2.3.2 混淆 2.4 Flash操作注意事项2.4.1 写操作2.4.2 读取操作 2.5 芯片手册补充2.5.1 状态寄存器2.5.2 指令集 3.软件操作W25Q644.S…...
数据库事务以及JDBC实现事务
一、数据库事务 数据库事务(Database Transaction)是数据库管理系统中的一个核心概念,它代表一组操作的集合,这些操作要么全部执行成功,要么全部不执行,即操作数据的最小执行单元,保证数据库的…...
C语言_函数调用栈的汇编分析
在 C 语言的底层实现中,函数调用栈是程序运行时内存管理的核心机制。它不仅负责函数间的控制转移,还管理局部变量、参数传递和返回值。本文将结合 C 语言代码和 x86-64 汇编指令,深入解析函数调用栈的工作原理。 一、函数调用栈的基本概念 …...
单片机调用printf概率性跑飞解决方法
最近移植软件到不同平台的单片机上时,遇到了软件概率性跑飞的问题,分析后原因均指向和printf相关的库函数(包括sprintf, vsnsprinft),在任务里调用这些函数就有概率在ucos切换任务时跑飞(中断)。…...
无人机空中物流优化:用 Python 打造高效配送模型
友友们好! 我是Echo_Wish,我的的新专栏《Python进阶》以及《Python!实战!》正式启动啦!这是专为那些渴望提升Python技能的朋友们量身打造的专栏,无论你是已经有一定基础的开发者,还是希望深入挖掘Python潜力的爱好者,这里都将是你不可错过的宝藏。 在这个专栏中,你将会…...
【RP2350】香瓜树莓派RP2350之低功耗
本文最后修改时间:2025年05月10日 01:57 一、本节简介 本节以树莓派pico2开发板为例,举例如何写一个低功耗驱动。 二、实验平台 1、硬件平台 1)树莓派pico2开发板 ①树莓派pico2开发板(作为仿真器) ②micro usb数…...
招行数字金融挑战赛数据赛道赛题一
赛题描述:根据提供的用户行为数据,选手需要分析用户行为特征与广告内容的匹配关系,准确预测用户对测试集广告的点击情况,通过AUC计算得分。 得分0.6120,排名60。 尝试了很多模型都没有能够提升效果,好奇大…...
仿真生成激光干涉包裹相位数据-用于深度学习训练!
该MATLAB/Octave代码实现了论文[1]中提出的相位提取算法,用于从灰度条纹图案中提取包裹相位图(相位值在-π到+π之间)。代码首先生成模拟的条纹图案,包含背景光、调制光和相位分布,并加入高斯噪声。随后,通过N步相移算法估计背景光和调制光,并计算包裹相位。结果显示包括…...
命令行解释器中shell、bash和zsh的区别
命令行解释器(Command Line Interpreter)是一个程序,它的主要作用是接收用户输入的命令,并执行相应的操作。它充当了用户与操作系统内核之间的桥梁。 一、什么是 Shell? Shell 是一个通用术语,指的是 命令…...
SQL 数据库监控:SQL语句监控工具与实践案例
SQL 数据库监控:SQL语句监控工具与实践案例 SQL语句监控的主要方法 SQL监控主要通过以下几种方式实现: 数据库内置监控功能:大多数数据库系统提供内置的SQL监控工具数据库性能视图/系统表:通过查询特定的系统视图获取SQL执行信…...
招行数字金融挑战赛数据分析赛带赛题二
赛题描述:根据提供的脱敏资讯新闻数据,选手需要对提供的训练集进行特征工程,构建资讯分类模型,对与测试集进行准确的新闻分类。 最终得分:0.8120。十二点关榜没看到排名,估算100? 训练集很小&am…...
llama.cpp初识
Llama.cpp:赋能本地大语言模型推理的核心引擎及其应用场景 引言:Llama.cpp 是什么? 大型语言模型 (LLM) 的兴起正在深刻改变人机交互和信息处理的方式。然而,这些强大的模型通常需要巨大的计算资源,使得它们在云端之…...
【EBNF】EBNF:扩展巴克斯-诺尔范式文件格式与实用写法详解
EBNF:扩展巴克斯-诺尔范式文件格式与实用写法详解 一、什么是 EBNF? Extended Backus-Naur Form (EBNF)是一种形式化的语法,用于指定编程语言或其他形式化语言的结构。它是Backus-Naur形式(BNF)的扩展,最初…...
Go语言运算符详解
文章目录 1. 算术运算符2. 关系运算符3. 逻辑运算符4. 位运算符5. 赋值运算符6. 其他运算符运算符优先级注意事项 Go语言提供了与其他语言类似的运算符,包括算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符等。这些运算符即可满足基本的运算需求。 1. 算…...
MySQL用户管理
目录 一、用户用户信息创建用户删除用户从远端登录修改用户密码 二、数据库的权限给用户授权回收权限 与Linux操作系统类似,MySQL中也有超级用户和普通用户之分。,如果一个用户只需要访问MySQL中的某一个数据库,甚至数据库中的某一个表&#…...
视频编解码学习三之显示器续
一、现在主流的显示器是LCD显示器吗? 是的,现在主流的显示器仍然是 LCD(液晶显示器,Liquid Crystal Display),但它已经细分为多种技术类型,并和其他显示技术(如OLED)形成…...
VSCode1.101.0便携版|中英文|编辑器|安装教程
软件介绍 Visual Studio Code是微软推出的一个强大的代码编辑器,功能强大,操作简单便捷,还有着良好的用户界面,设计得很人性化,旨在为所有开发者提供一款专注于代码本身的免费的编辑器。 软件安装 1、 下载安装包…...
Scala 中累加器的创建与使用格式详解
1. 内置累加器的创建与使用格式 1.1 创建内置累加器 // 通过 SparkContext 创建 val acc sc.longAccumulator("累加器名称") // Long 类型(默认初始值 0) val accDouble sc.doubleAccumulator("累加器名称") // Double 类型&a…...
【DNDC模型】双碳目标下DNDC模型建模方法及在土壤碳储量、温室气体排放、农田减排、土地变化、气候变化中的应用
由于全球变暖、大气中温室气体浓度逐年增加等问题的出现,“双碳”行动特别是碳中和已经在世界范围形成广泛影响。国家领导人在多次重要会议上讲到,要把“双碳”纳入经济社会发展和生态文明建设整体布局。同时,提到要把减污降碳协同增效作为促…...
深入剖析缓存与数据库一致性:Java技术视角下的解决方案与实践
一、缓存与数据库一致性问题根源 读写分离的架构矛盾 缓存作为数据库的“副本”,天然存在数据同步延迟。 高频读写场景下,缓存与数据库的更新顺序、失败重试等操作易引发不一致。 经典问题场景 场景1:先更新数据库,再删除缓存。…...
Anaconda环境中conda与pip命令的区别
文章目录 conda与pip的基本区别在Anaconda环境中的实际差异安装包环境管理依赖解决示例最佳实践建议 常见问题解答 conda与pip的基本区别 包来源与生态系统 conda:从Anaconda默认仓库或conda-forge等渠道获取包 不仅管理Python包,还能管理非Python依赖&…...
使用FastAPI和React以及MongoDB构建全栈Web应用05 FastAPI快速入门
一、FastAPI概述 1.1 什么是FastAPI FastAPI is a modern, high-performance Python web framework designed for building APIs. It’s rapidly gaining popularity due to its ease of use, speed, and powerful features. Built on top of Starlette, FastAPI leverages a…...
每日c/c++题 备战蓝桥杯(P1002 [NOIP 2002 普及组] 过河卒)
洛谷P1002 [NOIP 2002 普及组] 过河卒 题解 题目描述 过河卒是一道经典的动态规划题目。题目大意是:一个卒子从棋盘左上角(0,0)出发,要走到右下角(n,m),棋盘上有一个马在(x,y)位置,卒子不能经过马所在位置及其周围8个位置。求卒…...
kubectl系列(十二):查询pod的resource 配置
在 Kubernetes 中,可以通过 kubectl 命令快速查询 Pod 的资源请求(requests)和限制(limits)配置。以下是多种方法实现这一目标: 1. 查看 Pod 的资源请求和限制(基础版) 使用 kubec…...
前端面试2
1. 面试准备 1. 建立自己的知识体系 思维导图ProcessOn框架Vue elementUI自查 https://zh.javascript.info/ 借鉴 https://juejin.cn/post/6844904103504527374http://conardli.top/blog/article/https://github.com/mqyqingfeng/Bloghttp://47.98.159.95/my_blog/#html 2.技能…...
使用 Java 反射动态加载和操作类
Java 的反射机制(Reflection)是 Java 语言的一大特色,它允许程序在运行时检查、加载和操作类、方法、字段等元信息。通过 java.lang.Class 和 java.lang.reflect 包,开发者可以动态加载类、创建实例、调用方法,甚至在运行时构造新类。反射是 Java 灵活性的核心,广泛应用于…...
基于Dockers的Bitwarden的私有本地部署
基于Dockers的Bitwarden的私有本地部署 文章目录 基于Dockers的Bitwarden的私有本地部署 本文首发地址 https://h89.cn/archives/355.html bitwarden 默认连接的是国外服务器 https://bitwarden.com/ ,连接不是很稳定,也没有安全感,所以我选择…...
spark-Schema 定义字段强类型和弱类型
在数据处理和存储中,Schema(模式)定义了数据的结构和字段属性,其中字段的强类型和弱类型是重要的概念,直接影响数据的验证、存储和处理方式。以下是详细解释: 1. 强类型(Strongly Typed&#x…...
【第35节 数据库设计】
本章目录: 一、节概述二、知识详解1. 数据库设计的基本步骤2. 用户需求分析3. 概念结构设计(E-R建模)4. 逻辑结构设计5. 物理结构设计6. 数据库实施7. 数据库运行维护8. 商业智能(BI)与数据仓库数据仓库的特点: 9. OLT…...
C++基本知识 —— 缺省参数·函数重载·引用
C基本知识 —— 缺省参数函数重载引用 1. 缺省参数2. 函数重载3. 引用3.1 引用的基础知识3.2 引用的作用3.3 const 引用3.4 指针与引用的关系 1. 缺省参数 什么是缺省参数?缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数的时候,如…...
大数据基础——Ubuntu 安装
文章目录 Ubuntu 安装一、配置电脑二、安装系统 Ubuntu 安装 一、配置电脑 1、进入VMware 2、选择配置类型 3、选择硬件兼容性版本 4、当前虚拟机的操作系统 选择“稍后安装操作系统”(修改) 5、选择虚拟机将来需要安装的系统 选中“Linux”和选择…...
英伟达微调qwen2.5-32B模型,开源推理模型:OpenCodeReasoning-Nemotron-32B
一、模型概述 OpenCodeReasoning-Nemotron-32B 是一个大型语言模型,基于 Qwen2.5-32B-Instruct 开发,专为代码生成推理任务进行了后续训练,支持 32,768 个标记的上下文长度,适用于商业和非商业用途。 二、性能表现 在 LiveCode…...
苍穹外卖-创建阿里云oss工具包
添加配置信息: sky:alioss:endpoint: ***access-key-id: ***access-key-secret: ***bucket-name: *** 把配置的内容转换成对象: Component ConfigurationProperties(prefix "sky.alioss") Data public class AliOssProperties {private St…...
代码随想录训练营第二十一天 |589.N叉数的前序遍历 590.N叉树的后序遍历
589.N叉数的前序遍历: 状态:已做出 思路: N叉树的前序遍历和二叉树很像,我这里使用栈来实现。首先把根结点入栈,然后删除栈顶节点后把栈顶节点的所有子树都插入到栈,这里需要注意的是插入的方式是从最后一…...
鸿蒙跨平台开发教程之Uniapp布局基础
前两天的文章内容对uniapp开发鸿蒙应用做了一些详细的介绍,包括配置开发环境和项目结构目录解读,今天我们正式开始写代码。 入门新的开发语言往往从Hello World开始,Uniapp的初始化项目中已经写好了一个简单的demo,这里就不再赘述…...
面试中常问的设计模式及其简洁定义
🎯 一、面试中常问的设计模式及其简洁定义 模式名常被问到解释(简洁)单例模式✅ 高频保证一个类只有一个实例,并提供全局访问点。工厂模式✅ 高频创建对象的接口由子类决定,屏蔽了对象创建逻辑。抽象工厂模式✅提供多…...
关于 js:6. 网络与加密模块
一、AJAX AJAX(Asynchronous JavaScript And XML) 异步 JavaScript 与 XML(现在多为 JSON) 它允许网页在不重新加载整个页面的情况下,从服务器请求数据并更新页面内容。 主要用途: 提交表单时无需刷新页…...
量化交易系统开发经验分享--回测框架调研
一、前言 这段时间在集中做一个量化交易系统的开发任务,目前系统的MVP已经完成开发,后续会整理一些经验与成果和大家交流。刚好有一个前期做策略回测这块的调研,下面把调研的成果做一个整理总结先给大家分享一下,请批评指正。 在介…...
[学习]RTKLib详解:convkml.c、convrnx.c与geoid.c
本文是 RTKLlib详解 系列文章的一篇,目前该系列文章还在持续总结写作中,以发表的如下,有兴趣的可以翻阅。 [学习] RTKlib详解:功能、工具与源码结构解析 [学习]RTKLib详解:pntpos.c与postpos.c [学习]RTKLib详解&…...
【ajax基础】
提示:文章为 学习过程中的记录实践笔记。有问题欢迎指正。 文章目录 前言一、实现步骤二、完整示例三、封装总结 前言 AJAX 不是编程语言,是一种从网页访问web服务器的技术。 可以实现不刷新页面更新网页 在页面加载后从服务器请求/获取数据 在后台向服…...
Nodejs核心机制
文章目录 前言 前言 结合 Node.js 的核心机制进行说明: 解释事件循环的各个阶段。 答案 Node.js 事件循环分为 6 个阶段,按顺序执行: Timers:执行 setTimeout 和 setInterval 的回调。 Pending I/O Callbacks:处理系…...
Kubernetes 集群部署应用
部署 Nginx 应用 命令行的方式 1. 创建 deployment 控制器的 pod # --imagenginx:这个会从 docker.io 中拉取,这个网站拉不下来 # kubectl create deployment mynginx --imagenginx# 使用国内镜像源拉取 kubectl create deployment mynginx --imaged…...
【Linux篇】高并发编程终极指南:线程池优化、单例模式陷阱与死锁避坑实战
深入理解线程池设计与应用:高效并发编程的秘密 一. 线程池1.1 什么是线程池1.2 线程池的优点1.3 线程池的应用场景 二. 线程池设计三. 单例模式3.1 什么是单例模式3.2 单例模式特点3.3 实现单例模式方法3.3.1 饿汉实现⽅式3.3.2 懒汉实现⽅式 四. 线程安全和重入问题…...
学习和测试WebApi项目限制客户端ip访问接口(基于中间件)
WebApi项目需要限制仅允许有限的客户端访问接口,百度相关内容,网上很多介绍WebApi接口IP限流的文章,稍微调整就能用于限制IP访问,微软官网中也有文章介绍客户端 IP 安全列表(参考文献1),可以通过…...
闲鱼智能客服机器人-实现闲鱼平台7×24小时自动化值守
专为闲鱼平台打造的AI值守解决方案,实现闲鱼平台724小时自动化值守,支持多专家协同决策、智能议价和上下文感知对话。 🌟 核心特性 智能对话引擎 功能模块技术实现关键特性上下文感知会话历史存储轻量级对话记忆管理,完整对话历…...
Apache Ranger 2.2.0 编译
安装包下载: https://ranger.apache.org/download.html 编译环境: Linux centos7jdk 1.8maven 3.9.6gitpython 3 git 安装 yum -y install gitpython3安装 yum install epel-release -y yum install python3 python3-devel -y批量安装开发工具套件 …...
实战演练:用 AWS Lambda 和 API Gateway 构建你的第一个 Serverless API
实战演练:用 AWS Lambda 和 API Gateway 构建你的第一个 Serverless API 理论千遍,不如动手一遍!在前面几篇文章中,我们了解了 Serverless 的概念、FaaS 的核心原理以及 BaaS 的重要作用。现在,是时候把这些知识运用起来,亲手构建一个简单但完整的 Serverless 应用了。 …...
鱼眼相机生成-BEV鸟瞰图-入门教程
目录 原理介绍 1. IPM与BEV转换的核心原理 2. 尺度信息的来源 3. 尺度信息的准确性限制 4. 实际应用中的处理方法 代码实现: 360 BEV环视拼接算法 一、核心算法流程 三、实际应用挑战与优化 四、开源实现参考 原理介绍 1. IPM与BEV转换的核心…...
设计模式简述(十八)享元模式
享元模式 描述基本组件使用 描述 当内存中存在大量类似的对象时,可以考虑使用享元模式减少整体内存占用。 可以将相同的部分和不同的部分进行拆分,以达到多个对象共享相同部分内存的目的。 基本组件 通常享元对象通过共享的属性映射一个享元对象。 公…...
Google语法整理
以下是从整理出的 Google 语法: site:指定域名,如 “apache site:bbs.xuegod.cn”,可查询网站的收录情况 。 inurl:限定在 url 中搜索,如 “inurl:qq.txt”,可搜索 url 中包含特定内容的页面&a…...