韋東山STM32_HAl庫入門教程(SPI)學習筆記[09]內容

(1)SPI程序層次

一、核心邏輯:“SPI Flash 操作” 是怎么跑起來的?

要讀寫 SPI Flash,需同時理解?硬件連接(怎么接線)?和?軟件分層(誰負責發指令、誰負責控制邏輯),二者結合才能讓數據正確讀寫。

二、逐圖拆解(從 “硬件物理連接” 到 “軟件邏輯分層”)

1. 「硬件接線 - 實際底板」
  • 內容:展示 DShanMCU-F103 學習底板上,SPI Flash 模塊的物理安裝位置(絲印 “FLASH 模塊” 的排母接口)。
  • 作用:實操時知道 “模塊插哪里”,確保硬件連接正確。
  • 關鍵細節
    • 模塊名稱:“Flash 模塊”(紅框標注)。
    • 底板標識:DShanMCU-F103 Base Board,確認自己用的開發板匹配。
2. 「軟件層次 - 操作 Flash 的分工」
  • 內容:把 “操作 SPI Flash” 的軟件拆成 3 層,明確每層職責:
    • 應用程序(最上層):決定 “讀寫什么數據、在 Flash 的哪個位置操作”(比如你寫的?main.c?里,要存一個漢字字模到 Flash 地址?0x0800)。
    • Flash 驅動(中間層):把應用程序的 “讀寫需求” 轉成?SPI 協議命令(比如讀操作要發?0x03?指令 + 地址,寫操作要先發擦除命令再寫)。
    • SPI 控制器驅動(HAL)(最下層):最基礎的 “SPI 硬件控制”,負責發送具體的 SPI 時序信號(時鐘、數據),跟硬件寄存器直接交互。
  • 作用:理解 “軟件怎么分層協作”,開發時知道改哪一層(比如換 Flash 型號,只需改 “Flash 驅動層”;換主控芯片,可能改 “HAL 層”)。
3. 「硬件框圖 - 系統級連接邏輯」
  • 內容:從?SoC(芯片系統)?角度,展示 CPU、內存管理單元、SPI 控制器、Flash 之間的連接關系:
    • CPU:發讀寫指令(比如 “讀 Flash 地址?0x1000?的數據”)。
    • 內存管理單元:處理地址映射、片選信號(決定操作 RAM 還是 Flash 還是其他外設)。
    • SPI 控制器:把 CPU 的指令轉成 SPI 時序(時鐘 SCK、數據 MOSI/MISO、片選 CS 等),發給外部 Flash。
    • Flash:接收 SPI 信號,執行讀寫擦除操作,返回數據。
  • 作用:理解 “硬件模塊如何協同工作”,比如為什么操作 Flash 時要控制片選信號(片選 3 對應 Flash),地址怎么通過內存管理單元轉發。
4. 「硬件原理圖 - 引腳級連接」
  • 內容:SPI Flash 模塊與 STM32 主控的具體引腳連接
    • PA7(DI/SPI1 MOSI):主控 → Flash 發數據(Master Out Slave In)。
    • PA5(SCK/SPI1 SCK):主控給 Flash 提供時鐘信號。
    • PA6(DO/SPI1 MISO):Flash → 主控返回數據(Master In Slave Out)。
    • PB9(CS):片選信號,拉低時選中 Flash 模塊(同一總線上可能接多個 SPI 設備,靠 CS 區分)。
    • VCC +3.3V:給 Flash 模塊供電;GND:接地。
  • 關鍵細節
    • “M6/M12 要互斥操作”:同一套 SPI 引腳可能接多個模塊(比如 M6 和 M12),同一時間只能選一個(通過不同 CS 控制)。
    • 引腳功能:MOSI 發命令 / 地址 / 數據,MISO 收數據,SCK 同步時鐘,CS 選設備。

(2)SPI協議和SPI控制結構

一、知識地圖:SPI 學習的 “原子級” 邏輯鏈

要徹底掌握 SPI,需理解?“硬件物理層 → 信號時序層 → 控制器內部邏輯層 → 軟件驅動層”?的完整閉環

二、逐圖拆解:從 “物理線” 到 “寄存器位” 的原子級解析

1. 「硬件接線 - SPI 外設連接」
  • 核心作用:明確 SPI 控制器如何外接多個設備(SPI Flash、SPI OLED),理解?“共享總線 + 片選區分”?的設計。
  • 關鍵細節(陰暗角落!)
    • 總線共享SCK、DO(MOSI)、DI(MISO)?是共享總線,多個設備并聯在這三根線上。
    • 片選(CS)的靈魂
      • 每個設備有獨立?CS(CS0 連 Flash,CS1 連 OLED),低電平有效(拉低對應 CS 才選中設備)。
      • 同一時間只能有一個 CS 被拉低,否則總線沖突(比如同時選 Flash 和 OLED,MOSI 發的數據會亂套)。
    • GPIO 的隱藏作用:如果 SPI 控制器的?NSS(硬件片選)不用,可通過 GPIO 模擬片選(靈活,但占 GPIO 資源)。
  • 關聯下一張圖:知道設備咋連后,得理解 “SPI 信號咋交互” → 看時序圖
2. 「SPI 控制器內部框圖」
  • 核心作用:揭秘?“SPI 控制器內部咋把 CPU 數據轉成時序信號”,理解移位寄存器、緩沖器的關鍵作用。
  • 關鍵模塊 & 陰暗細節
    • 移位寄存器(Shift register)
      • 是 SPI 收發的核心!發送時,CPU 把數據寫入?Tx buffer,移位寄存器逐位把數據推到?MOSI;接收時,MISO?來的數據逐位移入移位寄存器,裝滿后送到?Rx buffer
      • 不管是 8 位還是 16 位幀,都靠它按位 “搬運” 數據(比如發 0x56,會拆成 8 個比特,在 SCK 時鐘下一位位發)。
    • Tx/Rx buffer(發送 / 接收緩沖)
      • Tx buffer:CPU 寫數據到這里,移位寄存器從這里取數據發出去(先入先出,發完一個字節才會取下一個)。
      • Rx buffer:移位寄存器收完數據,存在這里,CPU 從這里讀(必須及時讀,否則新數據會覆蓋舊數據,導致?OVR?溢出錯誤)。
    • 波特率發生器(Baud rate generator)
      • 決定?SCK?的頻率(比如?f_SCK = f_PCLK / (BR[2:0] + 1)),直接影響傳輸速度(太快可能導致從機跟不上,出現數據錯誤)。
    • 控制寄存器(SPI_CR1、SPI_CR2)
      • CPOL、CPHA:決定時鐘極性和相位(后面時序圖重點講)。
      • MSTR:設為 1 表示主控模式(開發板作為 SPI 主機控制外設)。
      • LSB FIRST:設為 1 則先傳低位(默認先傳高位,需看外設要求)。
      • TXEIE、RXNEIE:發送 / 接收中斷使能(數據發完 / 收到時觸發中斷,異步通信常用)。
  • 關聯下一張圖:理解控制器內部后,得掌握 “SCK、MOSI、MISO 咋配合發數據” → 看時序圖
