当前位置: 首页 > news >正文

STM32SPI实战-Flash模板

STM32SPI实战-Flash模板

  • 一,常用指令集(部分)
  • 二,组件库GD25QXX API 函数解析
    • 1,前提条件
    • 2,初始化与识别
        • 1, void spi_flash_init(void)
        • 2, uint32_t spi_flash_read_id(void)
    • 3,擦除操作
        • 1, void spi_flash_sector_erase(uint32_t sector_addr)
        • 2, void spi_flash_bulk_erase(void)
    • 3,写入操作
        • 1, void spi_flash_page_write(uint8_t *pbuffer, uint32_t write_addr, uint16_t num_byte_to_write)
        • 2, void spi_flash_buffer_write(uint8_t *pbuffer, uint32_t write_addr, uint16_t num_byte_to_write)
    • 4,读取操作
        • void spi_flash_buffer_read(uint8_t *pbuffer, uint32_t read_addr, uint16_t num_byte_to_read)
    • 5,辅助及底层函数
        • 1, void spi_flash_write_enable(void)
        • 2, void spi_flash_wait_for_write_end(void)
        • 3, uint8_t spi_flash_send_byte(uint8_t byte)
        • 4, uint16_t spi_flash_send_halfword(uint16_t half_word)
        • 5, 其他底层读取函数
  • 三,SPI CubeMX配置
    • 1,选择引脚
    • 2,参数配置
    • 3,配置CS (Chip Select) GPIO引脚:
    • 4,生成代码:
  • 四,Flash模板移植
    • 1 获取并放驱动文件
    • 二,移植模板

上一节介绍了通用的SPI模块的开发流程,本节以GD25QXX系列FLASH芯片为例,移植Flash模板

我们提供的GD25QXX驱动库 (gd25qxx.c 和 gd25qxx.h) 封装了与SPI FLASH通信的底层细节,提供了一系列简单易用的API函数供上层应用调用。这些函数依赖于STM32 HAL库的SPI和GPIO功能。

一,常用指令集(部分)

GD25QXX系列芯片支持一套丰富的操作指令。以下列出了一些驱动代码中涉及或常用的指令:

WREN (0x06): 写使能。在执行任何写入或擦除操作之前,必须先发送此指令。

WRDI (0x04): 写禁止。禁止写入操作。

RDID (0x90 或 0x9F): 读ID。读取制造商ID、器件ID等信息。0x90通常用于读取制造商和设备ID,0x9F (RDID JEDEC) 读取更详细的JEDEC ID信息。本驱动使用 0x90。

RDSR (0x05): 读状态寄存器。用于查询FLASH的当前状态,如WIP(写操作进行中)位、WEL(写使能锁存)位等。

WRSR (0x01): 写状态寄存器。用于修改状态寄存器的某些位(如块保护位),需谨慎使用。

READ (0x03): 读数据。从指定地址开始读取数据。

FAST_READ (0x0B): 快速读数据。通常在指令和地址后需要跟一个dummy byte。

PP (0x02): 页编程 (Page Program)。向指定页写入数据,写入前该页对应区域通常需要被擦除。本驱动中使用 WRITE (0x02) 宏定义此指令。

SE (0x20): 扇区擦除 (Sector Erase)。擦除指定的扇区(通常为4KB)。

BE32K (0x52): 32KB块擦除 (Block Erase)。

BE64K (0xD8): 64KB块擦除。

CE (0xC7 或 0x60): 全片擦除 (Chip Erase)。0xC7 和 0x60 作用相同。本驱动中使用 BE (0xC7) 宏定义此指令。

DP (0xB9): 深度掉电。

RES (0xAB): 从深度掉电模式唤醒,并读取电子签名。

具体指令值和支持情况请务必参考您所使用的GD25QXX芯片的最新数据手册

二,组件库GD25QXX API 函数解析

我们提供的GD25QXX驱动库 (gd25qxx.c 和 gd25qxx.h) 封装了与SPI FLASH通信的底层细节,提供了一系列简单易用的API函数供上层应用调用。这些函数依赖于STM32 HAL库的SPI和GPIO功能。

1,前提条件

在使用这些API之前,请确保你已经在STM32CubeMX中正确配置了SPI外设 (例如SPI2) 以及对应的MOSI, MISO, SCK引脚和片选(CS)引脚 (本驱动默认为PB12)。SPI外设和GPIO应已通过 MX_SPIx_Init() 和 MX_GPIO_Init() 完成初始化。驱动代码通过 extern SPI_HandleTypeDef hspi2; 来引用SPI句柄,请确保你的项目中定义了此句柄或将其修改为实际使用的句柄。

2,初始化与识别

1, void spi_flash_init(void)

用途: 初始化SPI FLASH芯片。
参数: 无。
返回: 无。
说明: 此函数主要确保SPI FLASH的片选(CS)引脚在初始状态时被拉高(即芯片未被选中)。它假设SPI外设本身(如时钟、模式、数据位等)和CS引脚的GPIO模式已由STM32CubeMX生成的代码(如 MX_SPIx_Init(), MX_GPIO_Init())配置完毕。你可以在此函数中添加读取Flash ID的操作,作为初始通信检查。

// 在系统初始化代码中调用
MX_SPI2_Init(); // 由CubeMX生成
spi_flash_init();// 可选:检查ID
uint32_t flash_id = spi_flash_read_id();
if (flash_id == EXPECTED_FLASH_ID) {// 初始化成功
} else {// FLASH ID不匹配或通信错误
}
2, uint32_t spi_flash_read_id(void)

