STM32 HAL库驱动W25QXX Flash

1. 概述

W25QXX系列是一种SPI接口的Flash存储器,广泛应用于嵌入式系统中作为数据存储设备。本文档详细介绍了基于STM32 HAL库的W25QXX Flash驱动实现,包括硬件连接、驱动函数实现以及使用示例。

项目源码仓库:STM32_Sensor_Drives

2. 硬件连接

在这里插入图片描述

W25QXX Flash通过SPI接口与STM32连接,主要包括以下引脚:

  • SCK - 连接到STM32的SPI1_SCK (PA5)
  • MISO - 连接到STM32的SPI1_MISO (PA6)
  • MOSI - 连接到STM32的SPI1_MOSI (PA7)
  • CS - 连接到STM32的GPIO (PA4)

3. 驱动实现

3.1 SPI配置

首先,我们需要配置SPI接口以与W25QXX通信。在spi.c文件中,SPI1的初始化配置如下:

void MX_SPI1_Init(void)
{hspi1.Instance = SPI1;hspi1.Init.Mode = SPI_MODE_MASTER;hspi1.Init.Direction = SPI_DIRECTION_2LINES;hspi1.Init.DataSize = SPI_DATASIZE_8BIT;hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;hspi1.Init.NSS = SPI_NSS_SOFT;hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;hspi1.Init.TIMode = SPI_TIMODE_DISABLE;hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;hspi1.Init.CRCPolynomial = 10;if (HAL_SPI_Init(&hspi1) != HAL_OK){Error_Handler();}
}

这里配置SPI为主模式,8位数据宽度,高电平空闲,第二个边沿采样,软件控制NSS,波特率预分频为8,MSB优先传输。

3.2 GPIO配置

W25QXX的片选信号需要通过GPIO控制,在gpio.c中配置如下:

void MX_GPIO_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};/* GPIO Ports Clock Enable */__HAL_RCC_GPIOC_CLK_ENABLE();__HAL_RCC_GPIOD_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();/*Configure GPIO pin Output Level */HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);/*Configure GPIO pin : PA4 */GPIO_InitStruct.Pin = GPIO_PIN_4;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

PA4配置为推挽输出模式,用于控制W25QXX的片选信号。初始状态设置为高电平(未选中)。

3.3 W25QXX命令定义

spi.h文件中,定义了W25QXX的各种命令和片选引脚:

#define ManufactDeviceID_CMD	0x90
#define READ_STATU_REGISTER_1   0x05
#define READ_STATU_REGISTER_2   0x35
#define READ_DATA_CMD	        0x03
#define WRITE_ENABLE_CMD	    0x06
#define WRITE_DISABLE_CMD	    0x04
#define SECTOR_ERASE_CMD	    0x20
#define CHIP_ERASE_CMD	        0xc7
#define PAGE_PROGRAM_CMD        0x02#define W25Q64_CHIP_SELECT_GPIO_Port GPIOA
#define W25Q64_CHIP_SELECT_Pin GPIO_PIN_4

这些命令用于实现读取ID、读写数据、擦除扇区等操作。

3.4 SPI基础通信函数

spi.c文件中,实现了三个基础的SPI通信函数:

/*** @brief    SPI发送指定长度的数据* @param    buf  —— 发送数据缓冲区首地址* @param    size —— 要发送数据的字节数* @retval   成功返回HAL_OK*/
static HAL_StatusTypeDef SPI_Transmit(uint8_t* send_buf, uint16_t size)
{return HAL_SPI_Transmit(&hspi1, send_buf, size, 100);
}/*** @brief   SPI接收指定长度的数据* @param   buf  —— 接收数据缓冲区首地址* @param   size —— 要接收数据的字节数* @retval  成功返回HAL_OK*/
static HAL_StatusTypeDef SPI_Receive(uint8_t* recv_buf, uint16_t size)
{return HAL_SPI_Receive(&hspi1, recv_buf, size, 100);
}/*** @brief   SPI在发送数据的同时接收指定长度的数据* @param   send_buf  —— 接收数据缓冲区首地址* @param   recv_buf  —— 接收数据缓冲区首地址* @param   size —— 要发送/接收数据的字节数* @retval  成功返回HAL_OK*/
static HAL_StatusTypeDef SPI_TransmitReceive(uint8_t* send_buf, uint8_t* recv_buf, uint16_t size)
{return HAL_SPI_TransmitReceive(&hspi1, send_buf, recv_buf, size, 100);
}

