在使用ucos ii时经常会有疑问,这个堆栈该给多大,虽然ucos ii 自带了任务堆栈检测,但是我觉得太麻烦了,而且还会占用资源,我投机取巧的使用内存是否为0来判断堆栈是否使用过,进而检测出堆栈的最大使用量,我的这个方法能快速的检测出历史堆栈的最大使用量,你的实际堆栈大小必须大于历史最大使用量。
原理就是将任务堆栈使用static标记,这样任务堆栈数组都会被清零,使用过程中随着数据入栈(变为非0了,全部入栈都是0的可能性微乎其微),然后数据虽然出栈了,但是并没有清零(也没必要),通过从栈底找到第一个非0的地方(就是被使用过),这样就类似水库中的水在墙壁上留下的印记,就知道历史最高水位一样,通过这个方法可以获取ucos ii的最大堆栈消耗甚至可以知道系统的最大堆栈消耗,通过长期的运行,获取到系统的最大堆栈占用量,能够合理的给每个任务分配堆栈大小。
/*************************************************************************************************************
* 文件名: TaskStackMonitor.c
* 功能: UCOS II 任务堆栈监视
* 作者: [email protected]
* 创建时间: 2019-01-25
* 最后修改时间: 2019-01-25
* 详细: 在最低优先级任务中进行调用,会监控所有任务的堆栈使用量,并记录最大值,最小值,同时记录CPU的最大与最小使用量
通过监测足够长的时间,可以保证任务堆栈的合理性
2019-01-28:增加CPU监控显示,增加socket状态显示
*************************************************************************************************************/
#include "system.h"
#include "usart.h"
#include "main.h"
#include "string.h"
#include "board.h"
#include "rtc.h"
#include "wdg.h"
#include "board.h"
#include "w5500_socket.h"
//从堆栈底部往上找到第一个非0位置,返回偏移:字
u32 FindFirstNonzeroPosition(u32 *pStackBottom, u32 StackSize);
//线程状态监控
void TaskStackMonitor(void)
{
u32 i;
u32 StackBottomAddr; //栈底地址
u32 StackResidual; //堆栈剩余
uart_printf("\r\n----------------任务状态----------------\r\n");
for(i = 0;i < sizeof(cg_TaskBuff)/sizeof(TASK_STACK_INFO);i ++)
{
StackBottomAddr = cg_TaskBuff[i].StackTopAddr+4;// - (cg_TaskBuff[i].StackSize*4) + 4; //计算栈底地址
StackResidual = FindFirstNonzeroPosition((u32 *)StackBottomAddr, cg_TaskBuff[i].StackSize); //计算使用量
uart_printf("任务%d,堆栈剩余:%d,使用量:%%%d\r\n", cg_TaskBuff[i].Proi, StackResidual, (cg_TaskBuff[i].StackSize-StackResidual)*100/cg_TaskBuff[i].StackSize);
}
uart_printf("CPU占用率:%%%d\r\n", OSCPUUsage);
//PrintfSocketsStatus(); //调试打印socket状态
}
//从堆栈底部往上找到第一个非0位置,返回偏移:字
u32 FindFirstNonzeroPosition(u32 *pStackBottom, u32 StackSize)
{
u32 i = 0;
for(i = 0;i < StackSize;i ++)
{
if(pStackBottom[i] != 0) break;
}
return i;
}
/*************************************************************************************************************
* 文件名: TaskStackMonitor.h
* 功能: UCOS II 任务堆栈监视
* 作者: [email protected]
* 创建时间: 2019-01-25
* 最后修改时间: 2019-01-25
* 详细: 在最低优先级任务中进行调用,会监控所有任务的堆栈使用量,并记录最大值,最小值,同时记录CPU的最大与最小使用量
通过监测足够长的时间,可以保证任务堆栈的合理性
*************************************************************************************************************/
#ifndef __TASK_STACK_MONITOR_H__
#define __TASK_STACK_MONITOR_H__
#include "system.h"
#include "ucos_ii.h"
#include "rtc.h"
#include "board.h"
#include "RTU.h"
//任务堆栈信息
typedef struct
{
u32 StackTopAddr; //任务栈顶地址(注意:堆栈为向下生长)
u32 StackSize; //任务堆栈大小(单位:CPU字长,STM32为4字节,32bit)
u8 Proi; //任务的优先级
}TASK_STACK_INFO;
void TaskStackMonitor(void);//任务堆栈监视
#endif //__TASK_STACK_MONITOR_H__
//任务堆栈指针相关的数组,放进去就可以了
//任务堆栈声明
static OS_STK TASK_MODBUS1_STK[MODBUS1_STK_SIZE]; //MODBUS 通信线程1
static OS_STK TASK_MODBUS2_STK[MODBUS2_STK_SIZE]; //MODBUS 通信线程2
static OS_STK TASK_MODBUS3_STK[MODBUS3_STK_SIZE]; //MODBUS 通信线程3 -扩展的TTL串口
static __align(8) OS_STK TASK_SYSTEM_STK[SYSTEM_STK_SIZE]; //系统主进程
static OS_STK TASK_GPRS_STK[GPRS_STK_SIZE]; //GPRS进程
static OS_STK TASK_BACK_STK[BACK_STK_SIZE]; //BACK进程
static OS_STK TASK_ASSIST_STK[ASSIST_STK_SIZE]; //ASSIST进程
static OS_STK TASK_COLL_STK[COLL_STK_SIZE]; //数据采集时间查询进程
static OS_STK TASK_KEY_STK[KEY_STK_SIZE]; //按钮查询进程
static OS_STK TASK_DEBUG_STK[DEBUG_STK_SIZE]; //DEBUG进程
static OS_STK TASK_W5500_STK[W5500_STK_SIZE]; //W5500进程
static OS_STK TASK_OVERLAY_STK[OVERLAY_STK_SIZE]; //视频叠加进程
static OS_STK TASK_SERVER_STK[SERVER_STK_SIZE]; //UDP Server服务器
//任务记录
const TASK_STACK_INFO cg_TaskBuff[13] =
{
{(u32)TASK_MODBUS1_STK, MODBUS1_STK_SIZE, MODBUS1_TASK_Prio},
{(u32)TASK_MODBUS2_STK, MODBUS2_STK_SIZE, MODBUS2_TASK_Prio},
{(u32)TASK_MODBUS3_STK, MODBUS3_STK_SIZE, MODBUS3_TASK_Prio},
{(u32)TASK_SYSTEM_STK, SYSTEM_STK_SIZE, SYSTEM_TASK_Prio},
{(u32)TASK_GPRS_STK, GPRS_STK_SIZE, GPRS_TASK_Prio},
{(u32)TASK_BACK_STK, BACK_STK_SIZE, BACK_TASK_Prio},
{(u32)TASK_COLL_STK, ASSIST_STK_SIZE, ASSIST_TASK_Prio},
{(u32)TASK_COLL_STK, COLL_STK_SIZE, COLL_TASK_Prio},
{(u32)TASK_KEY_STK, KEY_STK_SIZE, KEY_TASK_Prio},
{(u32)TASK_DEBUG_STK, DEBUG_STK_SIZE, DEBUG_TASK_Prio},
{(u32)TASK_W5500_STK, W5500_STK_SIZE, W5500_TASK_Prio},
{(u32)TASK_OVERLAY_STK, OVERLAY_STK_SIZE, OVERLAY_TASK_Prio},
{(u32)TASK_SERVER_STK, SERVER_STK_SIZE, SERVER_TASK_Prio},
};
//使用
TaskStackMonitor(); //任务堆栈监视
运行时间越久,这个越准,因为堆栈的占用是动态的,随着你的代码逻辑会变化。
//最终结果