【STM32】SPI通信外设硬件SPI读写W25Q64
SPI通信协议和W25Q64存储器芯片解读笔记:
【STM32】SPI通信协议&W25Q64Flash存储器芯片(学习笔记)-CSDN博客
SPI通信外设
SPI外设简介
- STM32内部集成了硬件SPI收发电路,可以由硬件自动执行时钟生成、数据收发等功能,减轻CPU的负担
- 可配置8位/16位数据帧、高位先行/低位先行(标黑常用)
- 时钟频率: fPCLK / (2, 4, 8, 16, 32, 64, 128, 256),就是SCK,一般体现的是传输速度,单位为Hz或者bit/s,最大进行2分频,72M/2=36MHz,I2C最大400kHz,SPI最大频率比I2C大90倍,SPI1时钟频率比SPI2大一倍
- 支持多主机模型、主或从操作
- 可精简为半双工/单工通信
- 支持DMA,DMA自动帮我们搬运数据
- 兼容I2S协议,数字音频信号传输的专用协议
- STM32F103C8T6 硬件SPI资源:SPI1(APB2的外设)、SPI2(APB1的外设)
兼容I2S协议: 应用场景
三条主要总线
STM32F103C8T6 有三条主要总线,分别是 AHB(高级高性能总线)、APB1(高级外设总线 1)和 APB2(高级外设总线 2),各总线频率如下:
- AHB 总线:其最高频率为 72MHz,通常系统时钟(SYSCLK)会直接作为 AHB 总线的时钟。
- APB1 总线:它的最高频率是 36MHz,APB1 总线挂载着一些低速外设,如 UART2 - UART5、SPI2、SPI3、I2C1、I2C2 等。
- APB2 总线:最高频率为 72MHz,APB2 总线挂载着高速外设,像 GPIO 端口、USART1、SPI1 等。
SPI框图


1. 引脚部分
- MOSI(Master Out Slave In):主设备输出,从设备输入引脚。在 SPI 通信中,主设备通过此引脚向从设备发送数据。
- MISO(Master In Slave Out):主设备输入,从设备输出引脚。从设备通过此引脚向主设备发送数据。
- SCK(Serial Clock):串行时钟引脚,由主设备产生,用于同步主设备和从设备之间的数据传输。
- NSS(Slave Select):从设备选择引脚,主设备通过拉低此引脚电平来选中对应的从设备。
2. 数据传输相关模块
- 发送缓冲区:用于暂存要发送的数据,数据由软件写入,然后发送到移位寄存器。
- 接收缓冲区:用于暂存接收到的数据,数据从移位寄存器读出后存储在此,等待软件读取。
- 移位寄存器:是 SPI 数据传输的核心部件。在发送数据时,它从发送缓冲区获取数据,按照时钟信号一位一位地通过 MOSI 引脚发送出去;在接收数据时,通过 MISO 引脚一位一位地接收数据,然后将完整的数据存入接收缓冲区。“LSBFIRST 控制位” 决定数据是低位(LSB) 在前还是高位(MSB) 在前进行传输。
3. 控制寄存器部分
- SPI_CR1:控制寄存器 1,包含多个控制位:
- LSBFIRST:控制数据传输时是低位在前还是高位在前。
- SPE(SPI Enable):使能 SPI 外设。
- BR[2:0]:波特率控制位,用于设置 SPI 的时钟频率,即波特率。
- MSTR(Master Select):主从模式选择位,设置该 SPI 设备为主设备还是从设备。
- CPOL(Clock Polarity):时钟极性控制位,决定 SCK 时钟信号的空闲状态电平(高电平或低电平)。
- CPHA(Clock Phase):时钟相位控制位,用于选择 SPI 的两种不同时钟相位模式。
- 还有其他如 CRC 相关控制位、双线双向模式控制位等,用于设置 SPI 的各种工作模式和特性。
- SPI_CR2:控制寄存器 2,包含以下控制位:
- TXEIE(Transmit Buffer Empty Interrupt Enable):发送缓冲区空中断使能位。
- RXNEIE(Receive Buffer Not Empty Interrupt Enable):接收缓冲区非空中断使能位。
- ERRIE(Error Interrupt Enable):错误中断使能位。
- SSOE(Slave Select Output Enable):从设备选择输出使能位。
- TXDMAEN(Transmit DMA Enable):发送直接内存访问(DMA)使能位。
- RXDMAEN(Receive DMA Enable):接收 DMA 使能位。
- SPI_SR:状态寄存器,包含多个状态标志位:
- BSY(Busy):忙标志位,当 SPI 正在进行数据传输时,该位被置 1。
- OVR(Overrun):溢出标志位,当接收缓冲区已满,又有新的数据到来时,该位被置 1。
- MODF(Mode Fault):模式错误标志位,当 SPI 在主模式下 NSS 引脚被意外拉低时,该位被置 1。
- CRCERR(CRC Error):CRC 校验错误标志位。
- TXE(Transmit Buffer Empty):发送缓冲区空标志位。
- RXNE(Receive Buffer Not Empty):接收缓冲区非空标志位。
- 波特率发生器:根据 SPI_CR1 中 BR [2:0] 的设置,产生相应频率的时钟信号 SCK,以控制数据传输的速率。
- 主控制电路:负责协调 SPI 各个模块的工作,根据控制寄存器的设置来管理数据的发送和接收过程。
- 通信电路:在 SPI 通信过程中,处理数据的收发以及与其他模块的交互,确保通信的正常进行。
大致分为两部分,左上角就是数据寄存器和移位寄存器配合部分;和串口、I2C的设计思路具有异曲同工之妙,主要为了实现数据流传输,右下角部分就是控制逻辑了;下面来看看框图细节:
首先,左上角核心部分,就是移位寄存器,右边的数据从低位,一位一位的从MOSI移出,然后MISO,一位一位的移入左边的数据高位,显然移位寄存器是一个右移的状态,所以目前图上表示的是低位先行的配置,对应右下角有一个LSBFLRST控制位,这一位可以控制是低位先行还是高位先行,可以查一下数据手册