这三个函数分别用于发送数据、接收数据和同时发送接收数据,是W25QXX驱动的基础。

3.5 W25QXX驱动函数实现

3.5.1 读取Flash ID
/*** @brief   读取Flash内部的ID* @param   none* @retval  成功返回device_id*/
uint16_t W25QXX_ReadID(void)
{uint8_t recv_buf[2] = {0};    //recv_buf[0]存放Manufacture ID, recv_buf[1]存放Device IDuint16_t device_id = 0;uint8_t send_data[4] = {ManufactDeviceID_CMD,0x00,0x00,0x00};   //待发送数据,命令+地址/* 使能片选 */HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_RESET);/* 发送并读取数据 */if (HAL_OK == SPI_Transmit(send_data, 4)) {if (HAL_OK == SPI_Receive(recv_buf, 2)) {device_id = (recv_buf[0] << 8) | recv_buf[1];}}/* 取消片选 */HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_SET);return device_id;
}

该函数用于读取W25QXX的制造商ID和设备ID,通过发送0x90命令和三个字节的地址(全0),然后读取两个字节的数据。

3.5.2 读取状态寄存器
/*** @brief     读取W25QXX的状态寄存器,W25Q64一共有2个状态寄存器* @param     reg  —— 状态寄存器编号(1~2)* @retval    状态寄存器的值*/
static uint8_t W25QXX_ReadSR(uint8_t reg)
{uint8_t result = 0; uint8_t send_buf[4] = {0x00,0x00,0x00,0x00};switch(reg){case 1:send_buf[0] = READ_STATU_REGISTER_1;case 2:send_buf[0] = READ_STATU_REGISTER_2;case 0:default:send_buf[0] = READ_STATU_REGISTER_1;}/* 使能片选 */HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_RESET);if (HAL_OK == SPI_Transmit(send_buf, 4)) {if (HAL_OK == SPI_Receive(&result, 1)) {HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_SET);return result;}}/* 取消片选 */HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_SET);return 0;
}

该函数用于读取W25QXX的状态寄存器,W25Q64有两个状态寄存器,通过参数reg选择要读取的寄存器。

3.5.3 等待Flash空闲
/*** @brief	阻塞等待Flash处于空闲状态* @param   none* @retval  none*/
static void W25QXX_Wait_Busy(void)
{while((W25QXX_ReadSR(1) & 0x01) == 0x01); // 等待BUSY位清空
}

该函数通过循环检查状态寄存器1的最低位(BUSY位),等待其变为0,表示Flash处于空闲状态。

3.5.4 读取数据
/*** @brief   读取SPI FLASH数据* @param   buffer      —— 数据存储区* @param   start_addr  —— 开始读取的地址(最大32bit)* @param   nbytes      —— 要读取的字节数(最大65535)* @retval  成功返回0,失败返回-1*/
int W25QXX_Read(uint8_t* buffer, uint32_t start_addr, uint16_t nbytes)
{uint8_t cmd = READ_DATA_CMD;start_addr = start_addr << 8;W25QXX_Wait_Busy();/* 使能片选 */HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_RESET);SPI_Transmit(&cmd, 1);if (HAL_OK == SPI_Transmit((uint8_t*)&start_addr, 3)) {if (HAL_OK == SPI_Receive(buffer, nbytes)) {HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_SET);return 0;}}HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_SET);return -1;
}

该函数用于从指定地址读取指定长度的数据,首先发送读取命令(0x03),然后发送3字节地址,最后读取数据。

3.5.5 写使能和写禁止
/*** @brief    W25QXX写使能,将S1寄存器的WEL置位* @param    none* @retval*/
void W25QXX_Write_Enable(void)
{uint8_t cmd= WRITE_ENABLE_CMD;HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_RESET);SPI_Transmit(&cmd, 1);HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_SET);W25QXX_Wait_Busy();}/*** @brief    W25QXX写禁止,将WEL清零* @param    none* @retval    none*/
void W25QXX_Write_Disable(void)
{uint8_t cmd = WRITE_DISABLE_CMD;HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_RESET);SPI_Transmit(&cmd, 1);HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_SET);W25QXX_Wait_Busy();
}

这两个函数分别用于使能和禁止写操作。在进行写入或擦除操作前,必须先调用写使能函数。

