windows下使用CLion+OpenOCD+expect做嵌入式開發(在CLion中編譯、燒錄、調試stm32)

簡介

實現了在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項目當模板,如下圖名稱和路徑。

pro_tmpl_stm32f10x

Step2 接下來如圖,我開發板芯片是stm32f103rbt,對應Chip family是Medium Density。Content選擇Empty吧,blink a led示例代碼沒啥好參考。對于Trace output應該是初始給你生成trace_puts、trace_printf函數之類代碼的c文件,好在Debug時電腦調試框能返回類似printf的打印信息。其他默認,我不認識。

Paste_Image.png

如果不知道Chip family選哪個可以打開STM32CubeMx,新建項目,找到自己芯片后看其英文介紹第一句就提示了其封裝密度類型:

查找密度類型

Step3 一路Next,到這選如圖配置,點Finish

step3

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按鈕,這樣便完成了驅動程序的替換。

如下圖:


Zadig
安裝配置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,配置如圖:

writeflash tool

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容