qtcreator + gcc +openocd 编译调试stm32
准备的软件:
1.Qtcreator
2. gcc-arm-none-eabi (新版的可能有bug )
3. openocd
4. python-2.7(用于qtceator 的gdb python版本调试器 用新版本)
安装好上面的软件
配置好openocd python的环境变量
打开qtceator , 菜单->帮助->关于插件 找到Bare Metal 插件 勾先该插件
关闭后 重新启动qtcreator
菜单->工具->选项 找到 设备选项 在Bare Metal页上 add ->OpenOCD 添加一个类型
名字可根据Stm32 cpu类型和下载器名命
Startup mode 选择为 TCP/IP mode
Host 填localhost 端口为3333
Executable file 填安装的openocd程序文件路径
Addition argument 填 -f interface/cmsis-dap.cfg -f target/stm32f4x.cfg
根据下载器及实际的Stm32 芯片填写 这些配置文件是在openocd安装的位置
Init command 填
set remote hardware-breakpoint-limit 6
set remote hardware-watchpoint-limit 4
monitor reset halt
monitor report_flash_progress on
monitor program "%{CurrentRun:Executable:FilePath}" reset
monitor poll on
monitor reset halt
b main
%{CurrentRun:Executable:FilePath}就当前qtcreator的变量 表示要下载的文件路径 在项目/运行/ 中填写的被调试的elf文件
b main 表示在main函数上下一个断点
Reset command 填
monitor reset halt
b main
点击Apply应用设置
然后到 设备页 新建一个 Bare Matel设备 选择刚刚设置好的openocd类型
点击apply 配置 Bare Metal设置完成
在编译器选项 新建 c 和 c++编译器 选择安装好的arm gcc 和 g++
点击Apply
转到debugger 页 新增一个调试器 选 arm-gdb-py python版本
点Apply
根据上面的设置 在构建套件页 增加一个套件
点Ok
Qtcreator 设置完毕
然后新建一个工程 选Non-Qt Project Plain C Application模版
项目的路径不要有空格和中文
构建位置填为 .\Qbs
Kits选建好的Stm32构建套件
Main.c文件 main()改成没有参数输入的
双击编辑 *.qbs文件
import qbs
import qbs.Environment
import qbs.FileInfo
import qbs.Probes
import qbs.TextFile
import qbs.Process //导入的一样qbs提供的库
//import qbs.ModUtils
//import qbs.Utilities
//import qbs.WindowsUtils
Project {
minimumQbsVersion: "1.7.1"
Product {
name:project.name
// type: 类型是指这个Product 最终需要生成的Artifact 的Filetags
//根据这里的设置来一步步向上查找顶Rule 再反向生成需要的文件
//如application= elf文件 - obj – c c++ asm
type: [
"flash",
"obj_asm_out",
// "application",
] // the final resualt Artifact filetags this produt want
Depends { name: "cpp" } //refer "cpp" Module 这里指引用Qbs
//的内建Module 常用”cpp”用于 c c++项目的编译
property string openocdDir:""
property string openocdInterfacePath:"\""+sourceDirectory+"/cmsis-dap-swd.cfg"+"\"" //"./cmsis-dap-swd.cfg" "interface/cmsis-dap.cfg" "interface/stlink.cfg"
// openocdInterfacePath 用于指定openocd接口配置 文件内容见下文所示
property string openocdCpuCfgPath:"target/stm32f4x.cfg" //"target/stm32f1x.cfg" 指定stm32 cpu配置文件
property string openocdEraseCpuSector:"" //"-c \"flash erase_sector 0 0 11\"" // flash erase_sector bank_num first last
//设置是否下载前擦除
property string toolChainPath:FileInfo.path(cpp.compilerPath)
property string elfSizeExePath:toolChainPath+"/arm-none-eabi-size.exe"
property string objCopyExePath:toolChainPath+"/arm-none-eabi-objcopy.exe"
property string objDumpExePath:toolChainPath+"/arm-none-eabi-objdump.exe"
//这里获取配置好的gnu-arm工具链路径
files: [
"driver/led.c",
"driver/stm32f4xx_it.c",
"driver/system_stm32f4xx.c",
"fwlib/inc/stm32f4xx_conf.h",
"fwlib/inc/stm32f4xx_gpio.h",
"fwlib/inc/stm32f4xx_rcc.h",
"fwlib/src/misc.c",
"fwlib/src/stm32f4xx_gpio.c",
"fwlib/src/stm32f4xx_rcc.c",
"include/led.h",
"include/main.h",
"include/stm32f4xx.h",
"include/stm32f4xx_conf.h",
"include/stm32f4xx_it.h",
"include/system_stm32f4xx.h",
"main/main.c",
]
// files:是指项目引入的源文件
Group { // Properties for the produced executable
fileTagsFilter: ["obj","objc","application","flash","obj_asm_out"] //,"application","obj","objc","asm","asm_cpp"
qbs.install: true
// qbs.installDir: "bin"
}
//Group 指定一些文件 相同的属性 如上是指定的fileTager文件的
//qbs.install属性设置为true 即这些文件安装到install-root
cpp.cLanguageVersion:"c11" //c语言指定的版本
cpp.includePaths:
[
"./CMSIS/Include",
"./CMSIS/Device/ST/STM32F4xx/Include",
"./fwlib/inc",
"./include",
// toolChainPath+"/../arm-none-eabi/include",
// toolChainPath+"/../arm-none-eabi/include/c++/8.2.1",
// toolChainPath+"/../arm-none-eabi/include/c++/8.2.1/tr1",
// toolChainPath+"/../arm-none-eabi/include/c++/8.2.1/arm-none-eabi",
// toolChainPath+"/../lib/gcc/arm-none-eabi/8.2.1/include"
]
//编译和编辑中用到的头文件引用路径
// cpp.compilerIncludePaths:base.concat(cpp.includePaths)
cpp.defines:["STM32F40_41xxx","STM32F407xx=1","_RTE_=1","__GCC=1","USE_STDPERIPH_DRIVER"]
//设置一些项目中用到的编译器预定义宏
Group {
files: ["./gcc/startup_stm32f40_41xxx.s"]
fileTags: "asm"
cpp.assemblerFlags:[
"-mcpu=cortex-m4","-mthumb","-gdwarf-2","-mthumb-interwork",
"-g",//"-Wall",
]
}
//设置汇编文件 gnu-arm-as编译器的编译参数
//这里的汇编文件不是keil格式的 而是gcc格式的 可以在stm32的标准库中
//找到
cpp.positionIndependentCode: false
cpp.enableExceptions: false
cpp.debugInformation: true
cpp.executableSuffix: ".elf"
cpp.driverFlags:[
"-mcpu=cortex-m4","-mthumb","-mthumb-interwork",
"-mfloat-abi=hard", //softfp
"-mfpu=fpv4-sp-d16"
]
// cpp.driverFlags 指的是编译和链接时直接传给编译器的参数
cpp.commonCompilerFlags:[
// "-fno-strict-aliasing","-g3",
"-g","-Wall","-O0",
"-fdata-sections",
"-ffunction-sections",
"-mapcs-frame","-mapcs-stack-check",
// "-flto","-fno-inline","-std=c99", "-flto"
]
//c c++编译时的一些公共参数
// cpp.driverLinkerFlags:["-mcpu=cortex-m4","-mthumb","-mthumb-interwork"]
cpp.linkerFlags:
[
"-T"+path+"/STM32F417IG_FLASH.ld",
// "-specs=nano.specs","-static","-specs=nosys.specs",
// "-nostartfiles",
// "?u,_printf_float",//"-cref","-u,Reset_Handler",
// "--start-group",
"-lnosys",
"-lgcc",
"-lc",
// "-lstdc++",
"-lm",
// "--end-group",
"-Map=Project.map",
"--gc-sections",
"--defsym=malloc_getpagesize_P=0x80",
]
//链接成elf 文件的linker参数 STM32F417IG_FLASH.ld可在Stm32标准库中找到 并放在本工程同级目录
// Properties
// {
// condition: qbs.buildVariant === "debug"
// cpp.defines: outer.concat(["DEBUG=1"])
// cpp.debugInformation: true
// cpp.optimization: "none"
// }
// Properties
// {
// condition: qbs.buildVariant === "release"
// cpp.debugInformation: false
// cpp.optimization: "small"
// }
//这里自定义一个生成规则 用于把生成过程中的”obj” “objc” Artifact 输//出作为输入 再生成汇编文件 new Command()是运行的命令
Rule
{
inputs: ["obj","objc"]
Artifact
{
filePath: input.baseName+".s"
fileTags: "obj_asm_out"
}
prepare:
{
var objDumpPath = product.objDumpExePath;
var argsObjDump = ["-S",input.filePath]; //,output.filePath
var cmdObjDump = new Command(objDumpPath, argsObjDump);
cmdObjDump.stdoutFilePath=output.filePath;
cmdObjDump.description = input.fileName+" convert to asm...";
return [cmdObjDump];
}
}
//这个Rule是用来将"application" Artifact输出文件 即elf文件转换为hex
//文件用于其它下载(如串口) 打印elf文件的大小信息
//再生成一个openocd的下载 bat文件 (这个command可以不要)
Rule
{
inputs: ["application"] //application
Artifact
{
filePath: input.completeBaseName+".hex"//qbs.installRoot + "output1" + ".hex" //installRoot install
fileTags: "flash"
}
prepare:
{
var sizePath = product.elfSizeExePath;
var argsSize = [input.filePath];
var objcopyPath = product.objCopyExePath;
var argsObjcopy = [input.filePath,"-Oihex",output.filePath];
var openocdDir=product.openocdDir;
var configInterfacePath = product.openocdInterfacePath;//"interface/stlink.cfg";
var configStm32CfgPath = product.openocdCpuCfgPath;"target/stm32f4x.cfg";
var argsFlashing =
" -f " + configInterfacePath+
" -f " + configStm32CfgPath+
" -c " + "init" +
" -c " + "halt" +
product.openocdEraseCpuSector +//"\"flash erase_sector 0 0 127\""
// " -c " + "reset" +
// " -c " + "halt" +
" -c " + "\"flash write_image erase " + "\\\"" + input.filePath + "\\\"" +"\"" +
" -c " + "\"verify_image " + "\\\""+ input.filePath +"\\\"" +"\""+
// " -c " + "reset"+
// " -c " + "halt"+
" -c " + "exit";
var cmdSize = new Command(sizePath, argsSize);
var cmdObjcopy = new Command(objcopyPath, argsObjcopy);
var cmdWriteFile=new JavaScriptCommand();
cmdWriteFile.maxExitCode=1000;
cmdWriteFile.writedata=
openocdDir+"openocd.exe " +
" -f "+ configInterfacePath
+" -f " + configStm32CfgPath;
cmdWriteFile.writedata1=openocdDir+"openocd.exe " + argsFlashing;
cmdWriteFile.askyesorno=
"@echo OFF\n"+
"set /a yes=0 \n"+
"set /a no=0 \n"+
"set /p YESORNO=\"Are you want to go on?(Y/N)\"\n"+
"if not defined YESORNO set YESORNO=y\n"+
"if %YESORNO%==y set /a yes=1 \n"+
"if %YESORNO%==n set /a no=1 \n"+
"if %YESORNO%==Y set /a yes=1 \n"+
"if %YESORNO%==N set /a no=1 \n"+
"set /a x=yes \"|\" no \n"+
"if %x% equ 0 GOTO TEND\n"+
"if %no% equ 1 GOTO TEND\n"+
"if %yes% equ 1 GOTO ALIVE\n"+
":ALIVE\n"+
"@echo ON\n";
cmdWriteFile.sourceDir=project.sourceDirectory;
cmdWriteFile.downloadBat=project.sourceDirectory+"/openocdDownload.bat";
cmdWriteFile.elfFile=input.filePath;
cmdWriteFile.sourceCode=function()
{
var file=TextFile(sourceDir+"/openocd.bat",TextFile.WriteOnly);
file.truncate();
file.writeLine(writedata);
file.writeLine("pause");
file.close();
var file1=TextFile(downloadBat,TextFile.WriteOnly);
file1.truncate();
file1.writeLine("@echo prepare to download elf file: ");
file1.writeLine("@echo "+elfFile);
file1.write(askyesorno);
file1.writeLine(writedata1);
file1.writeLine("pause");
file1.writeLine(":TEND");
file1.writeLine("exit");
file1.close();
var process=new Process();
process.exec("cmd",["/c","start "+downloadBat]); //+ "C:/Windows/System32/cmd.exe" %COMSPEC% qbs.shellPath
}
cmdSize.description = "Size of sections:";
cmdSize.highlight = "linker";
cmdObjcopy.description = "convert to bin...";
cmdObjcopy.highlight = "linker";
cmdWriteFile.description = "generate openocd download bat ...";
return [cmdSize,cmdObjcopy,cmdWriteFile];
}
}
}
}
构建好的qbs工程结构如图
startup_stm32f40_41xxx.s汇编启动文件 不是keil格式的 而是gcc格式的 可以在stm32的标准库中找到 上面的cmsis-dap接口配置文件cmsis-dap-swd.cfg 放在项目的同级目录 内容如下:
interface cmsis-dap
transport select swd
adapter_khz 2000
其它接口要百度
Qbs工程配置好后 就选 on RemoteOpenOCD的构建目标
点构建 如无错误如下所示
然后就点项目 设置运行配置为custom on remoteOpenocd 设置executable 为生成的elf文件
连接好下载器 点击调试运行就会进行下载并运行断点在main函数入口了
有时中断会断不下来 可能是gdb-py或者是openocd的bug吧,更换版本可能好点
有时调试会假死 在进程管理器中关掉openocd各gdb-py的假死进程 重连下载器才可以再进行调试
还有就是点连续运行后再按暂停是停不下来的
Restart debuggin 一按就会假死
单步调试较慢 步过一些时间较长的指命会假死
最好用 先下好断点-直接运行到断点-删除断点-单步/步过 的方式进行调试