3. 「SPI 時序圖(CPOL=1 系列)」
  • 核心作用:明確?“時鐘極性(CPOL)、相位(CPHA)”?如何決定數據采樣時機,是 SPI 通信的協議靈魂
  • 陰暗細節(逐周期解析!)
    • CPOL(時鐘極性)
      • CPOL=1SCK?空閑時是高電平CPOL=0:空閑時是低電平(決定時鐘的 “基礎電平”)。
    • CPHA(時鐘相位)
      • CPHA=0第一個時鐘沿(上升沿 / 下降沿,看 CPOL)采樣數據;
      • CPHA=1第二個時鐘沿采樣數據。
    • 以?CPOL=1, CPHA=0(Format A)為例
      • SCK?空閑高電平 → 第一個時鐘沿是下降沿(從高→低),此時采樣?MOSI/MISO?數據。
      • 數據傳輸:MOSI?先發?MSB(最高位),MISO?同步返回數據,8 個時鐘周期傳完 1 字節。
    • 以?CPOL=1, CPHA=1(Format B)為例
      • 第一個時鐘沿(下降沿)不采樣,第二個時鐘沿(上升沿)?采樣數據。
      • 注意?MISO?數據延遲:返回的?LSB?是 “之前傳輸字符的低位”(時序對齊需要,外設必須支持)。
  • 關鍵坑點
    • 時序匹配:主機和從機的?CPOL、CPHA?必須完全一致,否則數據采樣錯位(比如主機用 CPOL=1,從機用 CPOL=0,時鐘沿對不上,數據全錯)。
    • MSB/LSB 順序:默認?LSB FIRST=0(先傳 MSB),如果外設要求先傳 LSB,必須設?LSB FIRST=1(看 datasheet!)。
  • 關聯下一張圖:對比?CPOL=0?的時序,理解四種模式的差異 → 看CPOL=0 時序圖(圖 4)。
4. 「SPI 時序圖(CPOL=0 系列)」
  • 核心作用:補充?CPOL=0?時的時序差異,理解?“四種 SPI 模式”?的完整邏輯。
  • 陰暗細節(與 CPOL=1 對比)
    • CPOL=0SCK?空閑時是低電平,第一個時鐘沿是上升沿(從低→高)。
    • CPOL=0, CPHA=0(Format A)
      • 第一個時鐘沿(上升沿)采樣數據,MOSI?先發?MSBMISO?同步返回。
    • CPOL=0, CPHA=1(Format B)
      • 第一個時鐘沿(上升沿)不采樣,第二個時鐘沿(下降沿)?采樣數據。
      • MISO?返回的?LSB*?是 “之前傳輸字符的低位”(時序對齊邏輯和 CPOL=1 類似)。
  • 關鍵總結
    • 四種模式對應?CPOL(0/1)和?CPHA(0/1)的組合,必須和外設手冊一致(比如 SPI Flash 可能要求模式 0,OLED 可能要求模式 3)。
    • 常用模式:模式 0(CPOL=0, CPHA=0)?和?模式 3(CPOL=1, CPHA=1),因為它們都在上升沿采樣(不管空閑電平,只要沿對齊即可,兼容性好)。
5. 「SPI 傳輸示例(0x56 時序)」
  • 核心作用:用實際數據(0x56 = 0b0101 0110)演示?“時序圖如何對應二進制位”,把抽象時序落地。
  • 陰暗細節(逐位解析)
    • CS0 拉低:選中 SPI Flash,開始傳輸。
    • SCK 時鐘與數據的對應
      • 0x56 的二進制是?0b0101 0110MSB?是第 7 位(0),LSB?是第 0 位(0)。
      • 每個?SCK?周期對應 1 個比特:
        • 第 1 個 SCK 下降沿(CPOL=1, CPHA=0 時):發?0(MSB),Flash 采樣。
        • 第 2 個 SCK 下降沿:發?1,依此類推…
        • 第 8 個 SCK 下降沿:發?0(LSB),傳輸結束。
    • 采樣時機:Flash 在每個 SCK 的上升沿采樣(因為示例中可能用了模式 3?需要結合前面的模式圖驗證)。
  • 關鍵驗證
    • 數一下 SCK 周期和數據位是否對應(8 個周期傳 8 位),理解?MSB 先傳?的規則。
    • 對比前面的時序模式圖,看這個示例屬于哪種?CPOL、CPHA?組合(比如這里 SCK 空閑高電平 → CPOL=1;下降沿發數據,上升沿采樣 → 可能是模式 2 或 3?需要細扣)。
6. 「SPI 模式總結表」
  • 核心作用:把四種 SPI 模式的規則表格化,方便快速查詢和配置。
  • 陰暗細節(表格逐行解析)
    • 模式 0(CPOL=0, CPHA=0)
      • SCK 空閑低電平,第一個時鐘沿(上升沿)采樣數據 → 常用,很多外設默認支持。
    • 模式 1(CPOL=0, CPHA=1)
      • SCK 空閑低電平,第二個時鐘沿(下降沿)采樣數據 → 部分外設(如某些傳感器)可能用。
    • 模式 2(CPOL=1, CPHA=0)
      • SCK 空閑高電平,第一個時鐘沿(下降沿)采樣數據 → 較少用,但某些舊設備可能要求。
    • 模式 3(CPOL=1, CPHA=1)
      • SCK 空閑高電平,第二個時鐘沿(上升沿)采樣數據 → 常用(和模式 0 互補,覆蓋上升沿采樣場景)。
  • 關鍵技巧
    • 記不住四種模式?記住 “常用模式 0 和 3”,它們都在上升沿采樣(不管空閑電平),配置時先試這兩個模式,不行再查外設手冊。
7. 「SPI 傳輸示例(0x56 時序圖)」
  • 核心作用:和圖 5 呼應,用更規范的時序圖展示?0x56?傳輸,驗證?“數據位與時鐘沿的對應關系”
  • 陰暗細節(與圖 5 對比)
    • 圖 5 是手繪版,圖 7 是規范版,都展示?0x56 = 0b0101 0110?的傳輸。
    • 注意?CS0?拉低的時機(傳輸前拉低,傳輸后拉高),以及?SCK?周期數(8 個周期傳 1 字節)。
  • 關鍵驗證
    • 數?DO?線上的電平是否和?0b0101 0110?一致(第 1 個 SCK 周期是?0,第 2 個是?1… 第 8 個是?0)。
