做嵌入式开发,一定对Keil、IAR之类的远古IDE不陌生,虽编译、调试功能顶天但远古IDE的画风着实令人脑壳着实发疼。好一点的解决方案就是像IAR、Keil、Quartus ii等联调VS Code,即在VS Code编写(调试)代码,在IAR或Keil编译烧录。以VS Code强大而丰富的插件功能和简约优美的UI,可以使开发体验得到大幅提升。更好一点的方案是,编写与编译烧录一体化,配置配置VS Code也能做到。而这里则提供另一种嵌入式IDE——CLion(缘于稚晖君大佬的博客)
使用过JetBrains产品的一定对它印象深刻,比如Pycharm,界面美观、代码补全灵活、功能完善且强大,CLion也具备这些特点。同时由于CLion不像IAR、Keil那样有完善的嵌入式工具链,所以手动配置工具链,流程又长又容易出错。如果某个环节没有如教程那样复现,那将会是顶级的折磨,所以要做好心理准备和取舍,这里就先祝大家一步到位。
需要说明的是:
1,本篇可能相当啰嗦(主要是避免以后自己看不懂),建议按需查目录。同时由于水平有限,难免会有一些冗余的步骤和谬误,还望理解。
2,第一次配置工程或许会很麻烦,多尝试自会熟
3,配置CLion过程中遇到的问题放在本篇末尾
4,以下内容是基于Windows操作系统的搭建过程(网页端的目录在左侧←)
一、工具准备 ૮₍ ˃ ⤙ ˂ ₎ა
本着版本越新越好的原则(虽然往往会出现各种令人不快的兼容性问题),这里的版本都尽可能地为当下最新版
CLion:2023.3.3
-> の~~
STM32CubeMX:6.9.0
-> 到官网下载最新版即可,不必在意有没有SW4STM32选项(6.4.0版前是有的)
MinGW:13.2
-> 这有大佬的博客可以参考,MinGW-W64 下载、安装与配置
2024.9.22
博客vip了,那就再推荐一篇Windows 11安装 MinGW-w64 教程,也可自行查找。不过教程里的网站似乎不太好用,可以到github里下载Releases · niXman/mingw-builds-binaries
选择x86_64-14.2.0-release-posix-seh-ucrt-rt_v12-rev0即可,x86_64表示的是主机为64位的架构,posix是类Unix的API标准,seh是 Windows 特有的异常处理机制,ucrt是更现代的 C 运行时库
arm-none-eabi-gcc:13.2
-> 这个必须得说明一下,之前我下载的交叉编译工具链也是2019版的,但那个实在太老还停产了,构建时提示只支持11.0及以上的,当时没注意花了一夜才搞明白。交叉编译工具链
这里简单解释一下为什么用交叉编译工具链,我们知道嵌入式开发一般是基于arm架构的,但是我们的电脑一般都是Windows系统,那么在Windows下编译的工程最终会生成exe可执行文件。而这个可执行文件只能在Windows系统下运行,它包含了二进制文件(x86的机器码),但也包含了一些关于Windows操作系统的信息,并非纯二进制文件。
而Windows可以根据这个exe文件来为其分配内存之类的,让其在设备上运行。对于非Windows系统,甚至是裸机的嵌入式微处理器而言,自然是不能在上面直接运行的。而且由于两者不是同一个架构,指令不同,纯二进制机器码自然不能运行。
所以,这就要使用交叉编译器。交叉嘛,顾名思义,即宿主平台(你的电脑)与目标平台(嵌入式系统)不一致,可以将宿主平台代码转为目标平台。后面你会发现工程产生的文件是elf,而非平时遇到的exe。这个elf其实是linux下的可执行文件,既然elf和exe都是操作系统下的可执行文件,那为什么exe不行,elf就行呢?这是因为elf可转为其他平台架构下的二进制码。
如果你做安卓开发,那么你应该选用安卓交叉编译工具链。嵌入式开发自然要选择嵌入式的交叉编译工具链。
那么接下来解释一下为什么是这条工具链,我们从名称分析
arm-gnu-toolchain-13.2.rel1-mingw-w64-i686-arm-none-eabi.zip
arm-gnu-toolchain:适用于arm平台架构的gnu工具链
13.2.rel1:13.2是版本号,rel1是发行版号(release 1)
mingw-w64-i686:MinGW-w64是一个项目,提供了在Windows上使用GCC和其他GNU工具开发原生Windows应用程序和跨平台应用程序的能力。它包括对64位和32位Windows的支持。i686指的是32位的x86架构,这是较旧的Windows PC常见的CPU类型。
arm-none-eabi:表示该工具链用于编译代码到ARM架构的目标平台上,"none"意味着没有特定的操作系统环境关联,即裸机,而"EABI"是Embedded Application Binary Interface的缩写,专为嵌入式系统设计,通常用于无操作系统或轻量级操作系统如FreeRTOS、μC/OS-II等的开发。
2024.12.16
前几天发布了14.2版本的,这个版本可以使用C++20的module特性了,个人开发的话,如果想要体验一些C++新特性的话可以选择这个版本
OpenOCD:0.12.0
-> 选择下图这个压缩包Download OpenOCD for Windows ,选择第一个即可
二、基础搭建 ଘ(੭ˊ꒳ˋ)੭
1,安装
CLion、STM32CubeMX安装就不赘述了,其余三个解压在同一个目录下即可(请忽略起错名字的父目录MinGW-64,可以自行起个ToolsKits什么的正经名字)。
注意哈,整个路径千万不要有空格、中文什么的字符,否则构建时会出现十分离谱的事情
2024.10.13
并且用户名必须是英文等合法字符,不能含有中文、空格啥的,不然CLion连自己的构建工具都找不到!!如果你出现CMake运行错误什么什么的,那应该是用户名的问题
更改用户名可参考链接更改电脑本地用户名,将中文用户名给为英文名称
2,配置系统Path
先按一下Win+V观察剪贴板是否启用,再依次按Ctrl+C把三个文件夹下的bin的路径复制一下
然后,正式开始配置系统Path,打开【高级系统设置】-【环境变量】-【编辑】
一定要注意的是,打开的是系统变量下的Path,而不是上面那个用户变量!!!
然后,双击空白处,按Win+V,剪贴板上就会有刚刚复制的三个路径,填入即可
填完后要按三次确定,配置完后要重启,避免识别不到系统路径
3,检验
重启后运行cmd,分别执行下面代码检验是否系统路径配置成功。如果不成功需要再检查一下前面系统变量的配置,并尝试重启,因为有时候即便过程全对也会出现识别不到的现象
arm-none-eabi-gcc -v
gcc -v
三、配置CLion (꒪⌓꒪)
除了自搭gcc工具链外,其实JetBrains官方还有IAR工具链的搭建方法。
1,搭好工具链
打开所有设置(如果英文不习惯,在下面的插件/Plugins里可安装中文插件Chinese什么的)
找到工具链,可以直接添加也可以在默认的环境上更改,此处则选择后者
a.把工具集换成自己下载的那个mingw64的目录(不需要到它的下级目录bin)
b.把构建工具、C编译器、C++编译器改为自己的相应程序路径(照葫芦画瓢即可,13.2.1的那个也可以直接用不带版本名的gcc)
c.其余都不必改,以防出现不可知的错误
构建工具那一栏可以选择默认的ninja,不过调试打印的信息色彩上不够丰富
2,确认CMake
如果选择的是自行添加工具链且没有把它置顶,那么需要在CMake配置里更改一下工具链
3,配置程序的位置
找到你自己程序的安装位置即可,按一下测试,冒绿字即可
做好这一切之后,记得按确定!!
四、创建工程 ಠ_ರೃ
1,创建项目
完成配置CLion后选择新建项目,路径和名字不要有中文!这里以创建“jk触发器”为例
2,检验初始化工程(非初次可跳)
初始工程默认配置的是STM32F030F4PX芯片,且正常情况下一定是可以通过编译的, 这里之所以要进行这一步,就是为了确认工具链之类的是否搭好,避免后面STM32CubeMX都配好了,结果却不能编译调试的悲催情况。
配置文件先随便选一个或跳过都行,鉴于初次使用,这里先随便选一个。
这里之所以需要配置文件,是因为使用到了Open OCD烧录程序
点击左下角那个长得像方舟的符号,再点击它旁边那个长得就像刷新的东西,没有红字即为正常
然后点击最上面的那个小锤子,构建成功后会冒绿光并生成两个蓝色的文件,这一步很关键
(下面的窗口拉长了一点,为了方便观察)
这时如果你可以配置烧录配置文件了,没有连接板子或者连接了但没安装相应仿真器驱动就直接烧录(长得像运行的那个按钮),会出现含failed的红字。
这里可以新建一个后缀名为cfg的文件,我用的是仿真器是STLink(-V2),所以起名为stlink.cfg,板子是stm32f407VET6,配置如下(稚晖君那有更详尽的配置)
同时要说明的是J-Link的配置有些特殊,因为OpenCD无法直接识别J-Link,需要把J-Link驱动改为串行接口,具体操作。实测过程中,只需到zadig改一下就好了(需要插拔),当然不同人情况可能不同。
source [find interface/stlink.cfg]
# 其中下面这句不要省略,不然很容易出现连接不上的情况
transport select hla_swd
# 内存大小 这里我的是512kb,就是80000
# set WORKAREASIZE 0x80000
# chip name 改为相应型号
# set CHIPNAME STM32F407VET6
source [find target/stm32f4x.cfg]
然后改掉之前那个我们随便选的cfg文件
把这个cfg替换成我们新建的那个stlink.cfg,同时把下载改为始终(这个看个人需要),点击确定
虽然工程的芯片型号我还没改,但是依旧可以“烧进去”的。连接上板子,烧成功后虽是令人心颤的红字,但是很明显跟之前不一样,有了诸如flash地址、Program Finshed等更为详尽的信息,且右下角会有已下载固件的提示
3,配置工程(含cfg)
点击上面那个.ioc文件,可以看到这个界面,再点击通过STM32CubeMX打开
等一段时间STM32CubeMX就会自动打开,默认是这个STM32F030F4PX芯片。这里肯定要改,点击红圈位置
选择钟意的芯片,双击即可(我这里收藏过,你可以在左上方第一个搜索框内搜索,英文输入法)
为避免后续改名字的麻烦和文件残余的问题,更换芯片后就直接配置工程。点击上方的Project Manager一栏,然后在Toolchain/IDE一栏选择STM32CubeIDE(官方文档里说过),不用考虑什么SW4STM32或者CMake什么的
然后直接按Ctrl+S,它会给个框,按保存,再点击Yes,之后就会变成下面这个了
此时回到CLion,你会发现工程配置为正确的芯片了。不过左边的包可能还没被清掉,冒出个红字提示,emm,这个看情况吧,更严重一点的提示是找不到资源工程什么的。可以先不管它,等后续再配置STM32CubeMX生成代码时,会自动加载。也可以先重启一下CLion
如果你前面配置过cfg,这时cfg很可能被扬了,我是说最上面那个长得像爬虫的芯片,那个绿色标志没了
那么现在就可以体验一下配置cfg的整个流程了,点进编辑或是直接点击编辑配置,到里面点击左上角添加,然后找到那个OpenOCD下载并运行
然后,更改名称,添加可执行的二进制文件,添加面板配置文件( 点击选择路径下的第三个,可以直接到项目目录里),下载选择始终
4,配置芯片
接下来就可以通过点击目录里的ioc文件,回到STM32CubeMX,是一成不变的SYS、RCC和时钟树的配置,就不赘述了
这个外部晶振一定要对照一下自己手上这块板子的原理图,尽管芯片一样,但板子上的外部晶振就不一定了,有些是25MHz、8MHz,我这里是12MHz
设置好外部晶振频率并选择好通路后,直接在右侧输入想要达到的最终频率,它会自动配好
5,导出工程代码
Toolchain /IDE 要选STM32CubeIDE
到左侧的Code Generator里,勾选那个生成“.c/h”文件的,避免生成的代码乱七八糟的
生成后就可以点击close了,然后回到CLion,接下来可能弹出一个框让你选择配置文件,如果已经配置了,那么就直接跳过。没有的话,可以参考前面的配置
6,接下来,开测!
接上仿真器,点击烧录,再点击旁边那只爬虫就可以得到一堆红字,最上方会变成重新调试和停止,同时仿真器也会闪闪发光。
然后就可以设置断点什么的了,至于查看寄存器svd什么的其他博客里也有
嗯~,出乎意料,这不知道怎么回事就进去硬件错误里了,正常情况下不应该啊 。
注释掉了之后就能正常运行了???原因未知,先记录一下,方便以后定位
详情请见目录【七、某些BUG】-【卡死在SystemClock_Config里的Error_Handler】
【锚点】:系统时钟配置的隐患
【半解决】:按一下板子上的重置键,或者使用OpenCD自带的重置MCU
【半解决+1】:在Open CD的配置文件中重置选项勾选运行
7,移植库或文件
这个是比较简单的,要么在CLion项目打开的情况下,直接该替换替换,该删删,之后修改CMakeLists。要么直接把原有的工程文件夹拖到关闭项目后的CLion里,点击一下信任,再把之前用STM32CubeMX新建工程时产生的CMakeLists和启动文件复制粘贴过来。总之,多试多查,不怕犯错,但前提是要把重要工程都备份好。下面内容建议先了解一下CMake构建的过程
①半移植
即在原有基础上修修补补改改,原目录如下
现在开始移植
如果此时你以无需使用STM32CubeMX,那么与其有关的文件可以删掉,注意.idea不建议删掉(但也无所谓,顶多弹个框按一下确定),两个ld文件、Startup文件、syscalls.c和sysmem.c不要删除,其余自便。Startup文件、syscalls.c和sysmem.c都放到CORE里了
CLion里面如下,main文件其实放在USER/src或者USER/下比较好,放在外面等会又需要在CMakeLists多写一句
CMakeLists里面其余的可以先不了解,值得关注的是下面这三句
#这一句是包含头文件目录,只需把头文件目录包含进来即可
#目录之间需要用空格(换行也行)隔开,写文件名时CLion会自动补全
include_directories(Core/Inc Drivers/STM32F4xx_HAL_Driver/Inc Drivers/STM32F4xx_HAL_Driver/Inc/Legacy Drivers/CMSIS/Device/ST/STM32F4xx/Include Drivers/CMSIS/Include)
#这是添加预定义,-D后面紧跟着预定义的内容即可
#这一句就加了DEBUG、USE_HAL_DRIVER、STM32F407xx这三个预定义
#一般不需要改,看个人需求,与.h里的#define并无二致
add_definitions(-DDEBUG -DUSE_HAL_DRIVER -DSTM32F407xx)
#下面这一句是包含所有要编译的文件,包括启动文件
#其中GLOB_RECURSE表示使用通配符“*”,SOURSES是个变量名,可以理解为file变量
#使用了通配符需要用双引号括起来,“Core/*.*”表示包含Core目录下的所有文件
#注意!如果你是从Keil或IAR移植过来的,切忌不要把它的启动文件之类的带过来,只要带c相关的文件即可
file(GLOB_RECURSE SOURCES "Core/*.*" "Drivers/*.*")
根据刚才的目录,可以改为下面这样
include_directories(CORE/inc CORE/system DATA USER/inc DRIVER/inc)
add_definitions(-DDEBUG -DUSE_HAL_DRIVER -DSTM32F407xx)
file(GLOB_RECURSE SOURCES "CORE/*.*" "DRIVER/*.*" "DATA/*.*" "USER/*.*" main.cpp )
改了CMakeLists之后Cmake需要重新加载
如果你是使用C/C++混编,那么就要格外小心了,比如调用C++里调用C函数等
既是混编,那么一定要确保.c对应的头文件里一定要包含extern "C",否则C函数以C++的方式编译,那么就会导致链接时找不到对应函数符号。
如果出现下面错误,可以翻阅这两篇undefined reference,undefined reference to `_sbrk‘,主要原因是前面移植时没有把系统调用文件syscall和系统内存sysmem添加到项目里。
根本原因就是arm-none-eabi默认是使用startfiles,且编译器为了节省空间,删减了一部分代码, 导致有些方法只有声明没有实现,需要自己实现。
这一部分的具体解决方法在下面的目录【开发拓展-使用C++开发-找回编译器缺失的部分】
②完全移植
现以一个IAR工程为例
把整个文件夹移到一个纯天然目录(路径上不含任何中文、空格之类的),同时文件夹改成纯天然符合规范的名字
先删无关目录,如settings目录、Debug目录等。再删除原工程配置文件,如启动文件
再找到STM32CubeMX生成的配置文件,共7个,如下
上图还漏了一个sysmem.c文件,不然会报错_sbrk,其中STM32F407VETX_RAM.ld可不加,因为CMakeList里并没有链接它
复制到相应位置后,就可以在此工程右键打开CLion了
进去后,如下,确定即可,如果你不需要选择其他工具链的话
然后,打开CMakeLists.txt文件,重点是改下面三个,改法同方法①
然后改工程名字,poject里的第一个参数,改为你需要的名称(我这里是把DoJi改为BuJbDmji)
补充一句,如果你更换芯片了,那么这个ld文件也要改名
改完之后就可以重新加载CMake了,如果报错,自己再看看是不是某个文件夹忘加了或者工程里还有不必要的文件没删。根据提示,多试试
加载成功后,上方的名字也会改变
此时就可以构建了,至于添加Open CD之类的就不赘述了
总结:
1,复制工程目录
2,删不必要文件
3,修改CMakeLists
五,开发体验提升 (⑅˃◡˂⑅)
由于是更新多次,所以工程项目会经常替换,名字不一样很正常
1,查看寄存器
①下载
a.到STM32CubeMX里下载
点击【Help - Docs&Resources - System View Description】,就可以等待下载了
不过运气比较糟,下载了三次它还是未回应我
b.到官网去下载(推荐)
进入ST下载官网,在输入框内填上相应型号,然后经历漫长的等待,下载那个SVD
2024.10.20:
现在情形恰恰相反,官网会卡半天,CubeMX上几秒便可下载完成
②添加SVD
下载完后,为了方便添加以及后续可能出现的更换等等问题,于是在CLion的目录下新建了一个user_resources,里面专门用来添加各种包的。考虑到以后CLion可能会用于别的用途,那就再新建一个子目录MCU,在这里添加刚刚下载的SVD包,并解压
现在回到CLion,到相应调试窗口下,点击外设一栏,然后点击加载.svd文件,找到相应型号
选择第一个全都要或者挑选几个按需选
这里选择全都要,当然会冒出一堆外设,然后点击最外面的那个STM32F407.svd的,让它收起来,这样再展开时,所有外设都会默认收缩起来
2,开发改善
① 背景
打开所有设置或设置,然后【外观-背景图像】,选择想要的图片,确定即可
②去除特定警告
把光标放在报错位置,点击更多操作
2024.10.6
后面好像没有更多操作这个选项了,可以直接在设置里搜索检查,然后找到数据流分析,把无尽循环去掉
点击编辑检查配置文件设置
取消勾选,确定即可
3,一些插件与调试方法
① 调试方法-博客
② 2023年Clion插件推荐
③ 自动格式化代码
两种办法,一种是直接用调项目设置,另一种是使用Clang-Format。前者适合个人开发,因为更改项目设置只能应用到本地。后者适合团队,因为Clang-Format是个配置文件,可流转
对于第一种办法,找到下面设置,自行摸索即可
至于另一种,可以参考博客Clang-Format用法详解,下面是博客里的配置,挺像Java风格的
#Generated from CLion C/C++ Code Style settings
BasedOnStyle: Google
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: false
AfterClass: true
AfterControlStatement: Always
AfterEnum: true
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: true
AfterExternBlock: true
BeforeCatch: true
BeforeElse: true
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: "^ NOLINT:"
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: true
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
SortPriority: 0
- Regex: '^<.*\.h>'
Priority: 1
SortPriority: 0
- Regex: "^<.*"
Priority: 2
SortPriority: 0
- Regex: ".*"
Priority: 3
SortPriority: 0
IncludeIsMainRegex: "([-_](test|unittest))?$"
IncludeIsMainSourceRegex: ""
IndentCaseLabels: true
IndentGotoLabels: true
IndentPPDirectives: None
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ""
MacroBlockEnd: ""
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Right
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- "c++"
- "C++"
CanonicalDelimiter: ""
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
CanonicalDelimiter: ""
BasedOnStyle: google
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
Standard: Cpp11
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 4
UseCRLF: false
UseTab: Never
但是显而易见,每个人的代码风格都不一样,而对于没有接触过Clang-Format的人来说,学习成本略高。所以下面带来了如何使用AI配置(有时候它只能告诉你在准确位置,至于属性对不对试了才知道),示范如下
①打开某个网页版AI,如文心一言、智普清言等
②先写下诉求,并把Clang-Format代码复制过来(这个随意,只要描述清楚,代码可以不必复制)
③现学现用
配置和效果如下
# Generated from CLion C/C++ Code Style settings
BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: None
AlignOperands: Align
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Always
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterReturnType: None
AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: true
AfterClass: true
AfterControlStatement: Always
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterUnion: true
BeforeCatch: true
BeforeElse: true
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
BreakBeforeBinaryOperators: None
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
ColumnLimit: 0
CompactNamespaces: false
ContinuationIndentWidth: 8
IndentCaseLabels: true
IndentPPDirectives: None
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: true
MaxEmptyLinesToKeep: 2
NamespaceIndentation: All
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PointerAlignment: Right
ReflowComments: false
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 0
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
TabWidth: 4
UseTab: Never
4,示例工程
语音存储与回放(串行)_页写入_stm32f407VET6_非正点或野火实验平台_裸机(遗弃)
5,ITM与SWO
简单来说就是打印输出,没有打印输出的调试是不完整的
单片机的调试一般都是使用串口打印,虽说有了仿真器后可以像调试本地程序一样断点运行,但如果需要测试的数据量比较多,那么还是需要串口打印来记录的。除非像TI官方那样,CCS与TI板相互配合,有独特的调试方法
很遗憾我手上这块只有一个仿真接口,串口被仿真器占用掉了,连USB都没有,所以就想试试仿真器自带的打印功能。之前打印使用的是IAR的Terminal I/O,转到CLion后就完全不知道怎么做了
但为了不影响开发,可以姑且用平(?次)替ST-LINK Utility 试一试,如果你使用的是其他仿真器可以先查查有没有SWO模式之类的
【优点】:配置极为简单,速度还行
【缺点】:无法与CLion进行联合调试
需要加入头文件<stdio.h>,在合适的位置加入下面HAL自带的ITM_SendChar(char );函数就行了,输出内容为字符,自定义即可。要注意的是这个东西需要添加头文件"stm32f4xx.h",不要直接使用"core_m4.h",不然找错误会超乎想象
然后编译,烧录到板子上
完成后,可以到官网下载
下载完成后,就可以打开了
点击上方工具栏的【ST-LINK】-【Printf via SWO viewer】
或者点击一下这个示波器样的按钮
然后设置好System clock,这个要根据你自己的时钟树来设置,我的是168MHz,在前面STM32CubeMX关于时钟树配置中有(我已经运行过,所以下面会有输出,正常是空白),端口默认
按下start就可以直接运行已经烧录在MCU的程序了,但此时CLion是无法调试、下载的,被 ST-LINK Utility占用了,除非把 ST-LINK Utility关上。二选一的调试方法让人 (//̀Д/́/)
需要说明的是,只发送char是远远不能满足我们的需求的,还需要对printf进行重定向
①先打开syscalls.c文件(如果你没有,参考一下目录【使用C++开发-找回编译器缺失的部分】)
②找到 _write
③改为下面这样
ITM_SendChar((uint32_t)(*(ptr++)));// 加个括号防止歧义
2025.1.11
现在补上这个功能STM32使用ITM调试_通过仿真器实现串口打印-CSDN博客
6,集成的Git
CLion中已经集成了Git,可以很方便的使用Git。具体操作可以参考一下下面的这个视频Git/Gitee可视化版本管理—使用CLion加速STM32Cube项目开发_哔哩哔哩_bilibili
2024.10.6
得要学会远程仓库的管理,不然后面出现什么变故导致本地仓库没了,追悔莫及
7,打开汇编
右键代码,点击【显示程序集】,即可打开汇编代码
上方会出现一行命令,通过修改参数以查看不同情况下编译出的代码。比如,把-Og改为-O1,从调试级别变为了1级优化,再点击一下旁边的刷新 ,就可以看到一级优化下的汇编指令
2024.10.6
?!这是什么鬼?我都不记得有这回事了,一直没用这个功能
六、开发拓展 ଘ(੭ˊᵕˋ)੭* ੈ✩‧₊˚
1,使用C++开发
a.编程规范
①无论是c文件还是其对应的头文件都不要直接调用Cpp对应的头文件,不然会编译错误。网上是有如何在C中调用C++的,但还是不建议这么做。因为C++作为C的超集是兼容C的且功能更强大,适合当主体(main.cpp)使用。即便不使用模板、智能指针等泛型编程的东西(当然不一定带得动),重载、引用类型、bool类型、类的继承等就已经可以大大提升开发体验和效率了。
②在cpp文件或其头文件里调用C头文件时,直接用extern “C”包含需要的头文件即可,其实绝大多数都不必包含,因为HAL里已经做过了。
③定义中断服务例程和中断回调函数一定要用extern "C"括起来,不然可能会识别不到
b.找回编译器缺失的部分
和前面的移植一样,STM32CUbeMX生成的ld文件和启动文件都要复制过来,
除此之外!!还要把下面这个syscalls.c和sysmem.c文件复制过来
如果编译报错,缺少什么什么__sbrk之类的函数,那么就要在里面加上这些函数的实现方法(如果不适合,可到网上查这些函数的实现)
先是全局变量
extern int errno;
extern int _end;
然后是实现方法(函数)
caddr_t _sbrk(int incr)
{
static unsigned char *heap = NULL;
unsigned char *prev_heap;
if (heap == NULL)
{
heap = (unsigned char *) &_end;
}
prev_heap = heap;
heap += incr;
return (caddr_t) prev_heap;
}
下面是完整代码供参考
/**
******************************************************************************
* @file syscalls.c
* @author Auto-generated by STM32CubeIDE
* @brief STM32CubeIDE Minimal System calls file
*
* For more information about which c-functions
* need which of these lowlevel functions
* please consult the Newlib libc-manual
******************************************************************************
* @attention
*
* Copyright (c) 2020-2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* Includes */
#include <sys/stat.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <sys/times.h>
/* Variables */
extern int __io_putchar(int ch) __attribute__((weak));
extern int __io_getchar(void) __attribute__((weak));
extern int errno;
extern int _end;
char *__env[1] = { 0 };
char **environ = __env;
/* Functions */
void initialise_monitor_handles()
{
}
int _getpid(void)
{
return 1;
}
int _kill(int pid, int sig)
{
(void)pid;
(void)sig;
errno = EINVAL;
return -1;
}
void _exit (int status)
{
_kill(status, -1);
while (1) {} /* Make sure we hang here */
}
__attribute__((weak)) int _read(int file, char *ptr, int len)
{
(void)file;
int DataIdx;
for (DataIdx = 0; DataIdx < len; DataIdx++)
{
*ptr++ = __io_getchar();
}
return len;
}
__attribute__((weak)) int _write(int file, char *ptr, int len)
{
(void)file;
int DataIdx;
for (DataIdx = 0; DataIdx < len; DataIdx++)
{
__io_putchar(*ptr++);
}
return len;
}
int _close(int file)
{
(void)file;
return -1;
}
caddr_t _sbrk(int incr)
{
static unsigned char *heap = NULL;
unsigned char *prev_heap;
if (heap == NULL)
{
heap = (unsigned char *) &_end;
}
prev_heap = heap;
heap += incr;
return (caddr_t) prev_heap;
}
int _fstat(int file, struct stat *st)
{
(void)file;
st->st_mode = S_IFCHR;
return 0;
}
int _isatty(int file)
{
(void)file;
return 1;
}
int _lseek(int file, int ptr, int dir)
{
(void)file;
(void)ptr;
(void)dir;
return 0;
}
int _open(char *path, int flags, ...)
{
(void)path;
(void)flags;
/* Pretend like we always fail */
return -1;
}
int _wait(int *status)
{
(void)status;
errno = ECHILD;
return -1;
}
int _unlink(char *name)
{
(void)name;
errno = ENOENT;
return -1;
}
int _times(struct tms *buf)
{
(void)buf;
return -1;
}
int _stat(char *file, struct stat *st)
{
(void)file;
st->st_mode = S_IFCHR;
return 0;
}
int _link(char *old, char *new)
{
(void)old;
(void)new;
errno = EMLINK;
return -1;
}
int _fork(void)
{
errno = EAGAIN;
return -1;
}
int _execve(char *name, char **argv, char **env)
{
(void)name;
(void)argv;
(void)env;
errno = ENOMEM;
return -1;
}
至此,也算得上优雅地使用CLion了
2,使用FreeRTOS
如何在STM32CubeMX中配置FreeRTOS的教程网上有很多,不多言了。值得关注的是配置完后,要修改一下CMakeLists,把下面图中22到24行的注释取消掉(大家的行数可能不一样),因为生成的CMakeLists是默认关闭硬件浮点(FPU)的
另一方面,CLion里集成了FreeRTOS的相关调试,这个功能非常地好用。除了要启动这个功能外,还需要配置一下工程,可参考博客【嵌入式CLion】进阶调试
七、一天写不了几行BUG ( ´◔︎ ‸◔︎`)
有些由于忘了截屏,只能简单描述一下
1,文件在却显示No such file or directory
-> 这是因为文件冲突了,比如原先STM32F013F4PX的包没删,有两个main函数等;由STM32CubeMX生成代码时没有选STM32CubeIDE,使用默认的选择,生成包含所有库文件的代码,这就会有大量的重复
-》删除多余的库,再删除.idea文件夹和cmake-build-debug-mingw这样的编译文件。然后重新进入CLion,配置cmake什么一堆的,都是熟悉的内容
2,刚新建项目,CMake直接报出红字
或是出现no path什么的,那么就要好好看看设置里的工具链和CMake有没有配好,或是有没有下错版本。出现path很容易理解,可能配到了用户变量的Path,而非系统变量,也可能是没有重启
3,显示“不是有效的win32应用程序”
编译能过,但调试时显示运行 “'.elf' 时出错createprocess error=193, %1 不是有效的 win32 应用程序”之类的
-》这个之前没有使用C++时,重新调一下工具链和CMake就好了
-》(使用C++后?)没有配置OpenOCD也会出现,配置后就不会出现了,可以正常下载烧录
4,只有一个报错,并指向了蓝色的flash.id文件
进去一看有个提示,说什么【“ready”这个关键字只支持11.0以上的gcc,如果你有10.0的,请把它删除】。
-》按照本教程里的安装,下个13.2版本的arm-none-eabi-gcc,然后配置系统变量,加到工具链什么的
5,出现了完全摸不到头绪的提示
唯一能找到价值的还是标出的那一句
“ "F:\JetBrains\CLion 2023.1.1\OpenOCD-20231002-0.12.0\bin\openocd.exe" -c "tcl_port disabled" -c "gdb_port 3333" -c "telnet_port 4444" -s F:\JetBrains\CLion 2023.1.1\OpenOCD-20231002-0.12.0\share\openocd\scripts -f D:\EDA\MCU\untitled1\stlink.cfg -c "program D:/EDA/MCU/untitled1/cmake-build-debug/untitled1.elf" -c "init;reset init;" -c "echo (((READY)))" Open On-Chip Debugger 0.12.0 (2023-10-02) [https://github.com/sysprogs/openocd] Licensed under GNU GPL v2 libusb1 09e75e98b4d9ea7909e8837b7a3f00dda4589dc3 For bug reports, read http://openocd.org/doc/doxygen/bugs.html Unexpected command line argument: 2023.1.1\OpenOCD-20231002-0.12.0\share\openocd \scripts GDB Server stopped, exit code 1 进程已结束,退出代码0”
这个问了一下智谱AI 4.0,还真给回答出来了,于是我就照着做也确实解决了。这也许是一个不错的思路
tips:目前个人感觉是【通义前问2.5 >>?ChatGPT4.0 > 智普AI4.0 >> ChatGPT3.5】,单从免费上来看,这就很明显了。
——5.10
这就是前面为什么不让安装目录上出现空格和中文
6,卡死在SystemClock_Config里的Error_Handler
这个比较莫名奇妙,即便外部时钟源是按原理图配置的,使用IAR时能正常起振,这里却会死循环。
把SystemClock_Config里的Error_Handler直接去掉是可以运行的,但治标不治本的办法只是遮盖了失败的初始化,时钟频率只有正常的一小半。这都是用STM32CubeMX配置的,不应该有问题才对,同样的代码用IAR就没有问题,原因未知
无意中按了一下板子上的重启键就正常了,这一点让我想起了IAR中Debugger下关于ST-Link的配置,是有什么硬件复位、系统复位的。试了一下CLion自带的重置MCU,发现也可以使HSE正常初始化
由此推测cfg配置、GDB什么的存在某些问题没配好,希望有大佬可以讲解一下。
实在没有解决办法的话,只好把这个重置MCU当成程序运行键,毕竟IAR烧录完了之后还需要按一下运行键,这样似乎也不错?
把重置改为运行即可,其它教程好像不需要这样做,难道是我的配置和板子特殊?望大佬解答
7,出现一堆关于启动文件报错的红字
注意哈,移植时不要把IAR或Keil的启动文件也带进来了,启动文件要用STM32CubeIDE(MX)的,也不要把原项目工程的工程配置如.icf带过来,只要.c .h .cpp之类的文件即可
曾闻:硬件开发嘛,就是要反复折腾,折腾得多了,水平自然就提高了