天天看點

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

很多朋友在開發嵌入式的時候,一般常用Keil MDK、IAR等IDE工具,不過這些都是收費的,而且非常笨重,跨平台開發不友善,依次筆者将推薦使用VS Code開發嵌入式。

如果想要使用VS Code開發嵌入式,則需要以下工具:

(1) GNU Arm Embedded Toolchain:ARM用的GNU工具鍊,包括編譯器(gcc),調試器(gdb),連結器(ld)和其它工具,支援Windows、Linux、Mac。GCC(GNU Compiler Collection)。

(2) Git/make:代碼管理工具,但是我們這裡要使用的是下載下傳git附帶的git bash。這個bash是基于mingw的,非常輕量,甚至于make都沒有,但是可以安裝相應的插件來支援make,wget等工具。當然啦,如果是在Linux平台直接使用make工具就行。

(3) OpenOCD:一個開源的片上調試器(Open On-Chip Debugger)。OpenOCD負責把GDB的進階别指令轉換成JTAG指令,并通過特定下載下傳器的要求進行打包,準備調用OS提供的USB驅動由USB發送出去。GDB和OpenOCD之間使用TCP協定進行連接配接,說的簡單點,OpenOCD就是一個協定轉換工具。

本文将分為兩個部分:工具安裝,固件開發。

1軟體安裝

1.1 GCC編譯工具

編譯代碼需要編譯器,Linux系統的編譯器是GCC,而Windows的C/C++編譯器是Microsoft Visual C++,那麼要想在Windows也能GCC等一系列編譯工具,就需要安裝MinGW。

MinGW 是用于進行 Windows 應用開發的 GNU 工具鍊(開發環境),它的編譯産物一般是原生 Windows 應用,雖然它本身不一定非要運作在 Windows 系統下(是的 MinGW 工具鍊也存在于 Linux、BSD 甚至 Cygwin 下)。說的通俗點,MinGW就是你在Windows下使用GNU工具鍊的一個編譯工具。

MinGW編譯的程式隻能在X86上運作,不能運作在嵌入式的硬體平台,因為嵌入式平台大都是ARM體系結構,是以這就需要一個在Windows環境下能使用GNU編譯ARM體系結構的編譯工具,這也就是交叉編譯工具。

所謂交叉編譯工具就是在一種平台上編譯出能運作在體系結構不同的另一種平台上的程式,比如在PC平台(X86 CPU)上編譯出能運作在以ARM為核心的CPU平台上的程式,編譯得到的程式在X86 CPU平台上是不能運作的,必須放到ARM CPU平台上才能運作。

做過嵌入式開發的朋友都知道,在嵌入式開發過程中有主控端和目标機的角色之分:主控端是執行編譯、連結嵌入式軟體的計算機;目标機是運作嵌入式軟體的硬體平台。

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

嵌入式開發流程大緻就是在主控端完成目标的開發工具,使用功能交叉編譯工具生成固件,将固件燒寫到目标機,在開發初期,還需要線上調試等工作,這就需要諸如J-link等調試工具。

gcc-arm-none-eabi就是一個基于ARM的交叉編譯工具鍊,而且還是開源的,适用于Arm Cortex-M和Coretex-A系列處理器,包括GNU編譯器(GCC),以及GDB,不僅适用于Windows,還适用于Linux,MacOS上的交叉編譯。

好了,直接看下載下傳位址吧。

​​下載下傳位址​​

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

【注】這裡要選擇GUN-RM下的工具,GUN-A是Cortex-A系列的交叉編譯工具。

下載下傳後解壓,并把安裝目錄下的bin檔案夾添加到環境變量:

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

然後在指令視窗中輸入下面的指令驗證安裝是否成功:

#arm-none-eabi-gcc -v      

如果有資訊輸出,那就是裝好了。

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

值得注意的是,如果不是ARM體系,就需要更換交叉編譯工具,比如RISC-V,就需要安裝RISC-V的交叉編譯工具鍊。

1.2 make工具安裝

如果使用gcc來編譯工程,一般需要使用功能Makefile來管理工程,那麼就需要一個工具來識别Makefile檔案,也就是make工具,在Linux中已經自帶make了,在Windows就需要安裝make工具。

在安裝make工具之前,先來看看什麼是makefile?Makefile 可以簡單的認為是一個工程檔案的編譯規則,描述了整個工程的編譯和連結等規則。其中包含了那些檔案需要編譯,那些檔案不需要編譯,那些檔案需要先編譯,那些檔案需要後編譯,那些檔案需要重建等等。編譯整個工程需要涉及到的,在 Makefile 中都可以進行描述。換句話說,Makefile 可以使得我們的項目工程的編譯變得自動化,不需要每次都手動輸入一堆源檔案和參數。

