1. 程式人生 > >linux 下 VSCODE 使用CMake編譯STM32程式

linux 下 VSCODE 使用CMake編譯STM32程式

專案在做什麼

專案地址

  1. 本專案是為了研究MCU在linux下開發而做的
--build 存放cmake編譯生成的檔案
--cmake 存放cmake編譯時會用到的檔案,比如工具鏈檢查、編譯選項等
--prj windows下kei工程的工程檔案
--src 原始碼
  1. 本專案中大量的CMakeLists.txt的寫法參考了LoRa-Node

目前專案已經初步能夠運行了

使用的元件

  1. VSCODE -> 程式設計師使用的文字編輯器
  2. cmake -> 組織編譯邏輯
  3. arm-none-eabi --> 編譯,生成elf,bin,hex
  4. JLinkGDBServer -> GDB 伺服器
  5. cortex-debug -> 用來除錯程式

我的環境

jk@jk:~$ sudo lsb_release -a
[sudo] jk 的密碼: 
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.2 LTS
Release:    18.04
Codename:   bionic

至此,需要的元件就羅列清除了,下面來講下怎麼安裝

元件安裝

VSCODE

官網瞎下載安裝即可,每什麼可講的,記得安裝最新版

CMake(需要>3.6)

jk@jk:~$ cmake -version
cmake version 3.10.2

CMake suite maintained and supported by Kitware (kitware.com/cmake).

安裝方法:

  • Ubuntu 16.04/ Linux Mint 18: Since the official repository version is too old, one can use e.g. PPA
  • Linux Arch: pacman -S cmake

arm-none-eabi

  • GNU ARM-Toolchain
  • GNU/Linux:
    • Ubuntu 16.04/ Linux Mint 18: Since the official repository version is too old, one can use e.g. PPA
    • Ubuntu 18.04: the toolchain has been updated but there is a bug with libnewlib
      causing the linker to fail. sudo apt install gcc-arm-none-eabi
    • Linux Arch: pacman -S arm-none-eabi-gcc arm-none-eabi-newlib

JLinkGDBServer

jk@jk:~$ JLinkGDBServer -version
SEGGER J-Link GDB Server V6.48b Command Line Version

JLinkARM.dll V6.48b (DLL compiled Aug  2 2019 10:20:19)

去官網下載:JLinkGDBServer

我下載的是deb格式,使用dpkg -i安裝即可

cortex-debug

VSCODE 裡面搜尋cortex-debug 安裝即可

至此,安裝就完成了

思路

首先,我們來看main函式

/**
 * Main application entry point.
 */
int main( void )
{
    board_init();

    uart1.rx_complete_callback = uartCallback;
    
    while(1)
    {       
        HAL_UART_SendBytes((uint8_t*)"hello world\r\n",sizeof("hello world\r\n"));
        DelayMs(5000);
    }
}

這個程式就是將硬體初始化,然後5s列印一次"Hello World\r\n"

也就是說這個程式主要分為三個部分:

  • main
  • 驅動
  • MCU HAL 庫

我們來看一下原始碼結構,也可以得出結論

jk@jk:~/programe/stm32-linux-cmake$ tree -d -L 2 src
src
├── apps
└── boards
    ├── driver
    └── stm32

其中:

  • apps中存放的就是main.c檔案

  • boards中存放的是硬體部分

    • boards.driver存放的是硬體的驅動

    • boards.stm32中存放的就是stm32的HAL庫的程式碼

所以,我的思路是:

  • app/boards.driver/boards.stm32這三部分分別生成三個target,最後由這三個target生成exe

實現

AS WE KNOWN

  1. 編譯需要指定.c檔案,編譯.c檔案時由於多函式跨檔案呼叫是通過標頭檔案進行的,所以需要找尋標頭檔案,當頭檔案找不到時就無法呼叫指定函式

  2. 將.c檔案新增到指定target的方法,我這裡用了兩種,兩種方式都可以達到一樣的效果

    1. 一個個新增

      list(APPEND ${PROJECT_NAME}_SOURCES
          "${CMAKE_CURRENT_SOURCE_DIR}/main.c"
      )
    2. 新增指定資料夾下的所有c檔案

      file(GLOB ${PROJECT_NAME}_SOURCES "${CMAKE_CURRENT_LIST_DIR}/*.c")
  3. 將標頭檔案路徑新增倒target路徑,是通過cmake的target_include_directories()方法,其中,我們可以通過${CMAKE_CURRENT_SOURCE_DIR},直接新增目錄,但有時候,我們需要新增別的target的標頭檔案,此時可以用表示式去獲取target的標頭檔案,比如

    1. $<TARGET_PROPERTY:drivers,INTERFACE_INCLUDE_DIRECTORIES>

    通過這個表示式可以獲取到在drivers這個target中新增的標頭檔案

  4. CMake的用法還有很多,太靈活了,我這寫的也只是冰山一角,後面還要繼續努力

