龙空技术网

开发项目,分不清STM32栈的使用率?一文解析清楚

西小岑 945

前言:

如今咱们对“堆栈使用率行业标准”大概比较注重,兄弟们都需要分析一些“堆栈使用率行业标准”的相关文章。那么小编在网摘上汇集了一些关于“堆栈使用率行业标准””的相关内容,希望咱们能喜欢,朋友们快快来学习一下吧!

#单片机##嵌入式编程##RTOS##程序员#

1、前言

大部分从事STM32单片机嵌入式开发工作N年的人,项目也做了N个,是否曾经程序无缘无故跑飞了?系统无缘无故地重启?是否无缘无故发生“HardFault”呢?是否试着把栈调大一些,问题就少了,再调大一些,问题就不发生了呢?这些问题都是对栈大小的拿捏问题,即是否配置了合适的栈大小?那么多少算合适呢?有没有量化标准呢?关于RAM分配使用问题可以参考《从事开发多年,分不清STM32的RAM使用情况?一图弄清它 》包含已分配使用部分与剩余多少均有测试方法。

2、量化栈使用率

如何量化栈使用率,我们通常使用百分比来量化某些参数,比如考试100分满分60分及格,这里我们也使用百分比来量化栈使用率。量化公式如下:

栈使用率=100X历史使用栈大小/分配栈大小

注意:下文计算栈使用率时计算单位为字而不是字节(即四字节为一个计量单位)。

特别规定,系统连续运行一定时间(如24小时)后“栈使用率”不超过60%为及格。要注意的是这里我们是以不超60%为及格与考试不一样哟,与考驾照更不一样。要是达到90%及以上就等着系统崩溃吧。

3、堆栈的区别

在大部分时候我们都是将它拿到一起说的称之为“堆栈”以至于分开来说还有些人不明白,事实上堆与栈是两个不同的东西,即堆就是堆,栈就是栈。

堆:想象一下地面上有一堆沙,沙越多就堆得越高,如下图所示:

堆的类比图

如上图,堆形象地说就像堆沙堆一样,其地址从低到高向上增长,越堆越高,越拿走越低/少。

栈:其地址是从高到低向下增长的。想象有一条栈道,如下图所示:

栈的类比图

把堆与栈合并到一起如下图所示(以一个工程实例分配说明)

堆与栈结构图

上图是我一个工程堆栈分配的地址值,不同项目,或者随着项目功能的变化其值也会变化,或者改变S文件中堆栈大小配置项,其值也会变化。此处仅举例接近实现情况。

补充说明:上面说的向下或向上增长,指的都是地址。

4、统计栈历史使用率(百分比)

在文章《从事开发多年,分不清STM32的RAM使用情况?一图弄清它 》中已经介绍了栈顶地址的获取方法(即读取地址0x08000000处的值),再引用S文件中栈大小的值即可知道栈的大概情况;现在我们的任务是要统计栈使用率,所以首要任务是初始化栈区为某个已知值(比如0);然后从栈底开始读取栈的值,再与初始化值比较,与初始值相等即为尚未使用过,未使用计算器加1,如此往复读取整个栈区的地址空间,直到遇到于初始值不一致时退出读栈过程。这样就得到未使用部分的空间;据此即可计算栈的使用率。如下图所示:

计算栈使用率

5、测试栈使用率

为了测栈使用率,我定义两个不同函数,其内部定义不同数量的局部变量,调用函数前后统计栈使用率:我在配置栈总大小4096字节,函数Test1()中定义局部变量1000字节(使用率约24%),函数Test1()中定义局部变量3000字节(使用率约73%)。如下图所示:

6、关键测试代码如下