本文的make工具是依賴Git工具的,我相信很多朋友都用過Git,但是很少使用Git的make等功能。

Git的bash實際上也就是一個mingw,是可以支援部分Linux指令的,但是隻有少部分。在編譯代碼的時候經常會使用make指令反而在bash下預設是不支援的。

​​Make工具下載下傳位址​​

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

下載下傳make-4.1-2-without-guile-w32-bin.zip 檔案。

把該檔案進行解壓,把解壓出來的檔案全部拷貝的git的安裝目錄下:

C:\Program Files\Git\mingw64

把檔案夾進行合并,如果跳出來需要替換的檔案要選擇不替換。

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

這樣在git bash視窗下就可以執行make了。

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

沒有安裝Git先安裝Git工具。

​​Git下載下傳位址​​

1.3 OpenOCD安裝

OpenOCD是用于對MCU進行下載下傳仿真的工具,是一個開源軟體包。

​​下載下傳位址​​

下載下傳後解壓即可。

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

打開share/openocd/scripts,裡面有很多提前寫好的配置檔案:

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

其中interface目錄下都是接口相關配置檔案,例如jlink.cfg,stlink.cfg;target目錄下都是晶片相關的配置檔案,例如stm32f1x.cfg。

然後配置下環境變量:

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

然後在指令視窗中輸入下面的指令驗證安裝是否成功:

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

表示安裝成功。

值得注意的是,想要線上下載下傳調試就需要調試工具的驅動安裝。筆者首推J-link,幾乎所有的MCU都是可以用,當然如果是ST的MCU,也可以使用ST-Link。

2固件開發

在開始之前,需要準備一個基于GCC的工程,筆者這裡準備的是STM32的工程。

2.1 Visual Studio Code導入工程

選擇打開檔案夾,選擇STM32CubeMX建立的工程.

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

最後建立的工程如下:

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

2.2 Visual Studio Code工程配置

接下來就是配置工程。

1. settings.json

點選“在settings.json中編輯",修改為自己安裝git bash的路徑:

{
    "terminal.integrated.profiles.windows": {
        "my-bash": {
          "source": "Git Bash",
          "args": []
        }
      },
      "terminal.integrated.defaultProfile.windows": "my-bash"
}      

打開終端,就可以使用Bash了。當然了,這不是非必須的。

2. c_cpp_properties.json

VS Code隻是一個編輯器,它檢查代碼的時候并不會去讀makefile,是以有些宏定義需要自行配置。c_cpp_properties.json的作用就是配置工程的頭檔案、工具鍊、宏定義等參數。

打開c_cpp_properties.json配置檔案,輸入以下内容:

{
    "configurations": [
        {
            "name": "Win32",
            "includePath": [
                "D:/gcc/gcc-arm-none-eabi-10.3-2021.07/arm-none-eabi/include",
                "${workspaceFolder}/Core/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": [
                "USE_HAL_DRIVER",
                "STM32F103xE"
            ],
            "compilerPath": "D:/gcc/gcc-arm-none-eabi-10.3-2021.07/bin/arm-none-eabi-gcc.exe",
            "intelliSenseMode": "gcc-x64",
            "browse": {
                "limitSymbolsToIncludedHeaders": true,
                "databaseFilename": "",
                "path": [
                    "${workspaceFolder}"
                ]
            }
        }
    ],
    "version": 4
}      

name:這是用于标記使用的平台的标簽。除了win32還可以選Linux或Mac。也就是說,這個json裡“configuration“下可以寫三組配置,隻要每組配置前面寫上不同的平台,即可在不同的作業系統上使用就會自動适配不同的配置。

includePath:頭檔案路徑。第一個目錄是C語言标準庫的目錄, 剩下的幾個目錄直接從Makefile裡複制然後稍微修改下即可。"${workspaceFolder}"表示項目檔案夾;

defines:全局宏定義。

compilerPath:編譯器的路徑。

intelliSenseMode:因為我用的是gcc是以選gcc-x64。

browse:源檔案搜尋路徑。用來做代碼補全和查找定義的。這個路徑和includePath不同,browse.path是自動遞歸所有子目錄的。而include.path預設隻看本目錄。

3.tasks.json

tasks.json的作用就是配置工程的編譯、下載下傳等工作。如果沒有則需要建立tasks.json檔案,内容如下:

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
        "version": "2.0.0",
        "tasks": [
            {
                "label": "build",
                "type": "shell",
                "command": "make",
                "args": [
                    "-j4"
                ] 
            },
            {
                "label": "download",
                "type": "shell",
                "command": "openocd",
                "args": [
                    "-f",
                    "./interface/jlink.cfg",
                    "-f",
                    "./target/stm32f1x.cfg",
                    "-c",
                    "program build/STM32F103ZE.elf verify exit"
                ] 
            },
            {
                "label": "clean",
                "type": "shell",
                "command": "make",
                "args": [
                    "clean"
                ] 
            }
        ]
}      

