STM32通过SPI软件读写W25Q64

文章目录

1. W25Q64

2. 硬件电路

3. W25Q64框架图

4. 软件/硬件波形对比

5. 代码实现

5.1 MyI2C.c

5.2 MyI2C.h

5.3 W25Q64.c

5.4 W25Q64.h

5.5 W25Q64_Ins.h

5.6 main.c


1. W25Q64

对于SPI通信和W25Q64的详细解析可以看下面这篇文章

STM32单片机SPI通信详解-CSDN博客

对于STM32通过SPI硬件读写W25Q64的代码,可以看下面这篇文章

STM32通过SPI硬件读写W25Q64-CSDN博客

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

地址设计

  • 地址位数:指用于寻址的二进制位数。在计算机系统中,每个内存单元都有一个唯一的地址,通过地址可以访问和引用内存中的数据或指令。
  • 地址总线:用于地址传输的总线。W25Q64 的 24 位地址总线意味着它可以访问 2^24 个地址,即 16,777,216 个字节(16MB)的空间。
  • 地址位数与存储容量:地址位数越多,能寻址的存储空间越大。例如,8 位地址可以寻址 256 个字节,16 位地址可以寻址 65,536 个字节(64KB)。

W25Q64 的存储空间

  • 存储容量:W25Q64 具体的存储容量为 64Mbit,即 8MB,但其地址总线的设计可以支持更大的寻址空间。
  • 数据组织:存储器通常按字节组织,每个字节有唯一的地址。W25Q64 可以通过 24 位地址总线访问每个字节,这使得数据读写操作更加灵活和高效。

2. 硬件电路

引脚功能

VCC、GND

电源(2.7~3.6V)

CS(SS)

SPI片选

CLK(SCK)

SPI时钟

DI(MOSI)

SPI主机输出从机输入

DO(MISO)

SPI主机输入从机输出

WP

写保护

HOLD

数据保持

WP(Write Protect):写保护

WP 引脚用于实现硬件写保护功能。WP 引脚为低电平时,写保护有效,无法进行写操作;WP 引脚为高电平时,可以进行写操作。

HOLD:数据保持

HOLD 引脚为低电平时,芯片进入保持状态。当在进行正常的读写操作时,如果需要中断 SPI 通信以操作其他设备,可以将 HOLD 引脚置为低电平。此时,芯片会保持当前状态但释放总线控制权。这样可以在不中断当前操作的前提下,使用 SPI 总线与其他设备通信。操作完毕后,将 HOLD 引脚置为高电平,芯片将恢复并继续之前的操作。这个功能允许在不终止总线操作的情况下,实现 SPI 总线的中断处理。

3. W25Q64框架图

状态寄存器的 BUSY 和 WEL(Write Enable Latch)

BUSY:忙碌位

  • 功能:当设备正在执行写操作,例如页编程、扇区擦除、块擦除或整片擦除时,BUSY 位会被置为 1。这表示设备正在忙碌,不能接受新的写操作命令。此期间,任何尝试进一步指令的操作都会被忽略。
  • 状态变化:在写状态寄存器指令结束后,BUSY 位清零,表示设备已经准备好接受新指令。

WEL:写使能锁存位

  • 功能:在执行写使能指令后,WEL 位会被置为 1,表示芯片可以进行写入操作。当设备处于写失能状态时,WEL 位清零,表示不能进行写入操作。

什么情况下处于写失能状态

上电初始化

  • 默认状态:当芯片上电后,默认处于写失能状态,WEL 位为 0。

执行写入操作后

  • 写入指令结束:包括写使能指令、页编程、扇区擦除、块擦除等操作,在这些操作完成后,WEL 位会自动清零。这意味着每次执行写入操作后,设备自动进入写失能状态,不需要手动进行写失能操作。

操作顺序

  • 写使能:在进行任何写入操作前,必须先执行写使能指令,将 WEL 位置为 1。
  • 写入指令:执行写入指令后,WEL 位会被清零,确保安全性和数据完整性。每次写使能只对随后的单次写操作有效,保证每次写入前都需要显式地使能写入操作。

