【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程

文章目录

  • 前言
  • 一、选择正点原子串口实验的工程
  • 二、用AC6编译纯C语言代码
    • 1.打开魔法棒选择default compiler version6
    • 2.编译工程
    • 3.更改包含头文件依赖
    • 4.修改旧版代码
    • 5.重新编译
    • 6.烧录程序并查看效果
  • 三、用C++编写代码
    • 1.选择C++方式编译
    • 2.修改代码并编译
    • 3.用C++重写printf重定向
  • 注意
    • 1.AC6工程不要用中文路径
    • 2.更换新版本的STD库
    • 3.更多AC5转换到AC6的代码对应,可参考下图
  • 工程下载链接


前言

一:请先确保keil5的版本为5.30版本以上,笔者这里是5.36版本:
keil5版本
二:F4标准库的pack包本版是2.9.0以上,笔者这里是2.15版本:
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第1张图片

上述资源可在https://zhuanlan.zhihu.com/p/262507061找到
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第2张图片
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第3张图片


提示:本工程创建用例基于正点原子的F407标准库例程

一、选择正点原子串口实验的工程

工程如下图所示:
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第4张图片

把工程拷贝一份新工程到纯英文路径下,新工程打开后,点击魔法棒发现编译器默认是ARM Compiler 5版本,此时能够正常编译工程
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第5张图片

【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第6张图片

二、用AC6编译纯C语言代码

1.打开魔法棒选择default compiler version6

【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第7张图片
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第8张图片

2.编译工程

不出意外的话,会有很多的错误:
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第9张图片

主要的错误应该是这个,也就是未定义vfpcc:
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第10张图片

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
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第11张图片
比如我安装的是在D盘
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第12张图片
方法二:删除工程CORE里面的.h文件,
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第13张图片
再把\keil_V5\packs\ARM\CMSIS\5.8.0\CMSIS\Core\Include里的文件放到里面
(startup_stm32f40_41xxx.s不用动,因为我观察发现新旧版本的启动文件没有代码的变化,只是注释有变化)
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第14张图片
方法三:先把原工程包含文件目录的CORE路径删除
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第15张图片
然后点击Mange Run-Time Environment
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第16张图片
把CMSIS/CORE打勾
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第17张图片

4.修改旧版代码

此时重新编译会发现错误减少了,剩下的错误都是AC6不支持的语法代码了,我们要修改正点原子例程的代码来适配AC6
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第18张图片
错误有 #pragma import(__use_no_semihosting) 、__asm void WFI_SET(void)、__FILE,这些都是旧版编译器AC5的特定语法,新版AC6编译器已经不支持了,分别要把原代码报错的部分如下
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第19张图片

【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第20张图片

修改成如下格式同时兼容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探索者板子里看效果了,因为我手头上还没有板子,就不演示了
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第21张图片

6.烧录程序并查看效果

把程序烧进板子后可看到MCU已经通过串口发送数据了
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第22张图片

三、用C++编写代码

1.选择C++方式编译

之前我们做的只是把工程从AC5编译器更换到AC6,因为要体验完整的现代C++功能必须要升级到ARM Compiler 6,那么接下来我们就要把工程用C++编译了。
先右键main.c文件设置一下属性,选择Options for File ‘main.c’
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第23张图片

然后改成C++文件
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第24张图片

2.修改代码并编译

此时直接编译会有如下报错:
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第25张图片

因为原工程都是按照C语言方式编写代码,所以要添加C语言的兼容性处理如下图所示:
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第26张图片
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第27张图片
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第28张图片
然后重新编译就无报错了。
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第29张图片

这里会有一个空循环的warning,虽然目前程序有无影响,但我尝试了更新了标准库版本,但还是有warning,不过既然没影响那就忽略,想了解怎么更换新版本标准库的,可参考下面的注意2.更换新版本的STD库

3.用C++重写printf重定向

按上图所示,此时虽然工程可以正常编译,但一旦在主函数中添加了iostream等库,会报错说__stdout重定义了
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第30张图片
这是因为ac6里的BUG,具体原因我也不知道,大概是C++的iostream和stdio有冲突,所以必须要重写重定向函数,具体的解决方法我这里参考了STM32 C++编程系列2.5:让Keil MDK工程支持现代C++特性及填坑
打开Mange Run-Time Environment,进入Compiler->I/O,将里面的STDERR、STDIN、STDOUT勾选上,如下图所示:
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第31张图片

然后在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函数就行了,然后就能成功编译了,烧进板子里面可看到同样的效果。
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第32张图片


注意

1.AC6工程不要用中文路径

Arm compiler 6对中文路径支持不够友好,如果工程建立在中文路径下,编译后右键点击Go To Definition of "xxxx"会无法跳转到相应函数的位置,必须要把工程建立在纯英文路径下。

2.更换新版本的STD库

如上面所说,当工程编译通过后会有个空循环的warning,这里我也不知道要不要处理,不知道对工程效果有没有影响
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第33张图片
我怀疑是正点原子用的F4 pack包比较旧,以为他们用的是V1.4.0 固件库包的固件包:
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第34张图片
现在已经更新到了V1.9.0版本了,所以从官网下载最新固件(可参考STM32标准外设库(标准库)官网下载方法,附带2021最新标准固件库下载链接)后把新版本标准库里面文件覆盖原工程源码里面(具体操作可参考正点原子的STM32F4开发指南-库函数版本_V1.2.pdf里面的3.32节新建模板工程
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第35张图片
我在这里也简单说一下,因为只是替换成新文件,不像新建工程那样麻烦
①STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Project\STM32F4xx_StdPeriph_Templates,把如图的四个文件覆盖到原工程的USER里面
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第36张图片
②再进入STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Libraries\CMSIS\Device\ST\STM32F4xx\Include里把system_stm32f4xx.hstm32f4xx.h覆盖到原工程
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第37张图片
③确保这六个文件都覆盖了之后
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第38张图片
再进入标准库的STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Libraries\STM32F4xx_StdPeriph_Driver文件夹,把新的inc和src文件夹取代原工程的inc和src文件夹,这里建议先把原工程的文件夹里面的内容清空
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第39张图片
④此时编译会有一个error
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第40张图片
处理方法正点原子的教程里也有说明,先注释在这里插入图片描述main.h头文件,然后再注释TimingDelay_Decrement()函数在这里插入图片描述
⑤再把PLL 第一级分频系数 M 修改为 8
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第41张图片

⑥重新编译(Rebuild)后warning更多了,而且原来的空循环还存在
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第42张图片
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第43张图片
对此我也不知道为什么,也不知道对程序功能有没有影响,我会放出原工程供大家参考一下,有搞得原由的可评论区说一下
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第44张图片

3.更多AC5转换到AC6的代码对应,可参考下图

(来自https://blog.csdn.net/weixin_43644424/article/details/125048889)
【stm32f4 C++与C混合开发】建立keil5的ARM Compiler 6(AC6)标准库开发工程_第45张图片

工程下载链接

stm32f4标准库C++与C混合开发工程

你可能感兴趣的:(stm32,c++,arm)