8. 「SPI 主機模式配置步驟」
  • 核心作用:把 “軟件配置 SPI 控制器” 的步驟標準化,指導代碼怎么寫。
  • 陰暗細節(逐步驟解析)
    • 1. 配置波特率(BR [2:0])
      • 決定?SCK?頻率(f_SCK = f_PCLK / (BR + 1)),不能超過外設最大頻率(比如 SPI Flash 最大支持 80MHz,就不能設太高)。
      • 示例:BR[2:0] = 011?→ 分頻系數 4 →?f_SCK = 84MHz / 4 = 21MHz(假設 PCLK=84MHz)。
    • 2. 配置 CPOL、CPHA
      • 根據外設手冊選模式(比如 Flash 要求模式 0 → CPOL=0, CPHA=0)。
    • 3. 配置數據幀格式(DFF)
      • DFF=0?→ 8 位幀(常用);DFF=1?→ 16 位幀(某些設備如 OLED 可能用)。
    • 4. 配置 LSBFIRST
      • LSBFIRST=0?→ 先傳 MSB(默認,大多數外設要求);LSBFIRST=1?→ 先傳 LSB(少數外設如某些傳感器可能用)。
    • 5. 配置 NSS(片選)
      • 硬件模式:NSS?引腳接高電平,靠硬件自動控制;
      • 軟件模式:設?SSM=1, SSI=1,用軟件控制?CS(靈活,適合多設備)。
    • 6. 使能 SPI(MSTR、SPE)
      • MSTR=1?→ 主機模式;SPE=1?→ 使能 SPI 控制器。
  • 關鍵坑點
    • 波特率不能亂設:太高會導致從機收不到數據(比如 Flash 最大支持 30MHz,你設成 50MHz,就會丟數據)。
    • NSS 配置易錯:軟件模式下必須設?SSM=1,否則?NSS?引腳會自動拉低,導致總線沖突。

(3)SPI_HAL庫編程

第一步:先搞懂 SPI 是啥 —— 就像 “多人打電話”

SPI 是單片機(比如 STM32)和其他設備(比如傳感器、顯示屏)之間 “傳數據” 的一種方式,就像幾個人用電話通話:

  • 主機:STM32(相當于發起通話的人)
  • 從機:被控制的設備(比如傳感器,相當于接電話的人)
  • 線的作用
    • SCK 線:時鐘線(相當于 “喂喂喂” 的節奏,保證雙方語速一致)
    • MOSI 線:主機發、從機收(主機說話的線)
    • MISO 線:從機發、主機收(從機回話的線)
    • NSS 線:片選線(主機想跟哪個從機說話,就拉低對應從機的 NSS 線,相當于 “小明,聽我說”)

第二步:用 CubeMX “搭線路”—— 相當于 “插電話線”

在寫代碼前,需要用 STM32CubeMX 軟件配置 SPI 的 “硬件線路”,就像提前插好電話線、設置好通話規則。

1. 配置 SPI 核心參數

打開 CubeMX,找到 SPI 外設(比如 SPI1),配置以下參數:

  • 模式:選 “全雙工主機”(最常用,主機既能說也能聽)
  • 幀格式
    • 數據長度:8 位(一次傳 1 個字節,像一次說一個字)
    • 高位在前(MSB First,像說話從第一個字開始)
    • 時鐘極性 / 相位:默認選 “低電平空閑,第一個邊沿采樣”(記不住沒關系,CubeMX 默認值一般能用)
  • 時鐘分頻:比如 “分頻 8”(STM32 主頻 72MHz 的話,SPI 時鐘就是 9MHz,相當于說話的語速,不能太快否則從機聽不懂)
  • 下面會自動顯示引腳:比如 SPI1 的 SCK=PA5、MOSI=PA7、MISO=PA6(這些是硬件固定的,不用改)
2. 配置片選引腳

NSS 線(片選)一般用軟件控制(更靈活),需要手動配置一個普通 GPIO 當片選:

  • 選一個引腳(比如 PB9),模式設為 “推挽輸出”(能輸出高低電平)
  • 初始狀態設為 “高電平”(默認不選中從機,相當于 “先不打電話”)
3. 生成代碼

配置完后,點 “Generate Code” 生成初始化代碼,CubeMX 會自動幫我們寫好 SPI 的基礎設置(不用自己寫)。

第三步:用查詢方式 “發消息”—— 相當于 “對著電話一直說,等對方回應”

最基礎的通信方式:發送數據時,STM32 會 “一直等” 到發送完成,再做下一步(類似打電話時一直說,直到說完才停)。

1. 發送數據函數

HAL_SPI_Transmit()函數發送,格式:

HAL_SPI_Transmit(&hspi1, 發送的數據地址, 數據長度, 超時時間);
  • &hspi1:CubeMX 生成的 SPI1 結構體(相當于指定用哪部電話)
  • 發送的數據地址:比如&data(要發的數據存在哪里)
  • 數據長度:比如 1(發 1 個字節)
  • 超時時間:比如 100(最多等 100ms,沒發完就報錯)
2. 接收數據函數

HAL_SPI_Receive()函數接收,格式類似:

HAL_SPI_Receive(&hspi1, 接收數據的緩沖區地址, 長度, 超時時間);
3. 收發同時進行

HAL_SPI_TransmitReceive(),一邊發一邊收(全雙工的特點):

HAL_SPI_TransmitReceive(&hspi1, 要發的數據, 接收緩沖區, 長度, 超時時間);
舉個例子:給從機發命令并讀回數據
uint8_t send_data = 0x55;  // 要發的命令
uint8_t recv_data;         // 用來存接收的數據HAL_GPIO_WritePin(PB9_GPIO_Port, PB9_Pin, GPIO_PIN_RESET);  // 拉低PB9,選中從機(“小明,聽著”)
HAL_SPI_TransmitReceive(&hspi1, &send_data, &recv_data, 1, 100);  // 發命令同時收數據
HAL_GPIO_WritePin(PB9_GPIO_Port, PB9_Pin, GPIO_PIN_SET);    // 拉高PB9,釋放從機(“說完了”)

第四步:用中斷方式 “發消息”—— 相當于 “說完一段話就掛電話,對方聽完打回來”

查詢方式會讓 STM32 一直等著,效率低。中斷方式是:STM32 發起發送后,就去做別的事,等數據發完了,硬件會 “打斷” STM32,提醒它 “發送完成了”(類似發微信,不用一直盯著,收到回復再看)。

1. 中斷函數怎么用

用帶_IT后綴的函數,比如:

  • 發送:HAL_SPI_Transmit_IT(&hspi1, 數據地址, 長度);
  • 接收:HAL_SPI_Receive_IT(&hspi1, 緩沖區, 長度);
  • 收發同時:HAL_SPI_TransmitReceive_IT(...);

這些函數調用后會立刻返回,STM32 可以去干別的(比如亮燈、讀按鍵)。

2. 中斷是怎么 “提醒” 的(對應 “圖 4→圖 3→圖 5→圖 2”)

中斷的流程像 “快遞送貨”:

  • (SPI1_IRQHandler):硬件中斷入口(相當于快遞員到家門口按門鈴),這是 STM32 芯片自帶的函數,會自動調用 HAL 庫的處理函數。
  • (HAL_SPI_IRQHandler):HAL 庫的中斷總處理(相當于家人聽到門鈴,去開門),它會檢查是發送中斷還是接收中斷,然后調用具體的處理函數。
  • (中斷初始化邏輯):在調用_IT函數時,HAL 庫會提前 “綁定” 好具體處理函數(比如 8 位數據對應SPI_2linesRxISR_8BIT),相當于 “告訴家人,快遞來了怎么處理”。
  • (SPI_2linesRxISR_8BIT):實際處理接收的函數(相當于家人接過快遞,拆開看),會把收到的字節存到緩沖區,直到收完所有數據。
3. 收到 “提醒” 后做什么 —— 回調函數

當中斷處理完數據(比如發送完成、接收完成),HAL 庫會自動調用 “回調函數”,我們可以在回調函數里寫后續操作(比如收到數據后計算、點燈)。

常用回調函數:

  • HAL_SPI_TxCpltCallback():發送完成回調
  • HAL_SPI_RxCpltCallback():接收完成回調
  • HAL_SPI_ErrorCallback():出錯時回調

這一步放 “圖 6”,它列出了各種回調函數,說明什么時候會被調用

舉個例子:用中斷接收數據
uint8_t recv_buf[5];  // 接收緩沖區// 啟動中斷接收(收5個字節)
HAL_GPIO_WritePin(PB9_GPIO_Port, PB9_Pin, GPIO_PIN_RESET);  // 選中從機
HAL_SPI_Receive_IT(&hspi1, recv_buf, 5);  // 啟動接收,立刻返回// 接收完成后,自動調用這個回調函數
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) {if (hspi == &hspi1) {  // 確認是SPI1的中斷HAL_GPIO_WritePin(PB9_GPIO_Port, PB9_Pin, GPIO_PIN_SET);  // 釋放從機// 這里可以處理收到的recv_buf數據,比如打印、計算}
}

第五步:用 DMA 方式 “發消息”—— 相當于 “雇個快遞員,自己不用管”

如果要傳大量數據(比如發 1000 個字節),用查詢或中斷會占用 STM32 太多時間。DMA 方式相當于 “雇個快遞員”,讓 DMA 控制器直接搬運數據,STM32 全程不用插手(效率最高)。

DMA 函數怎么用

用帶_DMA后綴的函數,比如:

  • 發送:HAL_SPI_Transmit_DMA(&hspi1, 數據地址, 長度);
  • 接收:HAL_SPI_Receive_DMA(&hspi1, 緩沖區, 長度);
  • 收發同時:HAL_SPI_TransmitReceive_DMA(...);

調用后,DMA 會自動搬數據,完成后通過中斷通知 STM32(和中斷方式一樣,會調用回調函數)。

(4)SPI_Flash_W25Q64操作方法

一、基礎概念與整體邏輯

W25Q64 是常用的 SPI 接口 Flash 存儲芯片,要操作它(讀、寫、擦除等),需遵循?“先理解芯片存儲結構 → 掌握 SPI 指令交互流程 → 按步驟實現讀寫擦操作”?的邏輯。

簡單說:

  • 存儲結構:芯片像很多 “小格子”,有頁(256 字節)、扇區(4KB = 16 頁 )等劃分,操作要對應這些單位。
  • SPI 交互:通過 SPI 總線發 “指令 + 地址 + 數據”,讓 Flash 執行讀、寫、擦除,還要用狀態寄存器判斷操作是否完成。

二、核心流程串聯

1. 讀數據流程(最基礎,先學它!)

作用:從 Flash 指定地址把數據讀出來,比如讀取之前存的配置、日志。

(1)讀操作時序
  • (9.5.1 讀數據 - 時序圖)
    這是讀操作的完整 SPI 時序,步驟分解:

    1. 拉低 /CS:選中 Flash 芯片(相當于敲門說 “我要操作你啦” )。
    2. 發讀指令(0x03):通過 MOSI 線發指令?0x03,告訴 Flash “我要讀數據” 。
    3. 發 24 位地址:接著發要讀取的地址(比如?0x000000?),告訴 Flash “從這個位置開始讀” 。
    4. 讀數據:地址發完后,Flash 會從 MISO 線把數據傳回來,讀一個字節后,內部地址自動 +1,可連續讀很多數據,直到 /CS 拉高。
  • 補充解釋了 “發完地址后,Flash 持續輸出數據,直到操作結束”,幫你理解?“連續讀”?邏輯 —— 讀一個字節后,地址自動遞增,能一直讀到芯片末尾,不用每次重新發地址。

2. 寫數據流程(要擦除后才能寫,稍復雜 )

注意:Flash 特性是 “寫之前必須擦除”(因為只能從 1 改 0,擦除是把 0 改回 1 ),所以寫操作分?“擦除扇區 → 寫使能 → 燒寫頁”?三步。

(1)寫使能

作用:告訴 Flash “我要準備寫數據 / 擦除了,打開寫權限”,是寫、擦除操作的?必要前提?。

  • (寫使能時序)
    步驟:
    1. 拉低 /CS → 選中芯片。
    2. 發寫使能指令?0x06?→ 告訴 Flash “允許我寫數據啦” 。
    3. 拉高 /CS → 結束操作。

為什么必須?:Flash 有 “寫保護”,發?0x06?是解除保護的鑰匙,否則寫、擦除會失敗!

(2)擦除扇區

作用:寫數據前,必須把要寫的區域擦成 “全 1”,W25Q64 最小擦除單位是?扇區(4KB = 16 頁 )?。

  • (擦除扇區時序)
    步驟:
    1. 拉低 /CS → 選中芯片。
    2. 發擦除指令?0x20?→ 告訴 Flash “我要擦除扇區” 。
    3. 發 24 位地址 → 指定要擦除的扇區(比如地址?0x000000?對應第 0 扇區 )。
    4. 拉高 /CS → 啟動擦除(擦除需要時間,不是立刻完成 )。

怎么判斷擦除完成?:擦除時 Flash 內部忙,要讀?狀態寄存器(后面講)?看?BUSY?位,BUSY=0?才代表擦除完!

(3)燒寫頁

作用:把數據寫入 Flash,最小寫入單位是?頁(256 字節 )?,可從頁內任意位置開始寫,寫超頁末尾會 “繞回頁開頭” 。

  • (燒寫頁時序)
    步驟:
    1. 拉低 /CS → 選中芯片。
    2. 發頁編程指令?0x02?→ 告訴 Flash “我要寫數據到頁里” 。
    3. 發 24 位地址 → 指定要寫入的頁起始地址(比如?0x000000?對應第 0 頁 )。
    4. 發數據(最多 256 字節 )→ 把要存的數據通過 MOSI 發過去,寫超 256 字節會覆蓋頁開頭數據。
    5. 拉高 /CS → 啟動寫入(同樣要等狀態寄存器?BUSY=0?才完成 )。

3. 狀態寄存器

作用:不管是擦除還是寫入,Flash 都需要時間完成(尤其是擦除,可能要幾毫秒到幾十毫秒 )。通過讀?狀態寄存器?里的?BUSY?位,能判斷操作是否完成。

  • (狀態寄存器結構)
    這是狀態寄存器的 8 位含義,重點看?最低位(S0)→ BUSY 位?:
    • BUSY=1:Flash 正在忙(擦除、寫入中 ),不能執行新操作。
    • BUSY=0:操作完成,可執行下一個指令。

怎么讀狀態寄存器?:發專門的 “讀狀態寄存器指令(0x05?)”,流程類似讀數據:拉低 /CS → 發?0x05?→ 讀 1 個字節(狀態寄存器值 )→ 拉高 /CS 。

4. 芯片存儲結構說明

作用:理解 “頁、扇區、塊” 的劃分,知道操作單位,避免寫錯區域或擦除范圍不對。

  • (英文說明 - 存儲結構)
    關鍵翻譯:
    • W25Q64 有?32768 頁?,每頁 256 字節(所以總容量 32768×256 = 8MB = 64Mbit,對應型號 )。
    • 擦除單位:
      • 扇區(4KB ):16 頁擦除一次(0x20?指令 )。
      • 塊(32KB ):128 頁擦除一次(另一個指令,比如?0x52?,課程里沒細講但要知道有更大單位 )。
      • 塊(64KB ):256 頁擦除一次(指令?0xD8?)。
      • 整片擦除:全部內容擦除(指令?0xC7?)。

實際用:小數據寫入用 “頁編程”,大范圍擦除用 “扇區 / 塊擦除”,根據需求選。

三、完整操作流程總結(從讀 → 擦除 → 寫 )

把上面的步驟串起來,比如 “要寫數據到 Flash 某地址”,完整流程是:

  1. 擦除對應扇區

    • 發寫使能→ 發擦除扇區指令 + 地址→ 循環讀狀態寄存器,直到?BUSY=0
  2. 燒寫頁數據

    • 發寫使能→ 發頁編程指令 + 地址 + 數據→ 循環讀狀態寄存器,直到?BUSY=0?。
  3. 驗證數據(讀操作)

    • 用讀指令讀剛才寫的地址,對比數據是否正確。

(5)W25Q64 SPI Flash 驅動開發(內部函數篇)保姆級筆記

這篇筆記只關注 “如何實現功能”,不糾結代碼規范,帶你你從 “新建文件” 到 “每個函數怎么用” 一步步看懂,零基礎也能跟著做!

一、前期準備:新建文件并添加到工程

1. 新建文件

  • 打開你的工程文件夾(比如叫 “STM32_Project”),右鍵新建兩個文件:

    • 一個叫?driver_spi_flash.c(放具體代碼邏輯)
    • 一個叫?driver_spi_flash.h(放函數聲明,后面會用到)

2. 添加到工程

  • 打開編程軟件(比如 Keil MDK),右鍵 “Source Group”→“Add Files to Group”,選中剛新建的?driver_spi_flash.c,點 “Add”。

二、硬件配置回顧(代碼能跑的前提)

在寫代碼前,要先通過 CubeMX 配置好硬件,這些配置是代碼能正常運行的基礎:

1. SPI 外設配置

  • 簡單說:STM32 作為 “主機”,通過 SPI1 和 W25Q64 通信,用 “中斷方式” 收發數據(后面代碼里會用到?HAL_SPI_Transmit_IT?這類函數)。

2. 片選引腳配置

  • 作用:PB9 是控制 W25Q64 “是否工作” 的開關,拉低就是 “選中它”,拉高就是 “不選中”。

三、driver_spi_flash.c?代碼逐行解析

從最基礎的 “選芯片” 到 “發指令”,每個函數都講清楚怎么用、為什么這么寫:

1. 頭文件和變量聲明

#include "driver_spi_flash.h"  // 自己建的頭文件,后面會放函數聲明
#include "ascii_font.c"       // 可能是字庫文件,暫時用不到可以不管
#include "stm32f1xx_hal.h"    // HAL庫的核心文件,提供SPI、GPIO等函數// 聲明兩個等待函數(在其他文件里實現,比如spi.c)
void Wait_spi1_txcplt(void);       // 等SPI發送完成
void Wait_spi1_txrxcplt(void);     // 等SPI收發完成// 引用外部的SPI1配置結構體(CubeMX自動生成的,存著SPI的各種參數)
extern SPI_HandleTypeDef hspi1;

作用:引入必要的工具(函數、變量),讓下面的代碼能正常調用。

2. 內部函數:控制片選(選芯片 / 不選芯片)
用法:每次和 W25Q64 通信前,先用?spiFlash_select()?選中它;通信結束后,用?spiFlash_deselect()?釋放它。

3. 內部函數:寫使能(允許芯片被寫入 / 擦除)

// 發送“寫使能”指令(0x06),讓芯片允許后續的寫入或擦除操作
static int spiFlash_writeEnable(void)
{uint8_t BUF[1] = {0x06};  // 定義要發送的指令:0x06就是“寫使能”的意思// 用中斷方式發送這個指令:啟動發送后,函數會立刻返回,等發送完會觸發中斷HAL_SPI_Transmit_IT(&hspi1, BUF, 1);Wait_spi1_txcplt();  // 等待發送完成(這個函數會一直等,直到SPI發送完數據)
}

(片選拉低→發 0x06→片選拉高)
為什么要做:W25Q64 默認不允許寫入或擦除,必須先發這個指令 “解鎖”,否則后續操作會失敗。

4. 內部函數:讀狀態寄存器(判斷芯片是否忙)

// 讀芯片的狀態寄存器,判斷它是否在工作(比如正在擦除或寫入)
static int spiFlash_readstatus(void)
{uint8_t txBUF[2] = {0x05, 0xff};  // 要發送的內容:0x05是“讀狀態”指令,0xff是填充數uint8_t rxBUF[2] = {0, 0};        // 用來存接收到的數據(芯片返回的狀態)// 用中斷方式同時收發:發送指令的同時,接收芯片返回的狀態HAL_SPI_TransmitReceive_IT(&hspi1, txBUF, rxBUF, 2);Wait_spi1_txrxcplt();  // 等待收發完成return rxBUF[1];  // 返回狀態寄存器的值(第二個字節才是有效狀態)
}

(發 0x05 指令后,芯片返回狀態值)
作用:芯片擦除或寫入時會 “忙”,狀態寄存器的第 0 位是 “忙標志”(1 = 忙,0 = 空閑),讀它能知道芯片是否準備好接受新指令。

5. 內部函數:等待芯片空閑

// 一直等,直到芯片不忙(狀態寄存器的忙標志為0)
static int spiFlash_WriteRead(void)
{while(spiFlash_readstatus() & 1 == 1);  // 讀狀態,如果忙就一直等
}

用法:在擦除或寫入操作后調用,等芯片完成當前工作,再進行下一步。

6. 宏定義與全局聲明

// 定義SPI操作超時時間(單位:毫秒)
#define SPI_FLASH_TIMEOUT 1000// 聲明SPI等待函數(用于等待傳輸完成)
void Wait_spi1_txcplt(int timeout);       // 等待發送完成
void Wait_spi1_txrxcplt(int timeout);     // 等待收發完成
void Wait_spi1_rxcplt(int timeout);       // 等待接收完成// 聲明SPI句柄(在其他文件中初始化,如main.c)
extern SPI_HandleTypeDef hspi1;// 全局標志位(用于中斷與主程序同步,需在.c文件中定義)
static volatile uint8_t spi1_tx_done = 0;    // 發送完成標志
static volatile uint8_t spi1_rx_done = 0;    // 接收完成標志
static volatile uint8_t spi1_txrx_done = 0;  // 收發完成標志

作用:定義超時時間、聲明等待函數和 SPI 句柄,以及用于中斷同步的標志位。

7. 等待函數實現

 //SPI.C
static volatile int g_spil_tx_complete= 0;
static volatile int g_spil_txrx_complete= 0;
static volatile int g_spil_rx_complete= 0;
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{if(hspi == &hspi1){g_spil_tx_complete =1;}
}
void Wait_spi1_txcplt(int timeout)
{
while (g_spil_tx_complete == 0 && timeout--)
{HAL_Delay(1);
}g_spil_tx_complete = 0;
}void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{if(hspi == &hspi1){g_spil_txrx_complete =1;}
}void Wait_spi1_txrxcplt(int timeout)
{
while (g_spil_txrx_complete == 0 && timeout--)
{
HAL_Delay(1);
}g_spil_txrx_complete = 0;
}void Wait_spi1_rxcplt(int timeout)
{
while (g_spil_rx_complete == 0 && timeout--)
{HAL_Delay(1);
}g_spil_rx_complete = 0;
}

8. 對外函數

這些函數是給用戶直接調用的,比如讀 ID、擦除扇區等,雖然現在是空的,但框架要清楚:

(1)讀芯片 ID
int spi_flash_readID(void)
{uint8_t txBUF[2] = {0x9F, 0xff};  // 發送緩沖區:讀ID命令+填充字節uint8_t rxBUF[2] = {0, 0};        // 接收緩沖區spiFlash_select();  // 選中SPI閃存(拉低CS信號)// 以中斷方式發送2字節數據,同時接收2字節數據HAL_SPI_TransmitReceive_IT(&hspi1, txBUF, rxBUF, 2);Wait_spi1_txrxcplt(SPI_FLASH_TIMEOUT);  // 等待收發完成(帶超時)spiFlash_deselect();  // 取消選中(拉高CS信號)return rxBUF[1];  // 返回接收的ID值
}

功能:讀取 SPI 閃存的芯片 ID,用于驗證芯片是否正常連接。
關鍵細節

  • 命令0x9F是 SPI 閃存的 "讀 ID 命令"(不同芯片可能有差異)。
  • 發送緩沖區第二個字節0xff是占位符,SPI 通信為全雙工,發送的同時會接收數據。
  • 芯片 ID 通過rxBUF[1]返回(具體位置取決于芯片規格,部分芯片可能返回多字節 ID)
(2)擦除扇區
int spi_flash_ErasesSector(uint32_t addr)
{uint8_t txBUF[4] = {0x20};  // 發送緩沖區:扇區擦除命令/* 寫使能 */spiFlash_writeEnable();  // 發送寫使能命令,允許擦除操作/* 填充地址 */txBUF[1] = (addr >> 16) & 0xff;  // 地址高8位txBUF[2] = (addr >> 8) & 0xff;   // 地址中8位txBUF[3] = (addr >> 0) & 0xff;   // 地址低8位spiFlash_select();  // 選中芯片// 以中斷方式發送4字節命令(擦除命令+地址)HAL_SPI_Transmit_IT(&hspi1, txBUF, 4);Wait_spi1_txcplt(SPI_FLASH_TIMEOUT);  // 等待發送完成spiFlash_deselect();  // 取消選中/* 等待擦除完成 */spiFlash_WriteRead();  // 循環等待芯片空閑(狀態寄存器忙標志位清零)return 0;  // 返回成功狀態
}

功能:擦除指定地址addr所在的扇區(扇區大小由芯片決定,通常為 4KB/64KB)。
關鍵細節

  • 命令0x20是扇區擦除指令,必須配合地址使用。
  • 擦除前必須調用spiFlash_writeEnable(),否則操作無效(硬件保護機制)。
  • 擦除是耗時操作(毫秒級),spiFlash_WriteRead()會等待操作完成后再返回。
(3)寫數據
int spi_flash_writeData(uint32_t addr, uint8_t *data, uint32_t len)
{uint8_t txBUF[4] = {0x02};  // 發送緩沖區:頁寫命令/* 寫使能 */spiFlash_writeEnable();  // 允許寫入操作/* 填充地址 */txBUF[1] = (addr >> 16) & 0xff;  // 地址高8位txBUF[2] = (addr >> 8) & 0xff;   // 地址中8位txBUF[3] = (addr >> 0) & 0xff;   // 地址低8位spiFlash_select();  // 選中芯片/* 發送命令和地址 */HAL_SPI_Transmit_IT(&hspi1, txBUF, 4);Wait_spi1_txcplt(SPI_FLASH_TIMEOUT);  // 等待命令發送完成/* 發送數據 */HAL_SPI_Transmit_IT(&hspi1, data, len);  // 發送用戶數據Wait_spi1_txcplt(SPI_FLASH_TIMEOUT);  // 等待數據發送完成spiFlash_deselect();  // 取消選中/* 等待寫入完成 */spiFlash_WriteRead();  // 等待芯片完成寫入操作return 0;  // 返回成功狀態
}

功能:向指定地址addr寫入長度為len的字節數據(*data)。
關鍵細節

  • 命令0x02是 "頁寫命令",一次寫入不能跨頁(頁大小通常為 256 字節)。
  • 寫入前必須確保目標扇區已擦除(擦除后數據為0xff,才能寫入非0xff值)。
  • 分兩步發送:先發送命令和地址,再發送實際數據,符合 SPI 閃存的寫入時序要求。

(4)讀數據

int spi_flash_ReadData(uint32_t addr, uint8_t *datas, uint32_t len)
{uint8_t txBUF[4] = {0x03};  // 發送緩沖區:讀數據命令/* 填充地址 */txBUF[1] = (addr >> 16) & 0xff;  // 地址高8位txBUF[2] = (addr >> 8) & 0xff;   // 地址中8位txBUF[3] = (addr >> 0) & 0xff;   // 地址低8位spiFlash_select();  // 選中芯片/* 發送命令和地址 */HAL_SPI_Transmit_IT(&hspi1, txBUF, 4);Wait_spi1_txcplt(SPI_FLASH_TIMEOUT);  // 等待命令發送完成/* 讀取數據 */HAL_SPI_Receive_IT(&hspi1, datas, len);  // 接收數據到datas緩沖區Wait_spi1_rxcplt(SPI_FLASH_TIMEOUT);  // 等待接收完成spiFlash_deselect();  // 取消選中return 0;  // 返回成功狀態
}

功能:從指定地址addr讀取長度為len的字節數據,存儲到*datas緩沖區。

關鍵細節

  • 命令0x03是 "讀數據命令",支持連續讀取(不受頁限制)。
  • 讀取操作無需寫使能(只讀操作無硬件保護)。
  • 分兩步執行:先發送命令和地址,再接收數據,符合 SPI 閃存的讀取時序。

記得聲明

9.主函數

#include "main.h"
#include "dma.h"
#include "i2c.h"
#include "spi.h"
#include "usart.h"
#include "gpio.h"
#include "circle_buffer.h"
#include "driver_SPI_Flash.h"
void Wait_Tx_Complete(void);    // 等待UART發送完成
void Wait_Rx_Complete(void);    // 等待UART接收完成
void startuart1recv(void);      // 啟動UART1接收中斷
int UART1getchar(uint8_t *pVal);// 從UART1獲取一個字符
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c);//主設備發送完成回調函數
/* USER CODE END 1 */
void Wait_i2c1Tx_Complete(void);
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c);//主設備接收完成回調函數
void Wait_i2c1Rx_Complete(void);
void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c);//主設備發送完成回調函數
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c);//主設備發送完成回調函數
// 環形緩沖區相關變量:用于存儲按鍵數據
static uint8_t g_data_buf[100];  // 緩沖區的實際存儲空間(可以存100個字節)
static circle_buf g_key_bufs;    // 環形緩沖區結構體(管理存儲空間的讀寫)int main(void)
{int len;  // 臨時變量:存儲字符串長度// 定義要發送的字符串:\r\n是換行符(串口通信中常用)char *str = "Please enter a char: \r\n";char *str2 = "www.100ask.net";char c;   // 存儲接收的字符char flash_buf[20];HAL_Init();SystemClock_Config();// 初始化環形緩沖區:circld_buf_init是拼寫錯誤,正確應為circle_buf_init// 參數:緩沖區結構體、大小100、存儲空間g_data_bufcircld_buf_init(&g_key_bufs, 100, g_data_buf);MX_GPIO_Init();MX_DMA_Init();MX_I2C1_Init();MX_USART1_UART_Init();MX_SPI1_Init();OLED_Init();      // 初始化OLED屏幕OLED_Clear();     // 清屏(清除OLED上的所有顯示)
OLED_Printchinese(5,6);
int id =spi_flash_readID();
OLED_PrintHex(0,0,id,1);startuart1recv();  // 啟動UART1接收中斷:讓UART1準備好接收數據,收到數據后會觸發中斷
spi_flash_ErasesSector(0);//每個扇區大小是4096
spi_flash_writeData(0,str2,strlen(str2)+1);
spi_flash_ReadData(0,flash_buf,20);
OLED_PrintString(0,2,flash_buf);while (1){}
}

四、函數調用流程示例(以 “擦除扇區” 為例)

用一個實際操作演示這些內部函數怎么配合:

  1. 先調用?spiFlash_writeEnable()?發寫使能指令(解鎖);
  2. 調用?spiFlash_select()?選中芯片;
  3. 發送 “擦除扇區” 指令和地址;
  4. 調用?spiFlash_WriteRead()?等待擦除完成;
  5. 調用?spiFlash_deselect()?釋放芯片。

這樣一步步看下來,你能從 “文件怎么建” 到 “每個函數干什么” 再到 “函數怎么配合工作”,完全掌握這些內部函數的用法,后續完善對外函數時就會很輕松啦!

五、結果展示

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/94966.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/94966.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/94966.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

線上Linux服務器的優化設置、系統安全與網絡安全策略

一、Linux服務器的優化設置 線上Linux的優化配置序號基礎優化配置內容說明1最小化安裝系統【僅安裝需要的,按需安裝、不用不裝】,必須安裝的有基本開發環境、基本網絡包、基本應用包。2ssh登錄策略優化 Linux服務器上的ssh服務端配置文件是【/et…

基于人眼視覺特性的相關圖像增強基礎知識介紹

目錄 1. 傳統的灰度級動態范圍優化配置方法 2.基于視覺特性的灰度級動態范圍調整優化 1. 傳統的灰度級動態范圍優化配置方法 傳統的灰度級動態范圍調整方法主要包括線性動態范圍調整及非線性動態 范圍調整。線性動態范圍調整是最簡單的灰度級動態范圍調整方法,觀察…

Selenium使用超全指南

🍅 點擊文末小卡片 ,免費獲取軟件測試全套資料,資料在手,漲薪更快概述selenium是網頁應用中最流行的自動化測試工具,可以用來做自動化測試或者瀏覽器爬蟲等。官網地址為:相對于另外一款web自動化測試工具QT…

Go通道操作全解析:從基礎到高并發模式

一、channel類型 Go 語言中的通道(channel)是一種特殊的類型。它類似于傳送帶或隊列,遵循先進先出(FIFO)原則,確保數據收發順序的一致性。每個通道都是特定類型的導管,因此在聲明時必須指定其元素類型。 channel是一種類型, 一種引用類型。 聲明通道類型的格式如下:…

Linux網絡--1、網絡基礎

目錄 一、網絡發展 二、理解分層 2.1OSI七層模型 2.2TCP/IP分層模型 2.3分層的好處 三、認識協議 3.1初步認識 3.2了解指定組織 3.3具體協議理解 3.3.1是什么 3.3.2為什么 3.3.3與OS的關系 3.4總結 四、網絡傳輸流程 4.1局域網網絡傳輸 4.1.1通信過程 4.1.2概念解析 4.2跨網…

前端視角下關于 WebSocket 的簡單理解

參考 RFC 6455: The WebSocket Protocol WebSocket 協議基礎 協議本質:在單個 TCP 連接上提供全雙工通信通道的協議核心優勢: 雙向實時通信(服務器主動推送)低延遲(相比 HTTP 輪詢)高效數據傳輸&#xff0…