​​​​​​​

4. 软件/硬件波形对比

​​​​​​​

硬件数据波形变化紧贴SCK边沿 软件数据变化在边沿后有些延迟。

I2C:SCL低电平期间数据变化,高电平期间数据采样 SPI:SCK下降沿数据移出,上升沿数据移入。 两者最终波形的表现形式都是一样的,无论是下降沿变化还是低电平期间变化,它们都 是一个意思,都可以作为数据变化的时刻。

5. 代码实现

软件SPI读写W25Q64

对主机而言:时钟、主机输出、片选都是输出引脚为推挽输出,主机输入是输入引脚为浮空或者上拉

硬件与软件的区别

  • 硬件操作:SS(Slave Select)下降沿和数据移出是同时发生的,包括后续的SCK(Serial Clock)下降沿和数据移出也是同步进行的。
  • 软件操作:先发生SS下降沿或SCK下降沿,触发数据移出,然后在SCK上升沿移入数据。

数据交换过程

  1. SS下降沿:在SS下降沿之后,主机和从机同时开始移出数据。

    • 主机:移出最高位数据到MOSI(Master Out Slave In)。
    • 从机:移出最高位数据到MISO(Master In Slave Out),MISO的数据变化由从机控制,主机不需要干预。
  2. 掩码使用:通过掩码逐位提取数据进行操作,不会改变原始数据,数据可以重复使用。

    • 第一步:写MOSI,发送ByteSend的最高位。
    • 第二步:SCK上升沿触发移入数据。从机会在SCK上升沿自动读取MOSI的数据,主机则读取MISO的数据,接收从机的最高位。
    • 第三步:SCK下降沿触发移出下一位数据。在SCK下降沿之后,主机移出B6位数据,然后进入循环,SCK上升沿触发主机接收从机次高位,再SCK下降沿移出下一位,循环进行直到完成字节交换。

程序步骤

  1. 写MOSI:发送ByteSend的最高位数据。
  2. SCK上升沿:主机和从机同时移入数据,主机读取MISO的数据。此时,从机会自动读取MOSI的数据。
  3. SCK下降沿:触发主机和从机移出下一位数据。

在循环过程中

  • SCK上升沿:主机读取从机次高位数据。
  • SCK下降沿:移出下一位数据。

在函数结束时,将SCK置为0,表示时序结束。

具体步骤和时序图解释

  1. SS下降沿:主从机同时开始数据交换。
  2. 第一步:主机通过MOSI发送最高位数据。
  3. 第二步:SCK上升沿触发主从机同时读取数据,主机读取MISO上的数据。
  4. 第三步:SCK下降沿触发主从机移出下一位数据。

此过程循环,直到所有位的数据交换完成。函数结束时,将SCK置为0,表示一个字节的数据交换完成。

5.1 MyI2C.c

