
最近在 Stm32H747I-Disco 上做一套雙核采集加 GUI 顯示的小項目計劃把板載 SDRAM 當成幀緩沖來用。板子剛到手我對著官方原理圖把 FMC_SDRAM 的參數一個個敲進 CubeMX然后運行 ST 自帶的 SDRAM 例程結果在內存檢查環節直接翻車寫進去 0x00000001讀出來是 0x00000100寫進去 0xDEADBEEF讀回來成了 0xBEEFDEAD。我當時以為自己代碼配置錯了調了很久最后順著 SDRAM 連接關系一條線一條線地查才發現原理圖里的描述和實際 PCB 網絡之間存在一處很不顯眼的矛盾。這篇文章不打算只講“哪里字體標錯了”更重要的是把這次定位原理圖不一致的完整思路、驗證方法和處理方案整理出來。無論你是剛接觸這塊板子還是正在被 SDRAM 數據錯亂折騰這篇應該都能幫你省下不少時間。1. 板載 SDRAM 的正常連接應該是什么樣先建立基準排查任何“不一致”之前必須先知道“一致”長什么樣。如果對 SDRAM 和 FMC 控制器的正常連接沒有一個清晰的基準看到什么都會覺得像問題最后反而會把對的當成錯的改掉。1.1 這塊板子上 SDRAM 的型號和容量STM32H747I-Disco 板載的 SDRAM 顆粒是 IS42S16400J 這一類的 4M x 16bit 芯片總容量 8MB內部有 4 個 Bank行地址 12 位列地址 8 位刷新周期是 8192 行 / 64ms。注意不同批次或者不同 Rev 版本的板子顆粒型號可能有差異但基本都圍繞 8MB、16bit 這個規格展開。我當時第一反應是去用戶手冊里找 SDRAM 容量再對芯片絲印這里有一點要提醒手冊里寫的“SDRAM 8MB”是一個籠統值原理圖里標注的容量有時是“64Mbit”也就是 8MByte。如果你看到原理圖上寫著 64Mb別以為是 64MBSDRAM 的容量標注經常以 bit 為單位這個單位陷阱在不少開發板的文檔里都出現過。H747 這顆芯片的 FMC 控制器支持 SDRAM內部有兩個 SDRAM BankBank1 地址從 0xC0000000 開始Bank2 從 0xD0000000 開始。這塊板上的 SDRAM 掛在 Bank1 上所以基地址就是 0xC0000000。這個地址在后面的自檢程序里會直接用到。1.2 FMC 控制器與 SDRAM 的地址映射關系H7 系列 FMC 對 SDRAM 的地址映射邏輯比較特殊它不像 SRAM 那樣把地址線一根對一根簡單接過去。SDRAM 本身是行列地址復用所以 FMC 控制器需要根據配置的行地址位數、列地址位數、Bank 數自動在訪問時產生對應的地址時序。在 16bit 數據寬度下CPU 側的一次 32bit 讀操作會拆成兩個 16bit 訪問SDRAM 側地址線 A0 對應 CPU 地址的 bit1A1 對應 bit2以此類推。這解釋了為什么寫測試程序時用uint32_t *指針遞增地址每次加 4SDRAM 的 A0 才會變一次。如果數據寬度配置成 8bit地址對應關系又會不一樣。所以在核對原理圖之前必須確認 FMC_CR 里配置的數據寬度是多少。H747-Disco 板載 SDRAM 是 16bit 連接這一點在 CubeMX 的 FMC 配置里能看到數據寬度不是 32bit。這一點如果配錯后面的地址和數據都會亂套。1.3 一組標準的連接關系長什么樣SDRAM 連接可以分為四組信號數據線SDRAM 的 DQ0-DQ15 分別接到 FMC 的 D0-D15。地址線SDRAM 的 A0-A11 接到 FMC 的 A0-A11BA0、BA1 接到 FMC 的 BA0、BA1。控制線RAS、CAS、WE 對應 FMC 的 NRAS、NCAS、NWE片選 CS 對應 FMC 的 SDNE0時鐘使能 CKE 對應 FMC 的 SDCKE0時鐘 SDCLK 從 FMC 輸出到 SDRAM。字節使能SDRAM 的 DQM0、DQM1 對應 FMC 的 NBL0、NBL1分別控制低字節和高字節。這四組信號里數據線和字節使能的對應關系最容易出錯。DQM 是按字節 lane 來控制的DQM0 管 DQ0-DQ7DQM1 管 DQ8-DQ15。如果數據線高低字節整體交換了DQM 也必須跟著交換否則讀寫的時候字節使能會對不上。這是一個非常隱蔽的坑ST 的原理圖如果存在描述矛盾往往就出現在這種需要同步交換的地方。2. 原理圖“描述不一致”到底長什么樣基準清楚了再看我發現的問題。這個問題的表象是內存測試跑不過但真正的根因卻在原理圖的描述層面。把它完整還原出來大家以后遇到類似現象能少走很多彎路。2.1 翻車現場內存測試的異常 pattern我做了一個極簡的內存自檢代碼不長也就是往固定地址寫值再讀回來比較volatile uint32_t *pSDRAM (volatile uint32_t *)0xC0000000; pSDRAM[0] 0xDEADBEEF; pSDRAM[1] 0x12345678; for (volatile int i 0; i 200; i); // 等幾個刷新周期 printf(p[0] 0x%08X\n, pSDRAM[0]); printf(p[1] 0x%08X\n, pSDRAM[1]);預期輸出當然是原樣寫回原樣讀回但實際打印出來的是p[0] 0xBEEFDEAD p[1] 0x78563412這不是普通的位翻轉而是整個半字交換。數據的高 16bit 和低 16bit 對調了0xDEAD 跑到后面0xBEEF 跑到前面。如果只是某一位接觸不良或者時序錯誤出來的 pattern 不會這么整齊。這種整齊的錯位九成以上是數據線高低字節 lane 接反了。2.2 順著網絡逐條追查拿到這個現象我開始打開 ST 官方的原理圖 PDF找到 SDRAM 那一頁。第一眼看上去U8 芯片的 DQ0-DQ15 都標著 FMC_D0-FMC_D15順序和名字看起來完全正常。但再往下看問題來了。原理圖里有一段文字說明寫的是“SDRAM data bus DQ[15:0] is mapped directly to FMC_D[15:0]”。這個描述本身沒問題問題是它和實際連線對不上。我逐條追了 DQ15、DQ14 幾個網絡發現終端實際連接的 FMC 數據線方向和原理圖文字說明并不一致。換句話說原理圖畫面上看起來是直連但底層網絡表里已經做了高低字節 lane 的交換。這就形成了兩種信息圖上看起來是正常的順序連接文字描述也說是直連但實際的網表連接是交換的確切說是 DQ0-DQ7 整體接在了 FMC_D8-DQ15 的位置上DQ8-DQ15 整體接到了 FMC_D0-D7 上。原理圖的一個視圖和另一個視圖之間出現了矛盾。2.3 矛盾的具體位置為了確認這不是我自己看錯我把原理圖的方框圖部分和連線圖部分分開比對。方框圖里 U8 的引腳文字標注是順序編號的但連線圖中的網絡標號卻是反的。這種“符號引腳標注”和“網絡標號”不一致的情況在 EDA 圖紙里其實不少見尤其是當圖紙經過多次修改、某些局部用復制粘貼功能整理過之后。我手上這個版本的問題集中在兩點U8 的 DQ0-DQ7 在原理圖符號上標注為 DQ0-DQ7但網絡標號實際對應的是 FMC_D8-FMC_D15。文字說明里寫的“directly mapped”與實際網絡不一致。如果你在自己的 H747-Disco 上沒發現這個問題也不用奇怪。ST 官方原理圖是有 Rev 版本的不同批次的板卡可能對應不同版本圖紙而且這個差異主要影響的是“你照著原理圖手寫配置”的場景。用官方 BSP 例程的人可能從頭到尾都不會踩到。但我身邊確實有朋友是照著原理圖自己重新建工程的這種不一致真的能讓人排查到懷疑人生。2.4 為什么會出現這種不一致從工程角度來說原因并不難猜。SDRAM 數據線在 PCB 布線時為了減少過孔、縮短走線長度經常會在保持字節 lane 完整的前提下做 DQ 線的交換。這是非常常規的 PCB 設計技巧只要硬件上用同等方式把對應關系換回來邏輯上完全沒問題。ST 在部分板卡上確實這么做過。問題出在原理圖歸檔時某些版本沒有把交換后的網絡名同步到圖形注釋里。于是原理圖讀起來像直連但板子實際是交換的。再加上 CubeMX 的官方板級文件已經按真實網絡配好了BSP 代碼也能正常工作所以這個問題只影響“參考原理圖手寫配置”的那批人。另外還有一種可能就是 FMC 控制器本身提供了數據交換功能這點我后面會用一整節來展開。如果你用的是 H7 系列硬件上直接 支持 數據線交換的配置原理圖上的描述不自洽可能正是因為設計者打算用 FMC 的 DATASWZ 功能來適配但圖紙注釋沒更新。這解釋了為什么官方 BSP 能用、你自己配置卻出錯。3. 三路交叉驗證CubeMX、BSP 代碼和板級自檢發現了矛盾之后不能光憑“我覺得這里反了”就下結論。我把驗證過程分成三路每一路都獨立得出一個結論三個結論對上了才敢動手改配置。這套交叉驗證的方法我建議你也養成習慣。3.1 CubeMX 配置里能看出什么如果 ST 已經提供了這塊板子的官方板級描述CubeMX 里新建 STM32H747I-DISCO 工程FMC 的 SDRAM 配置默認就是和實物匹配的。打開 CubeMX進入 FMC 配置頁面可以看到數據寬度16 bit行地址位數12列地址位數8CAS 延遲3刷新計數根據 FMC 時鐘計算出來的值這些參數是 ST 的板級工程師根據實際 SDRAM 顆粒填好的可信度很高。如果你的工程是從零開始建的沒有加載官方板級描述就要特別注意這幾項。尤其是行地址 12 位和列地址 8 位這兩個值錯了SDRAM 的訪問地址映射會完全錯亂。數據寬度配成 32bit 而實際是 16bit也會導致高 16 位數據錯位。CubeMX 這一路驗證的結論是官方板級配置里并沒有對數據線交換做特殊處理也就是說板級配置默認假設了數據線是直連的。如果實物不是直連那問題就更可疑了。3.2 BSP 代碼反推 ST 的真實連接第二路是從 BSP 代碼反推。ST 的 Cube 包里有現成的板級驅動路徑一般在Drivers/BSP/STM32H747I-Discovery/stm32h747i_discovery_sdram.c還有同目錄下的 LCD 驅動文件。我打開 SDRAM 的 BSP 文件重點看SDRAM_Init函數里 FMC 初始化結構體怎么填有沒有類似DataSwap的字段。H7 系列的 FMC 是支持數據交換功能的在寄存器和 HAL 里都有對應接口。再看 LCD 驅動因為 LCD 控制器也要通過同樣的 FMC 數據總線訪問 SDRAM如果數據線有交換LCD 驅動里往往會有對應的字節序處理。實際翻下來BSP 里沒有做任何數據交換的補償操作說明至少在官方驅動看來數據線就是直接對應的。到這里CubeMX 和 BSP 代碼兩個結論一致官方認為沒交換。但事實是實測出現了半字錯位于是矛盾進一步集中到了“原理圖描述”和“實際網絡”之間。3.3 板級自檢程序數據線定位和地址線定位第三路是實測這是最終的裁決者。我寫了一個更細致的內存自檢程序專門用來區分數據線是哪種交換方式。思路很簡單往某個地址寫一個只含單個 bit 的值然后看讀回來的這個 bit 跑到哪個位置去。#include stm32h7xx_hal.h #include stdio.h void SDRAM_DataLine_Test(void) { volatile uint32_t *SDRAM (volatile uint32_t *)0xC0000000; // 測試 Bit0 SDRAM[0] 0x00000001; uint32_t val1 SDRAM[0]; // 測試 Bit8 SDRAM[0] 0x00000100; uint32_t val8 SDRAM[0]; // 測試 Bit15 SDRAM[0] 0x00008000; uint32_t val15 SDRAM[0]; printf(write 0x00000001 - read 0x%08X\n, val1); printf(write 0x00000100 - read 0x%08X\n, val8); printf(write 0x00008000 - read 0x%08X\n, val15); // 地址線測試連續寫地址值看有沒有鏡像 for (uint32_t i 0; i 64; i)