汽车电子expert成长之路

本博客发布的个人原创精品----嵌入式系统技术文章,欢迎大家参考学习,并转发分享!

浅谈嵌入式MCU软件开发之中断ISR的三种写法

0
阅读(9226)

浅谈嵌入式MCU软件开发之S12(X)系列MCU 中断ISR在CodeWarrior 5.1 IDE 中的三种写法

内容提要        


引言

1. 嵌入式MCU中断ISR的特点

2. 中断向量表及中断向量重映射

3. 为什么S12(X)的中断ISR必须放在未分页区(NON_BANKED)

4. CodeWarrior 5.1 IDE中S12(X)系列MCU中断ISR的三种写法详解及优缺点分析

方法一:在C文件中,建立中断向量表,使用‘@’重定义中断向量地址,并根据中断向量表地址将相应的中断ISR地址放在表中对应外设中断位置

方法二:在C文件中,直接采样关键词interrupt+中断向量号+ISR函数名来编写

方法三:在prm文件中定义:采样关键词VECROR+中断向量号+ISR函数名或关键词VECTOR+中断向量地址+ISR函数名(可实现中断向量地址重映射),然后再C文件中编写相应的ISR函数

总结


引言


前面在的技术分析文章--《浅谈嵌入式MCU软件开发之中断优先级与中断嵌套》(点击文章标题即可直接跳转阅读)中,我给大家分析了嵌入式MCU的中断工作机制、中断优先级和中断嵌套。今天我就结合NXP(老Freescale)的S12(X)系列汽车MCU给大家分享其在中断ISR(ISR--Interrupt Service Routine,中断服务函数 )在CodeWarrior 5.1 IDE中的三种写法。

1. 嵌入式MCU中断ISR的特点


        在之前我们谈到,嵌入式MCU中断ISR不同于其他用户软件程序,属于一类特殊的函数,其具有如下特点:


        1. 中断ISR必须是void型的无参数传递函数--无形参无返回值;


        2.中断ISR由硬件外设触发,而非其他函数调用,其运行时机具有随机不确定性和硬件实时性


        3.中断ISR运行时间必须尽量短,以保证外设中断的实时性;


        那么对于链接器来说,它是如何区分中断ISR和普通用户函数的呢?又是如何将其放置到MCU中断向量表对应外设的中断向量的呢?这就涉及到中断ISR的写法。

2. 中断向量表及中断向量重映射


        以下是S12G系列MCU datasheet(MC9S12G Family Reference Manual, Rev.1.25)关于其中断源和中断使能以及中断向量表的描述:


        S12G系列MCU的复位源和复位向量位置,其占据最高的三个中断向量地址,因此也具有最高优先级,从其中可以看到,其为时钟监测复位(Clock monitor reset)和看门狗复位(COP watchdog reset)提供了单独的复位向量,并且其具有局部使能控制,其余的MCU复位源共享同一个中断向量地址0xFFFE,且无局部使能。 我们知道,无论是外部时钟源丢失造成的时钟监测复位中断还是COP看门狗定时器溢出导致的看门狗复位,最终都必然会将MCU复位,但由于其具有单独的中断向量地址,所以我们实际上是可以打开其中断,让其进入自己的中断ISR的,在其中一户可以做一些特殊的处理,比如记录复位源以供后期分析或者做少量的中断工作(请注意,这个复位中断的执行时间非常短。只有若干个时钟周期,而且为了能够正常进入时钟监测复位和看门狗复位,MCU的外部RESET复位管脚上接的滤波电容不能够太大,否则会直接将其虑掉,无法进入)。当然,默认我们都是将时钟监测复位和看门狗复位的复位向量地址填入MCU的默认复位向量,让其与其他复位中断做同样的处理即可。

10.jpg


        另外,由于S12G向量的中断向量地址时由中断向量基地址(IVBR--Interrupt Vector Base Address Register)寄存器+中断向量偏移地址得到的,而MCU复位后IVBR寄存器的值为0xFF,所以S12G系列MCU的复位向量地址总是0xFFFE、0xFFFC和0xFFFA,修改IVBR并不影响复位中断向量。

11.jpg


Tips:通过修改IVBR寄存器可以实现外设中断向量表的偏移(也称为重定向----relocation),这对于开发BootLoader来说非常有用,因为带有BootLoader的MCU,其BootLoader和应用程序需要两个不同的外设中断向量表。


        下表列出了S12G系列MCU的所有片上外设中断向量表位置(地址)/全局使能控制位和局部使能位信息,不同的part和封装,其外设数量不同,所以其对应的外设数量和中断也不同,但只要某一外设存在,其中断向量相对地址都是固定的,其中黄色高亮的外设中断时可以将MCU从STOP低功耗模式唤醒,可以作为低功耗模式的系统唤醒源。

12.jpg

13.jpg


3. 为什么S12(X)的中断ISR必须放在未分页区(NON_BANKED)


        另外,由于S12(X)系列MCU的片上存储器基友分页(Bank/page)工作机制,其对应的分页地址(也称作逻辑地址--logical address)为分页号(page number) + 分窗口(page window--0x8000 to 0xBFFF)的24位地址,比如page_0x9的分页地址就是0x9_8000 to 0x9_BFFF,必须使用24位地址(使用__far指针)才能正确寻址,而我们的中断向量表中给每一个中断分配的中断向量都是两个字节,也就是16位,所以必须将中断ISR放在为分页区(NON_BANKED address, 其为CPU可以直接寻址的16位地址,具有唯一性)中。


        其在CodeWarrior 5.1 IDE具体实现方法是:


