嵌入式中断:统计各中断关键信息 (中断执行/挂起/被打断次数,中断优先级异常等)


先上图

统计各中断优先级、执行次数,被挂起次数、被打断次数及异常中断情况,并用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系列:

网络安全系列:

嵌入式开发系列:

AI / 机器学习系列:


在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HowieXue

求打赏~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值