### 幹活

編譯程式

  1. 在專案的根目錄建立CMakeLists.txt,將apps,boards.driver,boards.stm32三個subdirectory分別匯入,並在這三個資料夾中寫好分別的CMakeLists.txt

  2. 我們分別將三個target命名為

    • app -->project(app)
    • drivers -->project(drivers)
    • stm32l051 -->project(stm32l051)
  3. 並在其中新增好各自target的.c檔案和標頭檔案,具體見原始碼

  4. ok之後,CMake檔案就編寫完了

  5. 我們需要執行

    • mkdir build
    • cd build
    • cmake -DCMAKE_TOOLCHAIN_FILE="cmake/toolchain-arm-none-eabi.cmake" ..

    PS:

    • 在根目錄生成build資料夾,這是為了方便我們管理,後續如果要刪除編譯的產物,直接刪除build資料夾即可,也可以防止編譯的產物汙染程式碼
    • Makefile生成成功提示如下:
    jk@jk:~/programe/stm32-linux-cmake$ ./configure 
    -- The C compiler identification is GNU 8.3.1
    -- The CXX compiler identification is GNU 8.3.1
    -- Check for working C compiler: /home/jk/cross-tool/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gcc
    -- Check for working C compiler: /home/jk/cross-tool/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gcc -- works
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Detecting C compile features
    -- Detecting C compile features - done
    -- Check for working CXX compiler: /home/jk/cross-tool/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-g++
    -- Check for working CXX compiler: /home/jk/cross-tool/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-g++ -- works
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    Linker script: /home/jk/programe/stm32-linux-cmake/src/boards/stm32/cmsis/arm-gcc/stm32l051xx_flash.ld
    -- The ASM compiler identification is GNU
    -- Found assembler: /home/jk/cross-tool/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gcc
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /home/jk/programe/stm32-linux-cmake/build
  6. 執行make,編譯程式

    jk@jk:~/programe/stm32-linux-cmake/build$ make
    Scanning dependencies of target app
    [  3%] Building C object src/apps/CMakeFiles/app.dir/main.c.obj
    [  3%] Built target app
    Scanning dependencies of target stm32l051
    [  6%] Building ASM object src/boards/stm32/CMakeFiles/stm32l051.dir/cmsis/arm-gcc/startup_stm32l051xx.s.obj
    [  9%] Building C object src/boards/stm32/CMakeFiles/stm32l051.dir/cmsis/system_stm32l0xx.c.obj
    [ 12%] Building C object src/boards/stm32/CMakeFiles/stm32l051.dir/mcu/stm32/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal.c.obj
    [ 16%] Building C object src/boards/stm32/CMakeFiles/stm32l051.dir/mcu/stm32/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_adc.c.obj
    [ 19%] Building C object src/boards/stm32/CMakeFiles/stm32l051.dir/mcu/stm32/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_adc_ex.c.obj
    [ 22%] Building C object src/boards/stm32/CMakeFiles/stm32l051.dir/mcu/stm32/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_cortex.c.obj
    [ 25%] Building C object src/boards/stm32/CMakeFiles/stm32l051.dir/mcu/stm32/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_dma.c.obj
    [ 29%] Building C object src/boards/stm32/CMakeFiles/stm32l051.dir/mcu/stm32/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_flash.c.obj
    [ 32%] Building C object src/boards/stm32/CMakeFiles/stm32l051.dir/mcu/stm32/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_flash_ex.c.obj
    [ 35%] Building C object src/boards/stm32/CMakeFiles/stm32l051.dir/mcu/stm32/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_gpio.c.obj
    [ 38%] Building C object src/boards/stm32/CMakeFiles/stm32l051.dir/mcu/stm32/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_i2c.c.obj
    [ 41%] Building C object src/boards/stm32/CMakeFiles/stm32l051.dir/mcu/stm32/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_pwr.c.obj
    [ 45%] Building C object src/boards/stm32/CMakeFiles/stm32l051.dir/mcu/stm32/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_pwr_ex.c.obj
    [ 48%] Building C object src/boards/stm32/CMakeFiles/stm32l051.dir/mcu/stm32/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_rcc.c.obj
    [ 51%] Building C object src/boards/stm32/CMakeFiles/stm32l051.dir/mcu/stm32/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_rcc_ex.c.obj
    [ 54%] Building C object src/boards/stm32/CMakeFiles/stm32l051.dir/mcu/stm32/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_rtc.c.obj
    [ 58%] Building C object src/boards/stm32/CMakeFiles/stm32l051.dir/mcu/stm32/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_rtc_ex.c.obj
    [ 61%] Building C object src/boards/stm32/CMakeFiles/stm32l051.dir/mcu/stm32/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_spi.c.obj
    [ 64%] Building C object src/boards/stm32/CMakeFiles/stm32l051.dir/mcu/stm32/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_uart.c.obj
    [ 67%] Building C object src/boards/stm32/CMakeFiles/stm32l051.dir/mcu/stm32/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_uart_ex.c.obj
    [ 67%] Built target stm32l051
    Scanning dependencies of target drivers
    [ 70%] Building C object src/boards/driver/CMakeFiles/drivers.dir/board.c.obj
    [ 74%] Building C object src/boards/driver/CMakeFiles/drivers.dir/delay.c.obj
    [ 77%] Building C object src/boards/driver/CMakeFiles/drivers.dir/gpio-board.c.obj
    [ 80%] Building C object src/boards/driver/CMakeFiles/drivers.dir/gpio.c.obj
    [ 83%] Building C object src/boards/driver/CMakeFiles/drivers.dir/key_board.c.obj
    [ 87%] Building C object src/boards/driver/CMakeFiles/drivers.dir/led_board.c.obj
    [ 90%] Building C object src/boards/driver/CMakeFiles/drivers.dir/stm32l0xx_hal_msp.c.obj
    [ 93%] Building C object src/boards/driver/CMakeFiles/drivers.dir/stm32l0xx_it.c.obj
    [ 96%] Building C object src/boards/driver/CMakeFiles/drivers.dir/uart_board.c.obj
    [ 96%] Built target drivers
    Scanning dependencies of target arm_minisys
    [100%] Linking C executable arm_minisys
       text    data     bss     dec     hex filename
      14988      12     400   15400    3c28 arm_minisys
    [100%] Built target arm_minisys
    Scanning dependencies of target arm_minisys.hex
    [100%] Built target arm_minisys.hex
    Scanning dependencies of target arm_minisys.bin
    [100%] Built target arm_minisys.bin
  7. 至此,程式編譯完成