用途: 读取SPI FLASH的ID。通常包含制造商ID和设备ID。
参数: 无。
返回: uint32_t - 读取到的ID。例如,对于GD25Q40,可能返回 0xC84013 (制造商ID C8h, 设备ID 4013h)。高字节通常是制造商ID,后续字节是设备相关ID。
内部操作: 发送 RDID (0x90) 指令,然后读取三个字节的ID信息。

uint32_t id = spi_flash_read_id();
printf("Flash ID: 0x%08X\r\n", id);
// 一般 JEDEC ID (0x9F指令) 会返回 制造商ID, 存储器类型, 容量
// 此驱动使用的0x90指令返回的ID格式需查阅具体芯片手册

3,擦除操作

在对FLASH进行写入(编程)之前,目标区域的存储单元必须先被擦除。擦除操作会将指定区域的每一位(bit)都设置为1。

1, void spi_flash_sector_erase(uint32_t sector_addr)

用途: 擦除指定的扇区。一个扇区的大小通常为4KB。
参数: sector_addr - 要擦除扇区内的任意地址。驱动程序通常会根据此地址计算出扇区的起始地址。
返回: 无。
内部操作:
调用 spi_flash_write_enable() 发送写使能(WREN)指令。
发送扇区擦除(SE 0x20)指令,后跟24位地址。
调用 spi_flash_wait_for_write_end() 等待擦除操作完成。
注意: 传入的地址 sector_addr 会被用来定位扇区。例如,如果一个扇区是4KB (0x1000字节),地址0x0000至0x0FFF属于第一个扇区,地址0x1000至0x1FFF属于第二个扇区。擦除操作会擦除整个扇区。

// 擦除地址为0x2000所在的扇区 (即第二个扇区, 假设扇区大小4KB)
spi_flash_sector_erase(0x002000); // 擦除首个扇区
spi_flash_sector_erase(0x000000);
2, void spi_flash_bulk_erase(void)

用途: 擦除整个FLASH芯片。这是一个耗时操作,需谨慎使用。
参数: 无。
返回: 无。
内部操作:
调用 spi_flash_write_enable()。
发送全片擦除(BE/CE 0xC7)指令。
调用 spi_flash_wait_for_write_end() 等待擦除完成。
警告: 此操作会清除芯片上的所有数据!所需时间较长,具体时间取决于芯片容量。

// 警告:这将擦除芯片所有内容!
// printf("Performing bulk erase. This may take a while...\r\n");
// spi_flash_bulk_erase();
// printf("Bulk erase complete.\r\n");

3,写入操作

FLASH写入(编程)操作只能将存储单元的位从1变为0,不能从0变为1。因此,在写入之前,目标区域必须已经被擦除(所有位为1)。

1, void spi_flash_page_write(uint8_t *pbuffer, uint32_t write_addr, uint16_t num_byte_to_write)

用途: 向指定的页内写入数据。写入的数据长度不能超过一页的剩余空间,且不能跨页。FLASH的页大小通常为256字节 (由宏 SPI_FLASH_PAGE_SIZE 定义为 0x100)。
参数:
pbuffer: 指向包含待写入数据的缓冲区的指针。
write_addr: FLASH内部的24位起始写入地址。
num_byte_to_write: 要写入的字节数 (最大256,且不能跨页)。
返回: 无。
内部操作:
调用 spi_flash_write_enable()。
发送页编程(WRITE 0x02)指令,后跟24位地址。
发送 num_byte_to_write 个字节的数据。
调用 spi_flash_wait_for_write_end() 等待写入完成。
注意: 如果 write_addr + num_byte_to_write 超出当前页的边界,行为未定义或可能导致数据写入到下一页的起始部分,具体取决于FLASH芯片特性,但此函数设计上不处理跨页。

uint8_t data_to_write[10] = "HelloSPI";
// 确保目标扇区已擦除
spi_flash_sector_erase(0x000000); 
// 在地址0x0000处写入数据 (在第一页内)
spi_flash_page_write(data_to_write, 0x000000, strlen((char*)data_to_write));
2, void spi_flash_buffer_write(uint8_t *pbuffer, uint32_t write_addr, uint16_t num_byte_to_write)

用途: 向FLASH写入一个数据块,自动处理跨页写入。
参数:
pbuffer: 指向待写入数据缓冲区的指针。
write_addr: 起始写入地址。
num_byte_to_write: 要写入的总字节数。
返回: 无。
说明: 此函数是更高级的写入函数。它会判断起始地址和写入长度,如果发生跨页,则会将数据拆分成多个部分,分别调用 spi_flash_page_write() 进行写入。

uint8_t large_data_buffer[500];
// Fill large_data_buffer with data...
for(int i=0; i<500; i++) large_data_buffer[i] = i % 256;// 假设0x10000开始的区域已擦除
spi_flash_sector_erase(0x010000); // 擦除4KB
if (500 > 256) spi_flash_sector_erase(0x010000 + 0x1000); // 可能需要擦除更多扇区// 从地址 0x0100F0 开始写入500字节数据 (将会跨页)
spi_flash_buffer_write(large_data_buffer, 0x0100F0, 500);

4,读取操作