3.5.6 扇区擦除
/*** @brief    W25QXX擦除一个扇区* @param   sector_addr    —— 扇区地址 根据实际容量设置* @retval  none* @note    阻塞操作*/
void W25QXX_Erase_Sector(uint32_t sector_addr)
{uint8_t cmd = SECTOR_ERASE_CMD;sector_addr *= 4096;    //每个块有16个扇区,每个扇区的大小是4KB,需要换算为实际地址sector_addr <<= 8;W25QXX_Write_Enable();  //擦除操作即写入0xFF,需要开启写使能W25QXX_Wait_Busy();        //等待写使能完成/* 使能片选 */HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_RESET);SPI_Transmit(&cmd, 1);SPI_Transmit((uint8_t*)&sector_addr, 3);HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_SET);W25QXX_Wait_Busy();       //等待扇区擦除完成
}

该函数用于擦除指定的扇区,每个扇区大小为4KB。擦除前需要先使能写操作,擦除后需要等待操作完成。

3.5.7 页编程(写入数据)
/*** @brief    页写入操作* @param    dat —— 要写入的数据缓冲区首地址* @param    WriteAddr —— 要写入的地址* @param   byte_to_write —— 要写入的字节数(0-256)* @retval    none*/
void W25QXX_Page_Program(uint8_t* dat, uint32_t WriteAddr, uint16_t nbytes)
{uint8_t cmd = PAGE_PROGRAM_CMD;WriteAddr <<= 8;W25QXX_Write_Enable();/* 使能片选 */HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_RESET);SPI_Transmit(&cmd, 1);SPI_Transmit((uint8_t*)&WriteAddr, 3);SPI_Transmit(dat, nbytes);HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_SET);W25QXX_Wait_Busy();
}

该函数用于向指定地址写入数据,写入前需要先使能写操作,写入后需要等待操作完成。W25QXX的页大小为256字节,一次写入不能超过一页。

4. 使用示例

main.c文件中,展示了如何使用W25QXX驱动进行读写操作:

int main(void)
{/* 省略初始化代码 *//* Infinite loop *//* USER CODE BEGIN WHILE */printf("System will start while\n");printf("read data before write\r\n");W25QXX_Read(read_buf, 0, 10);snprintf(str, 15, "read data: %x", read_buf[2]);printf("%s\r\n", str);/* 擦除该扇区 */printf("erase sector 0 \r\n");W25QXX_Erase_Sector(0);/* 再次读数据 */printf("read erase data\r\n");W25QXX_Read(read_buf, 0, 10);memset(str, 0, sizeof(str)); // 清空读缓冲区snprintf(str, 15, "read data: %x", read_buf[2]);printf("%s\r\n", str);/* 写数据 */printf("write data \r\n");for (i = 0; i < 10; i++){write_buf[i] = i;}W25QXX_Page_Program(write_buf, 0, 10); // 写数据/* 再次读数据 */printf("read write data \r\n");W25QXX_Read(read_buf, 0, 10);memset(str, 0, sizeof(str)); // 清空读缓冲区snprintf(str, 15, "read data: %x", read_buf[2]);printf("%s\r\n", str);while (1){HAL_Delay(200);}
}

这个示例展示了完整的W25QXX操作流程:

  1. 首先读取Flash中的数据
  2. 擦除扇区0
  3. 再次读取数据,验证擦除效果
  4. 写入新数据
  5. 再次读取数据,验证写入效果

5. 串口打印功能

为了方便调试,在usart.c中实现了printf函数的重定向:

int fputc(int ch, FILE *f)
{if (f == stdout)  // 仅处理标准输出{HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 100);  // 阻塞发送if (ch == '\n')  // 发送\n时自动补充\rHAL_UART_Transmit(&huart2, (uint8_t *)"\r", 1, 100);}return ch;
}

通过重定向fputc函数,使得printf函数的输出通过UART2发送,便于观察程序运行状态。

6. 总结

本文档详细介绍了基于STM32 HAL库的W25QXX Flash驱动实现,包括:

  1. SPI和GPIO的配置
  2. W25QXX的命令定义
  3. 基础SPI通信函数
  4. W25QXX驱动函数实现(读ID、读写数据、擦除扇区等)
  5. 使用示例
  6. 串口打印功能

通过这些代码,可以方便地在STM32项目中使用W25QXX Flash进行数据存储和读取。