#include "stm32f10x.h"                  // Device header/*引脚配置层*//*** 函    数:SPI写SS引脚电平* 参    数:BitValue 协议层传入的当前需要写入SS的电平,范围0~1* 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SS为低电平,当BitValue为1时,需要置SS为高电平*/
void MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);		//根据BitValue,设置SS引脚的电平
}/*** 函    数:SPI写SCK引脚电平* 参    数:BitValue 协议层传入的当前需要写入SCK的电平,范围0~1* 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SCK为低电平,当BitValue为1时,需要置SCK为高电平*/
void MySPI_W_SCK(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);		//根据BitValue,设置SCK引脚的电平
}/*** 函    数:SPI写MOSI引脚电平* 参    数:BitValue 协议层传入的当前需要写入MOSI的电平,范围0~0xFF* 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置MOSI为低电平,当BitValue非0时,需要置MOSI为高电平*/
void MySPI_W_MOSI(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);		//根据BitValue,设置MOSI引脚的电平,BitValue要实现非0即1的特性
}/*** 函    数:I2C读MISO引脚电平* 返 回 值:协议层需要得到的当前MISO的电平,范围0~1* 注意事项:此函数需要用户实现内容,当前MISO为低电平时,返回0,当前MISO为高电平时,返回1*/
uint8_t MySPI_R_MISO(void)
{return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);			//读取MISO电平并返回
}/*** 函    数:SPI初始化* 注意事项:此函数需要用户实现内容,实现SS、SCK、MOSI和MISO引脚的初始化*/
void MySPI_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟/*GPIO初始化*/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);					//将PA4、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引脚初始化为上拉输入/*设置默认电平*/MySPI_W_SS(1);											//SS默认高电平MySPI_W_SCK(0);											//SCK默认低电平
}/*协议层*///SPI起始
void MySPI_Start(void)
{MySPI_W_SS(0);				//拉低SS,开始时序
}//SPI终止
void MySPI_Stop(void)
{MySPI_W_SS(1);				//拉高SS,终止时序
}/*** 函    数: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次,依次交换每一位数据{MySPI_W_MOSI(ByteSend & (0x80 >> i));		//使用掩码的方式取出ByteSend的指定一位数据并写入到MOSI线MySPI_W_SCK(1);								//拉高SCK,上升沿移出数据if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}	//读取MISO数据,并存储到Byte变量//当MISO为1时,置变量指定位为1,当MISO为0时,不做处理,指定位为默认的初值0MySPI_W_SCK(0);								//拉低SCK,下降沿移入数据}return ByteReceive;								//返回接收到的一个字节数据
}

5.2 MyI2C.h

#ifndef __MYSPI_H
#define __MYSPI_Hvoid MySPI_Init(void);
void MySPI_Start(void);
void MySPI_Stop(void);
uint8_t MySPI_SwapByte(uint8_t ByteSend);#endif

5.3 W25Q64.c

#include "stm32f10x.h"                  // Device header
#include "MySPI.h"
#include "W25Q64_Ins.h"//W25Q64初始化
void W25Q64_Init(void)
{MySPI_Init();					//先初始化底层的SPI
}/*** 函    数:MPU6050读取ID号* 参    数:MID 工厂ID,使用输出参数的形式返回* 参    数:DID 设备ID,使用输出参数的形式返回*/
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_JEDEC_ID);			//交换发送读取ID的指令*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//交换接收MID,通过输出参数返回*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//交换接收DID高8位*DID <<= 8;									//高8位移到高位*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//或上交换接收DID的低8位,通过输出参数返回MySPI_Stop();								//SPI终止
}//W25Q64写使能
void W25Q64_WriteEnable(void)
{MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_WRITE_ENABLE);		//交换发送写使能的指令MySPI_Stop();								//SPI终止
}//W25Q64等待忙
void W25Q64_WaitBusy(void)
{uint32_t Timeout;MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);				//交换发送读状态寄存器1的指令Timeout = 100000;							//给定超时计数时间while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01)	//循环等待忙标志位{Timeout --;								//等待时,计数值自减if (Timeout == 0)						//自减到0后,等待超时{/*超时的错误处理代码,可以添加到此处*/break;								//跳出等待,不等了}}MySPI_Stop();								//SPI终止
}/*** 函    数:W25Q64页编程* 参    数:Address 页编程的起始地址,范围:0x000000~0x7FFFFF* 参    数:DataArray	用于写入数据的数组* 参    数:Count 要写入数据的数量,范围:0~256* 注意事项:写入的地址范围不能跨页*/
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{uint16_t i;W25Q64_WriteEnable();						//写使能MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_PAGE_PROGRAM);		//交换发送页编程的指令MySPI_SwapByte(Address >> 16);				//交换发送地址23~16位MySPI_SwapByte(Address >> 8);				//交换发送地址15~8位MySPI_SwapByte(Address);					//交换发送地址7~0位for (i = 0; i < Count; i ++)				//循环Count次{MySPI_SwapByte(DataArray[i]);			//依次在起始地址后写入数据}MySPI_Stop();								//SPI终止W25Q64_WaitBusy();							//等待忙
}/*** 函    数:W25Q64扇区擦除(4KB)* 参    数:Address 指定扇区的地址,范围:0x000000~0x7FFFFF*/
void W25Q64_SectorErase(uint32_t Address)
{W25Q64_WriteEnable();						//写使能MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);	//交换发送扇区擦除的指令MySPI_SwapByte(Address >> 16);				//交换发送地址23~16位MySPI_SwapByte(Address >> 8);				//交换发送地址15~8位MySPI_SwapByte(Address);					//交换发送地址7~0位MySPI_Stop();								//SPI终止W25Q64_WaitBusy();							//等待忙
}/*** 函    数:W25Q64读取数据* 参    数:Address 读取数据的起始地址,范围:0x000000~0x7FFFFF* 参    数:DataArray 用于接收读取数据的数组,通过输出参数返回* 参    数:Count 要读取数据的数量,范围:0~0x800000*/
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{uint32_t i;MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_READ_DATA);			//交换发送读取数据的指令MySPI_SwapByte(Address >> 16);				//交换发送地址23~16位MySPI_SwapByte(Address >> 8);				//交换发送地址15~8位MySPI_SwapByte(Address);					//交换发送地址7~0位for (i = 0; i < Count; i ++)				//循环Count次{DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//依次在起始地址后读取数据}MySPI_Stop();								//SPI终止
}