void spi_flash_buffer_read(uint8_t *pbuffer, uint32_t read_addr, uint16_t num_byte_to_read)

用途: 从FLASH的指定地址读取一块数据到MCU的缓冲区。
参数:
pbuffer: 指向用于存储读取数据的MCU缓冲区的指针。
read_addr: FLASH内部的24位起始读取地址。
num_byte_to_read: 要读取的字节数。
返回: 无。
内部操作:
拉低CS片选。
发送读数据(READ 0x03)指令,后跟24位地址。
循环读取 num_byte_to_read 个字节:为每个要读取的字节发送一个DUMMY_BYTE (0xA5),并将接收到的字节存入 pbuffer。
拉高CS片选。

uint8_t read_buffer[10];
// 从地址0x000000读取之前写入的 "HelloSPI" (假设长度为8)
spi_flash_buffer_read(read_buffer, 0x000000, 8);
read_buffer[8] = '\0'; // Null-terminate for string operations
printf("Read data: %s\r\n", read_buffer);// 比较写入和读出的数据
// if (memcmp(data_to_write, read_buffer, 8) == 0) {
//    printf("Data verification successful!\r\n");
// } else {
//    printf("Data verification failed!\r\n");
// }

5,辅助及底层函数

这些函数主要被上述高级API内部调用,但在某些特定情况下,用户也可能需要了解或直接使用它们。

1, void spi_flash_write_enable(void)

用途: 发送写使能 (WREN) 指令 (0x06) 到FLASH芯片。
说明: 在执行任何擦除或写入操作(如扇区擦除、页编程)之前,必须先调用此函数来设置FLASH内部的写使能锁存(WEL)位。否则,擦除/写入指令将被忽略。

2, void spi_flash_wait_for_write_end(void)

用途: 等待FLASH内部的擦除或写入操作完成。
说明: FLASH的擦除和写入操作需要一定的时间(从几毫秒到几秒不等)。此函数通过循环读取状态寄存器(RDSR 0x05指令)并检查其中的WIP(Write In Progress)位来实现等待。当WIP位为0时,表示操作已完成。
注意: 这是一个阻塞型函数,在等待期间会占用CPU。对于需要实时响应的系统,可能需要考虑非阻塞的实现方式(如使用定时器和中断)。

3, uint8_t spi_flash_send_byte(uint8_t byte)

用途: 通过SPI发送一个字节,并返回接收到的一个字节。
参数: byte - 要发送的字节。
返回: uint8_t - SPI总线上同时接收到的字节。
说明: 这是底层的SPI单字节通信函数,基于 HAL_SPI_TransmitReceive()。在SPI全双工通信中,发送一个字节的同时也会接收一个字节。

4, uint16_t spi_flash_send_halfword(uint16_t half_word)

用途: 通过SPI发送一个半字 (16位),并返回接收到的一个半字。
参数: half_word - 要发送的半字。
返回: uint16_t - SPI总线上同时接收到的半字。
说明: 底层SPI双字节通信函数,基于 HAL_SPI_TransmitReceive()。

5, 其他底层读取函数

void spi_flash_start_read_sequence(uint32_t read_addr): 发送READ指令和地址,为连续读取做准备,但不结束SPI会话(CS保持低)。
uint8_t spi_flash_read_byte(void): 在已启动的读取序列中,发送一个dummy byte并读取一个字节。
说明: 这两个函数主要被 spi_flash_buffer_read() 内部使用,用于优化连续读取。一般情况下,用户直接使用 spi_flash_buffer_read() 即可。

三,SPI CubeMX配置

1,选择引脚

根据原理图,选择SPI的四个引脚:PB12,13,14,15
其中PB12配置为输出
在这里插入图片描述
根据四个引脚是SPI2,选择SPI2,并且选择全双工模式
在这里插入图片描述

2,参数配置

根据芯片类型选择极性和相位,不同的芯片支持的模式不同
在这里插入图片描述

参数设置
Frame Format: Motorola
Data Size: 8 Bits
First Bit: MSB First
Clock Polarity (CPOL): High. GD25QXX系列SPI FLASH通常支持SPI模式0 (CPOL=Low, CPHA=1 Edge an CubeMX) 和SPI模式3 (CPOL=High, CPHA=2 Edge in CubeMX)。此处的 High 是SPI模式3的CPOL要求。
Clock Phase (CPHA): 2 Edge. 结合CPOL=High,这构成了SPI模式3。请务必查阅您所用FLASH芯片的数据手册,确保所选SPI模式(CPOL/CPHA组合)受其支持。
Prescaler (for Baud Rate): 2. 此预分频值将决定SPI通讯的波特率。根据截图,产生的波特率为 22.5 MBit/s。实际波特率计算公式为:SPI_CLK = PCLK / PrescalerValueFromTable (其中PCLK是SPI外设的时钟源频率,PrescalerValueFromTable是基于设置值’2’从CubeMX内部表格确定的实际分频系数)。例如,若PCLK为45MHz且设置’2’对应实际分频系数2, 则SPI_CLK = 45MHz / 2 = 22.5MHz。选择合适的波特率需兼顾通讯速度和稳定性,且不能超过FLASH芯片规格上限。
NSS Signal Type: Software. 此选项通常与 “Hardware NSS Signal: Disable” 自动关联。再次确认由软件控制CS。
CRC Calculation: Disabled. 本驱动不使用CRC校验。
请添加图片描述
上图展示了一个基于STM32CubeMX的SPI配置实例。关键在于确保CPOL和CPHA的组合与您的FLASH芯片兼容,并且波特率设置合理。 不同系列的STM32芯片,其CubeMX界面和可用预分频值可能略有差异。

