前言
一:请先确保keil5的版本为5.30版本以上,笔者这里是5.36版本:
二:F4标准库的pack包本版是2.9.0以上,笔者这里是2.15版本:
上述资源可在https://zhuanlan.zhihu.com/p/262507061找到
提示:本工程创建用例基于正点原子的F407标准库例程
一、选择正点原子串口实验的工程
工程如下图所示:
把工程拷贝一份新工程到纯英文路径下,新工程打开后,点击魔法棒发现编译器默认是ARM Compiler 5版本,此时能够正常编译工程
二、用AC6编译纯C语言代码
1.打开魔法棒选择default compiler version6
2.编译工程
不出意外的话,会有很多的错误:
主要的错误应该是这个,也就是未定义vfpcc:
3.更改包含头文件依赖
这里的解决主要是参考AC5编译器 转换到 AC6 编译器 错误 error: unknown register name vfpcc in asm的解决方法
这里有三种方法解决,要么更改头文件路径,要么把新版本CMSIS文件放到工程目录里面,要么是在keil的Mange Run-Time Environment里面选上CMSIS/CORE ,后两种比较适合工程移动到其他PC运行。
方法一:把工程头文件路径的\Core换成\keil_V5\packs\ARM\CMSIS\5.8.0\CMSIS\Core\Include
比如我安装的是在D盘
方法二:删除工程CORE里面的.h文件,
再把\keil_V5\packs\ARM\CMSIS\5.8.0\CMSIS\Core\Include里的文件放到里面
(startup_stm32f40_41xxx.s不用动,因为我观察发现新旧版本的启动文件没有代码的变化,只是注释有变化)
方法三:先把原工程包含文件目录的CORE路径删除
然后点击Mange Run-Time Environment
把CMSIS/CORE打勾
4.修改旧版代码
此时重新编译会发现错误减少了,剩下的错误都是AC6不支持的语法代码了,我们要修改正点原子例程的代码来适配AC6
错误有 #pragma import(__use_no_semihosting) 、__asm void WFI_SET(void)、__FILE,这些都是旧版编译器AC5的特定语法,新版AC6编译器已经不支持了,分别要把原代码报错的部分如下
①
②
修改成如下格式同时兼容AC5和AC6编译器,其中__CC_ARM是AC5编译器定义的标识,GNUC 和__clang__是AC6定义的标识,由此判断编译器版本,这里主要参考了
超级无敌让stm32的printf兼容MDK各种编译器的方法
[keil5]从AC5到AC6的转变
①
#if 1
#ifdef __CC_ARM
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
};
#elif defined ( __GNUC__ ) || defined (__clang__)
__asm (".global __use_no_semihosting\n\t");
#endif
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
②
#ifdef __CC_ARM
__asm void WFI_SET(void)
{
WFI;
}
//关闭所有中断(但是不包括fault和NMI中断)
__asm void INTX_DISABLE(void)
{
CPSID I
BX LR
}
//开启所有中断
__asm void INTX_ENABLE(void)
{
CPSIE I
BX LR
}
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(u32 addr)
{
MSR MSP, r0 //set Main Stack value
BX r14
}
#elif defined ( __GNUC__ ) || defined (__clang__)
void WFI_SET(void)
{
__ASM volatile("WFI");
}
//关闭所有中断(但是不包括fault和NMI中断)
void INTX_DISABLE(void)
{
__ASM volatile("CPSID I");
__ASM volatile("BX LR");
}
//开启所有中断
void INTX_ENABLE(void)
{
__ASM volatile("CPSIE I");
__ASM volatile("BX LR");
}
//设置栈顶地址
//addr:栈顶地址
void MSR_MSP(u32 addr)
{
__ASM volatile("MSR MSP, r0"); //set Main Stack value
__ASM volatile("BX r14");
}
#endif
5.重新编译
此时可以看到编译已经通过了,可以烧到F407探索者板子里看效果了,因为我手头上还没有板子,就不演示了
6.烧录程序并查看效果
把程序烧进板子后可看到MCU已经通过串口发送数据了
三、用C++编写代码
1.选择C++方式编译
之前我们做的只是把工程从AC5编译器更换到AC6,因为要体验完整的现代C++功能必须要升级到ARM Compiler 6,那么接下来我们就要把工程用C++编译了。
先右键main.c文件设置一下属性,选择Options for File ‘main.c’
然后改成C++文件
2.修改代码并编译
此时直接编译会有如下报错:
因为原工程都是按照C语言方式编写代码,所以要添加C语言的兼容性处理如下图所示:
然后重新编译就无报错了。
这里会有一个空循环的warning,虽然目前程序有无影响,但我尝试了更新了标准库版本,但还是有warning,不过既然没影响那就忽略,想了解怎么更换新版本标准库的,可参考下面的注意2.更换新版本的STD库
3.用C++重写printf重定向
按上图所示,此时虽然工程可以正常编译,但一旦在主函数中添加了iostream等库,会报错说__stdout重定义了
这是因为ac6里的BUG,具体原因我也不知道,大概是C++的iostream和stdio有冲突,所以必须要重写重定向函数,具体的解决方法我这里参考了STM32 C++编程系列2.5:让Keil MDK工程支持现代C++特性及填坑
打开Mange Run-Time Environment,进入Compiler->I/O,将里面的STDERR、STDIN、STDOUT勾选上,如下图所示:
然后在usart.c代码里重写半主机模式的重定义函数:
#if 1
#ifdef __CC_ARM
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
#elif defined ( __GNUC__ ) || defined (__clang__)
//__asm (".global __use_no_semihosting\n\t");
#endif
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
其实就是把半主机模式删除了,因为官方已经自动添加进官方版本的重定向代码,我们只用保留重定向调用的fputc函数就行了,然后就能成功编译了,烧进板子里面可看到同样的效果。
注意
1.AC6工程不要用中文路径
Arm compiler 6对中文路径支持不够友好,如果工程建立在中文路径下,编译后右键点击Go To Definition of "xxxx"会无法跳转到相应函数的位置,必须要把工程建立在纯英文路径下。
2.更换新版本的STD库
如上面所说,当工程编译通过后会有个空循环的warning,这里我也不知道要不要处理,不知道对工程效果有没有影响
我怀疑是正点原子用的F4 pack包比较旧,以为他们用的是V1.4.0 固件库包的固件包:
现在已经更新到了V1.9.0版本了,所以从官网下载最新固件(可参考STM32标准外设库(标准库)官网下载方法,附带2021最新标准固件库下载链接)后把新版本标准库里面文件覆盖原工程源码里面(具体操作可参考正点原子的STM32F4开发指南-库函数版本_V1.2.pdf里面的3.32节新建模板工程)
我在这里也简单说一下,因为只是替换成新文件,不像新建工程那样麻烦
①STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Project\STM32F4xx_StdPeriph_Templates,把如图的四个文件覆盖到原工程的USER里面
②再进入STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Libraries\CMSIS\Device\ST\STM32F4xx\Include里把system_stm32f4xx.h 和stm32f4xx.h覆盖到原工程
③确保这六个文件都覆盖了之后
再进入标准库的STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Libraries\STM32F4xx_StdPeriph_Driver文件夹,把新的inc和src文件夹取代原工程的inc和src文件夹,这里建议先把原工程的文件夹里面的内容清空
④此时编译会有一个error
处理方法正点原子的教程里也有说明,先注释main.h头文件,然后再注释TimingDelay_Decrement()函数。
⑤再把PLL 第一级分频系数 M 修改为 8
⑥重新编译(Rebuild)后warning更多了,而且原来的空循环还存在
对此我也不知道为什么,也不知道对程序功能有没有影响,我会放出原工程供大家参考一下,有搞得原由的可评论区说一下
3.更多AC5转换到AC6的代码对应,可参考下图
(来自https://blog.csdn.net/weixin_43644424/article/details/125048889)
文章来源:https://www.toymoban.com/news/detail-607817.html
工程下载链接
stm32f4标准库C++与C混合开发工程文章来源地址https://www.toymoban.com/news/detail-607817.html
到了这里,关于【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!