這個檔案建立了三個任務,分别叫做build、download和clean,build任務就是在bash裡執行了make -j4,也就是使用多線程編譯,download也就是用于線上下載下傳固件,clean任務就是在bash裡執行了make clean。

值得注意的是,這裡需要拷貝OpenOCD中的interface和target中的相關配置,筆者是整個檔案夾都拷貝過來了。

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

2.3編譯

點選‘終端->運作任務’,然後虛着呢‘build’。

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

等待編譯完成即可。

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

這裡還可以直接使用終端編譯。

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

最後我們使用make編譯下前文建立的工程,編譯通過顯示如下:

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

2.4固件下載下傳

筆者這裡使用功能OpenOCD下載下傳。選擇“終端->運作任務…”

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

選擇task中配置的指令download。

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

複位裝置,即可下載下傳成功。

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

當然也可使用指令下載下傳。

# openocd -f ./interface/stlink.cfg -f ./target/stm32f1x.cfg -c 'program build/STM32F103ZE.elf verify exit‘      

效果和使用界面是一樣的。

當然換成J-Link下載下傳也是一樣的操作,隻是需要修改指令。

# openocd -f ./interface/jlink.cfg -f ./target/stm32f1x.cfg -c 'program build/STM32F103ZE.elf verify exit‘      

另外,還需要注意的是如果J-Link的接口是swd,則需要在interface/jlink.cfg中添加如下語句。

transport select swd      

當然還可以連接配接到OpenOCD守護程式燒寫。

$ openocd      
《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

值得注意的是,如果不加參數,需要事先配置openocd.cfg,

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

如果不想配置,也可使用以下指令:

#openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg      
《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

效果都是一樣的。

打開另一個終端,輸入以下指令連接配接到OpenOCD守護程式。後面的所有指令都是在這個終端運作的。

# telnet localhost 4444      

成功通過ST-Link連接配接到STM32上之後,OpenOCD會監聽本機的4444端口。通過telnet登入上去,之後就可以控制OpenOCD幹些什麼了。

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

接下來就可以燒寫固件了。

> program build/STM32F103ZE.elf verify reset exit      
《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

當然也可使用功能J-Link的J-Flash下載下傳固件。

如果是ST的,還可使用ST-Link下載下傳固件。

【注】OpenOCD使用J-link使用須知

在使用J-link之前,需要将J-link的驅動改為libusb,否則openocd無法連接配接到J-link。這裡使用的工具是UsbDriverTool。

UsbDriverTool下載下傳位址:https://visualgdb.com/UsbDriverTool/

使用功能方法如下:

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

如果使用J-link調試,則不修改修改。

2.5 ARM-GDB調試

直接使用GDB調試,

首先在終端輸入一下指令:

#openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg      
《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

【注】也可将指令配置成task任務。

【注】如果不帶參數啟動,openocd就會自動查找目前目錄下有沒有名為openocd.cfg的檔案,并把它作為配置檔案來啟動。

【注】openocd預設TCP/IP的3333端口作為gdb端口,4444作為telnet端口。在連接配接gdb之前,我們可以先用telnet連上試用一下。

如果你的電腦沒有開啟telnet功能,需要打開“啟用或關閉Windows功能”,然後在裡面找到“telnet用戶端”,啟動即可。

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

openocd運作時,這個終端就被占用了是以還需要再開一個終端。

GDB也屬于GNU項目的一部分,跟在Linux上調試是一樣的,隻是這裡使用的是交叉編譯工具中的GDB。

# arm-none-eabi-gdb build/STM32F103ZE.elf      
《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

關于GDB的使用請看筆者文章:

GDB

接下來需要連接配接openocd服務,openocd給GDB的TCP/IP端口是3333。

# target remote localhost:3333      
《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

接下來和在Linux中調試一樣。

在 gdb 中鍵入"l"(list)就可以檢視所載入的檔案,如下所示。

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

自行參考筆者關于GDB的博文去調試吧。

如果覺得使用指令不友善,也可安裝VS Code的插件,但是這是基于Cortex-M系列的MCU,如果是其他系列的,還是使用GDB吧。

2.6 VS Code插件Cortex-Debug調試

如果沒有安裝Cortex-Debug調試插件,需要先安裝。

2.6.1 Cortex-Debug調試配置

1.GDB server路徑設定

設定J-Link和OpenOCD的路徑。

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式
《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

根據實際情況設定。

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

2.launch.json檔案

