ubllp5ocauo6408388029.gif
点击上方蓝色字体,关注我们
许多 MCU 和 RTOS 提供了硬件(如 MPU - Memory Protection Unit)或软件(如 Stack Painting/Watermarking)机制来检测栈是否溢出。
Stack Painting 是在任务创建时,将其栈空间填充一个特殊值(如 0xCDCDCDCD),然后周期性检查栈底有多少这个值被覆盖了,从而了解栈的最大使用深度。
#define STACK_FILL_PATTERN 0xCDCDCDCD#define TASK_STACK_SIZE 1024 // Bytesuint8_t task_stack[TASK_STACK_SIZE];void InitializeTaskStack(uint8_t* stack_ptr, uint32_t stack_size) { uint32_t* pStack = (uint32_t*)stack_ptr; for (uint32_t i = 0; i sizeof(uint32_t); ++i) { pStack = STACK_FILL_PATTERN; }}// 在任务创建时调用 InitializeTaskStack(task_stack, TASK_STACK_SIZE);// 周期性检查函数uint32_t CheckStackHighWaterMark(uint8_t* stack_base, uint32_t stack_size) { uint32_t* pStack = (uint32_t*)stack_base; uint32_t unused_words = 0; // 从栈底向上检查,直到找到第一个非填充值 for (uint32_t i = 0; i sizeof(uint32_t); ++i) { if (pStack == STACK_FILL_PATTERN) { unused_words++; } else { break; // 已经到达被使用的区域 } } uint32_t used_bytes = stack_size - (unused_words * sizeof(uint32_t)); return used_bytes; }// 在监控任务或调试时调用// uint32_t max_stack_usage = CheckStackHighWaterMark(task_stack, TASK_STACK_SIZE);// printf("Task stack usage: %u bytes
", max_stack_usage);
3
实时性能
对于需要精确时间响应的系统(如控制系统、通信协议栈),实时性能至关重要。
关键指标:
中断延迟:从中断请求发生到中断服务程序 (ISR) 第一条指令开始执行的时间。任务响应时间:从事件发生(如中断、信号量释放)到相应处理任务开始执行的时间。任务完成时间:从任务开始执行到任务完成的时间。抖动:同一个事件的响应时间或完成时间的变化量。
在关键时间点(如中断入口/出口、任务开始/结束、事件触发点)翻转 GPIO,用示波器或逻辑分析仪精确测量时间间隔。这是最常用且直观的方法。
// 假设 PIN_ISR_ENTRY 连接到示波器通道 1// 假设 PIN_INT_TRIGGER 连接到示波器通道 2 (用于观察外部触发)#define PIN_ISR_ENTRY PB0#define PIN_INT_TRIGGER PC5 // 假设外部事件触发此引脚中断volatileuint64_t start_time = 0;volatileuint64_t isr_entry_time = 0;volatileuint32_t latency = 0;// 中断服务程序void EXTI5_IRQHandler(void) { // 第一件事:拉高引脚,标记 ISR 入口 SetPinHigh(PIN_ISR_ENTRY); isr_entry_time = GetHighResolutionTimestamp(); // 获取时间戳 // 计算延迟 (如果需要软件计算的话) // 注意:这里的 start_time 需要在触发中断的代码附近获取, // 且要考虑 GetHighResolutionTimestamp 本身的开销 // latency = isr_entry_time - start_time; // ... 处理中断 ... // 清除中断标志位 ClearInterruptFlag(EXTI_LINE_5); // 最后:拉低引脚,标记 ISR 出口 SetPinLow(PIN_ISR_ENTRY);}int main() { InitializeGPIO(PIN_ISR_ENTRY, OUTPUT); SetPinLow(PIN_ISR_ENTRY); // 初始为低 InitializeGPIO(PIN_INT_TRIGGER, INPUT_INTERRUPT); // 配置为中断输入 ConfigureInterrupt(EXTI_LINE_5, RISING_EDGE, EXTI5_IRQHandler); EnableInterrupt(EXTI_LINE_5); EnableGlobalInterrupts(); while(1) { // ... 主循环任务 ... // 模拟触发中断 (或者等待外部物理触发 PIN_INT_TRIGGER) // 如果是软件触发测试: // start_time = GetHighResolutionTimestamp(); // 记录触发前时间戳 // TriggerSoftwareInterrupt(EXTI_LINE_5); // 等待外部触发时,示波器直接测量 PIN_INT_TRIGGER 上升沿 // 到 PIN_ISR_ENTRY 上升沿的时间差即可得到硬件中断延迟。 } return0;}
如 Segger SystemView、Tracealyzer 等工具可以提供非常详细的系统事件追踪,包括中断、任务切换、API 调用等,并自动分析时间性能。
4
外设带宽
有时瓶颈不在 CPU 或内存,而在于外设(如 UART, SPI, I2C, ADC, DAC, USB 等)的数据处理能力。
如何测量:
理论计算:根据外设的时钟频率、配置(如波特率、采样率)计算理论上的最大数据传输速率。实际吞吐量测试:在特定时间内发送或接收大量数据,统计实际成功传输的数据量,计算实际速率。缓冲区监控:检查外设驱动程序的发送/接收缓冲区是否经常处于满或空的状态。例如,UART 接收缓冲区频繁溢出,表明 CPU 处理数据的速度跟不上接收速度。DMA 效率:如果使用 DMA,检查 DMA 传输完成所需时间以及 DMA 控制器本身的负载(如果可测量)。
5
功耗与温度
虽然不是直接的计算性能指标,但异常的功耗和温度升高往往是系统超负荷运行的副作用。
如何测量:
功耗:使用精密电源分析仪或在电源路径上串联采样电阻,用示波器或万用表测量电压降,计算电流和功耗。温度:使用 MCU 内建的温度传感器(如果有)或外部热电偶、红外热像仪测量芯片表面温度。
遇到性能瓶颈时,需要进行详细的性能分析来定位具体问题所在,然后采取针对性的优化措施(算法优化、代码优化、编译器优化、使用 DMA、调整任务优先级等)。
如果优化后仍无法满足需求,那么可能就需要考虑升级到性能更强的单片机了。
x5xciiqqs0a6408388129.jpg
eycqzqwhtin6408388230.gif
点击阅读原文,更精彩~ |