目前框图的LSBFIRST状态应该是1,低位先行,如果LSBFIRST给0的话,就是高位先行,这个框图还要变动一下,就移位寄存器变为左移,输出,从左边移出去,输入,从右边移进来。然后就是两个缓冲区,这两个缓冲区实际上就是数据寄存器DR,下面发送缓冲区就是发送数据寄存器TDR,上面接收缓冲区就是接收数据寄存器RDR,和串口那里一样,TDR和RDR占用同一个地址,统一叫做DR;
连续发送数据流过程:在主设备中,将要发送的数据写入发送缓冲区(TDR)。当移位寄存器没有数据移位时,TDR的数据就会转入至移位寄存器进行移位,当发送缓冲区中的数据发送完成后,SPI 状态寄存器(SPI_SR)中的 TXE(发送缓冲区空)标志位置 1。然后移位寄存器工作
移位寄存器工作:发送缓冲区的数据会在 SCK 时钟信号的驱动下,一位一位地移入移位寄存器,然后通过 MOSI 引脚发送到从设备。同时,从设备在 SCK 时钟的同步下,通过 MISO 引脚将数据发送回主设备(如果是双向通信)。
数据接收与存储:在主设备接收数据时,从设备发送的数据在 SCK 时钟信号的同步下,通过 MISO 引脚一位一位地移入主设备的移位寄存器,然后再存入接收缓冲区。当接收缓冲区中有数据时,SPI_SR 中的 RXNE(接收缓冲区非空)标志位置 1。
数据读取:软件可以通过查询 RXNE 标志位或者利用接收缓冲区非空中断,从接收缓冲区(SPI_DR)中读取接收到的数据。如果是连续接收数据,需要及时处理已接收的数据,以便接收新的数据,保证接收过程的连续性。
数据传输设计思路SPI和I2C和串口的区别:设计思路区别
框图中的“波特率发生器”有什么作用?
1. 定义与基本功能
波特率发生器是一种用于生成特定频率时钟信号的电路模块。在 SPI(Serial Peripheral Interface,串行外设接口)通信中,它的主要功能是产生串行时钟信号 SCK(Serial Clock)。SCK 信号用于同步主设备和从设备之间的数据传输,确保数据能够按照预定的速率和时序准确地发送和接收。
2. 波特率的设置与控制
- 控制位设置:波特率发生器的工作受 SPI 控制寄存器(如 SPI_CR1 中的 BR [2:0] 位)的控制。通过对这些控制位进行编程设置,可以改变波特率发生器的输出时钟频率。例如,在 STM32 的 SPI 模块中,BR [2:0] 位有不同的组合,对应不同的分频系数。
- 具体示例:当 BR [2:0] 设置为不同的值时,波特率发生器会将系统时钟(通常是 AHB 总线时钟)按照相应的分频系数进行分频,从而得到不同频率的 SCK 时钟信号。如果系统时钟频率为 72MHz,当 BR [2:0] 设置为 “110” 时,对应的分频系数为 128,那么此时 SCK 的频率就是 72MHz / 128,约为 562.5kHz。