7. 注意事项

  1. W25QXX的写入操作需要先擦除扇区,因为Flash只能将1变为0,不能将0变为1
  2. 擦除和写入操作前必须先使能写操作
  3. 写入数据不能跨页,一页为256字节
  4. 擦除和写入操作需要等待完成,否则可能导致数据错误
  5. 频繁擦写同一区域会导致Flash寿命减少,建议实现磨损均衡算法

8. 参考资料

  • W25Q64数据手册
  • STM32 HAL库文档
  • STM32 SPI通信指南

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

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

相关文章

Vivado自定义IP核学习笔记

文章目录【1】创建一个新的IP核【2】实现功能【3】编辑IP核【4】IP封装【5】创建Vivado工程【1】创建一个新的IP核 1.1 打开Vivado->点击【Tasks->Manage IP->New IP Location】->弹出窗口1.2 【Next】->设置IP属性->【Finish】->【OK】 【IP Location】…

【面试】高级开发面试场景题

1、如何保证MySql到ES的数据一致性? 答:ES是一个开元分布式搜索和分析引擎、它提供了全文搜索、结构化搜索分析以及这些组合的能力。 全文搜索能力:ES支持复杂的搜索能力,包括模糊匹配、短语查询、布尔查询等,并且可以快速的返回结果 实时数据分析:实时数据分析,支持对…

《 慢 SQL 分析与 SQL 优化实战指南》

&#x1f50d; 慢 SQL 分析与 SQL 优化实战指南、 &#x1f9e0;前言 在数据库性能调优中&#xff0c;慢 SQL 是性能瓶颈的常见元凶。 一次慢查询可能会拖垮整个业务线程池&#xff0c;甚至引发锁等待、雪崩效应。 对后端开发与 DBA 而言&#xff0c;快速定位并优化慢 SQL&am…

C#中如何运用JWT用户认证

一、JWT概述JSON Web Token&#xff08;JWT&#xff09;是一种轻量级的身份认证机制&#xff0c;广泛应用于分布式系统中的用户认证。它通过紧凑的JSON格式存储用户身份信息&#xff0c;并使用数字签名确保信息的完整性和真实性。与传统的基于Session的认证相比&#xff0c;JWT…

Hibernate 使用详解

在现代的Java开发中&#xff0c;数据持久化是一个至关重要的环节。而在众多持久化框架中&#xff0c;Hibernate以其强大的功能和灵活性&#xff0c;成为了开发者们的首选工具。本文将详细介绍Hibernate的原理、实现过程以及其使用方法&#xff0c;希望能为广大开发者提供一些有…

【图像算法 - 13】基于 YOLO12 与 OpenCV 的实时目标点击跟踪系统(系统介绍 + 源码详细)

基于 YOLO12 与 OpenCV 的实时点击目标跟踪系统 在计算机视觉领域&#xff0c;目标检测与跟踪是两个核心任务。本文将介绍一个结合 YOLO 目标检测模型与 OpenCV 跟踪算法的实时目标跟踪系统&#xff0c;该系统允许用户通过鼠标交互选择特定目标进行持续跟踪&#xff0c;支持多…

【数据库】 MySQL 表的操作详解

在 MySQL 数据库的日常开发与维护中&#xff0c;表的操作是最基础且最常用的部分。本文将从 创建表、查看表结构、修改表 以及 删除表 等方面进行详细讲解&#xff0c;并附上对应的 SQL 语句示例&#xff0c;方便在实际项目中直接应用。一、创建表 1.1 创建表语法 CREATE TABLE…

DiT: Transformer上的扩散模型

论文&#xff08;ICCV 2023&#xff09;&#xff1a;Scalable Diffusion Models with Transformers 代码和工程网页&#xff1a;https://www.wpeebles.com/DiT.html DiTs&#xff08;Diffusion Transformers&#xff09;是首个基于Transformer架构的扩散模型&#xff01;它在…

MySQL 索引:索引为什么使用 B+树?(详解B树、B+树)

文章目录一、二叉查找树(BST)&#xff1a;不平衡二、平衡二叉树(AVL)&#xff1a;旋转耗时三、红黑树&#xff1a;树太高由一个例子总结索引的特点基于哈希表实现的哈希索引高效的查找方式&#xff1a;二分查找基于二分查找思想的二叉查找树升级版的BST树&#xff1a;AVL 树四、…

ESP32入门开发·VScode空白项目搭建·点亮一颗LED灯