3,配置CS (Chip Select) GPIO引脚:

SPI相关的SCK, MISO, MOSI引脚在启用SPI并分配到具体引脚后,通常会自动配置为正确的复用功能模式 (Alternate Function Push-pull)。请在CubeMX的Pinout视图中确认这些引脚分配正确。

CS引脚配置:
1,选择一个未被占用的通用GPIO引脚作为CS信号线。本驱动在 gd25qxx.h 文件中通过宏定义 (如 SPI_FLASH_CS_GPIO_PORT 和 SPI_FLASH_CS_PIN,默认可能是 GPIOB, GPIO_PIN_12) 来指定CS引脚。您需要确保CubeMX中的配置与这些宏定义一致,或修改宏以匹配您的硬件连接。
2,在CubeMX中,找到您选定的CS引脚,将其配置为 GPIO_Output。
3,GPIO Output level: High (SPI片选通常是低电平有效,因此空闲时应为高电平,确保FLASH芯片初始未被选中)。
4,GPIO mode: Output Push Pull。
5,GPIO Pull-up/Pull-down: No pull-up and no pull-down。
6,Maximum output speed: Medium 或 High 通常足够。
7,(可选) User Label: 为该引脚设置一个易于识别的标签,如 SPI_FLASH_CS。

请添加图片描述

请务必将上图替换为您项目中CS引脚的实际CubeMX GPIO配置截图,并确保其与驱动代码中的CS引脚定义匹配。

4,生成代码:

完成所有SPI和GPIO配置后,保存 .ioc 文件。
在CubeMX中点击 “Generate Code” (或快捷键 ALT+K)。这将更新您的项目,生成或修改包括 main.c (包含 MX_SPIx_Init() 和 MX_GPIO_Init() 等函数初始化代码) 在内的相关文件。

四,Flash模板移植

1 获取并放驱动文件

获取文件: 从提供的地方获取 gd25qxx.c 和 gd25qxx.h 驱动文件。
放置文件: 将这两个文件复制到你项目的一个合适位置,例如在项目组件库目录下创建GD25QXX 文件夹,并将它们放入其中。在这里插入图片描述
将组件库文件添加到keil
在这里插入图片描述
在这里插入图片描述
打开组件库文件,发现这个spi句柄是hspi2,与我们原理图一致,所以不用修改
在这里插入图片描述
跳转到头文件里,发现组件库是用软件模拟的片选信号
在这里插入图片描述

二,移植模板

#include "gd25qxx.h"
#include   // For printf, etc.
#include  // For strlen, strcmp// 假设SPI2和相关GPIO已通过CubeMX初始化
// extern SPI_HandleTypeDef hspi2; (确保在main.c或对应位置有定义)void test_spi_flash(void) {uint32_t flash_id;uint8_t write_buffer[SPI_FLASH_PAGE_SIZE];uint8_t read_buffer[SPI_FLASH_PAGE_SIZE];uint32_t test_addr = 0x000000; // 测试地址,选择一个扇区的起始printf("SPI FLASH Test Start\r\n");// 1. 初始化SPI Flash驱动 (主要是CS引脚状态)spi_flash_init();printf("SPI Flash Initialized.\r\n");// 2. 读取Flash IDflash_id = spi_flash_read_id();printf("Flash ID: 0x%lX\r\n", flash_id);// 你可以根据你的芯片手册检查ID是否正确,例如 GD25Q64的ID可能是 0xC84017// 3. 擦除一个扇区 (大小通常为4KB)// 注意:擦除操作耗时较长printf("Erasing sector at address 0x%lX...\r\n", test_addr);spi_flash_sector_erase(test_addr);printf("Sector erased.\r\n");// (可选) 校验擦除:读取一页数据,检查是否全为0xFFspi_flash_buffer_read(read_buffer, test_addr, SPI_FLASH_PAGE_SIZE);int erased_check_ok = 1;for (int i = 0; i < SPI_FLASH_PAGE_SIZE; i++) {if (read_buffer[i] != 0xFF) {erased_check_ok = 0;break;}}if (erased_check_ok) {printf("Erase check PASSED. Sector is all 0xFF.\r\n");} else {printf("Erase check FAILED.\r\n");}// 4. 准备写入数据 (写入一页)const char* message = "Hello from STM32 to SPI FLASH! Microunion Studio Test - 12345.";uint16_t data_len = strlen(message);if (data_len >= SPI_FLASH_PAGE_SIZE) {data_len = SPI_FLASH_PAGE_SIZE -1; // 确保不超过页大小}memset(write_buffer, 0, SPI_FLASH_PAGE_SIZE);memcpy(write_buffer, message, data_len);write_buffer[data_len] = '\0'; // 确保字符串结束printf("Writing data to address 0x%lX: \"%s\"\r\n", test_addr, write_buffer);// 使用 spi_flash_buffer_write (可以处理跨页,但这里只写一页内)// 或者直接用 spi_flash_page_write 如果确定在一页内spi_flash_buffer_write(write_buffer, test_addr, SPI_FLASH_PAGE_SIZE); // 写入整页数据,包括填充的0// spi_flash_page_write(write_buffer, test_addr, data_len + 1); // 只写入有效数据printf("Data written.\r\n");// 5. 读取写入的数据printf("Reading data from address 0x%lX...\r\n", test_addr);memset(read_buffer, 0, SPI_FLASH_PAGE_SIZE);spi_flash_buffer_read(read_buffer, test_addr, SPI_FLASH_PAGE_SIZE);printf("Data read: \"%s\"\r\n", read_buffer);// 6. 校验数据if (memcmp(write_buffer, read_buffer, SPI_FLASH_PAGE_SIZE) == 0) {printf("Data VERIFIED! Write and Read successful.\r\n");} else {printf("Data VERIFICATION FAILED!\r\n");}printf("SPI FLASH Test End\r\n");
}// 在你的main函数中合适的位置调用 test_spi_flash()
// int main(void) {
//   ...
//   MX_SPI2_Init(); // CubeMX生成的SPI初始化
//   MX_GPIO_Init(); // CubeMX生成的GPIO初始化 (包括CS引脚)
//   test_spi_flash();
//   while(1) {}
// }