配置VSCODE

  • 配置launch.json (應以除錯)
  • 配置c_cpp_properties.json(用於編寫,閱讀程式碼)

此處我使用的是JLinkGDBServer,是因為之前使用openocd進行除錯時總是出現問題,而用JLinkGDBServer則沒發現什麼問題

launch.json

不講了,自己看配置檔案吧

c_cpp_properties.json

之前沒有仔細配置時,總是在程式碼編輯器的右邊出現紅色的錯誤提示,很是難受,配完之後,這些錯誤提示都消失了,就舒服了。

下面我講一下其中重的部分:

  1. includePath,這個下面一定要靶所有的標頭檔案的路徑都包含進去,不然找不到標頭檔案,就會出現波浪線的錯誤提示了,可以使用**的用法,來將目錄下的所有檔案都匯入

                "includePath": [
                    "${workspaceFolder}/src/**",
                    "/home/jk/cross-tool/gcc-arm-none-eabi-8-2019-q3-update/arm-none-eabi/include/**"
                ],
  2. defines,需要把程式中使用的巨集新增到此處,否則編輯器對預編譯巨集做的程式碼開關也是沒法正確識別,例如:

                "defines": [
                    "STM32L051xx",
                    "USE_HAL_DRIVER"
                ],
  3. compilerPath,這個填寫正確的gcc路徑,否則也是有奇奇怪怪的錯誤

最後的注意事項

  1. 當CMake config/generate失敗的時候,可以嘗試將build資料夾刪除,重新進行,可能會成功
  2. 因為此處我是通過傳遞工具鏈檔案的方式來傳遞工具鏈給CMake的,指令有些長,且難記,為了方便,我將指令做成了config檔案,在${ROOT}下有個config檔案,這樣,後面只要執行./config就可以生成Makefile檔案了
  3. 程式碼結構要清晰,要儘量接耦,否則CMakeLists.txt檔案編寫時會互相呼叫,顯得不整潔

感謝大家,有問題歡迎大家指