5.4 W25Q64.h

#ifndef __W25Q64_H
#define __W25Q64_Hvoid W25Q64_Init(void);
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID);
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count);
void W25Q64_SectorErase(uint32_t Address);
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count);#endif

5.5 W25Q64_Ins.h

#ifndef __W25Q64_INS_H
#define __W25Q64_INS_H#define W25Q64_WRITE_ENABLE							0x06
#define W25Q64_WRITE_DISABLE						0x04
#define W25Q64_READ_STATUS_REGISTER_1				0x05
#define W25Q64_READ_STATUS_REGISTER_2				0x35
#define W25Q64_WRITE_STATUS_REGISTER				0x01
#define W25Q64_PAGE_PROGRAM							0x02
#define W25Q64_QUAD_PAGE_PROGRAM					0x32
#define W25Q64_BLOCK_ERASE_64KB						0xD8
#define W25Q64_BLOCK_ERASE_32KB						0x52
#define W25Q64_SECTOR_ERASE_4KB						0x20
#define W25Q64_CHIP_ERASE							0xC7
#define W25Q64_ERASE_SUSPEND						0x75
#define W25Q64_ERASE_RESUME							0x7A
#define W25Q64_POWER_DOWN							0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE				0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET			0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID		0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID				0x90
#define W25Q64_READ_UNIQUE_ID						0x4B
#define W25Q64_JEDEC_ID								0x9F
#define W25Q64_READ_DATA							0x03
#define W25Q64_FAST_READ							0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT				0x3B
#define W25Q64_FAST_READ_DUAL_IO					0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT				0x6B
#define W25Q64_FAST_READ_QUAD_IO					0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO				0xE3#define W25Q64_DUMMY_BYTE							0xFF#endif

5.6 main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "W25Q64.h"uint8_t MID;							//定义用于存放MID号的变量
uint16_t DID;							//定义用于存放DID号的变量uint8_t ArrayWrite[] = {0x01, 0x02, 0x03, 0x04};	//定义要写入数据的测试数组
uint8_t ArrayRead[4];								//定义要读取数据的测试数组int main(void)
{/*模块初始化*/OLED_Init();						//OLED初始化W25Q64_Init();						//W25Q64初始化/*显示静态字符串*/OLED_ShowString(1, 1, "MID:   DID:");OLED_ShowString(2, 1, "W:");OLED_ShowString(3, 1, "R:");/*显示ID号*/W25Q64_ReadID(&MID, &DID);			//获取W25Q64的ID号OLED_ShowHexNum(1, 5, MID, 2);		//显示MIDOLED_ShowHexNum(1, 12, DID, 4);		//显示DID/*W25Q64功能函数测试*/W25Q64_SectorErase(0x000000);					//扇区擦除W25Q64_PageProgram(0x000000, ArrayWrite, 4);	//将写入数据的测试数组写入到W25Q64中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){}
}