上述代码仅为基本功能演示。实际应用中,你需要根据具体需求进行错误处理、地址管理、数据结构设计等。 SPI_FLASH_PAGE_SIZE (256字节) 是一个重要参数。

相关文章:

STM32SPI实战-Flash模板

STM32SPI实战-Flash模板 一&#xff0c;常用指令集&#xff08;部分&#xff09;二&#xff0c;组件库GD25QXX API 函数解析1,前提条件2,初始化与识别1, void spi_flash_init(void)2, uint32_t spi_flash_read_id(void) 3,擦除操作1, void spi_flash_sector_erase(uint32_t sec…...

CSS- 4.4 固定定位(fixed) 咖啡售卖官网实例

本系列可作为前端学习系列的笔记&#xff0c;代码的运行环境是在HBuilder中&#xff0c;小编会将代码复制下来&#xff0c;大家复制下来就可以练习了&#xff0c;方便大家学习。 HTML系列文章 已经收录在前端专栏&#xff0c;有需要的宝宝们可以点击前端专栏查看&#xff01; 点…...

【Retinanet】训练自己的数据集

目录 1.下载源码2.配置环境3.数据集准备4.训练自己的数据5.成功训练&#xff01; 1.下载源码 Retinanet代码&#xff1a;代码 下载到你的目录中&#xff0c;进行打开。 2.配置环境 这里就是cudapytorch&#xff0c;没有配置过的可以参考博客&#xff1a; 深度学习环境的搭建…...

微软将于 8 月 11 日关闭 Bing Search API 服务

微软宣布将于 2025 年 8 月 11 日正式关闭 Bing Search API 服务。届时&#xff0c;所有使用 Bing Search API 的实例将完全停用&#xff0c;同时不再接受新用户注册。 此次停用决定主要影响 Bing Search F1 及 S1 到 S9 资源的用户&#xff0c;以及 Custom Search F0 与 S1 到…...

探索 Python 的利器:help()、dir() 与 AI 工具的结合应用

引言 在编程世界中,Python 以其简洁的语法、强大的功能和丰富的库生态系统成为众多开发者的首选语言。无论是初学者还是资深工程师,在学习新模块、调试代码或探索未知功能时,常常需要有效的工具来帮助理解和解决问题。Python 提供了内置的 help() 和 dir() 函数,让开发者能…...

MySQL查询优化器底层原理解析:从逻辑优化到物理优化

MySQL查询优化器底层原理解析&#xff1a;从逻辑优化到物理优化 引言 在数据库系统中&#xff0c;SQL语句的执行效率直接影响着整个应用的性能表现。一条普通的SQL执行前会经历五个关键阶段&#xff1a;SQL输入、语法分析、语义检查、SQL优化、SQL执行。其中&#xff0c;SQL优…...

UI架构的历史与基础入门

本笔记的目的是通过一系列连贯的例子来探讨“事物-模型-视图-编辑器”这一隐喻。 这些例子都来自我的规划系统&#xff08;planning system&#xff09;&#xff0c;用于解释上述四个概念。所有例子都已实现&#xff0c;但并未在本文描述的清晰类结构中实现。 这些隐喻对应于《…...

(三)MMA(KeyCloak身份服务器/OutBox Pattern)

文章目录 项目地址一、KeyCloak二、OutBox Pattern2.1 配置Common模块的OutBox1. OutboxMessage2. 数据库配置OutboxMessageConfiguration3. 创建Save前的EF拦截器4. 创建Quartz后台任务5. 配置后台任务6. 注册服务2.2 创建OutBox的消费者项目地址 教程作者:教程地址:代码仓库…...

【通用智能体】Playwright:跨浏览器自动化工具

Playwright&#xff1a;跨浏览器自动化工具 一、Playwright 是什么&#xff1f;二、应用场景及案例场景 1&#xff1a;端到端&#xff08;E2E&#xff09;测试场景 2&#xff1a;UI 自动化&#xff08;表单批量提交&#xff09;场景 3&#xff1a;页面截图与 PDF 生成场景 4&am…...

单片机设计_停车场车位管理系统(AT89C52、LCD1602)

想要更多项目私wo!!! 一、电路设计 此电路由AT89C52单片机和LCD1602液晶显示模块等器件组成。 二、运行结果 三、部分代码 #include <reg52.h> //调用单片机头文件 #define uchar unsigned char //无符号字符型 宏定义 变量范围0~255 #define uint unsigned…...

