嵌入式中断:统计各中断关键信息 ,中断执行/挂起/被打断次数,中断优先级异常等
先上图
统计各中断优先级、执行次数,被挂起次数、被打断次数及异常中断情况,并用log打印出来:
实现方法:
核心方法是在中断处理函数(ISR)中,根据中断寄存器的数据,记录下当前中断执行次数、挂起Pending次数、被打断次数等数据,形成log统计并打印/存储。
因为每个中断产生时都会进入ISR,所以随着系统运行一定时间, 就可以统计出来所有中断的真实执行情况。
从代码实现上,就是创建一个统一API,例如interruptLog(),然后在每一个使用的IRQ Handler中调用该函数:
实现代码:
数据结构
下面进入代码讲解:
首先数据结构开始,创建一个数组InterruptLog[MAX_IRQNUM],用来记录各中断数据
InterruptInfo 格式如下, 具体含义参见注释:
其中主要的是:
-
中断执行次数:count
-
被挂起次数:pendingCnt
-
被打断(抢占)次数:preemptCnt
-
violatedCnt :代表当前中断优先级大于BasePRI
这种异常情况,(因为我们程序中定义各中断都不允许被打断,所以都不能大于BasePRI)
BASEPRI为优先级屏蔽寄存器,优先级数值大于或等于该寄存器的中断都会被屏蔽,为零时不屏蔽任何中断
初始化
然后我们在初始化中,根据使用的MCU和IRQ,填充InterruptLog数组的一些Name和irqNum。
这里使用多少中断 就填写,其他的都是Unknown。
不同的MCU irqNum也不同,需要根据Datasheet找到正确的num,具体可以查看MCU中断向量表
获取中断寄存器数据:ICSR
首先,因为我们要知道当前中断控制寄存器的内容,需要获取 ICSR (Interrupt Control and State Register) 中断控制状态寄存器内容
ARM NVIC Controller 寄存器说明(CortexM7):https://developer.arm.com/documentation/dui0646/c/cortex-m7-peripherals/nested-vectored-interrupt-controller?lang=en
因为我们用的MCU基于CortexM, 通过上面链接查询到ARM定义的CortexM7,基地址0xE000ED04就是ICSR数据,
如下图:
而通过ICSR数据定义,我们可以知道
- [8:0] Bit (VectActive)代表了当前Active且需要执行的中断Num
- [20:12] (VectPending)是被Pending的最高优先级中断Num
如下图VectActive 和VectPending描述
这样我们就可以统计出当前中断执行的次数count,和该中断执行时,被Pending的中断,并将相应的Cnt++:
代码如下:
void interruptLog(void)
{
uint32_t icsr = ( * ( ( volatile uint32_t * ) 0xE000ED04 ) );
uint8_t index = (uint8_t)(icsr & 0xFFUL); //Active Interrupt
// Current Interrupt count
InterruptLog[index].count++;
CheckActiveRegister(index);
//Pending Interrupt with higher priority
index = (uint8_t)((icsr & 0xFF000UL) >> 12);
if(index)
{
InterruptLog[index].pendingCnt++;
}
}
获取中断寄存器数据:IABR
根据IABR(Interrupt Active Bit Register)中断活动状态寄存器获取中断数据,用来得到其他中断被抢占的次数等
还是通过ARM NVIC寄存器表格,我们找到地址0xE000E300- 0xE000E31C为IABR,记录了所有中断Active状态的寄存器:
既我们可以通过NVIC_IABR0-NVIC_IABR7 知道,中断处理的当前时刻,有哪些其他中断是Active的(代表其他中断被该中断抢占了)
代码上, preemptCnt 和 violatedCnt 则需要通过CheckActiveRegister() ,检查当前所有Active的中断信息来换算,先将各IABR寄存器数据读出来:
通过For循环,遍历IABR0 - IABR7寄存器的数据
如果有Actived,则将IrqNum 通过IncreasePreemptCount(IrqNum, CurrentIndex)处理:
CheckActiveRegister整体代码如下:
void CheckActiveRegister(![uint32_t](https://img-blog.csdnimg.cn/c43a8ca13bdf45a18a95f4d44927ca94.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBASG93aWVYdWU=,size_20,color_FFFFFF,t_70,g_se,x_16)
CurrentIndex)
{
uint32_t IABR0 = ( * ( ( volatile uint32_t * ) 0xE000E300 ) );
uint32_t IABR1 = ( * ( ( volatile uint32_t * ) 0xE000E304 ) );
uint32_t IABR2 = ( * ( ( volatile uint32_t * ) 0xE000E308 ) );
uint32_t IABR3 = ( * ( ( volatile uint32_t * ) 0xE000E30C ) );
uint32_t IABR4 = ( * ( ( volatile uint32_t * ) 0xE000E310 ) );
uint32_t IABR5 = ( * ( ( volatile uint32_t * ) 0xE000E314 ) );
uint32_t IABR6 = ( * ( ( volatile uint32_t * ) 0xE000E318 ) );
uint32_t IABR7 = ( * ( ( volatile uint32_t * ) 0xE000E31C ) );
uint32_t IrqNum = 0;
uint32_t BitCmp = 0;
for ( int i = 0 ; i <32 ; i++ )
{
BitCmp = (uint32_t)(1<<i);
if ( IABR0 & BitCmp )
{
IrqNum = i; //for IABR0
IncreasePreemptCount(IrqNum, CurrentIndex);
}
if ( IABR1 & BitCmp )
{
IrqNum = i + 32;
IncreasePreemptCount(IrqNum, CurrentIndex);
}
if ( IABR2 & BitCmp )
{
IrqNum = i + 64;
IncreasePreemptCount(IrqNum, CurrentIndex);
}
if ( IABR3 & BitCmp )
{
IrqNum = i + 96;
IncreasePreemptCount(IrqNum, CurrentIndex);
}
if ( IABR4 & BitCmp )
{
IrqNum = i + 128;
IncreasePreemptCount(IrqNum, CurrentIndex);
}
}
}
换算中断抢占数据
在IncreasePreemptCount中,首先通过__get_BASEPRI()获取当前BasePRI的数值。
既程序设置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 的数值
(相关知识介绍可参考上篇博文:深入Contex-M中断优先级原理及设置 (万字总结))
之后判断如果IRQ_Index 不是当前中断,既该中断打断了其他中断,则将被打断的IRQ preemptCnt++(记录中断被打断的次数)
同时根据CurrentBasePRI和configMAX_SYSCALL_INTERRUPT_PRIORITY 数值,判断是否是异常情况,如果是,则记录异常的中断优先级和异常次数:
代码:
void IncreasePreemptCount(uint32_t IrqNum, uint32_t CurrentIndex)
{
uint32_t IRQ_Index = 0;
uint32_t CurrentPriorityValue = 0;
IRQ_Index = IrqNum + 16;
CurrentBasePRI = __get_BASEPRI();
//irqNum: Actived IRQ
//CurrentIndex: Preempt other's IRQ index
if ( IRQ_Index != CurrentIndex )
{
//Been Preempted
InterruptLog[IRQ_Index].preemptCnt++;
if (( CurrentBasePRI != configMAX_SYSCALL_INTERRUPT_PRIORITY ) && (CurrentBasePRI != 0x0 ))
{
ErrorCnt_Priority++;
}
else if ( CurrentBasePRI == configMAX_SYSCALL_INTERRUPT_PRIORITY)
{
CurrentPriorityValue = NVIC_GetPriority((IRQn_Type)(CurrentIndex - 16));
//if current Irq priority lower than ConfigMAX_SYSCALL, not permit to preempt
if (CurrentPriorityValue > configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY)
{
InterruptLog[CurrentIndex].violatedCnt++;
InterruptLog[CurrentIndex].violatedPriorityNum = CurrentPriorityValue;
}
}
}
else //Current IRQ , do nothing
{
}
}
打印/存储全部中断数据
在程序运行中,通过命令行或者定时等触发方式,将中断数据信息打印出来供分析:
STATIC void printInterruptLog(void)
{
int16_t index;
iprintf("\n****** Interrupts Statistics ******");
for(index = IRQ_INDEX_START; index < MAX_IRQNUM; index++)
{
//Check if interrupt occured & pended & preempted
if(InterruptLog[index].count || InterruptLog[index].pendingCnt || InterruptLog[index].preemptCnt)
{
iprintf("\nException %9s; Numer: %3d; Priority: %2d; Counts: %9d; Pending: %9d; Preempt: %9d; violatedCnt:%3d;violatedPriorityNum:%2d ",
InterruptLog[index].name,
(index-IRQ_INDEX_START),
NVIC_GetPriority((IRQn_Type)(index-IRQ_INDEX_START)),
InterruptLog[index].count,
InterruptLog[index].pendingCnt,
InterruptLog[index].preemptCnt,
InterruptLog[index].violatedCnt,
InterruptLog[index].violatedPriorityNum);
}
}
iprintf("\n************************************\n");
Debug查看运行情况
查看BasePRI :
我这里BasePRI 最大是0x20, 因为程序定义configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 为2
查看中断记录情况
博主热门文章推荐:
一篇读懂系列:
LoRa Mesh系列:
网络安全系列:
- ATECC508A芯片开发笔记(一):初识加密芯片
- SHA/HMAC/AES-CBC/CTR 算法执行效率及RAM消耗 测试结果
- 常见加密/签名/哈希算法性能比较 (多平台 AES/DES, DH, ECDSA, RSA等)
- AES加解密效率测试(纯软件AES128/256)–以嵌入式Cortex-M0与M3 平台为例
嵌入式开发系列:
- 嵌入式学习中较好的练手项目和课题整理(附代码资料、学习视频和嵌入式学习规划)
- IAR调试使用技巧汇总:数据断点、CallStack、设置堆栈、查看栈使用和栈深度、Memory、Set Next Statement等
- Linux内核编译配置(Menuconfig)、制作文件系统 详细步骤
- Android底层调用C代码(JNI实现)
- 树莓派到手第一步:上电启动、安装中文字体、虚拟键盘、开启SSH等
- Android/Linux设备有线&无线 双网共存(同时上内、外网)
AI / 机器学习系列:
- AI: 机器学习必须懂的几个术语:Lable、Feature、Model…
- AI:卷积神经网络CNN 解决过拟合的方法 (Overcome Overfitting)
- AI: 什么是机器学习的数据清洗(Data Cleaning)
- AI: 机器学习的模型是如何训练的?(在试错中学习)
- 数据可视化:TensorboardX安装及使用(安装测试+实例演示)