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个时钟周期。

根据测试结果,中断延迟时间测试结果汇总如下:

 

posted @ 2022-09-15 13:56  ZaiLi  阅读(1885)  评论(0编辑  收藏  举报