目录 1. 环境搭建 2. 创建项目 3. 调试相关介绍 4. 代码编写 4.1 包含头文件 4.2 引脚配置 4.3 设置输出电平 4.4 延时函数 4.5 调试 1. 环境搭建 默认已经搭建好环境&#xff0c;如果未搭建好可参考&#xff1a; ESP32入门开发Windows平台下开发环境的搭建…

ONLYOFFICE AI 智能体上线!与编辑器、新的 AI 提供商等进行智能交互

ONLYOFFICE AI 插件​迎来重要更新&#xff0c;带来了新功能和更智能的交互体验。随着 AI 智能体&#xff08;现为测试版&#xff09;的上线、带来更多 AI 提供商支持以及其他新功能&#xff0c;AI 插件已经成为功能强大的文档智能助理。 关于 ONLYOFFICE ONLYOFFICE 文档是多…

【C++进阶学习】第十一弹——C++11(上)——右值引用和移动语义

前言&#xff1a; 前面我们已经将C的重点语法讲的大差不差了&#xff0c;但是在C11版本之后&#xff0c;又出来了很多新的语法&#xff0c;其中有一些作用还是非常大的&#xff0c;今天我们就先来学习其中一个很重要的点——右值引用以及它所扩展的移动定义 目录 一、左值引用和…

【IoTDB】363万点/秒写入!IoTDB凭何领跑工业时序数据库赛道?

【作者主页】Francek Chen 【专栏介绍】⌈⌈⌈大数据与数据库应用⌋⌋⌋ 大数据是规模庞大、类型多样且增长迅速的数据集合&#xff0c;需特殊技术处理分析以挖掘价值。数据库作为数据管理的关键工具&#xff0c;具备高效存储、精准查询与安全维护能力。二者紧密结合&#xff0…

IEEE 2025 | 重磅开源!SLAM框架用“法向量+LRU缓存”,将三维重建效率飙升72%!

一、前言 当前研究领域在基于扩散模型的文本到图像生成技术方面取得了显著进展&#xff0c;尤其在视觉条件控制方面。然而&#xff0c;现有方法&#xff08;如ControlNet&#xff09;在组合多个视觉条件时存在明显不足&#xff0c;主要表现为独立控制分支在去噪过程中容易引入…

无人机遥控器教练模式技术要点

一、技术要点1.控制权仲裁机制&#xff1a;核心功能&#xff1a;清晰定义主控权归属逻辑&#xff08;默认为学员&#xff0c;但教练随时可接管&#xff09;。切换方式&#xff1a;通常通过教练遥控器上的物理开关&#xff08;瞬时或锁定型&#xff09;或软件按钮触发。切换逻辑…

【跨服务器的数据自动化下载--安装公钥,免密下载】

跨服务器的数据自动化下载功能介绍&#xff1a;上代码&#xff1a;发现好久没写csdn了&#xff0c;说多了都是泪~~ 以后会更新一些自动化工作的脚本or 小tricks&#xff0c;欢迎交流。分享一个最近在业务上写的较为实用的自动化脚本&#xff0c;可以批量从远端服务器下载指定数…

C++-->stl: list的使用

前言list的认识list是可以在固定时间&#xff08;O&#xff08;1&#xff09;&#xff09;内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 2. list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0…

本地WSL部署接入 whisper + ollama qwen3:14b 总结字幕

1. 实现功能 M4-1 接入 whisper ollama qwen3:14b 总结字幕 自动下载视频元数据如果有字幕&#xff0c;只下载字幕使用 ollama 的 qwen3:14b 对字幕内容进行总结 2.运行效果 source /root/anaconda3/bin/activate ytdlp &#x1f50d; 正在提取视频元数据… &#x1f4dd; 正在…

《Linux运维总结:Shell脚本高级特性之变量间接调用》

总结&#xff1a;整理不易&#xff0c;如果对你有帮助&#xff0c;可否点赞关注一下&#xff1f; 更多详细内容请参考&#xff1a;Linux运维实战总结 一、变量间接调用 在Shell脚本中&#xff0c;变量间接调用是一种高级特性&#xff0c;它允许你通过另一个变量的值来动态地访问…

ABP VNext + Akka.NET:高并发处理与分布式计算

ABP VNext Akka.NET&#xff1a;高并发处理与分布式计算 &#x1f680; 用 Actor 模型把高并发写入“分片→串行化”&#xff0c;把锁与竞态压力转回到代码层面的可控顺序处理&#xff1b;依托 Cluster.Sharding 横向扩容&#xff0c;Persistence 宕机可恢复&#xff0c;Strea…