在vscode檔案夾中建立一個launch.json,該檔案是調試的入口檔案。内容如下:

{
    "version": "0.2.0",
    "configurations": [
        {
            "cwd": "${workspaceRoot}",
            "type": "cortex-debug",
            "request": "launch",
            "name": "OpenOCD",
            "interface": "swd",
            "servertype": "openocd",
            "executable": "./build/STM32F103ZE.elf",
            //"runToMain": true,
            "device": "STM32F103ZE",
            "svdFile": "./STM32F103xx.svd",
            "configFiles": [
                "${workspaceRoot}/openocd.cfg"
            ],
            //"preLaunchTask": "build",
            "armToolchainPath": "D:/gcc/gcc-arm-none-eabi-10.3-2021.07/bin/"
        },
        {
            "cwd": "${workspaceRoot}",
            "type": "cortex-debug",
            "request": "launch",
            "name": "J-Link",
            "interface": "swd",
            "servertype": "jlink",
            "executable": "./build/STM32F103ZE.elf",
            //"runToMain": true,
            "device": "STM32F103ZE",
            "svdFile": "./STM32F103xx.svd",
            //"preLaunchTask": "build",
            "armToolchainPath": "D:/gcc/gcc-arm-none-eabi-10.3-2021.07/bin/"
        },
        {
            "cwd": "${workspaceRoot}",
            "type": "cortex-debug",
            "request": "launch",
            "name": "ST-Link",
            "interface": "swd",
            "servertype": "stlink",
            "executable": "./build/STM32F103ZE.elf",
            //"runToMain": true,
            "device": "STM32F103ZE",
            "svdFile": "./STM32F103xx.svd",
            //"preLaunchTask": "build",
            "armToolchainPath": "D:/gcc/gcc-arm-none-eabi-10.3-2021.07/bin/"
        }
    ]
}      

executable:編譯出的二進制檔案,也就是最終燒錄到單片機中的,這裡是elf檔案。根據晶片的不同,可能産生不同的名稱和字尾(例如TI的TM4C123晶片編譯出來的名稱是"main.axf")

request:可以選launch或attach。launch是指啟動調試時同時開始執行程式;attcah是指程式已經在運作了,然後開始調試。我沒測試過attach。

type:調試的類型,選cortex-debug,這是我們裝的插件。其實也可以填cppdbg之類的,但是那樣我們就得自己配置gdb了,配置起來将會非常麻煩。

device:目标晶片。如果你使用J-LINK GDB Server時必須要設定這個選項。

svdFile:svd檔案的路徑,每個MCU的各不相同。

servertype:要選擇的gdb server。我這裡用openocd。

configFiles:gdb的配置檔案路徑。openocd會自動讀目前目錄下的openocd.cfg檔案,這個選項不填也行。但是如果你想把openocd.cfg放在别處,就可以用這個選項指定配置檔案的路徑。

preLaunchTask:在啟動調試前,預先執行的任務。

armToolchainPath:工具鍊的路徑。

3.openocd.cfg檔案

在項目檔案夾下建立一個openocd.cfg檔案,用于配置調具體的調試器。内容如下:

# 選擇調試器為jlink
#source [find interface/jlink.cfg]
source [find interface/stlink-v2.cfg]

# 選擇接口為SWD
# transport select swd

# 選擇目标晶片
source [find target/stm32f1x.cfg]      

我這裡選擇使用ST-Link,SWD接口,目标晶片為stm32f1x。

【注】使用J-link調試也是一樣的,隻需将openocd.cfg檔案配置成J-link調試即可。

4.svd檔案

用于尋找STM32F1的svd檔案。CMSIS-SVD是CMSIS的一個元件,它包含完整微控制器系統(包括外設)的程式員視圖的系統視圖描述 XML 檔案。VS Code可以通過它來知道外設寄存器的位址分布,進而把寄存器内容展示到視窗中。

​​svd檔案位址​​

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

将下載下傳好的STM32F103xx.svd檔案放在項目檔案夾根目錄即可。

2.6.2 Cortex-Debug調試

直接按F5,openocd啟動時,會自動在目前目錄下尋找名為openocd.cfg的檔案作為配置檔案。調試界面如下:

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

界面左邊可以看到變量視窗、調用堆棧等。視窗中間就是單步調試的各個按鈕。這個就沒啥好說的了,趕緊去玩起來吧。

這裡可以在launch.json檔案添加多個選項,就可使用多種調試手段了。

《嵌入式 - 嵌入式大雜燴》基于VS Code開發嵌入式

Cortex-Debug的本質還是使用的OpenOCD。

本文介紹的開發方式不僅适用于ARM,還适用于RISC-V等錫系列的處理器。

歡迎訂閱我的微信公衆号