MCU中断延迟时间测试
关于中断延迟(响应)时间
目前MCU常用内核有ARM的Cortex-M0、M3、M4、M7以及RISC-V内核等,这些内核的延迟时间是不一样的。所谓中断延迟(响应)时间,即从中断触发条件产生到中断函数执行的时间。
中断延迟时间的单位是内核的时钟周期,比如若系统主频是72M,则内核的时钟周期是1/72M,即大约是0.014us。若一个MCU的中断延迟时间是5个时钟周期,系统主频为72M,则其中断延迟时间大概为0.07us。
通常MCU程序在SRAM运行的时候,是标准的零等待时间,但程序放到FLASH运行的时候,系统设置不同主频的时候,所对应的等待周期是不一样的,比如STM32F103,当系统主频在0-24MHz的时候,0等待周期;当系统主频在24-48MHz的时候,1等待周期;当系统主频在48-72MHz的时候,2等待周期。不同的等待周期是对中断延迟时间是有影响的。下表数据为ARM不同内核0等待时间的中断延迟时间。
关于中断延迟时间测试
本次测试使用RISC-V MCU CH32V307通过SysTick进行测试,CH32V307的SysTick进入中断的条件为当SysTick计数寄存器与SysTick比较寄存器的值相同时,触发中断。本次测试程序配置SysTick计数模式为向上计数,且选择HCLK作为时基,当计数器值与比较寄存器值相同时,会触发中断,此时SysTick计数器会重新从0开始计数,在中断第一时间读取SysTick计数器的值,然后减去汇编指令的执行周期,剩余就是中断的延迟响应时间。
测试程序如下:
#include "systick.h" #include "debug.h" void SysTick_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); //void SysTick_Handler(void) __attribute__((interrupt())); //SysTick 初始化配置 static uint32_t SysTick_Config(uint32_t ticks) { SysTick->CTLR = 0x00000000; //控制寄存器复位 SysTick->SR = 0x00000000; //状态寄存器复位 SysTick->CNT = 0; //计数器复位,设置初始值为0 SysTick->CMP = ticks; //给重加载寄存器赋值 SysTick->CTLR |= 0x0000000F; //启动系统计数器STK(HCLK时基),向上计数到比较值后重新从 0 开始计数 NVIC_SetPriority(SysTicK_IRQn, 0); //设置SysTick中断优先级 NVIC_EnableIRQ(SysTicK_IRQn); //使能开启Systick中断 SetVTFIRQ((u32)SysTick_Handler,SysTicK_IRQn,0,ENABLE); //免表中断,即不经过中断向量表的查表过程,直达中断函数入口 return (0); } void Systick_Init(void) { //此处在进行初始化的时候设置比较寄存器的值 //SysTick_Config(SystemCoreClock / 8000);//1ms 72M/8000/9000000 = 1/1000 = 1ms SysTick_Config(SystemCoreClock);//1ms 72M/8000/9000000 = 1/1000 = 1ms } void SysTick_Handler(void) { printf("CNT=%llx\n",SysTick->CNT ); NVIC_DisableIRQ(SysTicK_IRQn); }
打印结果如下:
由打印结果可知,SysTick计数器的值为15。
汇编代码如下:
000005f4 <SysTick_Handler>: 5f4: 1141 addi sp,sp,-16 5f6: c622 sw s0,12(sp) 5f8: 0800 addi s0,sp,16 5fa: e000f7b7 lui a5,0xe000f 5fe: 00c7a803 lw a6,12(a5) # e000f00c <_eusrstack+0xc000700c> 602: 479c lw a5,8(a5) 604: 863e mv a2,a5 606: 86c2 mv a3,a6 608: 678d lui a5,0x3 60a: ef478513 addi a0,a5,-268 # 2ef4 <__clzsi2+0x88> 60e: 1fe010ef jal ra,180c <printf> 612: 4531 li a0,12 614: 3da1 jal 46c <NVIC_DisableIRQ> 616: 0001 nop 618: 4432 lw s0,12(sp) 61a: 0141 addi sp,sp,16 61c: 30200073 mret 620: a001 j 620 <SysTick_Handler+0x2c> 622: a001 j 622 <SysTick_Handler+0x2e>
由汇编代码可知,汇编指令执行时间大约用到6个周期,计数器的值为15,因此中断延迟时间大概为9个时钟周期。
CH32V307还支持免表中断,当开启免表中断后,可不经过中断向量表的查表过程,直达中断函数入口,这样可以缩短中断延迟时间。程序打印结果如下:
由打印结果可知,当开启免表中断后,中断延迟时间缩短为7个时钟周期。
CH32V103的SysTick由于必须选择8分频,因此CH32V103测试可以选用定时器进行测试,此外系统主频设置为8M,减小FLASH等因素的影响。置一个1000周期并且计数递减的定时器,当定时器当前计数值由1变为0时,会触发硬件中断同时定时器本身也会重加载计数器值为1000继续递减工作,MCU内核系统在一系列操作后最终执行定时器的中断服务程序。在此服务程序开头进行当前定时器计数值的保存,然后关闭定时器,输出保持的数值,清除中断标志。通过换算与1000的差值,再减去汇编指令执行时间,得到的就是大概的中断延迟时间。
测试代码如下:
/********************************** (C) COPYRIGHT ******************************* * File Name : main.c * Author : WCH * Version : V1.0.0 * Date : 2020/04/30 * Description : Main program body. * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. * SPDX-License-Identifier: Apache-2.0 *******************************************************************************/ /* *@Note 串口打印调试例程: USART1_Tx(PA9)。 本例程演示使用 USART1(PA9) 作打印调试口输出。 */ #include "debug.h" /* Global typedef */ /* Global define */ /* Global Variable */ unsigned short cnt; void TIM1_UP_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); //void TIM1_UP_IRQHandler(void) __attribute__((interrupt())); /******************************************************************************* * Function Name : TIM1Init * Description : * Input : None * Return : None *******************************************************************************/ void TIM1Init(void) { TIM_TimeBaseInitTypeDef TimeBase_InitStructure={0}; RCC_APB2PeriphClockCmd( RCC_APB2Periph_TIM1, ENABLE ); TimeBase_InitStructure.TIM_Period = 1000; TimeBase_InitStructure.TIM_Prescaler = 0; TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TimeBase_InitStructure.TIM_CounterMode = TIM_CounterMode_Down; TimeBase_InitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit( TIM1, &TimeBase_InitStructure ); TIM1->CNT = 1000; TIM_ClearITPendingBit( TIM1, 0X0FF); TIM_ITConfig( TIM1, TIM_IT_Update, ENABLE ); NVIC_EnableIRQ( TIM1_UP_IRQn ); NVIC_SetFastIRQ((u32)TIM1_UP_IRQHandler, TIM1_UP_IRQn, 0); TIM_Cmd( TIM1, ENABLE ); } /********************************************************************* * @fn main * * @brief Main program. * * @return none */ int main(void) { RCC_ClocksTypeDef Clocks_InitStructure; Delay_Init(); USART_Printf_Init(115200); RCC_GetClocksFreq( &Clocks_InitStructure ); // printf("HCLK:%d\r\n",Clocks_InitStructure.SYSCLK_Frequency); // printf("APB2Clk:%d\r\n",Clocks_InitStructure.PCLK2_Frequency); // printf("APB1Clk:%d\r\n",Clocks_InitStructure.PCLK1_Frequency); TIM1Init(); while(1) { } } void TIM1_UP_IRQHandler(void) { printf("TIMCNT=%d\r\n",TIM1->CNT); TIM_Cmd( TIM1, DISABLE ); if(TIM_GetFlagStatus( TIM1, TIM_FLAG_Update )!=RESET) { TIM_ClearITPendingBit( TIM1, TIM_IT_Update ); /* Clear Flag */ } }
打印结果如下:
由打印结果可知,定时器计数器的值为987
汇编代码如下:
000001ee <TIM1_UP_IRQHandler>: 1ee: 1141 addi sp,sp,-16 1f0: c622 sw s0,12(sp) 1f2: 40013437 lui s0,0x40013 1f6: c2445583 lhu a1,-988(s0) # 40012c24 <_eusrstack+0x2000dc24> 1fa: 00002537 lui a0,0x2 1fe: 97050513 addi a0,a0,-1680 # 1970 <_read+0xe> 202: 2535 jal 82e <iprintf> 204: c0040413 addi s0,s0,-1024 208: 4581 li a1,0 20a: 8522 mv a0,s0 20c: 2ee9 jal 5e6 <TIM_Cmd> 20e: 4585 li a1,1 210: 8522 mv a0,s0 212: 2119 jal 618 <TIM_GetFlagStatus> 214: c501 beqz a0,21c <TIM1_UP_IRQHandler+0x2e> 216: 4585 li a1,1 218: 8522 mv a0,s0 21a: 2129 jal 624 <TIM_ClearITPendingBit> 21c: 4432 lw s0,12(sp) 21e: 0141 addi sp,sp,16 220: 30200073 mret
由汇编代码可知,汇编指令执行时间大约用到4个周期,计数器变化的值为13,因此中断延迟时间大概为9个时钟周期。
CH32V103还支持免表中断,当开启免表中断后,可不经过中断向量表的查表过程,直达中断函数入口,这样可以缩短中断延迟时间。程序打印结果如下:
由打印结果可知,当开启免表中断后,中断延迟时间缩短为7个时钟周期。
根据测试结果,中断延迟时间测试结果汇总如下: