排查)
做低功耗數(shù)據(jù)采集產(chǎn)品的時候選型定了 STM32U5G9NJH6Q外掛一片 64MB 的 octal NOR FlashMX25LM51245G當(dāng)日志存儲。方案在板子上跑得倒是挺順真正折磨人的是生產(chǎn)燒錄環(huán)節(jié)ST-Link 連上芯片STM32CubeProgrammer 里選好外部存儲器按下 Start直接給你甩一個錯誤彈窗。配套的官方教程只能覆蓋到官方開發(fā)板遇到定制板上的 OCTOSPI 問題網(wǎng)上能直接參考的實戰(zhàn)記錄很少最后只能靠一條條報錯信息去反推。折騰了快兩天總算把整套流程跑通。這篇文章就是那兩天的完整記錄寫給同樣在 STM32U5 上用 OCTOSPI 外擴 Flash、又在 STM32CubeProgrammer 里卡住的朋友新手可以參考這里面的使用入門路徑老手可以直接跳到問題排查章節(jié)。1. 先搞清楚 STM32CubeProgrammer 和 OCTOSPI 之間是怎么配合的1.1 External Loader 機制到底是怎么回事STM32CubeProgrammer 本身并不認(rèn)識任何外部 Flash 芯片。它燒寫外部存儲器的原理是通過 SWD 調(diào)試接口往 MCU 的 RAM 里加載一個叫 External Loader外部加載器的固件然后讓這個固件在目標(biāo)芯片上跑起來再由 CubeProgrammer 通過調(diào)試接口給固件下發(fā)讀、寫、擦除命令。這個固件的格式是 .stldrSTM32CubeProgrammer 安裝目錄下自帶一批對應(yīng)的是各家官方開發(fā)板的配置。這個機制設(shè)計得很巧妙把“復(fù)雜的 Flash 操作”下沉到芯片內(nèi)部執(zhí)行上位機只負(fù)責(zé)發(fā)指令和收數(shù)據(jù)。但它也帶來了一個非?,F(xiàn)實的問題你在 CubeProgrammer 里選擇外部存儲器時選的是某一個 .stldr 文件這個文件里的所有配置——GPIO 分配、OCTOSPI 時鐘、Flash 型號對應(yīng)的命令序列——都是按官方板子寫死的。你的板子只要有一處和官方板不同loader 初始化 OCTOSPI 的時候就可能直接失敗或者初始化成功了但讀寫的數(shù)據(jù)是錯的。我在 STM32U5G9NJH6Q 上遇到的那些報錯根源基本都是這個。注意官方 .stldr 文件位于 STM32CubeProgrammer 安裝目錄的 bin/ExternalLoader 文件夾下常見默認(rèn)路徑是 C:\Program Files\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader。凡是名稱里帶 STM32U5G9J-DK1 字樣的都是針對官方開發(fā)板的定制板只能拿來當(dāng)參考不能直接復(fù)用。1.2 STM32U5 的 OCTOSPI 有什么特殊性STM32U5G9NJH6Q 這顆料主核是 Cortex-M33內(nèi)部有 4MB Flash、2.5MB SRAM最大的亮點是帶兩個支持 Octal 的 OCTOSPI 外設(shè)可以外掛大容量 NOR Flash 做代碼 XIP 或者數(shù)據(jù)日志存儲。和老的 QUADSPI 相比OCTOSPI 支持 1/2/4/8 線寬支持 SDR/DDR 雙沿采樣還帶 DQS 引腳反饋時序上復(fù)雜了不少。有兩個點特別容易讓開發(fā)者翻車。第一U5 系列默認(rèn)開了 TrustZone。OCTOSPI 的寄存器和它用到的 RAM 區(qū)域都分 secure / non-secure 屬性而 external loader 默認(rèn)運行在 non-secure 域。如果你的 option bytes 里 TZEN 是打開的loader 要加載的 RAM 區(qū)域恰好被劃成了 secure那 CubeProgrammer 把 loader 寫進內(nèi)存之后它一執(zhí)行就崩表現(xiàn)出來就是各種莫名其妙的加載失敗。如果產(chǎn)品不需要 TrustZone最省事的做法是直接在 CubeProgrammer 的 Option Bytes 頁面里把 TZEN 關(guān)掉代價是觸發(fā)一次整片擦除數(shù)據(jù)記得提前備份。第二OCTOSPI 的時鐘源和分頻配置比較講究。U5 的 OCTOSPI 內(nèi)核時鐘可以從 PLL1Q、HCLK 等多個來源取具體走哪條路徑看 RCC 配置。如果 PLL 沒配好OCTOSPI 時鐘可能超出 Flash 支持的范圍。很多 8 線 Flash 標(biāo)稱支持 200MHz但在 1.8V VCC 下實際時序余量沒你想的那么大所以先把頻率降下來跑通再考慮拉高頻。還有一個容易被忽略的點U5G9NJH6Q 是 TQFP216 封裝IO 非常多但每個引腳的 AF復(fù)用功能映射和官方開發(fā)板并不完全一致。官方 loader 用的那幾個 GPIO在你的板子上可能是別的功能、甚至沒引出來。所以拿到一塊定制板的第一步永遠(yuǎn)是核對原理圖里 OCTOSPI 總線的實際走線再去數(shù)據(jù)手冊的 AF 映射表里查對應(yīng)關(guān)系不要想當(dāng)然。2. 我在 STM32U5G9NJH6Q 上踩過的三類典型問題2.1 連接階段就失敗No STM32 target found第一次拿到新板子接上 ST-Link打開 STM32CubeProgrammer點連接彈出來 No STM32 target found。這種問題我見過太多次按下面的順序排查基本能覆蓋 90% 的情況。首先看 NRST 有沒有接到 ST-Link 的 NRST 腳。CubeProgrammer 左側(cè)設(shè)置里有 Connect under reset 選項勾上之后調(diào)試器會在復(fù)位狀態(tài)下建立連接。如果用戶代碼里已經(jīng)把 PA13/PA14SWDIO/SWCLK復(fù)用成普通 GPIO 了用這種方式才能連上。前提是 NRST 必須連到調(diào)試器否則無法控制復(fù)位時序。其次查 option bytes。如果之前燒過 RDP讀保護等級RDP 不是 Level 0調(diào)試口會被鎖掉。RDP Level 1 還能靠 connect under reset 連上但無法讀寫Level 2 就直接永久鎖死只能換芯片。排查方法連接失敗時切到 Option Bytes 頁看能不能讀出來。如果讀出來發(fā)現(xiàn) RDP 是 Level 1 或 Level 2先把讀保護降級再繼續(xù)操作。還有一個很少被注意的點U5 有多路電源域。VDD 要供電、VDDA 模擬電源也要還有 VDDUSB、VDDIO2 這些獨立電源域。如果某一腳沒上電芯片可能進入異常狀態(tài)。同時 ST-Link 的 VTREF 參考電壓要和板子的 VDD 一致否則電平不匹配也會連接失敗。我當(dāng)時實際的處理是把 NRST 連線補上勾選 connect under reset問題立刻解決。后來才發(fā)現(xiàn)這塊板子之前跑測試程序時把 SWD 引腳占用了新程序里沒來得及改回 AF 功能導(dǎo)致第二次連接失敗。這個坑非常典型尤其是量產(chǎn)返修板十有八九是這個問題。2.2 能連上芯片但選擇外部存儲器時報錯External loader not found / Init error連接芯片這一步過了接下來在外部存儲器下拉框里選擇工作區(qū)會遇到兩類報錯。第一類是 External loader not found。說明 bin/ExternalLoader 目錄下沒有匹配的 .stldr 文件。要確認(rèn)你選的存儲區(qū)比如 OCTOSPI1 起始地址 0x90000000對應(yīng)的 loader 是否存在。如果板子是定制的八成沒有現(xiàn)成 loader只能自己寫后面第 3 節(jié)會詳述。第二類是 Init error 或 Read error。說明 loader 文件加載成功了但 loader 里的 Init() 函數(shù)返回失敗。Init() 做的工作通常包括初始化 OCTOSPI GPIO、配置 OCTOSPI 控制器、向 Flash 發(fā)讀 ID 命令、比對 ID 是否正確。任何一步失敗都會導(dǎo)致加載器初始化失敗。這里給一個特別管用的排查手段不要靠瞎猜先用一個最小的 STM32 測試工程直接用 OCTOSPI 的 SDR 模式、標(biāo)準(zhǔn) SPI 命令比如 0x9F 讀 JEDEC ID把 Flash 的 ID 讀出來。如果讀得出來說明硬件鏈路供電、IO、時鐘、Flash 本身都是通的問題就在 loader 的配置。如果讀不出來先查硬件Flash 的 VCC 供電、WP 引腳是不是被拉低、CS 上拉是否正常。這個“先讀 ID”的習(xí)慣能省掉大量無效時間。我當(dāng)時調(diào)試的時候loader 里怎么配都報 Init error后來一查發(fā)現(xiàn)是 Flash 的 VCC 標(biāo)稱 1.8V而板子上的 LDO 壞了實際電壓只有 1.2VFlash 根本沒法工作。這屬于硬件問題軟件配置再對也白搭。2.3 擦除、編程或校驗階段報錯Memory programming error / Data mismatch能初始化、能讀數(shù)據(jù)但一擦除一寫入就報錯或者校驗不通過。這類問題是 OCTOSPI 場景里最磨人的我把它拆成三個最常見的根因。第一個是 DDR 模式配置問題。8 線 NOR Flash 普遍支持 DDRDTR雙沿傳輸時序參數(shù)非常敏感。loader 里開了 DDR但 DQS 反饋信號沒接或者 DQS 時序配置不對寫入的數(shù)據(jù)就會錯位。校驗的時候報的地址通常在 0x90000000 附近和數(shù)據(jù)偏移量相關(guān)看起來很隨機。最粗暴有效的方法先把 loader 改成 SDR 模式跑通再回頭調(diào) DDR。不要一上來就追求高性能先把鏈路打通。第二個是寫保護沒解鎖。很多 NOR Flash 上電后默認(rèn)開啟寫保護需要發(fā) Write Enable0x06才能擦寫。如果 loader 的 Write 和 SectorErase 流程里沒有先發(fā)寫使能命令擦除可能靜默失敗或者編程時寫進去的依然是 0xFF。這個問題很隱蔽因為報錯不一定出現(xiàn)但數(shù)據(jù)校驗會失敗。第三個是時鐘頻率過高。前面說過1.8V 供電下 Flash 的實際頻率上限會降低。如果 loader 里配置的 OCTOSPI 時鐘是 100MHz但 Flash 在這個電壓下只能穩(wěn)定跑 66MHz就會出現(xiàn)偶發(fā)性錯誤。錯誤可能不是每次都出現(xiàn)而是在某個地址段反復(fù)失敗。生產(chǎn)線上這種“一會兒好一會兒壞”的問題最可怕直接把頻率降到標(biāo)稱值的 50% 再試。心得OCTOSPI 相關(guān)問題調(diào)試優(yōu)先級永遠(yuǎn)是先 READ 后 WRITE先 SDR 后 DDR先低頻后高頻。按這個順序能把變量的維度從 5 個降到 1 個排查效率提升非常明顯。3. 從零適配一塊板載 OCTOSPI Flash 的完整記錄3.1 用 STM32CubeMX 生成 External Loader 基礎(chǔ)工程如果你的板子沒有現(xiàn)成 loader就得自己寫一個。ST 的 STM32CubeU5 固件包里通常自帶 External Loader 的參考工程位置一般在 Projects/STM32U5G9J-DK1/Applications/ExternalLoader 下不同版本固件包路徑略有差異。先把它拷貝出來作為藍(lán)本。這個工程的核心是幾個函數(shù)簽名由 STM32CubeProgrammer 的加載器協(xié)議固定int Init(void); // 初始化 OCTOSPI 和 Flash 芯片 uint32_t Read(uint32_t Address, uint32_t Size, uint8_t *Buffer); // 讀數(shù)據(jù) uint32_t Write(uint32_t Address, uint32_t Size, uint8_t *Buffer); // 頁編程 uint32_t SectorErase(uint32_t StartAddress, uint32_t Size); // 扇區(qū)擦除 uint32_t MassErase(void); // 全片擦除還有一個描述 Flash 特性的結(jié)構(gòu)體CubeProgrammer 會用它來規(guī)劃整個地址空間和操作粒度const LoaderS Loader { MY_BOARD_OCTOSPI_MX25LM51245G, // loader 名稱會顯示在 CubeProgrammer 下拉框里 0x90000000UL, // 外部存儲器起始地址 0x04000000UL, // 存儲器容量64MB 0x00000100UL, // 編程頁大小256 字節(jié) 0x00001000UL, // 擦除塊大小4KB ... Init, Read, Write, SectorErase, MassErase, };生成工程后第一件事是核對芯片型號和 RAM 起始地址。STM32U5 的 SRAM 在 TZEN 打開時loader 需要放在 non-secure 可訪問的區(qū)域典型地址是 0x30000000 起的 non-secure alias。如果 TZEN 關(guān)閉直接用 0x20000000 這個常規(guī)區(qū)域就行。另外CubeProgrammer 對 loader 的 API 版本有檢查如果提示 Old API version多半是你 loader 工程里的 STM32_ExternalLoader.h 頭文件版本和當(dāng)前 CubeProgrammer 不匹配去固件包里找對應(yīng)版本替換即可。3.2 修改 GPIO、時鐘和 Flash 命令配置工程生成好之后重點改三塊配置。第一塊是 GPIO。打開 STM32U5G9NJH6Q 的數(shù)據(jù)手冊找到 OCTOSPI1 的 Alternate Function 映射表對照你的原理圖把 IO0~IO7、CLK、CS、DQS 對應(yīng)的 GPIO 引腳和 AF 號全部填到 CubeMX 里。每個 IO 都要配置成高速 push-pull上下拉狀態(tài)按 Flash 手冊要求設(shè)置一般是默認(rèn)帶上拉更穩(wěn)。這里特別提醒一句網(wǎng)上很多教程直接抄官方板子的引腳放到自己的板子上未必成立務(wù)必以自己原理圖和 AF 表為準(zhǔn)。第二塊是時鐘。OCTOSPI 內(nèi)核時鐘建議先從 PLL1Q 取頻率先設(shè)保守一點比如 50MHz。等整條鏈路跑通了再根據(jù) Flash