3. 对通信的影响
- 数据传输速率:波特率发生器生成的 SCK 时钟频率决定了 SPI 通信的数据传输速率。较高的 SCK 频率意味着数据可以在更短的时间内传输完成,从而提高通信效率。但同时,过高的频率可能会受到传输线的电气特性、设备的处理能力等因素的限制,导致数据传输错误。
- 设备兼容性:不同的 SPI 设备可能对 SCK 时钟频率有不同的要求。波特率发生器可以根据与之通信的从设备的特性,灵活地调整 SCK 时钟频率,以确保主设备和从设备之间能够正常通信。例如,一些低速的 SPI 从设备可能无法处理过高频率的时钟信号,此时就需要通过波特率发生器降低 SCK 的频率。
4. 与其他模块的协同工作
波特率发生器产生的 SCK 时钟信号与 SPI 的其他模块协同工作。在数据发送过程中,发送缓冲区的数据会在 SCK 时钟信号的驱动下,一位一位地通过 MOSI 引脚发送出去;在数据接收过程中,MISO 引脚上的数据会在 SCK 时钟信号的同步下,一位一位地被移入移位寄存器。同时,波特率发生器的工作也受到主控制电路的管理和协调,以确保整个 SPI 通信过程的稳定和准确。
NSS任何实现多主机切换功能?NSS多主机切换
SPI基本结构 
主模式全双工连续传输 
1. 时钟与数据传输
- SCK(时钟信号):由主设备产生,为数据传输提供同步时钟,确保主从设备数据收发时序一致。
- 数据发送(MOSI 输出):主设备通过 MOSI 依次发送数据
0xF1、0xF2、0xF3,每个数据按位(b0~b7)在 SCK 驱动下传输。 - 数据接收(MISO 输入):主设备通过 MISO 接收从设备返回的数据
0xA1、0xA2、0xA3,同样按位同步接收。
2. 关键标志位变化
- TXE(发送缓冲区空标志):
- 硬件自动置位:当发送缓冲区数据发送到移位寄存器后,TXE 被硬件置 1,表示发送缓冲区空闲。
- 软件清除:需通过软件向
SPI_DR写入新数据来清除该标志(如写入0xF1后,TXE 置 1,再写入0xF2即清除)。
- RXNE(接收缓冲区非空标志):
- 硬件自动置位:当接收缓冲区收到完整数据(如
0xA1)后,RXNE 被硬件置 1。 - 软件清除:通过软件读取
SPI_DR数据来清除该标志(如读取0xA1后,RXNE 标志清除)。
- 硬件自动置位:当接收缓冲区收到完整数据(如
- BSY(忙标志):
- 硬件自动置位:SPI 开始数据传输时,BSY 置 1,表示外设处于忙状态。
- 硬件自动清除:所有数据传输完成后,BSY 由硬件自动清零。
3. 软件操作流程
- 发送数据:软件先写入第一个数据(如
0xF1到SPI_DR),等待 TXE=1(发送缓冲区空),再写入下一个数据(如0xF2),循环直至数据发送完毕。 - 接收数据:软件等待 RXNE=1(接收缓冲区有数据),然后从
SPI_DR读取数据(如读取0xA1),完成一次接收操作,循环处理后续数据。
4. 整体时序逻辑
- 发送缓冲区、接收缓冲区与标志位
TXE、RXNE配合,确保数据连续发送和接收。BSY标志则全程反映 SPI 外设的工作状态,直到所有数据传输结束才恢复空闲状态。通过这种机制,SPI 实现了主模式全双工连续数据传输的高效控制。
非连续传输 
这张图展示了 STM32 SPI 外设工作在 非连续传输发送模式(BIDIMODE=0 且 RXONLY=0)时,TXE(发送缓冲区空标志)和 BSY(忙标志)的变化过程,具体解析如下:
1. 基础配置与信号
- 时钟配置:图中示例配置
CPOL=1(时钟空闲状态为高电平)、CPHA=1(数据在时钟第二个边沿采样),定义了 SPI 通信的时钟极性和相位。 - SCK(时钟信号):由主设备生成,为数据传输提供时序同步,驱动 MOSI 引脚按位发送数据。
- MOSI(输出):主设备通过 MOSI 依次发送数据
0xF1、0xF2、0xF3,每个数据按b0~b7位在 SCK 时钟下逐位传输。
2. 关键标志位变化
- TXE(发送缓冲区空标志):
- 当发送缓冲区数据转移到移位寄存器后,硬件自动将
TXE置 1,表示缓冲区空闲。 - 软件需通过向
SPI_DR写入新数据来清除该标志(如写入0xF1后,TXE置 1,写入0xF2时清除)。
- 当发送缓冲区数据转移到移位寄存器后,硬件自动将
- BSY(忙标志):
- 硬件控制:SPI 开始数据传输时,
BSY置 1,表示外设处于忙状态;所有传输结束后,BSY自动清零。
- 硬件控制:SPI 开始数据传输时,
3. 非连续传输的软件操作特点
- 数据写入延迟:与连续传输不同,非连续传输中软件写入数据存在延迟。例如:
- 先写入
0xF1到SPI_DR,启动第一次传输。 - 等待
TXE=1后,较晚写入0xF2;再次等待TXE=1后,又较晚写入0xF3。
- 先写入
- 结束等待:最后需等待
BSY=0,确认所有非连续的传输操作完全结束,SPI 外设回归空闲状态。
4. 整体时序逻辑
图中体现了非连续传输的 “间隔性”:每次数据发送依赖软件主动写入新数据触发,且两次传输之间存在等待 TXE 标志的间隔。BSY 标志全程反映 SPI 工作状态,仅在数据传输过程中保持为 1,其余时间(如等待软件写入新数据阶段)可能短暂清零,直到所有传输完成最终清零。
非连续传输在SCK频率低时无影响;
在SCK频率高时有缺点:不同SCK频率不同间隙的示波器波形
最后看手册很重要:坚持到最后都牛逼
硬件SPI读写W25Q64
本节代码在软件SPI读写W25Q64芯片基础上做修改,代码参考软件SPI读写W25Q64芯片:【江协科技STM32】软件SPI读写W25Q64芯片(学习笔记)-CSDN博客
接线图

因为这里是硬件读写SPI,涉及STM32内部硬件外设的引脚,都要查询引脚定义表
引脚定义表 

引脚复用:

硬件SPI
硬件SPI代码实际上就两部分:
1、SPI外设初始化代码
Ⅰ开启时钟,开启SPI和GPIO的时钟
Ⅱ 初始化GPIO口,其中,SCK和MOSI是由硬件外设控制的输出信号,所以配置为复用推挽输出;MISO是由硬件外设控制的输入信号,所以配置为上拉或者浮空输入。
Ⅲ 配置SPI外设,初始化SPI。
Ⅳ 使能SPI。
void HerSPI_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); //开启SPI1的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA4引脚初始化为推挽输出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA5和PA7引脚初始化为复用推挽输出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); //将PA6引脚初始化为上拉输入/*SPI初始化*/SPI_InitTypeDef SPI_InitStructure; //定义结构体变量SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //模式,选择为SPI主模式SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //方向,选择2线全双工SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //数据宽度,选择为8位SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //先行位,选择高位先行SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128; //波特率分频,选择128分频SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //SPI极性,选择低极性SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //SPI相位,选择第一个时钟边沿采样,极性和相位决定选择SPI模式0SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS,选择由软件控制SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC多项式,暂时用不到,给默认值7SPI_Init(SPI1, &SPI_InitStructure); //将结构体变量交给SPI_Init,配置SPI1/*SPI使能*/SPI_Cmd(SPI1, ENABLE); //使能SPI1,开始运行/*设置默认电平*/HerSPI_W_SS(1); //SS默认高电平
}
SCK和MOSI是由硬件外设控制的输出信号,所以配置为复用输出,至于为什么,看这个:【STM32】I²CC通信外设&硬件I²CC读写MPU6050(学习笔记)_编缉i2c地址-CSDN博客
找到复用引脚输入输出标题有解释
2、SPI外设操作时序,完成交换应该字节的流程
①等待TXE为1,等待发送寄存器位空,发送寄存器不为空就不着急写
②写入数据到发送数据寄存器,开始产生时序
③等待RXNE=1,接收数据寄存器非空
④ 读取接收到的数据并返回
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET); //等待发送数据寄存器空SPI_I2S_SendData(SPI1, ByteSend); //写入数据到发送数据寄存器,开始产生时序while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET); //等待RXNE 接收数据寄存器非空return SPI_I2S_ReceiveData(SPI1); //读取接收到的数据并返回
}
库函数的使用本章涉及较少,具体可自行查看库函数:
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct); //初始化SPI
void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState); //使能SPI运行
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);//发送数据,通过SPIx/I2Sx外设传输数据。
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx); //返回SPIx/I2Sx外设最近接收到的数据。FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG); 检查指定的SPI/I2S标志是否设置
相关文章:
【STM32】SPI通信外设硬件SPI读写W25Q64
SPI通信协议和W25Q64存储器芯片解读笔记: 【STM32】SPI通信协议&W25Q64Flash存储器芯片(学习笔记)-CSDN博客 SPI通信外设 SPI外设简介 STM32内部集成了硬件SPI收发电路,可以由硬件自动执行时钟生成、数据收发等功能&…...
风暴潮、潮汐潮流模拟:ROMS模型如何精准预测海洋现象?
海洋数值模拟的崛起与 ROMS 的关键角色 🌊在海洋科学的浪潮中,海洋数值模拟正以迅猛之势崛起,成为科研与实际应用领域不可或缺的利器。ROMS(Regional Ocean Modeling System)作为其中的佼佼者,凭借其高效、…...
Spring JDBC Template与事务管理:基于XML与注解的实战指南
摘要 本文深入解析Spring JDBC Template与事务管理的核心技术,结合XML配置与注解方式两种主流方案,通过转账案例完整演示数据库操作与事务管理的最佳实践。文章涵盖JDBC Template的核心用法、事务配置语法、常见问题及性能优化建议,帮助开发…...
【Keil5-开发技巧】
Keil5-开发技巧 ■ Keil5利用AStyle插件格式化代码第一步:下载AStyle插件第二步:添加AStyle插件第三步:AStyle插件介绍■ 一键转UTF-8编码■ Keil5利用AStyle插件格式化代码 第一步:下载AStyle插件 AStyle下载 第二步:添加AStyle插件 解压后 astyle-3.6.7-x64 在重命…...
Uniapp:基于 Vue.js 的高效跨平台开发框架
Uniapp 介绍 Uniapp(全称:Universal Application)是一款基于 Vue.js 的跨平台开发框架,由 DCloud 公司开发和维护。它允许开发者使用一套代码同时构建运行在多个平台(如 iOS、Android、Web、小程序、快应用等…...
form 表单内容序列化成一个字符串
html <form id"form1" action"http://localhost:8080/xxx" method"post"> <p >关键字1: <input type "text" name"keyword1" /></p> <p >关键字2: <input t…...
电脑上不了网普通用户排除方法
1:首先通过电脑的运行/CMD/ipconfig /all 命令查看电脑的ip地址是否正常如图: 2:在命令行中运行:ping 127.0.0.1 如图则正常,否则要重新安装网卡驱动 程序。 3:用ping命令,ping一下同网段的电…...
【C#】WinForm自定义控件及窗体
前言 WinForm(Windows Forms)是Microsoft.NET框架中的技术,用于开发Windows桌面应用程序。它提供了一套丰富的控件和组件。通过拖放控件、编写事件处理程序等方式快速构建用户界面。 通过属性窗口定制这些控件的外观和行为。 通过数据绑定&am…...
基于虚拟知识图谱的语义化决策引擎
在数字化转型浪潮中,企业数据资产的价值释放面临两大挑战:海量异构数据的整合困局与业务-技术语义鸿沟。本文解析飞速创软灵燕智能体平台的创新解决方案——通过构建业务语义驱动的虚拟知识图谱系统,实现企业数据的智能认知与决策赋能。 一、…...
七天免登录 为什么不能用seesion,客户端的http请求自动携带cookei的机制(比较重要)涉及HTTP规范
如果是七天免登录,和session肯定没关系,因为session不能持久化,主要是客户端一旦关闭,seesion就失效了/// 所以必须是能持久化的,这就清晰了,要莫在的服务器保存,要摸在客户端设置 cook机制 1. 使用Cookie实现七天免登录 前端(登…...
HarmonyOS:@AnimatableExtend 装饰器自学指南
在最近的项目开发中,我遇到了需要实现复杂动画效果的需求。在探索解决方案的过程中,我发现了 AnimatableExtend 装饰器,它为实现动画效果提供了一种非常灵活且强大的方式。然而,在学习这个装饰器的过程中,我发现相关的…...
主流NoSQL数据库类型及选型分析
在数据库领域,不同类型的数据库针对不同场景设计,以下是四类主流NoSQL数据库的对比分析: 一、核心特性对比 键值数据库(Key-Value) 数据模型:简单键值对存储 特点:毫秒级读写、高并发、无固定…...
kubernetes|云原生|kubeadm-1.25.7集群单master+外部etcd集群+kubeadm-init+cri-docker文件形式快速部署
一、 前言和写作原因 本文做一个kubernetes集群部署记录,实在是部署的东西太多了,害怕忘记,kubernetes集群的部署又细节比较多,因此,在这里做一个尽量详细的记录 三个VMware虚拟机,IP分别为192.168.123.…...
Qt 导入TagLib库
文章目录 0. 前言和环境介绍1. 下载TagLib2. 下载zlib3. 修改.pro文件4. 测试代码 0. 前言和环境介绍 最近在使用Qt写一个播放器,需要解析mp3文件,于是研究了一下如何导入TagLib库 Qt构建套件:Desktop Qt6.8.2 MinGW64-bit Qt Creator安装目录: D:\bit…...
新能源汽车充换站如何实现光储充一体化管理?
长三角某换电站光伏板晒到发烫,却因电网限电被迫切机;北京五环充电站每月多缴6万超容费;深圳物流车充电高峰排队3小时...当95%的充换站深陷“用不起绿电、扛不住扩容、算不清碳账”困局,安科瑞用一组真实数据撕开行业潜规则&#…...
【数据分享】2000—2024年我国省市县三级逐年归一化植被指数(NDVI)数据(年平均值/Shp/Excel格式)
之前我们分享过2000-2024年我国逐年的归一化植被指数(NDVI)栅格数据,该逐年数据是取的当年月归一化植被指数(NDVI)的年平均值。!该数据来源于NASA定期发布的MOD13A3数据集!很多小伙伴拿到数据后…...
【leetcode题解】链表
目录 链表 两数相加 两两交换链表中的节点 重排链表 合并 K 个升序链表(困难) K 个一组翻转链表 链表 1. 常用技巧 画图!!!(直观形象,便于我们理解)引入虚拟“头”节点…...
本地部署Dify 添加Ollama模型DeepSeek
1、准备工作 本地ollama 加载DeepSeek。 安装并登录Dify。 2、添加Ollama模型服务商 在设置-》模型服务上里添加Ollama模型服务商,也叫插件。 3、添加DeepSeek 使用终端命令 ollama list查询deepseek名称,如deepseek-r1:14b。 在Ollama插件冲添加…...
QEMU源码全解析 —— 块设备虚拟化(7)
接前一篇文章:QEMU源码全解析 —— 块设备虚拟化(6) 本文内容参考: 《趣谈Linux操作系统》 —— 刘超,极客时间 《QEMU/KVM源码解析与应用》 —— 李强,机械工业出版社 特此致谢! QEMU初始化阶段的块设备虚拟化 从模板生成类和类的实例化 上一回在讲解QEMU中类继承…...
图论 | 岛屿数量(深搜,广搜)
岛屿数量 acm模式:99.岛屿数量 核心代码模式: 200. 岛屿数量 思路 遍历grid,如果它是1,则通过bfs/dfs将这个小岛的grid变为0 dfs def dfs(grid,i,j):if i<0 or j<0 or i>len(grid) or j>len(grid[0]):returnif g…...
iOS:GCD信号量、同步、异步的使用方法
信号量的详细用法,可以用此方法进行队列管理 -(void)dispatchSignal{//crate的value表示,最多几个资源可访问dispatch_semaphore_t semaphore dispatch_semaphore_create(3);dispatch_queue_t quene dispatch_get_global_queue(DISPATCH_QUEUE_PRIORI…...
MSP430 Proteus 仿真作品
https://www.dong-blog.fun/post/1998 1 、 电子万年历(采用 DS1302 及 及 TC72 等芯片) 基本要求: 可显示年、月、日、星期、时、分、秒; 有温度显示功能。 发挥部分: 可调节时间和日期; 有农历显示功能 &…...
Windows打开ftp局域网共享
前提是windows已经设置好开机账号密码了,否则教程不适用 第一先打开电脑ftp共享配置 点击保存即可 2.设置要共享到其他电脑的文件路径(如果你要共享整个盘你就设置整个盘,如果是共享盘中某文件就设置某文件,这里是某文件&#x…...
基于HTML的邮件发送状态查询界面设计示例
以下是一个基于HTML的邮件发送状态查询界面设计示例,结合筛选功能、状态展示和重新发送操作,采用Bootstrap框架实现响应式布局: <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"&…...
聊聊langchain4j的MCP
序 本文主要研究一下langchain4j对Model Context Protocol (MCP) 的支持 MCP MCP协议规定了两种传输方式: HTTP:客户端请求一个SSE(Server-Sent Events)通道以从服务器接收事件,然后通过HTTP POST请求发送命令。这…...
我爱学算法之——滑动窗口攻克子数组和子串难题(中)
学习算法,继续加油!!! 一、将 x 减到 0 的最小操作数 题目解析 来看这一道题,题目给定一个数组nums和一个整数x;我们可以在数组nums的左边或者右边进行操作(x减去该位置的值)&#…...
从零开始上手huggingface
1. 环境配置 # git 安装:https://git-scm.com/ # git lfs安装:https://git-lfs.com git lfs install # huggingface-cli 安装:https://huggingface.co/docs/hub/index pip install huggingface_hub2. 网站直接下载模型 可能会中断ÿ…...
MySQL 死锁问题分析与解决方案
**** 一、死锁原因分析 死锁通常由以下场景引发: 事务执行顺序不一致:多个事务以不同顺序访问相同资源。索引缺失:全表扫描导致行锁升级为表锁。长事务或大事务:长时间持有锁资源,增加冲突概率。隔离级别设置&#x…...
用 pytorch 从零开始创建大语言模型(六):对分类进行微调
用 pytorch 从零开始创建大语言模型(六):对分类进行微调 6 微调用于分类6.1 微调的不同类别6.2 准备数据集6.3 创建数据加载器6.4 使用预训练权重初始化模型6.5 添加分类头部6.6 计算分类损失和准确率6.7 在监督数据上微调模型6.8 使用LLM进…...
dify1.1.1安装
1、 按照GitHub上操作 下载源码,没有安装git的,可以下载成zip包, unzip 解压 git clone https://github.com/langgenius/dify.git cd dify cd docker cp .env.example .env2、启动前 ,先改下 docker-compose.yaml,…...