【android bluetooth 协议分析 01】【HCI 层介绍 5】【SetEventMask命令介绍】

1. HCI_Set_Event_Mask 命令作用 项目内容命令名HCI_Set_Event_MaskOCF0x0001作用主机通过设置 Event Mask 告诉控制器&#xff1a;我只对某些事件感兴趣&#xff0c;屏蔽其他事件&#xff0c;以减少中断。事件来源事件是 HCI 与主机之间通信的反馈机制&#xff0c;控制器通过…...

python打卡day29

类的装饰器 知识点回顾 类的装饰器装饰器思想的进一步理解&#xff1a;外部修改、动态类方法的定义&#xff1a;内部定义和外部定义 回顾一下&#xff0c;函数的装饰器是 &#xff1a;接收一个函数&#xff0c;返回一个修改后的函数。类也有修饰器&#xff0c;类装饰器本质上确…...

【数据结构】树状数组

树状数组 假设一个数可以 x x x可以被二进制分解成 x 2 i 1 2 i 2 . . . 2 i m x 2^{i_1} 2^{i_2} ... 2^{i_m} x2i1​2i2​...2im​&#xff0c;不妨设 i 1 > i 2 > . . . > i m i_1 > i_2 > ... > i_m i1​>i2​>...>im​&#xff0c;进…...

Java虚拟机 - JVM与Java体系结构

Java虚拟机 JVM与Java体系结构为什么要学习JVMJava与JVM简介Java 语言的核心特性JVM&#xff1a;Java 生态的基石JVM的架构模型基于栈的指令集架构&#xff08;Stack-Based&#xff09;基于寄存器的指令集架构&#xff08;Register-Based&#xff09;JVM生命周期 总结 JVM与Jav…...

翻译:20250518

翻译题 文章目录 翻译题一带一路中国结 一带一路 The “One Belt and One Road” Initiative aims to achieve win-win and shared development. China remains unchanged in its commitment to foster partnerships. China pursues an independent foreign policy of peace, …...

SparkSQL基本操作

以下是 Spark SQL 的基本操作总结&#xff0c;涵盖数据读取、转换、查询、写入等核心功能&#xff1a; 一、初始化 SparkSession scala import org.apache.spark.sql.SparkSession val spark SparkSession.builder() .appName("Spark SQL Demo") .master("…...

Ansible模块——文件内容修改

修改文件单行内容 ansible.builtin.lineinfile 可以按行修改文件内容&#xff0c;一次修改一行&#xff0c;支持正则表达式。 选项名 类型 默认值 描述 attributesstrnull 设置目标文件的 Linux 文件系统属性&#xff08;attribute bits&#xff09;&#xff0c;作用类似于…...

基于单片机路灯自动控制仪仿真设计

标题:基于单片机路灯自动控制仪仿真设计 内容:1.摘要 本设计旨在解决传统路灯控制方式效率低、能耗大的问题&#xff0c;开展了基于单片机的路灯自动控制仪仿真设计。采用单片机作为核心控制单元&#xff0c;结合光照传感器、时钟模块等硬件&#xff0c;运用相关软件进行编程和…...

Spring Web MVC————入门(3)

今天我们来一个大练习&#xff0c;我们要实现一个登录界面&#xff0c;登录进去了先获取到登录人信息&#xff0c;可以选择计算器和留言板两个功能&#xff0c;另外我们是学后端的&#xff0c;对于前端我们会些基础的就行了&#xff0c;知道ajax怎么用&#xff0c;知道怎么关联…...

拓展运算符与数组解构赋值的区别

拓展运算符与数组解构赋值是ES6中用于处理数组的两种不同的特性&#xff0c;它们有以下区别&#xff1a; 概念与作用 • 拓展运算符&#xff1a;主要用于将数组展开成一系列独立的元素&#xff0c;或者将多个数组合并为一个数组&#xff0c;以及在函数调用时将数组作为可变参…...

【Linux】第二十章 管理基本存储

目录 1. 对 Linux 磁盘进行分区时有哪两种方案&#xff1f;分别加以详细说明。 2. 简单说下创建MBR磁盘分区涉及哪几个步骤&#xff1f; 3. 创建GPT分区与创建MBR分区有什么不同&#xff1f; 4. 在创建分区时就会在分区上创建文件系统吗&#xff1f; 5. 如何持久挂载文件系…...

DeepSeek本地部署全攻略:从零搭建到Web可视化及数据训练