自動化一鍵部署 LNMP 環境

第一步:準備環境 & 準備腳本文件1. 你在 CentOS 7 的服務器/虛擬機里打開終端,確認你有 root 權限或者能用 sudo。輸入下面命令確認你的系統版本:cat /etc/centos-release你應該看到類似:CentOS Linux release 7.9.2009 (Core…

react之React.cloneElement()

react提供的這個方法克隆組件的方法,可能我們在平常的開發中用的很少,主要可能是我們并不知道或者并不了解這個方法。因為我在之前react的children文章中用到過,所以我就進行了一系列的測試,發現真的非常的好用。我們同樣使用一些…

學習Java的Day27

今天學習的主要內容是在IntelliJ IDEA開發環境中,通過部署Tomcat服務器并連接MySQL數據庫,實現了一個完整的留言板系統。這個項目涵蓋了前后端開發的全流程,具體包括以下關鍵環節:開發環境搭建使用IntelliJ IDEA Ultimate版&#…

【計算機網絡 | 第3篇】物理媒介

文章目錄物理媒介介紹與物理媒體的分類🥝成本考量引導型傳輸媒體🍋引導型傳輸媒體:雙絞線🍋?🟩雙絞線類別雙絞線的發展歷程雙絞線的物理限制引導型傳輸媒體:同軸電纜🍋?🟩結構組成…

golang的切片

切片 為什么需要切片 用于元素的個數不確定,所以無法通過數組的形式來進行統計。此時就需要切片 切片,也因此可以粗略地理解為動態數組數組的長度不能用變量來確定,這時候切片slice也就派上用場了 切片地基本介紹 切片的英文是slice切片是數組…

在labview中實現視頻播放

這里分享一個迅雷的視頻播放控件APlayer,非常的好用。具體操作步驟如下: 1.下載控件: 首先下載http://aplayer.open.xunlei.com/codecs.zip,將codecs文件解壓后打開,按快捷鍵contrlA,隨后contrlc復制里面所有的文件;…

ubuntu 22.04 使用yaml文件 修改靜態ip

前提: 啟動服務 sudo systemctl start systemd-networkd 設置開機自啟 sudo systemctl enable systemd-networkd 檢查狀態(確保顯示 active (running)) sudo systemctl status systemd-networkd 若想停止: 停止當前運行的服務 sud…

閘機控制系統從設計到實現全解析:第 4 篇:Redis 緩存與分布式鎖實現

第 4 篇:Redis 緩存與分布式鎖實現 一、Redis 在系統中的核心作用票證信息緩存:將高頻訪問的票證數據(如狀態、有效期)緩存至 Redis,減少數據庫查詢,提升驗證響應速度。分布式鎖:在高并發場景下…

北京天津唐山廊坊滄州打撈日記

北京天津唐山廊坊滄州打撈日記 打撈搜蚯蚓疏通 北京:護城河畔的情誼打撈 清晨,北京的護城河在朝陽的映照下泛著微光。我接到一位年輕小伙的電話,聲音中滿是焦急。原來,他與女友在河邊約會時,不小心將女友送他的定情玉佩…

全志刷機工具:PhoenixSuit-全志芯片處理器-刷機工具安裝包及最詳細使用教程指南

全志刷機工具:PhoenixSuit-全志芯片處理器刷機工具安裝包及最詳細使用教程指南,此文章主要是分享機頂盒、電視盒子,全志芯片盒子(其中包含全志處理器、全志芯片、全志CPU等等)的刷機工具、刷機工具安裝教程以及如何使用…

淺談 VM 橋接模式:讓虛擬機像真實電腦一樣接入網絡

在虛擬化環境中,虛擬機(Virtual Machine, VM)與外部網絡之間的通信方式有多種,比如 NAT 模式、Host-Only 模式、橋接模式(Bridged Networking) 等。其中,橋接模式是最接近“真實物理機”網絡行為…

計算機視覺(1)-圖像采集設備選型全景表(工業 + 醫療 + 車載)

圖像采集設備選型全景表(工業 醫療 車載)一份面向工程師的“場景—設備—協議”速查表1 工業 & 醫療 & 通用場景應用場景主流設備形態接口 / 協議典型性能突出優勢致命短板動態范圍工業檢測AOI / 量測 / 缺陷工業相機 采集卡Camera Link HSCo…

計算機視覺(3)深度學習模型部署平臺技術選型與全棧實踐指南

一、部署平臺概述與分類 深度學習模型部署平臺的分類需兼顧技術特性與應用場景的適配性,基于“技術定位-場景適配”雙維度分類法,可將其劃分為通用開源框架、云廠商服務及專用邊緣工具三大類,各類別在設計目標、核心能力與場景覆蓋上呈現顯著…

Scratch編程:槍戰游戲(附源碼)

🎮 操作說明 W / A / S / D 或 方向鍵:移動 C:滑鏟 空格鍵:取消滑鏟 鼠標點擊:開火 數字鍵 1 / 2 / 3 / 4:切換武器 G:快速使用道具 F:近戰攻擊 Q:瞄準 / 使用技能…