1、教程说明
最近花了一些时间,重新学习野火stm32f4的1.8标准库视频,跟着火哥一起用keil5软件敲代码,还是用不习惯,就想着用clion折腾下标准库开发,顺便学习下重温cmake的用法。
这里只说明基于nucleo f4板子标准库开发的一些注意事项,使用野火教程的模板。
下面是成品图:

1_成品图
2、教程准备
这里参考稚晖君的教程,把clion的环境配好。
配置CLion用于STM32开发 【优雅の嵌入式开发】
以及网页链接up主的标准库 网页链接
3、注意使用
(1)外设驱动库文件
这里我们参考标准外设库STM32F4xx_DSP_StdPeriph_Lib_V1.8.0里面的SW4STM32的模板工程,找到f446RE的工程并打开,SW4STM32同样是GCC环境的。

2_SW4STM32
参考STM32F4xx_StdPeriph_Driver中的c文件调用,把f446RE不支持的外设删除,相对应的头文件也要删除。然后复制到clion工程里面。

3_删除无用c文件
(2)Cmake文件修改
1)第一个是头文件路径修改,就是需要用到的头文件,都要把路径写到cmake文件里面,编译器才能识别到。剩下的板级支持包分开写,就是为了跟着火哥上课学习外设时,另外一个一个加,比如LED的头文件。

这里我们说下这个,(复制跳转)include_directories的用法
https://cmake.org/cmake/help/latest/command/include_directories.html?highlight=include_directories#command:include_directories

5_头文件命令
2)第二个是c文件,以及启动文件路径的修改,把相关源文件和启动文件链接到编译器。使用GLOB命令使用通配符模式匹配来查找文件。file(GLOB SOURCES "src/*.*")使用这个通配符,表示所有".*"结尾的文件都会包含到这个SOURCES变量。他的用法也可以参考上面的教程网址,在快速搜索哪里搜索关键词file或者GLOB。

6_源文件命令
3)第三个是宏定义文件的修改。我们在使用keil5编译工程前都会添加两个头文件。

7_宏定义使用
而在clion上面,当我们用cubemx生成代码的时候,我们会发现,在stm32f4xx.h这个头文件上面,添加两个宏定义#define USE_STDPERIPH_DRIVER 和 #include STM32F446xx。编译后,编译器会提示该头文件STM32F446xx重定义了,经过一番研究发现。原来在cubemx软件和clion生成代码的时候,已经在cmake文件上添加了相关的宏定义。我们按照图片修改就是了。

8_宏定义的使用
下面是add_definitions命令的使用方法和解释。

9_宏定义的意思
(3)Printf串口打印
1)主要问题
我们一般使用printf打印log,用来调试程序。要解决的问题是将printf的输出重定向到串口,然后通过串口将数据发送出去。但是clion的printf打印函数,并不像keil5那样,他们对应的函数并不相同。所以用野火的keil5重定向函数,在clion并没有输出,反而会编译报错。
通过查看编译器的报错提示,原来printf打印函数对应的底层调用函数,并没有定义。但是查看1.8标准库的SW4STM32例子,也没有看到相关的重定义函数和文件。进而找到HAL库的printf例子,发现工程里多了一个syscalls.c文件--该文件已经定义好了相关的函数。我们把这个文件复制到工程的User目录下,并编译,报错已经解决。

10_printf重定义
虽然报错已经解决但是printf还是没有输出。与在keil5中重定义fputs()函数不一样,在GCC编译器中(stm32标准库)需要重定义的是__io_putchar(int ch)。

11_对应函数弱定义
为什么可以确定是这个函数呢?我们通过查看stm官方的stm32f4的1.8标准库中的例程查到printf的使用方式。我们要学会使用官方的例程和文档,这种方式是一手途径,效率是最高的。

12_文件解释
所以我们按照官方的做法,在串口定义的.c文件哪里,添加重定义函数。

13_添加相对重定义函数
接着重写函数,这两个函数的写法是和野火教程的一模一样的。

14_野火格式printf
到这里为止,我们一般就可以愉快的玩耍printf函数了,但是还有一些小问题要解决。
2)次要问题
第一个问题--printf打印不加n,没有输出。
解决方式,在#include "bsp_debug_usart.h"开头包含头文件#include <stdio.h>,然后在串口初始化时调用setvbuf(stdout, NULL, _IONBF, 0),设置buffer缓存为0,这样一有数据就发送,不然会等到缓存满或有回车换行符才发送。如果没有这句,你的printf又没n,log就会打不出来。

15_解决问题1
第二个问题,半主机模式,(这个模式存疑,网上很多教程)
我没有禁用半主机模式,也是可以正常使用printf的,不知道这个原因是什么。
半主机重定向 http://www.mculover666.cn/posts/2251182441/
hal库的设置 https://shawnhymel.com/1873/how-to-use-printf-on-stm32/
下面的内容都是存疑的:
printf 函数使用了半主机模式,所以直接使用标准库会导致程序无法运行,因此必须提前告知编译器不使用半主机模式--在syscalls.c文件下添加其中一个。
第一种方法(参考):
不使用半主机模式 /* 告知连接器不从C库链接使用半主机的函数 */
#pragma import(__use_no_semihosting)
第二种方式:
#if !defined(OS_USE_SEMIHOSTING) //如果定义了OS_USE_SEMIHOSTING
中间是代码
#endif //#if !defined(OS_USE_SEMIHOSTING)

16-解决问题2
个人结论:既然官方例程在SW4STM32例子中和syscalls.c文件中没有撰写这个,还有本人在GCC环境编译也没有出现问题。我觉得这个可以暂时忽略掉,就是不用考虑半主机问题。
(4)测试效果
printf输出没有问题,浮点数打印也没有问题。

结果
(4)F446RET6工程文件
链接:https://pan.baidu.com/s/1310oFNVDqsHYru2nMPkjHA
提取码:w747


17_结尾图
上一篇:STM32驱动WS2812B
下一篇:使用Rust开发STM32嵌入式程序入门教程
推荐阅读最新更新时间:2026-03-19 19:50
- LTC2945IUD-1 在 -48V 系统中使用低侧检测进行电源监控的典型应用
- 用于 24V 汽车应用的 LTC4367IDD 过压电源保护控制器的典型应用
- LTC2635-HZ12 四通道、12 位数模转换器的典型应用
- 使用 ROHM Semiconductor 的 BU4821 的参考设计
- 使用 Analog Devices 的 AD9625 的参考设计
- LT1307CS8 高压反激式转换器的典型应用电路
- AD7858L 3V 至 5V 单电源、200ksps、8 通道、12 位采样 ADC 的典型应用
- AND8337/D、1.2V、1.5V DC 至 DC 单路输出电源参考设计
- DER-713 - 使用 InnoSwitch3-EP PowiGaN 和 MinE-CAP 的 65 W 高功率密度适配器
- LDK120C11R 1.1V低压降稳压器典型应用(可调版)电路

【野火】《电机应用开发实战指南—基于STM32》
【得捷Follow me第4期】+小白手把手带你学习rp2040+w5500(所有任务)
非常经典的关于LLC的杨波博士论文
XC6406PP60DL






京公网安备 11010802033920号