关于keil卡在systeminit中,然后出现void HardFault_Handler(void)的几个问题详解

版权声明:本文为博主原创文章,未经博主允许不得转载。    https://blog.csdn.net/u013184273/article/details/83860802

相信很多程序员在用keil的时候都会遇到在仿真调试时,一直停在SystemInit()中的等待晶振中,怎么也出不来。出现HardFault时,可能不是因为内存溢出,访问越界或是堆栈溢出,有可能仅仅只是自己一些最基本的规则没弄清楚而导致,所以分享下自己一些小问题,让新手朋友们出现类似问题可以借鉴下;


STM32启动文件简单解析(V3.5.0) 以:SystemInit函数详解

具体的函数调用顺序如下:
①startup_stm32f10x_hd.s(启动文件)→②SystemInit()→③SetSysClock ()→④SetSysClockTo72()
说明:由于在上面只定义了SYSCLK_FREQ_72MHz,因而在执行SetSysClock ()函数的时候,会选择进入SetSysClockTo72()函数,以设置系统时钟为72MHz
如果还是想深究SystemInit()函数,那你就打开浏览器上百度或者谷歌进行搜索,网上自来一大堆的。。。。。。。俗话说:小孩勤,喜欢人。

话不多说,进入正题:

当我们仿真一段程序,进入仿真界面的时候,首先是一下这个图:关于keil卡在systeminit中,然后出现void HardFault_Handler(void)的几个问题详解_第1张图片 

这是DEBUG的程序的入口,对,就是这几个汇编的启动代码。如果想知道启动文件的详情,那就打开浏览器上百度或者谷歌进行搜索,网上自来一大堆的。。。。。。。俗话说:小孩勤,喜欢人。

接下来看到  SystemInit函数,按F12进入这个函数你会看到:

关于keil卡在systeminit中,然后出现void HardFault_Handler(void)的几个问题详解_第2张图片

如果想知道这个函数是干什么的,那就打开浏览器上百度或者谷歌进行搜索,网上自来一大堆的。。。。。。。俗话说:小孩勤,喜欢人。

你会看到    RCC->CR |= (uint32_t)0x00000001;  这一句话代表:操作时钟控制寄存器,将内部8M高速时钟使能,从这里可以看出系统启动后是首先依靠内部时钟源而工作的。那么问题来了,所谓的一直卡在SystemInit(),其实是卡在RCC->CR |= (uint32_t)0x00000001;  这一句(不是很绝对),

要怎么解决呢?分为3种情况:

A:

1.选上”Use MicroLIB”这是KEIL自带的一个简易的库,例如你用printf函数的时候,就会从串口1 输出字符串,直接默认定向到串口1

关于keil卡在systeminit中,然后出现void HardFault_Handler(void)的几个问题详解_第3张图片

2.microlib 是缺省 C 库的备选库。 它用于必须在极少量内存环境下运行的深层嵌入式应用程序。 这些应用程序不在操作系统中运行。microlib 不会尝试成为符合标准的 ISO C 库。 
microlib 进行了高度优化以使代码变得很小。 它的功能比缺省 C 库少,并且根本不具备某些 ISO C 特性。某些库函数的运行速度也比较慢,例如,memcpy()。 

3.

microlib 与缺省 C 库之间的主要差异是: 

microlib 不符合 ISO C 库标准。 不支持某些 ISO 特性,并且其他特性具有的功能也较少。 

microlib 不符合 IEEE 754 二进制浮点算法标准。 

microlib 进行了高度优化以使代码变得很小。 

无法对区域设置进行配置。 缺省 C 区域设置是唯一可用的区域设置。 

不能将 main() 声明为使用参数,并且不能返回内容。 

不支持 stdio,但未缓冲的 stdin、stdout 和 stderr 除外。 

microlib 对 C99 函数提供有限的支持。 

microlib 不支持操作系统函数。 

microlib 不支持与位置无关的代码。 

microlib 不提供互斥锁来防止非线程安全的代码。 

microlib 不支持宽字符或多字节字符串。 

与 stdlib 不同,microlib 不支持可选择的单或双区内存模型。 microlib 只提供双区内存模型,即单独的堆栈和堆区。 

