錯(cuò)undefined reference?一文教你排查MX_USART1_UART_Init缺失問題)
最近在折騰 STM32N6這顆芯片和之前玩過的 M 系列有個(gè)很不一樣的地方內(nèi)部直接集成了 Neural-ART NPU跑 AI 模型不再依賴 CPU 純算硬扛。我照著官方 Neural-ART 教程拉了一個(gè)示例工程目標(biāo)是在一個(gè)輕量分類模型上把推理結(jié)果通過串口打印出來。結(jié)果倒好編譯階段一路綠燈鏈接階段突然報(bào)錯(cuò)undefined reference to MX_USART1_UART_Init這類錯(cuò)誤對(duì)老手來說不算陌生基本就是“函數(shù)聲明能找到但實(shí)現(xiàn)沒被鏈接進(jìn)來”。奇怪的是我在工程里明明看到了 usart.c函數(shù)也寫得清清楚楚為什么還是 undefined reference這個(gè)坑我前前后后折騰了很久最后定位到問題出在構(gòu)建系統(tǒng)的源文件列表上——外設(shè)初始化文件壓根沒被收進(jìn)編譯目標(biāo)。這篇文章把完整排查過程、修復(fù)操作以及一套通用的 undefined reference 排查方法都整理出來。不管你是用 STM32CubeIDE還是跟我一樣用 CMake 命令行構(gòu)建都應(yīng)該用得上。1. 先搞懂錯(cuò)誤本質(zhì)鏈接器到底在抱怨什么1.1 undefined reference 不等于代碼寫錯(cuò)了很多人看到 “undefined reference” 會(huì)下意識(shí)覺得是“函數(shù)沒定義”。對(duì)但也不全對(duì)。把編譯和鏈接兩個(gè)階段拆開看就很清楚了編譯階段編譯器拿.c文件做語(yǔ)法分析和代碼生成它只需要在頭文件里看到函數(shù)的聲明知道“有這么個(gè)函數(shù)、參數(shù)長(zhǎng)這樣”就夠了所以main.c調(diào)用MX_USART1_UART_Init()時(shí)只要包含了usart.h編譯就能過。真正的問題出在最后的鏈接階段鏈接器要把所有.o目標(biāo)文件、靜態(tài)庫(kù)和啟動(dòng)文件拼成最終的可執(zhí)行程序這時(shí)它必須找到每一個(gè)被調(diào)用函數(shù)的具體實(shí)現(xiàn)也就是符號(hào)對(duì)應(yīng)的機(jī)器碼。找遍所有輸入文件都沒找到就報(bào)undefined reference。打個(gè)比方編譯階段就像你在一份 API 手冊(cè)上看到了某函數(shù)的介紹可以放心在代碼里寫調(diào)用鏈接階段則像你真正打電話得確保對(duì)方手機(jī)開機(jī)、號(hào)碼也有對(duì)應(yīng)的終端。如果手冊(cè)有記錄但電話打不通那問題就不在“手冊(cè)寫錯(cuò)了”而在“對(duì)方根本沒接入網(wǎng)絡(luò)”。對(duì)應(yīng)到工程里常見的原因是源文件沒參與編譯、函數(shù)實(shí)現(xiàn)被條件編譯關(guān)掉了、文件被構(gòu)建系統(tǒng)排除或者鏈接時(shí)庫(kù)的順序不對(duì)。所以看到這個(gè)報(bào)錯(cuò)不要急著懷疑是不是算法模型出了問題也不要覺得是工具鏈壞了。這是一個(gè)純粹的構(gòu)建配置問題排查路徑非常固定下面我會(huì)按順序拆開講。1.2 為什么偏偏是 MX_USART1_UART_InitMX_前綴是 STM32CubeMX 生成代碼的標(biāo)志。USART1表示目標(biāo)外設(shè)是 USART1UART_Init是初始化函數(shù)合起來就是“初始化 USART1 的 CubeMX 生成函數(shù)”。在 Neural-ART 教程工程里串口通常是用來打印推理結(jié)果、模型運(yùn)行耗時(shí)和日志信息的。模型跑完 NPU 之后分類結(jié)果、置信度、幀率這些數(shù)據(jù)都要靠串口送到 PC 端調(diào)試助手所以 UART 幾乎是所有 AI 示例工程的標(biāo)配外設(shè)。這里要特別注意MX_USART1_UART_Init不是 HAL 庫(kù)自帶的函數(shù)它是 CubeMX 根據(jù)你在.ioc文件里的外設(shè)配置動(dòng)態(tài)生成的用戶層代碼。它的定義在Core/Src/usart.c里聲明在Core/Inc/usart.h里。如果鏈接器說找不到這個(gè)符號(hào)問題大概率不是 ST 官方 HAL 庫(kù)的問題而是工程自己生成的外設(shè)初始化代碼沒有被完整納入構(gòu)建流程。搞清這一點(diǎn)后面的排查思路就清晰了不要先去查 HAL 庫(kù)的源碼先把“這個(gè)函數(shù)定義在哪個(gè)文件、這個(gè)文件是否被編譯”這兩件事弄清楚。1.3 STM32N6 工程中這個(gè)符號(hào)的“出身”STM32N6 的工程結(jié)構(gòu)和老 M 系列有一點(diǎn)明顯不同。老工程通常是 CubeMX 生成一個(gè) IDE 工程源文件集中在Core/Src然后用 IDE 直接編譯。N6 的 AI 示例工程很多時(shí)候是從 ST 的 Edge AI package 拉下來的構(gòu)建方式可能是 CMake、Makefile甚至 ST 自己的命令行工具鏈。這類工程里Core/Src/usart.c不一定被自動(dòng)收集尤其當(dāng)構(gòu)建腳本用的是顯式源文件列表時(shí)多一個(gè)少一個(gè)文件非常常見。另外N6 的教程工程為了封裝模型處理邏輯往往會(huì)把代碼拆成App、Core、Middlewares多個(gè)目錄。有的版本甚至?xí)淹庠O(shè)初始化文件放到 App 層比如App/app_usart.c。這種情況下符號(hào)名可能變成APP_USART1_Init或者文件位置變了但main.c里的調(diào)用還是舊的MX_USART1_UART_Init名字對(duì)不上也會(huì)出現(xiàn) undefined reference。先理解符號(hào)的“出身”再動(dòng)手排查比一股腦重裝工具鏈高效得多。2. 五步排查法定位符號(hào)為什么沒被鏈接進(jìn)來2.1 第一步先確認(rèn)定義真的存在排查的第一步永遠(yuǎn)是搜代碼。在工程根目錄執(zhí)行g(shù)rep -rn MX_USART1_UART_Init --include*.c --include*.h .正常情況下會(huì)看到兩種結(jié)果一個(gè)在.h文件里的聲明一個(gè)在.c文件里的函數(shù)定義。如果連定義都搜不到那就說明 CubeMX 生成代碼時(shí)沒把 USART 外設(shè)的初始化代碼生成出來或者生成到了別的目錄。打開.ioc文件在 Pinout Configuration 里確認(rèn) USART1 是否已經(jīng)配置為啟用狀態(tài)然后在 CubeMX 里重新生成一次代碼。如果定義存在但鏈接還是報(bào)錯(cuò)先別急著看 IDE用nm看一眼目標(biāo)文件到底有沒有把符號(hào)編譯出來。假設(shè)你的構(gòu)建輸出目錄是buildarm-none-eabi-nm build/Core/Src/usart.o | grep MX_USART1如果輸出類似00000000 T MX_USART1_UART_Init說明目標(biāo)文件里確實(shí)有這個(gè)定義如果沒有任何輸出說明你搜到的.c文件可能沒有參與編譯。我在實(shí)際排查中就遇到過一種情況源碼目錄里有一個(gè)usart.c但它根本沒被 CMake 的源文件列表引用所以編出來的usart.o壓根不存在自然搜不到符號(hào)。2.2 第二步確認(rèn)定義沒有被條件編譯掐掉還有一種情況是函數(shù)定義存在但被預(yù)處理指令包住了實(shí)際編譯時(shí)被跳過。CubeMX 生成的usart.c里雖然一般不會(huì)給MX_USART1_UART_Init套#ifdef但 HAL 庫(kù)整體的外設(shè)模塊可以裁剪。stm32n6xx_hal_conf.h中有一個(gè)HAL_UART_MODULE_ENABLED宏如果它被注釋掉或者沒定義HAL 層就不會(huì)編譯 UART 相關(guān)代碼某些版本的生成邏輯甚至?xí)製sart.c的內(nèi)容也弱化。檢查方法很簡(jiǎn)單打開stm32n6xx_hal_conf.h搜HAL_UART_MODULE_ENABLED。正常情況下應(yīng)該是#define HAL_UART_MODULE_ENABLED如果是/* #define HAL_UART_MODULE_ENABLED */這種注釋狀態(tài)說明在 CubeMX 的 Advanced Settings 里把 UART 模塊裁剪掉了。重新打開.ioc把 UART 模塊勾回來重新生成代碼。這里還要留意不要只改宏因?yàn)?CubeMX 重新生成時(shí)可能會(huì)覆蓋掉其他手動(dòng)修改最好是走.ioc配置流程而不是手改配置頭文件。2.3 第三步確認(rèn)源文件真的參與編譯了這一步是大多數(shù)人卡住的地方也是我這次踩坑的核心原因。如果你用 STM32CubeIDE右鍵項(xiàng)目在 Project Explorer 里找到Core/Src/usart.c看文件名上有沒有“排除出構(gòu)建”的灰色減號(hào)標(biāo)記。如果有右鍵文件 → Resource Configurations → Exclude from Build取消勾選即可。但更隱蔽的情況是文件看起來在工程里IDE 也能看到但構(gòu)建系統(tǒng)壓根沒把它編進(jìn)去。CubeIDE 底層如果是 CMake 工程源文件列表由CMakeLists.txt控制如果是普通 Managed Build 工程Eclipse 會(huì)從項(xiàng)目路徑掃描源文件。后者一般不會(huì)漏前者特別容易漏。命令行構(gòu)建的話直接檢查構(gòu)建腳本。教程工程最常見的是 CMake 的顯式源文件列表set(SOURCES Core/Src/main.c Core/Src/stm32n6xx_it.c App/ai_runtime.c )如果這個(gè)列表里沒有Core/Src/usart.c那鏈接器永遠(yuǎn)找不到這個(gè)函數(shù)。修復(fù)就是加一行set(SOURCES Core/Src/main.c Core/Src/usart.c Core/Src/stm32n6xx_it.c App/ai_runtime.c )如果工程用的是file(GLOB_RECURSE ...)方式收集源文件那新增文件后沒有重新運(yùn)行 cmake 配置也會(huì)導(dǎo)致文件沒進(jìn)構(gòu)建。解決辦法是刪掉build目錄重新執(zhí)行cmake -S . -B build讓 GLOB 重新掃描。2.4 第四步檢查鏈接腳本和庫(kù)依賴順序如果MX_USART1_UART_Init的實(shí)現(xiàn)被打包到了靜態(tài)庫(kù)里比如官方的某個(gè)板級(jí)支持庫(kù).a那鏈接順序就變得非常關(guān)鍵。GNU ld 處理靜態(tài)庫(kù)時(shí)原則是“遇到未解析符號(hào)才從庫(kù)里抽取目標(biāo)文件”。如果鏈接命令里引用這個(gè)符號(hào)的目標(biāo)文件排在庫(kù)文件后面符號(hào)可能就無法被解析。典型報(bào)錯(cuò)場(chǎng)景是arm-none-eabi-gcc ... main.o libbsp.a -o app.elf有些情況下這樣能過有些情況下就必須把庫(kù)放到引用它的人后面。最保險(xiǎn)的辦法是給鏈接器加組選項(xiàng)arm-none-eabi-gcc ... -Wl,--start-group main.o libbsp.a -Wl,--end-group -o app.elf--start-group會(huì)讓鏈接器在庫(kù)組內(nèi)反復(fù)掃描直到符號(hào)解析完成或沒有新符號(hào)可以被解析。不過在我這次遇到的問題里usart.c并不是庫(kù)而是被遺漏的源文件所以更可能是源文件列表的問題庫(kù)順序是后面才需要考慮的排查方向。如果你在鏈接日志里看到了libneural-art.a之類的庫(kù)并且錯(cuò)誤恰好出現(xiàn)在庫(kù)后面的符號(hào)引用那可以先嘗試調(diào)整順序再用--start-group包住庫(kù)組。不要一上來就改鏈接腳本很多時(shí)候不是鏈接腳本的問題。2.5 第五步警惕 C/C 符號(hào)修飾差異還有一個(gè)不太常見但一碰就容易懵的原因C 和 C 的符號(hào)修飾規(guī)則不同。C 編譯器會(huì)對(duì)函數(shù)名做 name mangling比如void MX_USART1_UART_Init(void)在 GCC ARM C 編譯下會(huì)變成_Z21MX_USART1_UART_Initv。如果調(diào)用方的文件被當(dāng)成 C 編譯而被調(diào)用的定義文件被當(dāng)成 C 編譯兩邊符號(hào)對(duì)不上就會(huì)報(bào) undefined reference。用nm一眼就能看出來arm-none-eabi-nm build/App/main.o | grep MX_USART1 arm-none-eabi-nm build/Core/Src/usart.o | grep MX_USART1如果一邊是U MX_USART1_UART_Init一邊是T _Z21MX_USART1_UART_Initv那基本就是編譯語(yǔ)言不統(tǒng)一。解決辦法是給頭文件加extern C保護(hù)#ifdef __cplusplus extern C { #endif void MX_USART1_UART_Init(void); #ifdef __cplusplus } #endif如果是 CubeMX 生成的頭文件新版本一般已經(jīng)帶了保護(hù)但有些舊版本沒有。另外如果教程工程把工程拆成了安全區(qū)/非安全區(qū)兩個(gè)子工程也就是 TrustZone 場(chǎng)景還要確認(rèn)MX_USART1_UART_Init的調(diào)用方和定義方在同一個(gè)子工程里??绻こ淘L問外設(shè)初始化函數(shù)需要額外做符號(hào)導(dǎo)出或者放到共享庫(kù)中不能默認(rèn)兩邊都能看到。3. 針對(duì) Neural-ART 教程工程的實(shí)操修復(fù)過程3.1 復(fù)現(xiàn)我的失敗現(xiàn)場(chǎng)我這次用的是官方某個(gè) STM32N6 AI package 里帶的人臉檢測(cè)示例構(gòu)建流程是 CMake。目錄結(jié)構(gòu)大概是project/ ├── CMakeLists.txt ├── App/ │ ├── main.c │ └── ai_runtime.c ├── Core/ │ ├── Inc/ │ └── Src/ │ ├── main.c │ ├── usart.c │ └── stm32n6xx_it.c └── Middlewares/ └── neural_art/執(zhí)行cmake -S . -B build cmake --build build結(jié)果在鏈接階段報(bào)錯(cuò)[ 90%] Linking C executable test_fd /usr/bin/ld: CMakeFiles/test_fd.dir/App/main.c.o: in function main: main.c:(.text0xa4): undefined reference to MX_USART1_UART_Init collect2: error: ld returned 1 exit status我第一時(shí)間在源碼里搜索usart.c確實(shí)存在MX_USART1_UART_Init定義也清清楚楚。我用nm檢查卻發(fā)現(xiàn)build目錄下壓根沒有生成usart.o。這時(shí)才意識(shí)到問題出在CMakeLists.txt的源文件列表。這個(gè)教程工程為了便于用戶閱讀把主程序放在App/main.c但外設(shè)初始化的usart.c還留在Core/Src而 CMake 列表里只寫了Core/Src/main.c和Core/Src/stm32n6xx_it.c忘了加usart.c。3.2 CubeIDE 工程里的操作路徑如果你不是在命令行用 CMake而是在 STM32CubeIDE 里直接打開工程處理路徑稍有不同。先看 Project Explorer 里Core/Src/usart.c是否存在。文件存在時(shí)確認(rèn)它沒有被標(biāo)記為“排除出構(gòu)建”。Eclipse 對(duì)這種文件會(huì)在文件圖標(biāo)上疊加一個(gè)減號(hào)鼠標(biāo)放上去會(huì)提示“Exclude from build”。如果有排除標(biāo)記右鍵文件 → Resource Configurations → Exclude from Build把勾去掉。如果文件根本不在工程樹里右鍵工程 → Refresh讓 IDE 重新掃描磁盤。如果掃描后還是沒有大概率是工程文件.project里的 source entry 配置不對(duì)或者工程目錄和源碼目錄不一致。這時(shí)候別死磕 IDE打開.cproject和.project看 source entry 路徑把Core/Src目錄加回來。CubeIDE 還有一種情況如果你使用 CubeMX 重新生成代碼時(shí)選錯(cuò)了“Application Structure”比如從“Advanced”切回“Basic”生成的外設(shè)文件位置會(huì)變化。舊文件被留在磁盤上但新的構(gòu)建列表已經(jīng)不再包含它。碰到這種“文件在但參與不了構(gòu)建”的情況最好的辦法是備份自己的修改然后用 CubeMX 重新生成一個(gè)干凈的工程再手動(dòng)合并修改。3.3 Makefile/CMake 工程里的處理路徑Makefile 工程相對(duì)好處理。打開 Makefile找到C_SOURCES變量的定義看里面有沒有Core/Src/usart.c或者wildcard方式。STM32CubeMX 默認(rèn)生成的 Makefile 會(huì)通過通配符自動(dòng)收集Core/Src/*.c一般不會(huì)漏。但教程工程的 Makefile 可能是手寫的源文件列表寫得很隨意這種就要手動(dòng)補(bǔ)。C_SOURCES \ Core/Src/main.c \ Core/Src/usart.c \ Core/Src/stm32n6xx_it.c改完之后執(zhí)行make clean再重新make。為什么要 clean因?yàn)榕f的main.o可能還殘留著 undefined reference 的信息如果 Makefile 沒正確追蹤頭文件依賴你可能改了構(gòu)建列表但沒有觸發(fā)重新鏈接這時(shí)候全量重建是最省心的。CMake 工程更簡(jiǎn)單把usart.c添加進(jìn) target 的源文件列表即可。但要注意如果你用的是target_sources要確保在正確的 target 下添加。比如add_executable(test_fd ${SOURCES}) target_include_directories(test_fd PRIVATE Core/Inc) target_sources(test_fd PRIVATE Core/Src/usart.c)添加后建議rm -rf build再重新配置。因?yàn)?CMake 的配置緩存有時(shí)會(huì)保留舊的源文件列表尤其當(dāng)你不是從CMakeLists.txt的根目標(biāo)添加時(shí)增量配置不一定生效。刪除 build 目錄是最不費(fèi)腦子的做法。3.4 重新生成后的驗(yàn)證清單修好構(gòu)建列表后重新編譯。鏈接通過只是第一步我建議按下面的清單做一遍驗(yàn)證避免表面上修好了燒到板子上又發(fā)現(xiàn)串口不工作確認(rèn)編譯日志里真的出現(xiàn)了usart.c的編譯命令。開著make VERBOSE1或者 CMake 的CMAKE_VERBOSE_MAKEFILEON看到usart.c被編譯到目標(biāo)文件才說明文件真正參與了構(gòu)建。用nm確認(rèn)符號(hào)類型。arm-none-eabi-nm build/.../usart.o | grep MX_USART1看到T MX_USART1_UART_Init表示定義存在且是全局可鏈接符號(hào)。燒錄后打開串口調(diào)試助手看有沒有模型初始化日志。如果沒有任何輸出先用調(diào)試器檢查huart1.gState是否是HAL_UART_STATE_READY檢查 USART1 的 GPIO 復(fù)用配置是否正確。檢查時(shí)鐘樹。USART1 在 STM32N6 上的時(shí)鐘源可能來自某個(gè) PLL 或 HSI如果時(shí)鐘樹配置不對(duì)波特率會(huì)偏移串口打印亂碼也是常有的事。鏈接成功不代表外設(shè)配置成功這是兩件事別混在一起。4. 這類錯(cuò)誤的常見變體和速查表4.1 不只是 MX_ 函數(shù)其他高頻 undefined reference 變體嵌入式里undefined reference太常見了遠(yuǎn)不止MX_函數(shù)這一種。比較高頻的幾個(gè)undefined reference to main整個(gè)工程的入口函數(shù)缺失。常見于啟動(dòng)文件選錯(cuò)、刪了 main 函數(shù)、或者鏈接時(shí)沒把包含 main 的目標(biāo)文件加進(jìn)來。undefined reference to HAL_UART_Transmit這是 HAL 庫(kù)函數(shù)缺失。原因通常是 HAL 庫(kù)源文件沒加入構(gòu)建或者stm32n6xx_hal_conf.h里裁剪了 UART 模塊。undefined reference to _exit、undefined reference to __aeabi_*這是編譯器 runtime 庫(kù)缺失。多半是工具鏈安裝不完整或者鏈接選項(xiàng)里少了--specsnano.specs之類的參數(shù)。undefined reference to printf常見于使用半主機(jī)模式時(shí)標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)不完整。嵌入式里一般用 retarget 重定向或者勾選 MicroLib。這些變體的排查思路和MX_USART1_UART_Init是相通的先定位符號(hào)屬于哪個(gè)源文件或庫(kù)再確認(rèn)這個(gè)文件或庫(kù)是否被鏈接器作為輸入。不要去背每個(gè)符號(hào)的含義掌握方法比記結(jié)論重要。4.2 問題排查速查表線索可能原因推薦操作grep 整個(gè)工程都搜不到函數(shù)定義CubeMX 沒生成該外設(shè)代碼或文件在別的目錄打開.ioc確認(rèn) USART 外設(shè)已啟用重新生成代碼usart.c存在但文件上有灰色減號(hào)IDE 將文件排除出構(gòu)建右鍵文件取消 Exclude from Buildusart.c存在但構(gòu)建日志里沒有它CMakeLists 或 Makefile 源文件列表遺漏把文件路徑加入構(gòu)建列表重新配置只有聲明沒有定義定義文件被重命名或移動(dòng)對(duì)比官方例程統(tǒng)一函數(shù)名和路徑nm發(fā)現(xiàn)符號(hào)帶_Z前綴C/C 編譯語(yǔ)言不一致給頭文件加extern C鏈接命令里庫(kù)順序不對(duì)靜態(tài)庫(kù)解析符號(hào)時(shí)序問題使用-Wl,--start-group/--end-groupHAL_UART_MODULE_ENABLED被注釋外設(shè)宏被裁剪HAL 源碼未編譯在 CubeMX 中恢復(fù) UART 模塊并重新生成工程拆分為安全區(qū)/非安全區(qū)子工程函數(shù)定義和調(diào)用不在同一側(cè)將定義移到調(diào)用方同一子工程或通過接口導(dǎo)出這張表基本覆蓋了這類錯(cuò)誤的 90% 場(chǎng)景。剩下的 10% 屬于工具鏈本身的 bug 或工程緩存問題一般通過 clean 和重新配置就能解決。4.3 為什么教程工程最容易被這種坑到Neural-ART 這類 AI 教程工程和普通外設(shè)例程不同它同時(shí)涉及 CubeMX 生成代碼、NPU 庫(kù)、AI 模型文件、可能的 C 接口層甚至還有 RTOS。工程結(jié)構(gòu)比“點(diǎn)燈工程”復(fù)雜一個(gè)量級(jí)構(gòu)建配置很容易出問題。很多教程壓縮包發(fā)布時(shí)作者是為了在某個(gè)特定版本的工具鏈上演示他可能忘了更新 CMakeLists或者發(fā)出來的工程已經(jīng)經(jīng)過本地手工調(diào)整換一臺(tái)電腦、換一個(gè)工具鏈版本就露餡。還有一個(gè)現(xiàn)實(shí)因素是STM32N6 比較新各種 AI package 的版本迭代很快。有時(shí)候你下載的是舊教程配套的 CubeMX 版本卻是新的重新生成代碼后文件結(jié)構(gòu)變了但教程里的構(gòu)建腳本還是老一套兩邊對(duì)不上就報(bào)錯(cuò)。所以我的建議是先把官方壓縮包存一份不動(dòng)然后另建目錄做修改。一旦遇到奇怪的構(gòu)建問題就用git diff或者直接對(duì)比官方目錄重點(diǎn)看源文件列表差異往往幾秒就能發(fā)現(xiàn)問題。5. 寫到最后三個(gè)實(shí)用小習(xí)慣踩過這次坑之后我給自己定了三條規(guī)矩。第一從 GitHub 或者官方包拉教程工程時(shí)先跑一遍構(gòu)建確認(rèn)基礎(chǔ)環(huán)境沒問題再做任何代碼修改。如果這步就報(bào)鏈接錯(cuò)誤優(yōu)先看源文件列表別上來就怪編譯器。第二CubeMX 生成的外設(shè)代碼盡量不要手動(dòng)重命名比如把MX_USART1_UART_Init改成自己的InitUart之類的除非你能保證同步修改所有聲明、定義和頭文件保護(hù)規(guī)則。保持默認(rèn)鏈路以后重新生成代碼、升級(jí)版本都能省很多事。第三凡是鏈接錯(cuò)誤報(bào)的是MX_開頭的符號(hào)先懷疑構(gòu)建配置再去翻代碼。這類函數(shù)是 CubeMX 自動(dòng)生成的幾乎不會(huì)有邏輯問題大概率是文件沒有參與構(gòu)建。修完這個(gè)報(bào)錯(cuò)后面真正讓我頭疼的其實(shí)是 NPU 模型推理性能和串口打印格式的調(diào)優(yōu)。但反過來想如果當(dāng)時(shí)沒有把鏈接錯(cuò)誤這一套徹底研究透后面遇到模型庫(kù)鏈接失敗、符號(hào)沖突的時(shí)候我可能還會(huì)走很多彎路。希望這篇記錄能幫你早點(diǎn)從“編譯不過”的泥潭里跳出來把時(shí)間花