#pragma CODE_SEG NON_BANKED   /*#pragma CODE_SEG标志以下为代码段(code segment)定义,后面跟的NON_BANKED 为具体的prm文件中定义的段,告诉链接器将这之后的代码链接到NON_BANKED段*/

/*put your ISR here*/

#pragma CODE_SEG DEFAULT /*恢复为默认段存储*/

14.jpg

        

4.  CodeWarrior 5.1 IDE中S12(X)系列MCU中断ISR的三种写法详解及优缺点分析


 有了以上基础知识,我们就可以来看看具体在CodeWarrior 5.1 IDE给S12(X)系列MCU开发中断ISR了。具体有三种方法:


方法一:在C文件中,建立中断向量表,使用‘@’重定义中断向量地址,并根据中断向量表地址将相应的中断ISR地址放在表中对应外设中断位置:


#define __CPU_VECTOR_BASE_ADDR__  0xFE80

/* ISR prototype */

typedef void (*near tIsrFunc)(void);

/*lint -save  -e950 Disable MISRA rule (1.1) checking. */

static const tIsrFunc _InterruptVectorTable[] @__CPU_VECTOR_BASE_ADDR__ = { /* Interrupt vector table */

  /*put your ISR functions list here according to the interrupt vector order*/

&Keys_Interrupt_ISR;

};

static const tIsrFunc _ResetVectorTable[] @0xFFFAU = { /* Reset vector table */

 /* Reset handler name                    Address Name           Description */

 &_WatchDog_EntryPoint,                /* 0xFFFA  ivVcop         used by PE */

 &_ClockMonitor_EntryPoint,            /* 0xFFFC  ivVclkmon      used by PE */

 &_EntryPoint                          /* 0xFFFE  ivVreset       used by PE */

};

        然后在C文件中使用关键词interrupt定义中断ISR函数如下:

#pragma CODE_SEG NON_BANKED

interrupt void Keys_Interrupt_ISR(void)

{

      /*your own ISR here*/

}

#pragma CODE_SEG DEFAULT


优缺点:


        1. 为了保证程序正常工作,CPU需要知道当前中断向量表已经由默认的0xFF80偏移到了0xFE80: 对于S12(X)MCU:需要在工程初始化时将中断向量基地址(IVBR)设置为“@”之后的16位地址的高8位,即0xFE;


        2. 可以非常方便的实现中断向量表偏移重定向;


        3. 通过函数数组的方式统一管理MCU的中断向量,不容易出错,推荐使用方法;


方法二: 在C文件中,直接采样关键词interrupt+中断向量号+ISR函数名来编写


#pragma CODE_SEG NON_BANKED


void interrupt VectorNumber_Vtimch0 tmisr0(void)

{

       /*your own ISR here*/ 

}

#pragma CODE_SEG DEFAULT


优缺点:


        1. VectorNumber_Vtimch0为中断向量号,在芯片头文件(如mc9s12g128.h,建工程时根据用户的选择自动加入到工程中)中默认定义的,无需关系中断向量表;


          2. 这种方法虽然最为简单,但无法实现中断向量表偏移,只能使用默认中断向量表地址;


方法三:在prm文件中定义:采样关键词VECROR+中断向量号+ISR函数名或关键词VECTOR+中断向量地址+ISR函数名(可实现中断向量地址重映射),然后再C文件中编写相应的ISR函数


        首先,在prm文件最后定义如下:


VECTOR 0 _Startup(根据中断向量号定义,其默认中断向量地址为0xFFFE,_Startup为复位中断ISR函数名)


        然后,C文件中使用关键词interrupt定义中断ISR函数如下: 


#pragma CODE_SEG NON_BANKED

interrupt void _Startup (void)

{

      /*your own ISR here*/

}

#pragma CODE_SEG DEFAULT


优缺点:


        1. 使用关键词VECTOR+中断向量地址+ISR函数名,通过单独修改每一个中断向量地址可以实现中断向量表偏移(与方法一相同,修改IVBR寄存器,),但实现繁琐,不推荐使用;

         2. 若使用关键词VECROR+中断向量号+ISR函数名定义,则无法实现中断向量偏移,并且需要在prm文件中需要将0xFF80开始的128个字节默认中断向量地址保留,以存放ISR函数,这样CPU才能正常响应中断。


        总结


保证S12(X)系列MCU内核CPU能够正常识别运行中断ISR的关键步骤如下:


        1. 使用关键词interrupt定义无参数无返回值的中断ISR;


        2. 初始化中断向量表,并将其地址高8位赋给IVBR寄存器(对应方法二和方法三,使用默认中断向量表,则无需此步骤);


        3. 使用关键词#pragma CODE_SEG NON_BANKED将中断ISR放在MCU未分页存储器(P-Flash)中;


        Tips:虽然本文是基于S12(X)讲的中断ISR的实现方法,但其同样适用于S08系列MCU和MagniV S12Z系列MCU以及相应的CodeWarrior 6.x和CodeWarrior 10.6/7 IDE.