可以合理地将 microlib 与 --fpmode=std 或 --fpmode=fast 配合使用。 
microlib 中的函数负责: 

创建一个可在其中执行 C 程序的环境。 这包括: 

创建一个堆栈 

创建一个堆(如果需要) 

初始化程序所用的库的部分组成内容。 

调用 main() 以开始执行程序。 

要使用 microlib 构建程序,必须使用命令行选项 ??library_type=microlib。 根据需要,编译器、汇编程序或链接器可使用此选项处理不同的文件。 将此选项与链接器配合使用时,将覆盖所有其他选项。

4.


//加入以下代码,支持printf函数,而不需要选择use MicroLIB 
#if 1 
#pragma import(__use_no_semihosting)              
//标准库需要的支持函数                  
struct __FILE  
{  
int handle;  

};  

FILE __stdout;        
//定义_sys_exit()以避免使用半主机模式     
_sys_exit(int x)  
{  
x = x;  
}  
//重定义fputc函数  
int fputc(int ch, FILE *f) 
{       
#ifdef COM_EX 
com_snd(COM2, 1, (unsigned char*)&ch); 
#else 
com_snd(COM1, 1, (unsigned char*)&ch); 
#endif 
return ch; 

#endif  

/*使用microLib的方法,在keil里面要勾选“Use MicroLIB”*/ 
// 以便使用 printf 函数 
// #define UTCHAR_PROTOTYPE int fputc(int ch, FILE *f) 
// UTCHAR_PROTOTYPE 
// { 
// #ifdef COM_EX 
//  com_snd(COM2, 1, (unsigned char*)&ch); 
// #else 
//  com_snd(COM1, 1, (unsigned char*)&ch); 
// #endif 
//  
//  return ch; 
// }  
我试了试确实是可以,不知道,如果不用Use MicroLIB,prinft 和sprintf  其他影响不,我去验证一下

B:

1、采用target对话框中的ROM和RAM地址

   采用此方式,需在Linker选项卡中勾选Use Memory Layout from Target Dialog选项(选中这一项实际上是默认在Target中对Flash和RAM的地址配置,编译链接时会产生一个默认的脚本文件),并且在Target中设置好RAM、ROM地址,图2所示。MDK会根据Target选项中设定的RAM和ROM地址自动加载生成一个加载文件。最后链接器会根据此文件中的信息对目标文件进行链接,生成axf镜像文件。

 

关于keil卡在systeminit中,然后出现void HardFault_Handler(void)的几个问题详解_第4张图片

C:

选项One ELF Section per Function

关于keil卡在systeminit中,然后出现void HardFault_Handler(void)的几个问题详解_第5张图片

选项One ELF Section per Function的主要功能是对冗余函数的优化。通过这个选项,可以在最后生成的二进制文件中将冗余函数排除掉(虽然其所在的文件已经参与了编译链接),以便最大程度地优化最后生成的二进制代码。

而该选项实现的机制是将每一个函数作为一个优化的单元,而并非整个文件作为参与优化的单元。

选项One ELF Section per Function所具有的这种优化功能特别重要,尤其是在对于生成的二进制文件大小有严格要求的场合。人们习惯将一系列接口函数放在一个文件里,然后将其整个包含在工程中,即使这个文件将只有一个函数被用到。这样,最后生成的二进制文件中就有可能包含众多的冗余函数,造成了宝贵存储空间的浪费。

选项One ELF Section per Function对于一个大工程的优化效果尤其突出,有时候甚至可以达到减半的效果。当然,对于小工程或是少有冗余函数的工程来说,其优化效果就没有那么明显了。

以上三种方法是解决系统启动卡在SystemInit的方法,有不对的地方欢迎指出,鄙人马上修正。

也许你遇不到,也许你能遇到,反正它就在那里。

就像我们做了一道算术题,很多人会想,怎么老遇到着急不会的题呢?有没有搞错啊。。。。。。

但是就有那么少些人会说:我真幸运,又遇到了一到自己不会的算术题。

态度决定高度,细节决定成败!!!


 

你可能感兴趣的:(STM32)