使用 VSCode 開發調試 STM32 單片機嘗試
本文記錄基於 Windows + DAP-Link 開發 STM32F103C8T6 的實踐過程,其他操作系統或芯片應該也只是大同小異的問題。
注意:工作空間中千萬不要出現中文目錄和空格!
一、環境准備
硬件環境就是 STM32F103C8T6 核心板和 DAP 調試器,復雜的主要在軟件部分。
調試時需要讓gdb鏈接openocd,因此需要telnet工具。Windows下直接在Windows功能里打開telent client並重啟就行
1.1_軟件
-
VSCode
可以使用普通版或便攜版,我使用的是大佬制作的便攜版:https://portapps.io/app/vscode-portable/ -
STM32CubeMX
用來生成 Markfile 工程,已有工程模板的話不必須安裝。使用 CubeMX 時需要用到 Java , Java 64位下載地址:https://java.com/en/download/manual.jsp -
GNU Arm Embedded Toolchain
ARM 的 GUN 工具鏈,下載地址:https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads
安裝完成后需要添加到環境變量,使用命令arm-none-eabi-gcc -v
測試。 -
OpenOCD
下載調試用的工具,已編譯好的Windows平台可用二進制文件下載地址: https://gnutoolchains.com/arm-eabi/openocd/
同樣安裝完成后需要添加到環境變量,使用命令openocd -v
測試。 -
make
下載地址:http://gnuwin32.sourceforge.net/packages/make.htm
同上,環境變量,make -v
-
添加環境變量
因為所有個工具都放在了同一個目錄下,所以我喜歡這么加環境變量,環境變量添加完后需要點擊所有的“確定”然后重啟命令窗口才生效。
如果出現配置完環境變量 VSCode 中的終端識別不到的情況重啟電腦可以解決。
1.2_VSCode-插件
- C/C++
實時語法檢查。
-
ARM
ARM的匯編語法高亮 -
Cortex-Debug
MCU的調試核心,比 VSCode 默認的調試界面強大很多。為了更好的使用這個工具進行調試我們還需要對應單片機的 .svd 文件,這個文件定義了某個芯片的非常詳細的信息,包含了哪些片內外設、每一個外設的硬件寄存器、每一個寄存器中每一個數據位的值以及詳細的說明信息等等。svd 文件可以在單片機的固件庫原包里找到,也可以去其他地方下載,這里推薦一個地方:<https://github.com/posborne/cmsis-svd/tree/master/data>
二、用CubeMX新建Makefile工程
CubeMX 的下載和安裝就不多說了,注意運行它需要 java ,而且網絡不好的情況還需要掛代理。
2.1_添加軟件包
啟動 CubeMX 后點擊 "Help" -> "Manage embedded software packages"
可以進入軟件包管理頁(快捷鍵Alt+U
)。進入后根據需要安裝相應型號的軟件包即可。
2.2_創建工程
-
點擊軟件首頁的
ACCESS TO MCU SELECTOR
進入MCU選擇器選擇一個芯片,然后軟件會跳到工程配置界面。 -
配置RCC. 時鍾是必須要配置的,先在
Pinout&onfiguration
里配置時鍾源引腳,然后去Clock Configuration
中配置時鍾樹。配置時鍾樹時先選擇好時鍾源輸入,然后在HCLK(MHz)
中輸入需要的頻率並回車軟件就會自動配置后面的部分。 -
選擇調試模式和基礎時鍾源。在
Pinout&onfiguration
->SYS
->Debug
中選擇Serial Wire
,時鍾源同樣在SYS
標簽下,選擇默認的SysTick
即可。 -
初始化一個連接 LED 的 GPIO 口。
2.3_生成工程
-
進入
Project Manager
選項卡,依次填入工程名稱、工程路徑、Toolchain選擇Makefile. -
點擊
GENERAATE CODE
即可在指定路徑生成工程。
三、VSCode_寫代碼和編譯
3.1_編譯和下載
將 CubeMX 生成的工程文件夾拖入到 VSCode 中打開,這時候如果不出意外的話在 VSCode 的終端中輸入 make
就可以成功的編譯你的工程了(前提是正確安裝了 "GNU Arm Embedded Toolchain" 和 "make" 並配置了環境變量)。
程序編譯完成就可以得到 hex 文件了,想要把這個文件下載進單片機有一百種辦法,你可以在 J-Flash、OpenOCD、pyOCD 等工具中選擇一個你喜歡的。
3.2_vscode的配置文件
通過上面的介紹已經可以對工程進行編譯和下載了,但是仍有很多不足。比如工程文件中會有一大堆畫波浪線的錯誤(前提是安裝了C/C++插件)、工程的編譯下載過程很繁瑣等,因此我們還需要進一步配置些東西。
vscode的配置文件是放在與 .vscode
這個文件夾下的,如果 VSCode 未自動創建的話我們就需要手動創建,它在工程的根目錄下。
-
c_cpp_properties.json
- 在
.vscode
目錄下建立c_cpp_properties.json
文件,需要用這個文件記錄頭文件的包含路徑和全局宏定義。具體要哪些路徑和哪些宏定義可以去Makefile
目錄下的C_INCLUDES
和C_DEFS
找。 - 不過只把那幾項添加進來還是不夠的,有些用到的頭文件 CubeMX 生成的工程里並沒有攜帶,這些C語言的標准庫頭文件是需要在 arm-none-eabi-gcc 的安裝路徑里找的。至於怎么找當然是有技巧的,在命令行里輸
echo 'main(){}' | arm-none-eabi-cpp -E -v -
(CMD) 就可以看到 arm-none-eabi-cpp 默認的頭文件和庫文件路徑了,把它們添加進去。 - 但是當你把這幾個頭文件加進去你會發現還是會報錯,多半是什么 uint32_t 未定義之類的。這是因為 GNU-ARM 的 stdint.h 會向下鑽取並生成 stdint-gcc.h ,后者依賴於一個宏,同時它也依賴其他宏才能到達那里,但是由於這些宏我們未定義所以會報錯。解決這個問題的辦法就是手動定義那些宏,使用
arm-none-eabi-gcc -dM -E - < nul
(CMD) 可以查看 arm-gcc 的內置宏定義,查出來結果可能有幾百條,不過不要在乎那些,把這些全添加進去報錯就會消失了,(在添加的時候只保留第一個空格前邊的內容,這點小事讓Excel干就好了)。 - 如果還是提示有問題那多半是 VSCode 的問題,重啟一下試試(打開命令窗口輸 ">reload windw")。
- (PS.額外添加的這點東西只是為了告訴vscode我們有哪些東西,並不會影響gcc的編譯(畢竟我們添加的都是gcc默認包含的),更不要在Markfile文件中同步修改)
{ "configurations": [ { "name": "Win32", "includePath": [ "${workspaceFolder}/**", "c:/42hdst/software/hardware/gcc-arm-none-eabi/9.2.1/bin/../lib/gcc/arm-none-eabi/9.2.1/include", "c:/42hdst/software/hardware/gcc-arm-none-eabi/9.2.1/bin/../lib/gcc/arm-none-eabi/9.2.1/include-fixed", "c:/42hdst/software/hardware/gcc-arm-none-eabi/9.2.1/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/include", "${workspaceFolder}/Inc/*", "${workspaceFolder}/Drivers/STM32F1xx_HAL_Driver/Inc/*", "${workspaceFolder}/Drivers/STM32F1xx_HAL_Driver/Inc/Legacy/*", "${workspaceFolder}/Drivers/CMSIS/Device/ST/STM32F1xx/Include/*", "${workspaceFolder}/Drivers/CMSIS/Include/*" ], "defines": [ "_DEBUG", "UNICODE", "_UNICODE", "USE_HAL_DRIVER", "STM32F103xB", "__DBL_MIN_EXP__", "__HQ_FBIT__", ...... ...... ...... "__ATOMIC_RELEASE" ], "intelliSenseMode": "msvc-x64" } ], "version": 4 }
- 在
-
tasks.json 和 launch.json
至此用 VSCode 愉快的寫代碼就已經沒有問題了,剩下的就是調試方面的工作了。這兩個文件分別是任務和調試用的,用VSCode調試代碼痛快不痛快方便不方便就全看這兩個文件的水平高低了,想要寫得好就得有個好老師,先觀摩觀摩 github 上的一個項目:😂
四、Github項目——VS-Code-STM32-IDE
- 項目地址:https://github.com/damogranlabs/VS-Code-STM32-IDE
- This project transform VS Code to a great IDE that can be used with STM32CubeMX tool to create a projects without any limitations and code size restrictions, without any bloatware and fast user setup (once all prerequisites are installed). Project is based on python scripts and is therefore fully customizable. OpenOCD tool and Cortex-Debug VS Code plugin is used for debug purposes.
4.1_環境准備
使用這個工具必須要用的除了上面提到的 arm-gcc、make、openocd 三個軟件和 C/C++、ARM、Cortex-Debug 三個插件外還需要用到 Python 和 VSCode 中的 Python 插件。
4.2_使用方法
-
准備安裝好所有的工具和軟件。arm-gcc、make、openocd 三個工具怎么用自己定吧,可以配置好環境變量也可以按他說的解壓到推薦目錄
%userprofile%\ AppData \ Roaming \ GNU MCU Eclipse
中。
-
需要有一個用 CubeMX 生成的 Makefile 工程,工程從哪來前面說過了。
-
將從 GitHub 上下載到的 "ideScipts" 文件夾復制到工程目錄下。
-
使用 VSCode 打開工程文件夾,然后用 python 運行 update.py 腳本,按提示操作(如果他的 python 腳本有什么語法錯誤的話不用理他,超綱了。能用就行):
-
第一次運行不成功,說需要
.code-workspace
文件,所以在工程的根目錄下給它建一個。 -
正式運行
update.py
腳本,這次運行應該就不會報錯了,按它的步驟走就行了:# 因為我的 arm-gcc 添加到了環境變量,所以它能檢測到,不廢話當然 yes. Default path to 'arm-none-eabi-gcc executable (arm-none-eabi-gcc.exe)' detected at 'C:\42HDST\Software\Hardware\gcc-arm-none-eabi\9.2.1\bin\arm-none-eabi-gcc.EXE' Use this path? [y/n]: y # 確認 make 的路徑,還是yes. Default path to 'make executable (make.exe)' detected at 'C:\42HDST\Software\Hardware\make\3.8.1\bin\make.EXE' Use this path? [y/n]: y # 確認 openocd 的路徑,還是yes. Default path to 'OpenOCD executable (openocd.exe)' detected at 'C:\42HDST\Software\Hardware\OpenOCD\0.10.0\bin\openocd.EXE' Use this path? [y/n]: y # 調試器配置文件的路徑 Enter path or command for 'OpenOCD ST Link interface path ('stlink.cfg')': Paste here and press Enter: C:\42HDST\Software\Hardware\OpenOCD\0.10.0\share\openocd\scripts\interface\cmsis-dap.cfg WARNING: Exception error overwriting 'toolsPaths.json' file: [Errno 2] No such file or directory: 'C:/Users/luhua/AppData/Roaming/Code/User/toolsPaths.json' # 芯片配置文件的路徑 Enter path(s) to OpenOCD configuration file(s): Example: 'target/stm32f0x.cfg'. Absolute or relative to OpenOCD /scripts/ folder. If more than one file is needed, separate with comma. Paste here and press Enter: C:\42HDST\Software\Hardware\OpenOCD\0.10.0\share\openocd\scripts\target\stm32f1x.cfg # svd文件路徑 Enter path or command for 'stm32SvdPath': Paste here and press Enter: ./STM32F103xx.svd
-
-
腳本運行完之后能會有些報錯,但是無傷大雅,現在我們絕對可以用它生成的文件下載和調試工程了。即便不直接用它生成的東西對我們自己寫配置文件也是很有參考價值的。
五、自定義配置文件
已經有了一個好老師,接下來我們嘗試自己寫任務和調試的配置文件吧,這看起來很簡單。
5.1_任務文件
任務文件 tasks.json ,有了任務這個東西我們就可以一鍵讓系統自動執行好幾條指令了,不用再一條一條敲。要實現 STM32 的基本開發我們總要最少設置這么幾個任務——編譯、下載並復位、編譯且下載並復位。
-
編譯工程,Build project. 參考參考大佬的代碼,然后修修補補就成了。
/* !!! json 文件中不能有注釋,復制代碼時一定要刪除干凈 !!! */ { /* 標簽 */ "label": "Build project", /* build 組里邊默認執行的任務,就是按 Ctrl+Shift+B 肯定執行它 */ "group": { "kind": "build", "isDefault": true }, /* 命令和參數,這里用的是絕對路徑,如果配置好了環境變量也可以不寫路勁。"args" 里是參數,可以寫多個 */ "type": "shell", "command": "C:/42HDST/Software/Hardware/make/3.8.1/bin/make.EXE", "args": [ "GCC_PATH=C:/42HDST/Software/Hardware/gcc-arm-none-eabi/9.2.1/bin", /* -j4 是說使用四個CPU線程編譯,我的CPU只有4個線程,四舍五入就是滿載了 */ "-j4" ], /* 這一塊的意思是如果在運行任務時終端出現了 "Warning"、"error" 等等東西就把他們顯示到問題面板上,詳見圖 */ "problemMatcher": { "pattern": { "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", "file": 1, "line": 2, "column": 3, "severity": 4, "message": 5 } }, "presentation": { /* 使用這句話獲取控制面板焦點,也就是說這個任務執行完焦點自動跑到終端輸出哪里 */ "focus": true } }
-
編譯下載並復位,CPU: Build, Download and run
{ /* !!! json 文件中不能有注釋,復制代碼時一定要刪除干凈 !!! */ "label": "CPU: Build, Download and run", "type": "shell", /* 這次使用的是相對路徑,LHardware是我配置的一個環境變量,LHardware=C:/42HDST/Software/Hardware */ "command": "${env:LHardware}/OpenOCD/0.10.0/bin/openocd.EXE", "args": [ /* 這三個文件一定要找對,調試器配置文件、芯片配置文件和編譯生成的 .elf 文件 */ "-f", "${env:LHardware}/OpenOCD/0.10.0/share/openocd/scripts/interface/cmsis-dap.cfg", "-f", "${env:LHardware}/OpenOCD/0.10.0/share/openocd/scripts/target/stm32f1x.cfg", "-c", "program build/STM32F103C8Tx.elf verify reset exit" ], "problemMatcher": [], /* 這個任務依賴於另一個任務 */ "dependsOn": "Build project" }
5.2_調試文件
-
有了 "Build Project" 任務我們就可以寫調試文件
launch.json
了,因為調試之前總需要重新下載代碼吧。 -
要想好好調試先得有個好工具,所以再次強調一下 "Cortex-Debug" 插件和對應芯片的 .svd 文件。
{ /* !!! json 文件中不能有注釋,復制代碼時一定要刪除干凈 !!! */ "name": "Cortex debug", "type": "cortex-debug", "request": "launch", /* 調試器選擇,很重要 */ "servertype": "openocd", /* 輸出路徑 */ "cwd": "${workspaceFolder}", /* elf 文件的路徑,調試用的 */ "executable": "build/STM32F103C8Tx.elf", /* svd 文件路徑 */ "svdFile": "./STM32F103xx.svd", /* 下面這兩個是調試器和芯片的配置文件 */ "configFiles": [ "${env:LHardware}/OpenOCD/0.10.0/share/openocd/scripts/interface/cmsis-dap.cfg", "${env:LHardware}/OpenOCD/0.10.0/share/openocd/scripts/target/stm32f1x.cfg" ], /* 依賴於 "Build project" 任務 */ "preLaunchTask": "Build project" }
P、重要補充
-
進一步解決 C/C++ 插件的報錯
經過之前的一通操作,又是加 include 路徑又是加 define 的,基本已經可以達到 C/C++ 插件在用戶代碼的文件里面不報錯了。但是仍有意外,我們不可能只在用戶代碼里呆着而是經常需要進到庫函數里面看看,但是這個時候報錯就又來了,比如在 stm32f1xx_hal_gpio.c 中就會瘋狂報錯,雖然知道這並不影響什么,在 keil 里邊庫函數還有幾個報錯呢,但是就是很不痛快。
不過好在功夫不負有心人,這個問題的終極解決方案終於被我給找到了,我們只需要在
c_cpp_properties.json
文件中略微調一個值就可以。打開c_cpp_properties.json
翻到最后應該能找到這么一句話"intelliSenseMode": "msvc-x64"
這是什么意思?這是說智能感應模式是msvc-x64,搜搜msvc是什么鬼吧——MSVC是指微軟的VC編譯器!淦,我們明明用的是 GCC 吧!坑人呢。所以直接把這個 "msvc-x64" 改成 "gcc-x86" 莫名其妙的問題就不會再出現了,如果不放心可以建個標簽把 gcc 的路徑寫進去。(這里之所以改成 "gcc-x86" 是因為不能選 "gcc-arm" 😂) -
關於 GCC 編譯生成的 bin 文件太大的問題
GCC 編譯生成的文件是大,雖然我們用的是 GNU Arm Embedded Toolchain, 但編譯出來的文件依然很大。一個基礎的配置了 LED 燈的代碼 Keil 編譯出來只有 5k 而 GCC 編譯出來的卻有 29k ,優化調到 OPT = -Os 也沒什么效果。關於這個問題我想說的有以下幾點:
- 避免使用 printf() 等 C 語言標准庫函數可以大幅減小 bin 文件的體積。
- 在 Makefile 中不使用
-u _printf_float
和-u _scanf_float
參數可以大幅減小 bin 文件的體積,只是這樣 C 標准庫中浮點數和字符串互轉的功能就不起作用了。 - 刪掉沒有使用的函數也能縮小 bin 文件大小,但是有點麻煩。
- GCC 編譯出來的文件雖然大,但它和 Keil 編譯出的文件關系也只是加法不是乘法,因為在 LED 和 Printf 的基礎工程上再加上 FreeRTOS 編譯出來的 bin 文件大小是 34k 較之前只增了 5k ;同樣在此基礎上加上 FreeRTOS 后 Keil 編譯出來的 bin 文件是 13k ,較之前增加了 8k ,這樣看 GCC 就沒有那么可惡了。
- 如果使用了 OS 的話,那幾乎可以肯定它是一定使用了 C 標准庫的函數了,所以。。。
- 想來在單片機領域 VSCode 和 GCC 算是高級玩家的奢侈玩法,因此那種對於 Flash 只有幾 KB 甚至不到 1KB 的芯片還是乖乖用 Keil 吧。雖然用盜版軟件不好,但是我們是學習用途嘛。
參考資料
- IntelliSense doesn't recognize uint32_t type only.
- Using Visual Studio Code for ARM Development – Defines