最近在学习RISC-V架构的MCU,特地学习了一下“中断处理机制”,对比之前使用过的Cortex-M3内核单片机,研究它们在中断执行和处理上的差异和效率。
样品选择
CH32V103C8T6和CH32F103C8T6
- CH32V103C8T6,沁恒微电子的RSIC-V架构32位通用型MCU,支持IMAC指令集,内置PFIC中断控制器。PFIC是该公司自研设计的结构,所以用法上有独自的特色,部分功能还需搭配软件平台(推荐MRS IDE)实现。
- CH32F103C8T6,Cortex-M3内核32位通用型MCU,内置NVIC中断控制器。资源及软件工具兼容市场上主流的设备(如Keil、Link等)。
测试方法
中断延迟(响应)时间,即从中断触发条件产生到执行中断服务(用户代码)的时间。
方法:使用芯片自带的定时器资源,通过定时器具有的自动重加载功能,无需软件代码参与。设置一个1000周期并且计数递减的定时器,当定时器当前计数值由1变为0时,会触发硬件中断同时定时器本身也会重加载计数器值为1000继续递减工作,MCU内核系统在一系列操作后最终执行定时器的中断服务程序。在此服务程序开头进行当前定时器计数值的保存,然后关闭定时器,输出保持的数值,清除中断标志。通过换算与1000的差值,得到的就是大概的中断响应时间(其实这个时间应该再减去对计数值保存的指令代码,后面会提到)。
为了单纯测试内核设计,排除其他因素影响,我们采用较低的频率,以时钟周期为单位进行测试计算。因为在此测试过程中,会涉及MCU内部的内核中断处理逻辑、flash读取(代码指令)、数据操作(SRAM)等几个大方面,其中flash有等待周期,可能不同的硬件平台结果不一样,所以使用较低的主频,可以降低非内核处理的其他因素影响结果
测试
硬件平台CH32F103开发板,下载最新EVT包,进行代码修改。使用Keil编译器
C代码,如下:
#include "debug.h"
/*******************************************************************************
* Function Name : TIM1Init
* Description :
* Input : None
* Return : None
*******************************************************************************/
void TIM1Init(void)
{
TIM_TimeBaseInitTypeDef TimeBase_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_TIM1, ENABLE );
TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TimeBase_InitStructure.TIM_CounterMode = TIM_CounterMode_Down;
TimeBase_InitStructure.TIM_Period = 1000;
TimeBase_InitStructure.TIM_Prescaler = 0;
TIM1->CNT = 1000;
TIM_TimeBaseInit( TIM1, &TimeBase_InitStructure );
TIM_ClearITPendingBit( TIM1, 0X0FF);
TIM_ITConfig( TIM1, TIM_IT_Update, ENABLE );
NVIC_EnableIRQ( TIM1_UP_IRQn );
TIM_Cmd( TIM1, ENABLE );
}
/*******************************************************************************
* Function Name : main
* Description : Main program.
* Input : None
* Return : None
*******************************************************************************/
unsigned short cnt;
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);
Delay_Ms( 200 );
TIM1Init();
while(1)
{
;
}
}
void TIM1_UP_IRQHandler(void)
{
cnt = TIM1->CNT;
TIM_Cmd( TIM1, DISABLE );
printf("#%d\n",cnt);
if(TIM_GetFlagStatus( TIM1, TIM_FLAG_Update )!=RESET)
{
TIM_ClearITPendingBit( TIM1, TIM_IT_Update ); /* Clear Flag */
}
}
中断部分进行反汇编,如下:
TIM1_UP_IRQHandler PROC
;;;63 void TIM1_UP_IRQHandler(void)
00008e b570 PUSH {
r4-r6,lr}
;;;64 {
;;;65 cnt = TIM1->CNT;
000090 480d LDR r0,|L1.200|
000092 8800 LDRH r0,[r0,#0]
000094 4c19 LDR r4