目录 1. 环境准备与硬件要求2. 安装Ollama框架3. 部署DeepSeek模型4. Web可视化配置5. 数据投喂与模型训练6. 进阶技巧与常见问题1. 环境准备与硬件要求 硬件配置建议 基础配置:16GB内存 + RTX 3060显卡(流畅运行7B参数模型)进阶配置:32GB内存 + RTX 4090显卡(支持14B模型…...

JavaScript性能优化实战(12):大型应用性能优化实战案例

在前面的系列文章中,我们探讨了各种JavaScript性能优化技术和策略。本篇将聚焦于实际的大型应用场景,通过真实案例展示如何综合运用这些技术,解决复杂应用中的性能挑战。 目录 电商平台首屏加载优化全流程复杂数据可视化应用性能优化案例在线协作工具的实时响应优化移动端W…...

前缀和——中心数组下标

此题我们不应局限于前缀和的模板&#xff0c;因为该中心下标把数组分为两个部分且每个部分都要求和&#xff0c;我们就一个再创建一个”后缀和” 定义两个数组f&#xff0c;g。f[i]表示[0,i-1]所有元素的和 f[i]f[i-1]nums[i-1];g[i]表示[i1,n-1]的和 g[i]g[i1]nums[i1];因为依…...

Java——创建多线程的四种方式

一、继承Thread 步骤 1.定义一个类继承Thread 2.重写run方法&#xff0c;在方法中设置线程任务&#xff08;此线程具体执行的代码&#xff09; 3.创建自定义线程类对象 4.调用Thread中的start方法&#xff0c;开启线程&#xff0c;jvm自动调用run方法 常用方法 void sta…...

广域网学习

PPPoE技术&#xff08;拨号上网&#xff09; PPPoE &#xff08; PPP over Ethernet &#xff0c;以太网承载 PPP 协议&#xff09;是一种把 PPP 帧封装到以太网帧中的链路层协议。 PPPoE 可以使以太网网络中的多台主机连接到远端的宽带接入服务器。 应用场景 PPPoE 组网结构采…...

inverse-design-of-grating-coupler-3d

一、设计和优化3D光栅耦合器 1.1 代码讲解 通过预定义的环形间距参数(distances数组),在FDTD中生成椭圆光栅结构,并通过用户交互确认几何正确性后,可进一步执行参数扫描优化。 # os:用于操作系统相关功能(如文件路径操作) import os import sys# lumapi:Lumerical 的…...

渗透测试流程-中篇

#作者&#xff1a;允砸儿 #日期&#xff1a;乙巳青蛇年 四月廿一&#xff08;2025年5月18日&#xff09; 今天笔者带大家继续学习&#xff0c;网安的知识比较杂且知识面很广&#xff0c;这一部分会介绍很多需要使用的工具。会用各种工具是做网安的基础&#xff0c;ok咱们继续…...

2026武汉门窗门业移门木门铝艺门智能锁展会3月国博举办

展出面积&#xff1a;60000㎡ 观众&#xff1a;80000人次 参展企业&#xff1a;800 专业活动&#xff1a;20 2026武汉门窗门业移门木门铝艺门智能锁展会3月国博举办 2026第二届中国武汉整装定制家居暨门窗装饰材料博览会/2026武汉建博会 时间&#xff1a;2026年3月20-22日 …...

如何用mockito+junit测试代码

Mockito 是一个流行的 Java 模拟测试框架&#xff0c;用于创建和管理测试中的模拟对象(mock objects)。它可以帮助开发者编写干净、可维护的单元测试&#xff0c;特别是在需要隔离被测组件与其他依赖项时。 目录 核心概念 1. 模拟对象(Mock Objects) 2. 打桩(Stubbing) 3. 验…...

31、魔法生物图鉴——React 19 Web Workers

一、守护神协议&#xff08;核心原理&#xff09; 1. 灵魂分裂术&#xff08;线程架构&#xff09; // 主组件中初始化Workerconst workerRef useRef(null);​useEffect(() > {workerRef.current new Worker(new URL(./creatureWorker.js, import.meta.url));workerRef.…...

洛谷题目:P4052 [JSOI2007] 文本生成器 题解 本题(极难)

个人介绍: 题目传送门: P4052 [JSOI2007] 文本生成器 - 洛谷 (luogu.com.cn) 前言: 这道题要求计算长度为 m 的文章中,至少包含一个给定单词的可读文章的数量,并且结果需要对 10007 取模。下面是小亦为大家逐步分析解题思路: 题目整体思路: 为了方便计算…...

【Linux】命令行参数和环境变量

目录 一、命令行参数 二、环境变量 &#xff08;一&#xff09;PATH &#xff08;二&#xff09;查看环境变量 &#xff08;三&#xff09;获取环境环境变量 &#xff08;四&#xff09;为什么要环境变量 &#xff08;五&#xff09;环境变量特点总结 &#xff08;1&am…...

AGI大模型(23):LangChain框架快速入门之LangChain介绍

1 什么是LangChain? LangChain是一个基于大语言模型用于构建端到端语言模型应用的框架,它提供了一系列工具、套件和接口,让开发者使用语言模型来实现各种复杂的任务,如文本到图像的生成、文档问答、聊天机器人等。 官网地址:https://python.langchain.com/docs/introduc…...

vmware虚拟机运行多个产生卡顿问题

最近在工作中使用电脑运行两个虚拟机&#xff0c;用来测试程序。运行的时候发现电脑会非常卡顿。导致调试工作进行到一半就会闪退卡死。 首先尝试的解决方案是开一个虚拟机&#xff0c;然后在windows上部署测试程序&#xff0c;后面发现操作很受限制。然后使用windows管…...

八股碎碎念01——HashMap原理

Java面试题周总结 HashMap HashMap实现原理 Java 1.7版本 在Java1.7中HashMap通过数组链表的方式实现&#xff0c;由于链表查询速度为O(n)&#xff0c;因此在插入大量元素后查询速度会明显降低。 Java 1.8版本 在Java1.8中对HashMap进行改进&#xff0c;采用数组链表/红黑…...

长篇小说《白鹿原》原著版本在当当网可购到

著名作家陈忠实所真实描写上世纪1959年、1960年、1961年我国三年饥荒时人吃人的长篇小说《白鹿原》原著版本&#xff0c;现能在当当网上购到&#xff0c;价格仅26元。特此推荐。 笔者是从那段不堪回首的饥饿历史中幸存下来的过来人&#xff0c;也是在改革开放初期的文艺复兴年代…...

ColorAid —— 一个面向设计师的色盲模拟工具开发记

我正在参加CodeBuddy「首席试玩官」内容创作大赛&#xff0c;本文所使用的 CodeBuddy 免费下载链接&#xff1a;腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 起因&#xff1a;CodeBuddy&#xff0c;说干就干 起初只是一个随口的想法——我想做一个“色盲辅助工具”&…...

对称加密与非对称加密在 JWT 中的应用详解

文章目录 对称加密与非对称加密在 JWT 中的应用详解引言对称加密与非对称加密概述对称加密&#xff08;Symmetric Encryption&#xff09;非对称加密&#xff08;Asymmetric Encryption&#xff09; 对称加密生成和验证 JWT 的过程生成 JWT&#xff08;HS256 示例&#xff09;验…...

Python 中 if 和 else 基础知识的详解和使用

一、基本语法结构 if 条件1:# 条件1 为真时执行的代码块 elif 条件2:# 条件1 不成立&#xff0c;条件2 成立时执行 else:# 所有条件都不成立时执行注意&#xff1a; elif 是“else if”的缩写&#xff0c;可以有多个&#xff1b;else 可省略&#xff1b;条件表达式必须是可以…...

学习黑客Active Directory 入门指南(三)

Active Directory 入门指南&#xff08;三&#xff09;&#xff1a;关键服务、用户与组管理 &#x1f91d;&#x1f4bb; 大家好&#xff01;欢迎来到 “Active Directory 入门指南” 系列的第三篇。在前两篇中&#xff0c;我们已经了解了AD的基本概念、逻辑结构&#xff08;对…...

10.9 LangChain LCEL革命:43%性能提升+声明式语法,AI开发效率飙升实战指南

LangChain 表达式语言(LCEL)架构解析:新一代链式编排引擎 关键词:LangChain Expression Language, Runnable 协议, 链式编排, 并行处理, 生产级应用开发 1. LCEL 设计理念与技术突破 LangChain Expression Language(LCEL)是 LangChain v0.3 的核心革命性升级,重新定义…...

一文读懂-嵌入式Ubuntu平台

现在直接在一些嵌入式Soc上移植ubuntu来用到产品上&#xff0c;刚开始感觉还挺臃肿的&#xff0c;后来细聊了下感觉还是有一定的优势。 ubuntu相信大家在熟悉不过了&#xff0c;几乎无处不在&#xff0c;小到咖啡机&#xff0c;大到火星车&#xff0c;为什么ubuntu如此广泛&am…...

centos7.9扩展已有分区空间

新增50G硬盘 分区 fdisk /dev/sdb Command (m for help): p #打印分区表Disk /dev/sdb: 53.7 GB, 53687091200 bytes, 104857600 sectors Units sectors of 1 * 512 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 byte…...

ubuntu22.04搭建ROS2环境

在 Ubuntu 22.04 上安装 ROS 2&#xff08;Humble Hawksbill&#xff09;时&#xff0c;针对国内网络问题&#xff0c;建议使用镜像源加速。以下是分步指南&#xff1a; 1. 更换 Ubuntu 系统源&#xff08;使用清华镜像&#xff09; sudo sed -i "shttp://.*archive.ubunt…...

java中sleep()和wait()暂停线程的区别

1. Thread.sleep() 所属类&#xff1a;它是Thread类的静态方法。作用&#xff1a;让当前正在执行的线程暂停指定的时间&#xff0c;在暂停期间&#xff0c;线程会一直持有对象锁&#xff08;也就是synchronized锁&#xff09;。中断响应&#xff1a;当线程处于sleep()状态时&a…...

printf函数参数与入栈顺序

01. printf()的核心功能 作用&#xff1a;将 格式化数据 输出到 标准输出&#xff08;stdout&#xff09;&#xff0c;支持多种数据类型和格式控制。 int printf(const char *format, ...);参数&#xff1a; format&#xff1a;格式字符串,字符串或%开头格式符...&#xff1a;…...

代码案例分析

以下是一个使用线性回归进行简单房价预测的机器学习代码案例分析&#xff1a; 代码示例 import numpy as np import matplotlib.pyplot as plt from sklearn.linear_model import LinearRegression from sklearn.model_selection import train_test_split # 生成一些示例数据…...

Baklib赋能企业知识资产AI化升级

AI驱动知识管理革新 在数字化转型浪潮中&#xff0c;企业知识管理的范式正经历AI技术的深度重构。传统知识库受限于静态存储与人工维护&#xff0c;而Baklib通过构建知识中台架构&#xff0c;将多模态数据处理与语义理解引擎深度融合&#xff0c;实现知识资产的动态聚合与智能…...

Leetcode 3552. Grid Teleportation Traversal

Leetcode 3552. Grid Teleportation Traversal 1. 解题思路2. 代码实现 题目链接&#xff1a;3552. Grid Teleportation Traversal 1. 解题思路 这一题的话核心就是一个广度优先遍历&#xff0c;我们只需要从原点开始&#xff0c;一点点考察其所能到达的位置&#xff0c;直至…...