#define APP_START_ADDRESS 		0x08000000			//开始地址(栈顶地址)#define RAM_START_ADDRESS 		0x20000000			//RAM开始地址#define APP_STACK_SIZE			0x1000				//栈大小(字节数量)4KB#define APP_HEAP_SIZE			0x1000				//堆大小(字节数量)4KB#define MCU_RAM_SIZE			0x5000				//20KB// --------------------------------------------------#define BUFF_SIZE				1024				//定义公共大小	char buff[BUFF_SIZE];								//定义公共变量int GetGloablVarSum(void){	int i,sum=0;									//局部变量	for(i=0;i<BUFF_SIZE;i++)						//公共变量初始化			{		buff[i]=i;		sum+=buff[i];								//计算功能变量的和	}	return sum;										//返回计算和		}// --------------------------------------------------int GetHeapVarSum(void){	int i,sum=0;									//定义公共变量	char *p1,*p2=(char*)malloc(BUFF_SIZE);			//从堆区申请内存	p1=p2;	memcpy(p2,buff,BUFF_SIZE);						//将公共变量的值拷贝到申请的内存中			p1=p2;	for(i=0;i<BUFF_SIZE;i++)						//计算申请内存中值的和			{		sum+=*p2++;	}	return sum;										//返回计算和}// --------------------------------------------------void TellAboutRam(){	unsigned int StackTopAddr=(*(__IO u32*)APP_START_ADDRESS);				//获取栈顶地址	unsigned int HeapTopAddr=StackTopAddr-APP_STACK_SIZE;					//获取堆顶/栈底地址	unsigned int GlobalVarSize=HeapTopAddr-APP_HEAP_SIZE-RAM_START_ADDRESS;	//计算公共变量区域大小	unsigned int RamFreeSize=MCU_RAM_SIZE-(StackTopAddr-RAM_START_ADDRESS);	//计算空闲区大小	printf ("栈	顶\t=\t%08XH \n",StackTopAddr); 	printf ("堆	顶\t=\t%08XH \n",HeapTopAddr); 	printf ("未分配区\t=\t%d Byte\n",RamFreeSize);	printf ("栈	区\t=\t%d Byte\n",APP_STACK_SIZE);	printf ("堆	区\t=\t%d Byte\n",APP_HEAP_SIZE);	printf ("公共变量区\t=\t%0d Byte\n",GlobalVarSize);}// --------------------------------------------------u8 GetMcuStackUsePercent(u8 temp){	u8 StackUsePercent=0;							//栈使用百分比	u32 i;	u32 StackTopAddr=0;								//栈顶地址	u32 StackAddrNow=0;								//栈当前地址(读值时用)	u32 StackTotalWordSize=0;						//栈的字大小	u32 StackData=0;								//栈中的数据	u32 StackUnuseWordSize=0;						//栈中未使用字数量统计	StackTotalWordSize=APP_STACK_SIZE/4;			//计算栈控制字数量	StackTopAddr=(*(__IO u32*)APP_START_ADDRESS);	//读取栈顶地址	StackAddrNow=StackTopAddr-APP_STACK_SIZE;		//计算栈底地址	for(i=0;i<StackTotalWordSize;i++)	{		StackData=(*(__IO u32*)StackAddrNow);		//读取栈地址处的值		StackAddrNow+=4;							//地址往栈顶增加				if(StackData==0)							//将栈数据与初始值比较		{			StackUnuseWordSize++;					//栈数据==初始值		}		else		{			break;									//栈数据不等于初始值,退出统计过程		}	}													//计算栈使用率	StackUsePercent=100*(StackTotalWordSize-StackUnuseWordSize)/StackTotalWordSize;	#if 0		printf(" ------------- %d -------------\r\n",temp);		printf(" 1 APP_START_ADDRESS=%08XH \r\n",APP_START_ADDRESS);		printf(" 2 Stack Addr Top=%08XH Buttom=%08XH\r\n",StackTopAddr,StackTopAddr-APP_STACK_SIZE);		printf(" 3 STACK_SIZE=%04XH %d Bytes\r\n",APP_STACK_SIZE,APP_STACK_SIZE);		printf(" 4 StackSize Total=%d Unuse=%d Used=%d\r\n",StackTotalWordSize,StackUnuseWordSize,StackTotalWordSize-StackUnuseWordSize);	#endif	return StackUsePercent;							//返回栈使用率}void Test1(void){	char temp[1000];			//局部变量(1000/4096=0.244=24%)	int i,sum=0;				//局部变量	for(i=0;i<1000;i++)										{		temp[i]=i;		sum+=temp[i];			//计算功能变量的和	}}void Test2(void){	char temp[3000];			//局部变量(3000/4096=0.732=73.2%)	int i,sum=0;				//局部变量	for(i=0;i<3000;i++)										{		temp[i]=i;		sum+=temp[i];			//计算功能变量的和	}}int main (void)  {              	SER_Init ();                  	printf ("------------- Hello World ------------\n");      	printf ("测试公共变量\t=\t%d \t\n",GetGloablVarSum());      	printf ("测	堆区\t=\t%d \t\n",GetHeapVarSum()); 	TellAboutRam();	printf(" Test1前 栈使用率=%d%%\r\n",GetMcuStackUsePercent(1));		Test1();	printf(" Test1后 栈使用率=%d%%\r\n",GetMcuStackUsePercent(1));	Test2();	printf(" Test2后 栈使用率=%d%%\r\n",GetMcuStackUsePercent(1));		while (1) 	{                   		DelayMs(1000);		}                           }
7、仿真测试

测试结果如下图

仿真测试栈使用百分比

8、总结

统计栈使用率很简单,关键是需要得到栈顶地址与栈大小。还有其他方法欢迎留言交流。喜欢我的文章请关注收藏。

标签: #堆栈使用率行业标准