STM32 HAL庫驅動W25QXX Flash

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…