​​​​​​​

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

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

相关文章

工作实践:11种API性能优化方法

一、索引优化 接口性能优化时&#xff0c;大家第一个想到的通常是&#xff1a;优化索引。 确实&#xff0c;优化索引的成本是最小的。 你可以通过查看线上日志或监控报告&#xff0c;发现某个接口使用的某条SQL语句耗时较长。 此时&#xff0c;你可能会有以下疑问&#xff…

Web渗透:XSS-DOM-based XSS

DOM-based XSS&#xff08;基于DOM的跨站脚本攻击&#xff09;是一种XSS攻击类型&#xff0c;其特点是恶意脚本通过操作文档对象模型&#xff08;DOM&#xff09;直接在客户端执行&#xff0c;而无需经过服务器的处理。这种攻击主要利用客户端JavaScript代码中的漏洞&#xff0…

BP神经网络-入门到理解-长文讲述

本文来自&#xff1a;老饼讲解-BP神经网络 https://www.bbbdata.com 目录 一、BP神经网络的仿生意义 二、BP神经网络的结构 三、BP神经网络的前馈与后馈 3.1 BP神经网络的前馈 3.2 什么是BP神经网络的后馈 四、BP神经网络的训练 4.1 BP神经网络归一化 4.2 梯度下降算法…

完胜PSP的神器

小鸡模拟器&#xff0c;顾名思义&#xff0c;它是一个能够模拟多种经典游戏平台的软件&#xff0c;从家用游戏机到掌上游戏机&#xff0c;几乎覆盖了所有知名的老式游戏设备。这意味着&#xff0c;通过小鸡模拟器&#xff0c;我们可以在手机上重温那些陪伴我们度过童年时光的经…

springboot学习-图灵课堂-最详细学习

springboot-repeat springBoot学习代码说明为什么java -jar springJar包后项目就可以启动 配置文件介绍 springBoot学习 依赖引入 <properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.target>8</mav…

【教程】PVE下uhd630核显直通HDMI输出 以NUC9为例村雨Murasame

大家好&#xff0c;村雨本雨又来发教程了 最近在搞小主机&#xff0c;之前hp400g3仅仅200多元成功核显直通HDMI&#xff0c;作为简单NAS、解码机、伺服机、中控都非常棒&#xff0c;待机仅9w 村雨Murasame&#xff1a;【教程】7代核显直通HDMI成功输出画面 PVE下7代intel核显…

数仓中数据分层的标准流向解读

在大数据开发中&#xff0c;数据分层是一个至关重要的概念。合理的数据分层可以有效地提升数据处理的效率和质量。本文将详细介绍数据分层的标准流向和相关注意事项&#xff0c;并结合实际应用进行说明。 数据分层的标准流向 根据行业标准&#xff0c;数据分层的标准流向如下…

IOS开发学习日记(十五)

