本文簡要介紹了如何使用CLion做STM32上的嵌入式開發(fā)。整個開發(fā)流程不離開CLion環(huán)境,提高工作效率。
JetBrain家的開發(fā)工具基本都是最棒的,對編程語言、框架支持得最好。CLion經(jīng)過幾年的發(fā)展,從無到有,成為了一個越來越完善的C++ IDE。在官方論壇上,用戶呼聲很高的一個需求就是remote debugging、嵌入式開發(fā)。
在最近幾次更新里,CLion逐漸添加了remote debugging相關(guān)的支持。筆者經(jīng)過摸索配置,終于能夠在CLion中完成嵌入式開發(fā)的全套流程了。從寫代碼、編譯,到燒寫、調(diào)試,全都在CLion環(huán)境下完成,不用來回切換窗口了。雖然CLion在調(diào)試方面的功能還不夠完善,但代碼編輯絕對是一流的,調(diào)試的“手感”也比Eclipse舒服很多。
本文以STM32開發(fā)為例講解開發(fā)環(huán)境的配置。理論上,其他ARM的MCU是一樣的原理,可根據(jù)需要自行修改配置。
環(huán)境
本文所使用的軟硬件工具有:
· IDE:CLion 2016.3(建議使用這個版本,稍早的版本不確定能不能用)
· toolchain / 編譯器:GNU GCC for ARM (gcc-arm-none-eabi)
· Debugger:OpenOCD 0.9.0
· MCU:STM32F103,自己的板子
· 仿真器:STLink-v2
配置方法
前提條件
首先假設(shè)你已經(jīng)有了一份項目代碼,安裝好了 gcc-arm-none-eabi toolchain,能夠用Makefile或Eclipse等方式正常編譯。這部分內(nèi)容不再詳述。本文中,項目名為 testprj 。
CMake配置
CLion使用CMake系統(tǒng),項目文件就是 CMakeLists.txt 。
首先,創(chuàng)建一個公用的toolchain配置文件 toolchain-arm-eabi-gcc.cmake ,這個文件讓CMake使用 gcc-arm-none-eabi toolchain中的工具,并設(shè)置特定平臺的編譯參數(shù)等。文件內(nèi)容如下。
toolchain-arm-eabi-gcc.cmake
include(CMakeForceCompiler)
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR cortex-m3)
find_program(ARM_CC arm-none-eabi-gcc ${TOOLCHAIN_DIR}/bin)
find_program(ARM_CXX arm-none-eabi-g++ ${TOOLCHAIN_DIR}/bin)
find_program(ARM_OBJCOPY arm-none-eabi-objcopy ${TOOLCHAIN_DIR}/bin)
find_program(ARM_SIZE_TOOL arm-none-eabi-size ${TOOLCHAIN_DIR}/bin)
CMAKE_FORCE_C_COMPILER(${ARM_CC} GNU)
CMAKE_FORCE_CXX_COMPILER(${ARM_CXX} GNU)
set(CMAKE_ARM_FLAGS
"-mcpu=cortex-m3 -mthumb -fno-common -ffunction-sections -fdata-sections"
)
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "cortex-m3")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_ARM_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_ARM_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${CMAKE_ARM_FLAGS}")
else ()
message(WARNING
"Processor not recognised in toolchain file, "
"compiler flags not configured."
)
endif ()
# fix long strings (CMake appends semicolons)
string(REGEX REPLACE ";" " " CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}" CACHE STRING "")
set(BUILD_SHARED_LIBS OFF)
然后,在代碼根目錄下創(chuàng)建 CMakeLists.txt ,下面只是個示例(寫得也不夠好)。具體寫法根據(jù)項目調(diào)整。注意最后一段,需要生成出 .elf 和 .bin 文件。
CMakeLists.txt
cmake_minimum_required(VERSION 3.3)
project(testprj)
enable_language(ASM)
set(BUILD_MODE DEBUG)
set(USER_C_FLAGS "-mcpu=cortex-m3 -mthumb -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -ffreestanding -fno-move-loop-invariants -Wall")set(USER_CXX_FLAGS "-std=c++11 -fabi-version=0 -fno-exceptions -fno-rtti -fno-use-cxa-atexit -fno-threadsafe-statics")set(USER_C_DEFINES
"-DARM_MATH_CM3 \
-DUSE_FULL_ASSERT \
-DTRACE \
-DOS_USE_TRACE_SEMIHOSTING_STDOUT \
-DSTM32F10X_HD \
-DUSE_STDPERIPH_DRIVER \
-DHSE_VALUE=8000000"
)
if(BUILD_MODE STREQUAL "DEBUG")
set(USER_C_FLAGS "${USER_C_FLAGS} -Og -g3")
set(USER_C_DEFINES "${USER_C_DEFINES} -DDEBUG")else()
set(USER_C_FLAGS "${USER_C_FLAGS} -O3")
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${USER_C_FLAGS} ${USER_C_DEFINES}")set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${USER_C_FLAGS} ${USER_CXX_FLAGS} ${USER_C_DEFINES}")
include_directories(
${TOOLCHAIN_DIR}/arm-none-eabi/include
include
system/include
)
file(GLOB_RECURSE SOURCE_FILES
system/*.c system/*.cpp
user/*.c user/*.cpp
)
set(CMAKE_EXE_LINKER_FLAGS
"${CMAKE_EXE_LINKER_FLAGS} -L\"${PROJECT_SOURCE_DIR}/ldscripts\" -T libs.ld -T mem.ld -T sections.ld -fmessage-length=0 -fsigned-char -ffreestanding -nostartfiles -specs=nano.specs -Xlinker --gc-sections -Wl,-Map=${PROJECT_NAME}.map")
add_executable(${PROJECT_NAME}.elf ${SOURCE_FILES})
set(HEX_FILE ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.hex)set(BIN_FILE ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bin)
add_custom_command(TARGET ${PROJECT_NAME}.elf POST_BUILD
COMMAND ${CMAKE_OBJCOPY} -Oihex $<target_file:${project_name}.elf>${HEX_FILE}
COMMAND ${CMAKE_OBJCOPY} -Obinary $<target_file:${project_name}.elf>${BIN_FILE}
)
CLion編譯設(shè)置
在CLion中打開 testprj 目錄,成為一個CLion項目。
打開 Preferences > Build, Execution, Deployment > CMake ,右側(cè) CMake options 填入
-DTOOLCHAIN_DIR=-DCMAKE_TOOLCHAIN_FILE=
,其中兩個尖括號內(nèi)的內(nèi)容換成實際的路徑。
Generation path選項保留默認(rèn)的 cmake-build-debug ,如果修改了,后續(xù)有的步驟也需要對應(yīng)修改。
點 OK 后,再選擇菜單 Tools > CMake > Reset Cache and Reload Project ,CMake窗口會有信息輸出。如果看到 Generating done 的提示就可以了。做這一步是因為修改了 CMake options ,需要重新生成CMake cache。新版本CMake會有兩處warning,忽略。點擊CMake窗口工具欄按鈕 Open CMakeCache File ,查看 ARM_CC 等路徑是否已經(jīng)設(shè)置正確。
Build一下試試。如果有編譯錯誤,修復(fù)之,直到正常編譯出 .elf 和 .bin 文件。
調(diào)試設(shè)置
首先建立一個公共的配置文件 writeflash.expect.sh ,這是供 expect 命令使用的,功能是telnet連上OpenOCD,交互式輸入燒寫、halt等命令。內(nèi)容如下。
?。?/span>Mac系統(tǒng)自帶了expect命令。Windows系統(tǒng)應(yīng)該可以裝ActiveState提供的 Expect for Windows ,沒試過。)
writeflash.expect.sh
#!/usr/bin/expect
set timeout 10
spawn telnet localhost 4444expect ">"
send "reset init\n"expect ">"
send "flash write_image erase [lindex $argv 0] 0x8000000\n"expect ">"
send "reset halt\n"expect ">"
send "arm semihosting enable\n"expect ">"
send "exit\n"expect off
然后,到CLion添加自定義工具, Preferences > Tools > External Tools ,點“+”號。
第一個工具是OpenOCD, Program 和 Parameters 中分別填入 openocd 的路徑和命令行參數(shù),與用其他工具調(diào)試時保持一致。勾上“Show console when a message is printed…”。
第二個工具命名為“Write Flash”, Program 選擇 expect 命令, Parameters 寫
$ProjectFileDir$/cmake-build-debug/$ProjectName$.bin
,其中尖括號內(nèi)的內(nèi)容換成文件實際路徑。
然后添加調(diào)試配置項。CLion菜單 Run > Edit Configurations ,點左上角“+”號,添加 GDB Remote Debug 項。修改名稱為 testprj remote debug 。
· GDB 選擇 gcc-arm-none-eabi 目錄下的 bin/arm-none-eabi-gdb 。(請注意這個選項是CLion 2016.3才有的,如果是稍早些的版本,要在 Preference > Toolchain > Debugger 里改全局debugger設(shè)置。)
· target remote args 填 localhost:3333 。
· Symbol file 選擇編譯生成的 cmake-build-debug/testprj.elf 。
· Before launch 框點“+”號,添加 Run Another Configuration ,選擇 Build All ,這樣在啟動調(diào)試前會先編譯項目。(也可以選擇 testprj.elf ,但要保證 Executable 下拉框選中Not selected )。
· 再次點“+”號,選擇 Run External Tool ,選擇剛才添加的 Write Flash 工具。
調(diào)試
首先手工啟動OpenOCD,這個步驟只需要一次,后續(xù)調(diào)試時不需要。選擇菜單 Tools > External Tools > OpenOCD ,OpenOCD會運(yùn)行在Run窗口中,并順利連上設(shè)備。建議在Run窗口上點右鍵,選擇 Split Mode ,這樣調(diào)試的時候可以同時看到兩個工具窗口的信息。
激動人心的調(diào)試時刻到了!在代碼里下個斷點,選擇菜單 Run > Debug… > testprj remote debug ,CLion會首先編譯項目,從OpenOCD的輸出信息可以看到燒寫完畢,debugger連上來了。Debug窗口已經(jīng)打開,代碼窗口停在了斷點處!
不便之處
由于IDE尚不完善,前面這些配置方法有些屬于hack,不是IDE原生支持的。所以在調(diào)試過程中有些不便之處,相信CLion會持續(xù)改進(jìn)的。
· 目前CLion調(diào)試不能查看內(nèi)存、寄存器,不能查看匯編。我是基本不需要的,有些人的工作也許要經(jīng)常用到。
· 調(diào)試時,編譯失敗無法停止,還會繼續(xù)debug。
· 調(diào)試一次之后,由于當(dāng)前configuration修改了,無法“只Build”不調(diào)試。需要手工修改configuration為Build All或testprj.elf,才能使用Build功能。(記住 Run > Edit Configurations 的快捷鍵會方便很多。)
來源:王曉磊的Blog