簡介
實現了在CLion中編譯、燒錄、調試stm32。
主要參考了 在Mac下使用CLion做嵌入式開發和CLion for embedded development。
步驟簡要是:建立Eclipse項目并轉為CLion項目->在CLion中編譯->在CLion中調用OpenOCD和Tcl expect腳本直接燒寫程序->CLion gdb窗口中調試。
步驟操作比較多,也可直接安裝配置環境后直接用我github上的項目pro_tmpl_stm32f10x,基于stm32f10x的,若其它芯片則可用Eclipse生成固件庫然后代替項目中system文件夾并修改CMakelist編譯參數就行。
若在linux或mac系統下用Clion,配置應該差不多,就expect腳本里聲音提示函數部分要修改一下。
以下所有代碼塊推薦在github我的項目里拷貝,因為github里代碼可能有所更新
PS:本人是剛入門STM32的菜雞,由于之前寫java和php、c++等用了Jetbrains系列的IDE后在其它方面用其它開發工具感覺體驗差不少,于是打什么代碼都想用Jetbrains系列的IDEs,比如去年的匯編就用CLion打(有時間另開篇寫設置匯編代碼高亮和代碼提示等的相關方法)。由上述所以剛學STM32就不用MDK不用Keil,直接用CLion和Eclipse開搞了。
先放張CLion里寫stm32的工作圖:
更新進展
- 2017年9月24日 21:38:08 修改一些描述,不影響步驟。
- 2017年9月14日 11:20:52 教程基本完工。更新了github上的代碼。更新參考資料鏈接。
- 2017年9月13日 22:57:09 expect腳本基本完工,繼續更新本文。將更新github上本項目代碼。明天再寫。
2017年9月13日 22:01:56 實現各種常見下載時問題的報錯紅字提示。
2017年9月12日 20:24:06 實現下載過程error、warn、Succeed三種聲音提示。優化代碼快完成。
2017年9月11日 實現一鍵run/debug!,美滋滋。。run配置完成。速度 rebuild 2s~3s,少量改動再build則 5s,expect腳本約2s 496ms,總5s左右。 - 2017年9月10日 11:29:27更正文章部分描述錯誤。繼續寫CLion調試最后的步驟
- 2017年9月9日 22:27:23 寫到三種調試方式,優化在CLion中的OpenOCD和WriteFlash配置。
- 2017年9月9日 11:25:05 實現在CLion中調用OpenOCD和ActiveTcl 的expect進行下載調試,速度20s左右比較慢(【更正】:其實燒錄下載不慢,是timeout設置過長拖了10s才打開gdb)。繼續寫本教程。
- 2017年9月8日 19:53:22 教程寫到創建eclipse項目。。
- 2017年9月1日 成功在CLion中編譯STM32項目,未實現在CLion中直接調用OpenOCD進行調試。
環境
截止至2017年9月7日 22:21:01,本文所使用的軟硬件工具如下,都是盡量最新版:
- 電腦系統:win7x64
- IDE:
- CLion 2017.xxx
- Eclipse Oxygen
- toolchain / 編譯器:GNU GCC for ARM (gcc-arm-none-eabi)
- Debugger:OpenOCD-20170821、Ozone
- MinGW:裝在C:\MinGW
—————— - MCU:STM32F103RBT6,Alientek Mini v2.0開發板
- 仿真器:J-Link-ARM v8
基本環境搭建和配置
安裝CLion
下載地址https://www.jetbrains.com/clion/。最新版本CLion 2017.2.2 但該版本有一bug:打字時不能顯示中文輸入法的輸入界面(當然打五筆的盲輸不慌,哈),只裝了QQ五筆其他中文輸入法沒試過應該也顯示不了。不習慣的可以用1.2.x版本就能打中文,但新版新功能多一些。
推薦的基本配置方法見我之前文章: JetBrains公司系列IDE開發工具通用初始配置推薦(Android Studio、InterlliJ、PHPStorm、CLion、Webstorm等)
安裝 MinGW:
官網是http://www.mingw.org/,下載鏈接
裝在C:\MinGW,典型安裝就行。后面編譯時少了mingw中的啥工具就再補。
安裝 GNU Tools for ARM Embedded Processors
這是由 ARM 維護的一套工具,包括 GCC 編譯器,GDB 調試工具等,在此下載和安裝GNU Tools for ARM Embedded Processors
我是下載了最新的gcc-arm-none-eabi-5_4-2016q3-20160926-win32.exe,我電腦64位的,可以用。
安裝 Eclipse 及插件CDT GNU Cross Development Tools
安裝 Eclipse OXYGEN ,
安裝插件:下載插件離線包 https://sourceforge.net/projects/gnuarmeclipse/files/Current%20Releases/3.x/ ,最新為 ilg.gnuarmeclipse.repository-3.4.1-201704251808.zip
然后單擊菜單欄中的 Help -> Install New Software… ,單擊Add...,Archive...,添加剛下載的zip文件,確定后在列表中勾選插件名,從中安裝 CDT GNU Cross Development Tools.
//TODO
后面的調試部分有兩個方案,各需下載不同工具,下載地址和配置見后面各方案。
創建項目
注:看了CLion for embedded development里用到STM32CubeMx(使用詳解)STM32CubeMx是STM32系列單片機初始化代碼項目生成工具,支持的IDE有如下圖
其中SW4STM32是基于Eclipse的IDE,不建議用它,而用Eclipse OXYGEN原版+GNU 插件,因為相對于專版的SW4STM32,Eclipse里可以寫更多東西,加其他通用插件。
扯回來,說到STM32CubeMx,試了下在CLion里編譯老報各種錯,所以先不用它了,它的好處是設計獨立單板PCB時對引腳等繁瑣配置的簡化操作,修改更新配置也方便,但對于用開發板的就先用不著它,因為開發板一般都有配套相關模塊配置代碼庫。等到設計單板時再用它生成配置代碼(選用上圖的Other Toolchains,代碼最簡,應該可配合在同一CLion項目目錄下使用)。
1.創建Eclipse項目
Step1 File->new C Project,如下紅框,強烈建議路徑命名C:\Code\Embedded\STM32\pro,原因參見我的文章代碼的文件和路徑命名規范和目錄結構規劃,
因為在Eclipse建立一個基于Stm32f10x的project以后的項目都可以直接復制這個項目文件夾修改成新名稱的項目,所以我直接建個pro_tmpl_stm32f10x項目當模板,如下圖名稱和路徑。
Step2 接下來如圖,我開發板芯片是stm32f103rbt,對應Chip family是Medium Density。Content選擇Empty吧,blink a led示例代碼沒啥好參考。對于Trace output應該是初始給你生成trace_puts、trace_printf函數之類代碼的c文件,好在Debug時電腦調試框能返回類似printf的打印信息。其他默認,我不認識。
如果不知道Chip family選哪個可以打開STM32CubeMx,新建項目,找到自己芯片后看其英文介紹第一句就提示了其封裝密度類型:
Step3 一路Next,到這選如圖配置,點Finish
2.轉為CLion項目,在CLion中編譯
CLion中打開文件夾C:\Code\Embedded\STM32\pro_tmpl\pro_tmpl_stm32f10x
,
根目錄下新建CMakeLists.txt,代碼內容如下(自行修正路徑)
project(pro_tmpl_stm32f10x)
cmake_minimum_required(VERSION 3.3)
enable_language(ASM)
set(BUILD_MODE DEBUG)
#在Clion中Settings>Build>Cmake的Cmake options加入下面一行(自行修正路徑),不包含#符號。
#-DTOOLCHAIN_DIR="C:\Program Files (x86)\GNU Tools ARM Embedded\6 2017-q2-update" -DCMAKE_TOOLCHAIN_FILE="toolchain-arm-eabi-gcc.cmake"
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")
#下面可選 -DSTM32F10X_MD -DSTM32F10X_HD等。一般選高點的_HD也不影響運行。
set(USER_C_DEFINES
"-DARM_MATH_CM3 \
-DUSE_FULL_ASSERT \
-DTRACE \
-DOS_USE_TRACE_SEMIHOSTING_STDOUT \
-DSTM32F10X_MD \
-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
${TOOLCHAIN_DIR}/arm-none-eabi/include/c++/6.3.1
${TOOLCHAIN_DIR}/arm-none-eabi/include/c++/6.3.1/arm-none-eabi/thumb/v7-m
${TOOLCHAIN_DIR}/arm-none-eabi/include/c++/6.3.1/backward
${TOOLCHAIN_DIR}/lib/gcc/arm-none-eabi/6.3.1/include
${TOOLCHAIN_DIR}/lib/gcc/arm-none-eabi/6.3.1/include-fixed
)
include_directories(
#STM32固件庫
system/include
system/include/cmsis
system/include/stm32f1-stdperiph
#ALIENTEK開發板的代碼庫
board/ALIENTEK/System/delay
board/ALIENTEK/System/sys
board/ALIENTEK/System/usart
#ALIENTEK開發板的模塊代碼庫
board/ALIENTEK/MiniSTM32Board/LED
board/ALIENTEK/MiniSTM32Board/KEY
#自己的include
user/include
)
file(GLOB_RECURSE C_FILES
#stm32固件庫
system/*.c system/*.cpp
#ALIENTEK開發板的代碼庫
board/*.c board/*.cpp
#自己的
user/*.c user/*.cpp
)
set(SOURCE_FILES ${C_FILES})
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}
)
項目根目錄下新建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)
# specify the cross compiler指定交叉編譯器
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)
在Clion菜單中File>Settings>Build....>Cmake的Cmake options加入下面一行(自行修正路徑)
-DTOOLCHAIN_DIR="C:\Program Files (x86)\GNU Tools ARM Embedded\6 2017-q2-update" -DCMAKE_TOOLCHAIN_FILE="toolchain-arm-eabi-gcc.cmake"
點擊Tools>CMake>ResetCache and Reload Project,當你每次編譯報錯時,可以第一步用這命令重清下緩存再試。
打開View>ToolWindows>Cmake窗口,點擊Tools>CMake>Reload CMake Project,一般會報一些warning,最后會提示
-- Configuring done
-- Generating done
CMake Warning:
Manually-specified variables were not used by the project:
CMAKE_TOOLCHAIN_FILE
-- Build files have been written to: C:/Code/Embedded/STM32/pro_tmpl/pro_tmpl_stm32f10x/cmake-build-debug
再Run>Build,第一次編譯整個目錄會比較久,后面編譯就自動只編譯修改過的文件。buil成功最后會提示:
[100%] Built target pro_tmpl_stm32f10x.elf
然后查看cmake-build-debug下有.bin和.hex生成,就算build成功了。
我把根目錄的include和src放新建的user文件夾了,以和system、board對立區分。放張目錄圖:
根目錄中board放的是開發板的庫,如上圖有ALIENTEK的Mini開發板提供的常用庫System和模塊或驅動:KEY、LED。至于user、board標藍色,system標黃色,用右鍵就可以標。
注:不建議把Stm32核心庫system文件夾和開發板模塊文件等放在項目外部鏈接的方式。原因是1.我試過多種方法編譯stm32庫為靜態庫然后在項目里鏈接編譯老是報各種錯。2.system里放的是.c源代碼不是預編譯好的.s匯編文件或.a靜態庫文件,比較利于學習底層代碼,代碼調試時也容易定位深層代碼查找bug原因。3.代碼項目總量就10幾20MB,不算大。
燒寫和調試
有兩種方案,
方案一是Eclipse或Segger Ozone(舊名J-LinkDebugger)中調試,
方案二是CLion中調用OpenOCD和Tcl expect腳本直接燒寫后啟動內部gdb窗口進行調試,
但由于CLion IDE尚不完善,gdb不是IDE原生支持的,所以在調試過程中有些不便之處,比如調試不能查看內存、寄存器,不能查看匯編,但一般不需要用到。//TODO不知道新版本CLion有改進沒,我再看看。
由于兩種方案在win下所用的J-Link仿真器驅動不同,所以
方案一和二同時只能二選一!!。(//TODO我再找找有沒其他兼容的方法。)
方案一中Eclipse調試方法:
單擊菜單欄中的 Run -> Debug Configurations… 在左側的 GDB SEGGER J-Link Debugging下的xxxDebug右鍵Duplicate復制一個新的配置,修改如圖紅框:
然后Debug既可。
方案一中Segger Ozone調試方法:
Ozone>File -> New -> New Project Wizard…
新建Ozone項目后File -> Save Project As…, 選擇CLion項目根目錄,保存名為 run.jdebug 。
在CLion>File>Settings>Tools>External Tools,右側新建Tool,配置如圖
以后在CLion中可以Tools>External Tools>Ozone Debugger,就能打開Ozone手動調試。
方案二:CLion中直接燒寫調試
安裝Jlink仿真器驅動程序
上面提到,這個驅動比較特別,與默認自動裝的jlink驅動似乎有些不同,只能搭配OpenOCD使用,對Eclipse Ozone 會報錯。
登陸網站http://zadig.akeo.ie/下載zadig軟件,下載速度太慢,可另點通用USB驅動Zadig 2.3.701 綠色版
引用openOCD的使用1: 應用openOCD和Jlink仿真器連接Freescale K60系列 MCU文章所說:
說的直白一些openOCD只把Jlink仿真器當作普通的USB設備來使用,不使用Jlink自帶的仿真器驅動程序,如果已安裝了Jlink仿真器驅動程序,這個過程就是把原先的驅動程序換掉。
直接引用文章步驟:
- 插入Jlink仿真器(如果是第一次插入Jlink仿真器,系統會要求安裝驅動程序,我們可以點擊取消,不必理會),運行zadig軟件。
- 選擇Options-List All Devices。
- 在其下拉列表中選擇J-Link。
- 選擇WinUSB驅動程序,點擊Reinstall Driver按鈕或Replace Driver按鈕,這樣便完成了驅動程序的替換。
如下圖:
安裝配置OpenOCD
打開http://gnutoolchains.com/arm-eabi/openocd/下載安裝。這里用最新版本OpenOCD 0.10.0 [2017-08-21][OpenOCD-20170821.7z]。安裝路徑推薦放C:\Code\Embedded\env\OpenOCD\
在CLion>File>Settings>Tools>External Tools,右側新建Tool,配置如圖
其中
-f interface/jlink.cfg -f target/stm32f1x.cfg
,jlink.cfg表示使用jlink仿真器,stm32f1x.cfg表示下載到的系列MCU。在OpenOCD\OpenOCD-20170821\share\openocd\scripts
目錄可以找到各類cfg配置文件。注:如果使用LCD等模塊其引腳與jtag接口沖突,則需要在
C:\Code\Common\Environment\OpenOCD\OpenOCD-20170821\share\openocd\scripts\interface\jlink.cfg
腳本中的interface jlink
這行下方加上一行:
#transport select jtag
transport select swd
就把jlink的默認jtag模式改為swd模式,只占用2個IO引腳。
運行Run>Extral tools>OpenOCD,提示如下則成功連接了仿真器:
........
Info : No device selected, using first device.
Info : J-Link ARM V8 compiled Nov 28 2014 13:44:46
Info : Hardware version: 8.00
Info : VTarget = 3.319 V
Info : clock speed 1000 kHz
Info : JTAG tap: stm32f1x.cpu tap/device found: 0x3ba00477 (mfg: 0x23b (ARM Ltd.), part: 0xba00, ver: 0x3)
Info : JTAG tap: stm32f1x.bs tap/device found: 0x16410041 (mfg: 0x020 (STMicroelectronics), part: 0x6410, ver: 0x1)
Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints
如提示如下連接失敗,則關閉gdb等正在運行的項,再次運行OpenOcd。或Ctrl+Shift+Esc,關閉openocd進程。再不行,則win任務欄右下角那點彈出J-link, 重插USB線再試:
Open On-Chip Debugger 0.10.0 (2017-08-21) [https://github.com/sysprogs/openocd]
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "jtag". To override use 'transport select <transport>'.
adapter speed: 1000 kHz
adapter_nsrst_delay: 100
jtag_ntrst_delay: 100
none separate
cortex_m reset_config sysresetreq
Warn : Failed to open device: LIBUSB_ERROR_ACCESS.
Info : No device selected, using first device.
Error: No J-Link device found.
安裝ActiveTcl的expect并配置成CLion外部工具
Mac系統自帶expect命令,但在Windows系統要裝ActiveState提供的Expect for Windows(不推薦在此下載)
參考關于expect在Windows上的安裝和使用,可直接下載expect壓縮包(推薦),解壓到C:\Code\Embedded\env\Expect-5.21r1b1\
。
再在C:\Code\Embedded\env
目錄下建立一個公共的配置文件writeflash.expect.sh,這是供expect命令使用的,功能是telnet連上OpenOCD,交互式輸入燒寫、halt等命令。內容如下,比較長。(可在CLion中安裝sh插件以高亮sh代碼)
(更新-2017年9月13日 從十幾行改到150行,最好在github我的庫里拷貝會比較新:)
#!/usr/bin/expect
#author: @fightfa
#2017年9月13日 22:01:56 實現各種常見下載時問題的報錯紅字提示。
#2017年9月12日 20:24:06 聲音提示實現。優化代碼快完成。
#2017年9月11日 實現一鍵run/debug,run配置完成。速度 rebuild 2s~3s,少量改動build則 5s,本腳本2s 496ms,總5s左右
#必看說明:!!!
#教程詳見:http://www.lxweimin.com/p/f8a939ad7efd
#CLion中Run/Debug的romote debug和romote run互相切換時不能先自動關閉gbd窗口中原已運行的Tab,要手動關閉!# TODO 解決左邊說的。
#Run/debug Configuraton 中remote debug中必須手動指定Symbol file即elf文件路徑,emote run中則Symbol file置空。
#窗口輸出文字都用了英文寫。
set now [clock clicks]
set timeout 8
#TODO * 傳入elf路徑參數給CLion中的gdb remote 再使CLion開gdb窗口。(難實現。)
#TODO 優化參數傳入格式
#TODO 處理可能的因地址含有空格而須帶雙引號的參數處理的問題
#注:將傳入的地址中所有的左斜/替換為右斜\,由于在win中地址是\\左斜,linux mac中expect shell等只支持/右斜的地址,expectForWin也是
set num [regsub -all "\\\\" [lindex $argv 0] "\/" projectFileDir]
set num [regsub -all "\\\\" [lindex $argv 1] "\/" bin_path]
#action:執行動作:-Run或 -Debug
set action [string tolower [lindex $argv 2]]
set bin_path $projectFileDir/$bin_path
#必須是嚴格格式《空格{》《}空格》。。
# 保持 } elseif 才能不報錯。。
if {$action=="-debug"} {
send_user ">>Action is Debug\n"
} elseif {$action=="-run"} {
send_user ">>Action is Run\n"
} else {
set action "-debug"
send_user "\n>>warn: Unkown Action of \'$action\', now is default to Debug\nYou can set argv\[1\] \'-run\' or \'-debug\' \n"
}
global MessageBeep
set MessageBeep $projectFileDir/MessageBeep.exe
proc beepError {} {
global MessageBeep
exec $MessageBeep 16
}
proc beepSucceed {} {
global MessageBeep
exec $MessageBeep 64
}
#proc beepOk{} {
# global MessageBeep
## 0 這聲音其實更常用在warn上。。
# exec $MessageBeep 0
#}
#發出鐺一聲,提示build完成
#beepOk
#結束與openocd會話的函數:
proc exitSession {code} {
#結束會話,不結束會話可能會導致下次燒錄時send出錯吧#TODO 驗證左邊。
send "exit\n"
expect "Connection closed by foreign host"
set timeout 0
exit $code
}
#TODO 不能是send_error $expect(0,string) 原因未知
#TODO send_error 偶爾顯示不出來就exit了 原因未知
proc abort {str} {
beepError
send_error \n$str
global timeout
exitSession 1
}
proc abortTimeout {} {
global timeout
beepError
puts "\n"
send_error "\n>>error:timeout ${timeout}s! please check the above, or:\nyou may try extend the timeout value (or set -1 to be not timeout) and run again."
exitSession 2
}
proc abortCatch {str} {
beepError
send_error "\n>>error:process is interrupted because catch the follow from above :"
send_user \n$str
global timeout
exitSession 1
}
proc abortWithTips {} {
abort "failed! please check the above, or:\n1.Power on your board;\n2.Rerun the tool OpenOCD\n3.Modify or comment this line in expect.writeflash.sh and rerun"
}
#步驟1:登錄建立與會話
spawn telnet localhost 4444
#不能如下行這樣寫,會卡上3、4秒。。。
#expect { ">" {}
expect {
-re "\ntelnet: Unable to connect to remote host.*\n>" {
abortWithTips
}
">" {}
timeout {abortTimeout}
}
#步驟2:復位板子
send "reset init\n"
#send "reset\n"
#send "init\n"
#send "halt\n"
#timeout {abortTimeout} 這句放前面會慢4s。。。
#不能用^或\A匹配開頭。。
expect {
-nocase -re "\n(error|failed).*\n>" {
abortWithTips
}
">" {}
timeout {abortTimeout}
}
#步驟3:燒錄bin文件到板子
send "flash write_image erase $bin_path 0x8000000\n"
expect {
-re "\nwrote(.*)>" {
#發出鐺一聲,提示燒寫完成
beepSucceed
}
-re "\n(error|failed|couldn't open.*)>" {
abortCatch $expect_out(1,string)
}
timeout {abortTimeout}
}
#TODO 上一指令和下面幾個指令偶爾運行會出現多一個 >字符。導致顯示信息有時重復和亂了行順序。。影響不大。
#步驟4:燒錄完發下面指令給板子
if {$action=="-debug"} {
#下載完斷點調試:
send "halt\n"
expect ">"
#打開調試通信模式,可以CLion下方的OpenOCD窗口中看到接收trace_puts等的返回信息,這里必須加。
send "arm semihosting enable\n"
expect ">"
} elseif {$action=="-run"} {
#下載完直接運行,其中edit configurations->remote run 中 Symbol file必須保持為空
#這兩行可不加:
# send "reset\n"
# expect ">"
#打開調試通信模式,可以在CLion下方的OpenOCD窗口中看到接收trace_puts等的返回信息,若不用到則可關。
send "arm semihosting enable\n"
# send "arm semihosting disable\n"
expect ">"
}
#步驟5:計算本腳本運行時間,不包括Build時間 #TODO * 計入Build時間的方法
set time [expr [clock clicks]-$now]
set seconds [expr $time/1000]
set millis [expr $time%1000]
set now [clock format [clock seconds] -format {%H:%M:%S}]
#send_user "\n>>w 燒寫成功!耗時 ${seconds}s ${millis}ms\n"
send_user "\n>>$now Write Flash finished in ${seconds}s ${millis}ms\n"
#expect off
exitSession 0
其中發出聲音提示是調用了Windows的API MessageBeep,由于MessageBeep函數參數不帶句柄回調,所以直接在expect中寫
exec "C:/windows/system32/cmd.exe" "/c rundll32 user32 MessageBeep "
(最后有個空格)或在cmd中打rundll32 user32 MessageBeep
是可以直接發聲,但發聲隨機,不能指定。所以我開CLion寫了個簡單的MessageBeep.exe程序,如下
#include <windows.h>
/*expect腳本中使用方法示例:
exec "MessageBeep.exe" 48
常見聲音提示:
#define MB_OK 0
#define MB_ICONWARNING 48
// ASTERISK :星號
#define MB_ICONASTERISK 64
#define MB_ICONERROR 16
*/
int main(int argc, char *argv[]) {
UINT beep = MB_ICONERROR;
if (argc > 1) beep = (UINT) atoi(argv[1]);
MessageBeep(beep);
return 0;
}
//expect腳本中直接運行:(只能發一種不確定聲音)
// exec "C:/windows/system32/cmd.exe" "/c rundll32 user32.dll MessageBeep "
// 只能發一種不確定聲音:
// system("rundll32 user32.dll,MessageBeep ");
編譯成MessageBeep.exe后復制到stm32項目根目錄下即可供expect腳本調用。也可直接在我github項目上復制。
個人啰嗦PS:
1.為了寫set num [regsub -all "\\\\" $bin_path0 "\/" bin_path]
這一行實現替換字符串效果,我可瞎找了一個晚上試了近一百行代碼啊。。最終在這找到解決思路:Tcl 編程簡介。ActiveTcl expect和其他系統的expect在語法上還是有不少不同的。。。。
2.為實現expect中發出聲音提示,嘗試了不少思路,如運行bat、vbs、腳本等。
然后在CLion中File>Settings>Tools>External Tools,右側新建Tool,命名為Write Flash & Run
,配置如圖:
Parameters為
$ProjectFileDir$\expect.writeflash.sh $ProjectFileDir$ cmake-build-debug\$ProjectName$.bin -run
,路徑使用了CLion的$xx$參數(注意$xx$參數只有在Build運行后才能使用),比較通用,避免建了新項目又得改這里。
路徑中盡量用右斜,windows里的路徑\左斜默認表示法真是坑人啊。
再選中Write Flash & Run,點擊右上方復制圖標,生成新tool,命名為
Write Flash & Debug
,配置同上,但Parameters為$ProjectFileDir$\expect.writeflash.sh $ProjectFileDir$ cmake-build-debug\$ProjectName$.bin -debug
接下來是Run>Edit Configurations...中,添加gdb remote配置:
其中Symbol file必須置空,這樣執行Run可燒錄完直接讓板子運行而忽略所有代碼斷點。記得勾選single instance only。下方添加的工具是點擊綠色+號出現的前兩項。
再復制remote run為remote debug,修改如下部分:
其中Symbol file是你生成的elf文件路徑:
C:/Code/Embedded/STM32/pro_tmpl/pro_tmpl_stm32f10x/cmake-build-debug/pro_tmpl_stm32f10x.elf
好了,現在你打開OpenOCD tool,(打開一次且鏈接上仿真器了就行)
再運行 romote run:
如下,編譯燒錄成功,板子運行了!
OpenOCD顯示了板子傳回的“hello stm”的信息:
我在main.c中寫的demo是:
int
main(int argc, char *argv[]) {
int i = 0;
delay_init(); //延時函數初始化
LED_Init(); //初始化與LED連接的硬件接口
//CLion調試時在OpenOCD窗口console中有返回信息:
trace_puts("hello stm32");
for (int i = 0; i < 5; i++) {
LED0 = 0;
LED1 = 0;
delay_ms(300); //延時300ms
LED0 = 1;
LED1 = 1;
delay_ms(300); //延時300ms
}
while (1) {
i = ++i % 6;
LED0 = 1;
LED1 = 1;
delay_ms(300); //延時300ms
LED0 = 1;
LED1 = 0;
delay_ms(300); //延時300ms
if (i == 3) {
LED0 = 0;
delay_ms(300); //延時300ms
}
}
}
切換到romote debug前,先將gdb的romote run進程結束掉,再選擇remote debug,運行:
可以進行調試了,斷點、跟蹤變化等。但試了中途改變量值繼續運行會沒變化,可能clion gdb工具里還沒實現直接修改寄存器的功能,也未有查看變量地址等功能。應該CLion以后版本會支持吧。
OpenOCD,remote run/debug等都可設置快捷鍵。推薦Shift+Alt+D打開openocd,Shift+Alt+X編譯燒錄,Alt+2為Cmake窗,Alt+3為Build窗,等等。
最后一步是把項目pro_tmpl_stm32f10x上傳到github倉庫了,點這里。
好了,現在終于可以愉快的敲C代碼了!
相關資料
//TODO
參考文章
使用CLion做嵌入式開發
CLion for embedded development
使用 Eclipse 和 ARM GCC 搭建 STM32 開發環境
OpenOCD User’s Guide
gdb + openocd 調試嵌入式軟件
expect腳本相關:
Tcl 編程簡介
expect學習筆記及實例詳解
//TODO