目录 App启动过程及生命周期 App的启动 UIApplication UIApplicationDelegate 通过App生命周期回调实现启动页 闪屏的实现 简单实现闪屏功能 App启动过程及生命周期 App的启动 main函数前 动态链接 / 二进制文件加载 / runtime / 类的加载 ...... main函数 int main(int…

事件驱动架构详解:触发与响应构建高效系统

目录 前言1. 事件驱动架构概述1.1 什么是事件1.2 事件驱动架构的核心概念 2. 事件驱动架构的实现2.1 基于消息队列的实现2.2 基于发布-订阅模式的实现2.3 基于流处理的实现 3. 事件驱动架构的优势3.1 松耦合性3.2 可扩展性3.3 异步处理3.4 灵活性 4. 事件驱动架构的应用场景4.1…

镜像发布至dockerHub

1、login 没有账号的话去注册一个 https://hub.docker.com docker login 输入账号密码和账号2、修改镜像名格式 可以直接招我的修改 格式为你的 hub名/镜像名 3、推送

svm和决策树基本知识以及模型评价以及模型保存

svm和决策树基本知识以及模型评价以及模型保存 文章目录 一、SVM1.1&#xff0c;常用属性函数 二、决策树2.1&#xff0c;常用属性函数2.2&#xff0c;决策树可视化2.3&#xff0c;决策树解释 3&#xff0c;模型评价3.1&#xff0c;方面一&#xff08;评价指标&#xff09;3.2&…

Android基于MediaBroswerService的App实现概述

mSession.setPlaybackState(mStateBuilder.build()); // 5. 关联 SessionToken setSessionToken(mSession.getSessionToken()); } } 根据包名做权限判断之后&#xff0c;返回根路径 Override public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundl…

如何生成protobuf文件

背景 protobuf是一种用于序列化结构数据的工具&#xff0c;实现数据的存储与交换&#xff0c;与编程语言和开发平台无关。 序列化&#xff1a;将结构数据或者对象转换成能够用于存储和传输的格式。 反序列化&#xff1a;在其他的计算环境中&#xff0c;将序列化后的数据还原为…

Vue3.4新增的defineModel的使用

define-model的作用 在3.3及之前的版本&#xff0c;父子组件之间的通讯&#xff0c;一直都是靠props&#xff08;父传子&#xff09;和emit&#xff08;子传父&#xff09;来实现。而define-model整合了这两种方法&#xff0c;只需要在父组件中定义define-model的方法&#xf…

GIT回滚

1. 使用 git revert git revert 命令会创建一个新的提交&#xff0c;这个提交会撤销指定提交的更改。这通常用于公共分支&#xff08;如 main 或 master&#xff09;&#xff0c;因为它不会重写历史。 git revert HEAD # 撤销最近的提交 # 或者指定一个特定的提交哈希值 …

Net开源项目推荐-WPF控件样式篇

Net开源项目推荐-WPF控件样式篇 HandyControlWPFDeveloperswpf-uidesignLive-ChartsAvalonDock HandyControl WPF控件库,比较常用的WPF开源控件库&#xff0c;对WPF原有控件样式都进行了重写和扩展&#xff0c;也增加了许多特别的控件&#xff0c;非常好用 github仓库&#x…

Day14—基于Langchain-chatchat搭建本地智能

一、基于Langchain-chatchat搭建本地智能 知识问答系统 1、项目介绍 基于 ChatGLM 等大语言模型与 Langchain 等应用框架实现&#xff0c;开一种利用 langchain 思想实现的基于本地知识库的问答应用&#xff0c;目标期望建立一套对中文场景与开源模型支持友好、可离线运行的知…

Claude3.5:编码螃蟹游戏就是这么轻松

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型重新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;则…

爱眼小妙招:台灯怎么选?学生如何正确使用台灯?

视力是心灵的窗户&#xff0c;尤其对于儿童来说更为重要。然而&#xff0c;随着现代生活方式的改变&#xff0c;孩子们面临越来越多的视力挑战。据统计&#xff0c;在近视学生中&#xff0c;近10%的人患有高度近视&#xff0c;而这一比例随年级的增加而逐渐上升。从幼儿园的小小…

电子杂志制作工具推荐:让你轻松成为编辑大人

在这个数字化的时代&#xff0c;电子杂志已经成为信息传播的重要载体。它不仅能够满足人们对阅读的需求&#xff0c;还能够提供更加丰富、互动的阅读体验。因此&#xff0c;掌握一款好用的电子杂志制作工具&#xff0c;已经成为每个编辑大人的必备技能。接下来告诉大家一个超简…