3.为什么S12(X)的中断ISR必须放在未分页区(NON_BANKED)
4.CodeWarrior5.1IDE中S12(X)系列MCU中断ISR的三种写法详解及优缺点分析
方法一:在C文件中,建立中断向量表,使用‘@’重定义中断向量地址,并根据中断向量表地址将相应的中断ISR地址放在表中对应外设中断位置
方法二:在C文件中,直接采样关键词interrupt+中断向量号+ISR函数名来编写
方法三:在prm文件中定义:采样关键词VECROR+中断向量号+ISR函数名或关键词VECTOR+中断向量地址+ISR函数名(可实现中断向量地址重映射),然后再C文件中编写相应的ISR函数
总结
在之前我们谈到,嵌入式MCU中断ISR不同于其他用户软件程序,属于一类特殊的函数,其具有如下特点:
1.中断ISR必须是void型的无参数传递函数--无形参无返回值;
2.中断ISR由硬件外设触发,而非其他函数调用,其运行时机具有随机不确定性和硬件实时性
那么对于链接器来说,它是如何区分中断ISR和普通用户函数的呢?又是如何将其放置到MCU中断向量表对应外设的中断向量的呢?这就涉及到中断ISR的写法。
以下是S12G系列MCUdatasheet(MC9S12GFamilyReferenceManual,Rev.1.25)关于其中断源和中断使能以及中断向量表的描述:
另外,由于S12G向量的中断向量地址时由中断向量基地址(IVBR--InterruptVectorBaseAddressRegister)寄存器+中断向量偏移地址得到的,而MCU复位后IVBR寄存器的值为0xFF,所以S12G系列MCU的复位向量地址总是0xFFFE、0xFFFC和0xFFFA,修改IVBR并不影响复位中断向量。
Tips:通过修改IVBR寄存器可以实现外设中断向量表的偏移(也称为重定向----relocation),这对于开发BootLoader来说非常有用,因为带有BootLoader的MCU,其BootLoader和应用程序需要两个不同的外设中断向量表。
下表列出了S12G系列MCU的所有片上外设中断向量表位置(地址)/全局使能控制位和局部使能位信息,不同的part和封装,其外设数量不同,所以其对应的外设数量和中断也不同,但只要某一外设存在,其中断向量相对地址都是固定的,其中黄色高亮的外设中断时可以将MCU从STOP低功耗模式唤醒,可以作为低功耗模式的系统唤醒源。
另外,由于S12(X)系列MCU的片上存储器基友分页(Bank/page)工作机制,其对应的分页地址(也称作逻辑地址--logicaladdress)为分页号(pagenumber)+分窗口(pagewindow--0x8000to0xBFFF)的24位地址,比如page_0x9的分页地址就是0x9_8000to0x9_BFFF,必须使用24位地址(使用__far指针)才能正确寻址,而我们的中断向量表中给每一个中断分配的中断向量都是两个字节,也就是16位,所以必须将中断ISR放在为分页区(NON_BANKEDaddress,其为CPU可以直接寻址的16位地址,具有唯一性)中。
其在CodeWarrior5.1IDE具体实现方法是:
#pragmaCODE_SEGNON_BANKED/*#pragmaCODE_SEG标志以下为代码段(codesegment)定义,后面跟的NON_BANKED为具体的prm文件中定义的段,告诉链接器将这之后的代码链接到NON_BANKED段*/
/*putyourISRhere*/
#pragmaCODE_SEGDEFAULT/*恢复为默认段存储*/
有了以上基础知识,我们就可以来看看具体在CodeWarrior5.1IDE给S12(X)系列MCU开发中断ISR了。具体有三种方法:
方法一:在C文件中,建立中断向量表,使用‘@’重定义中断向量地址,并根据中断向量表地址将相应的中断ISR地址放在表中对应外设中断位置:
#define__CPU_VECTOR_BASE_ADDR__0xFE80
/*ISRprototype*/
typedefvoid(*neartIsrFunc)(void);
/*lint-save-e950DisableMISRArule(1.1)checking.*/
staticconsttIsrFunc_InterruptVectorTable[]@__CPU_VECTOR_BASE_ADDR__={/*Interruptvectortable*/
/*putyourISRfunctionslisthereaccordingtotheinterruptvectororder*/
&Keys_Interrupt_ISR;
};
staticconsttIsrFunc_ResetVectorTable[]@0xFFFAU={/*Resetvectortable*/
/*ResethandlernameAddressNameDescription*/
&_WatchDog_EntryPoint,/*0xFFFAivVcopusedbyPE*/
&_ClockMonitor_EntryPoint,/*0xFFFCivVclkmonusedbyPE*/
&_EntryPoint/*0xFFFEivVresetusedbyPE*/
然后在C文件中使用关键词interrupt定义中断ISR函数如下:
#pragmaCODE_SEGNON_BANKED
interruptvoidKeys_Interrupt_ISR(void)
{
/*yourownISRhere*/
}
#pragmaCODE_SEGDEFAULT
优缺点:
1.为了保证程序正常工作,CPU需要知道当前中断向量表已经由默认的0xFF80偏移到了0xFE80:对于S12(X)MCU:需要在工程初始化时将中断向量基地址(IVBR)设置为“@”之后的16位地址的高8位,即0xFE;
2.可以非常方便的实现中断向量表偏移重定向;
3.通过函数数组的方式统一管理MCU的中断向量,不容易出错,推荐使用方法;
voidinterruptVectorNumber_Vtimch0tmisr0(void)
1.VectorNumber_Vtimch0为中断向量号,在芯片头文件(如mc9s12g128.h,建工程时根据用户的选择自动加入到工程中)中默认定义的,无需关系中断向量表;
2.这种方法虽然最为简单,但无法实现中断向量表偏移,只能使用默认中断向量表地址;
首先,在prm文件最后定义如下:
VECTOR0_Startup(根据中断向量号定义,其默认中断向量地址为0xFFFE,_Startup为复位中断ISR函数名)
然后,C文件中使用关键词interrupt定义中断ISR函数如下:
interruptvoid_Startup(void)
1.使用关键词VECTOR+中断向量地址+ISR函数名,通过单独修改每一个中断向量地址可以实现中断向量表偏移(与方法一相同,修改IVBR寄存器,),但实现繁琐,不推荐使用;
2.若使用关键词VECROR+中断向量号+ISR函数名定义,则无法实现中断向量偏移,并且需要在prm文件中需要将0xFF80开始的128个字节默认中断向量地址保留,以存放ISR函数,这样CPU才能正常响应中断。
保证S12(X)系列MCU内核CPU能够正常识别运行中断ISR的关键步骤如下:
1.使用关键词interrupt定义无参数无返回值的中断ISR;
2.初始化中断向量表,并将其地址高8位赋给IVBR寄存器(对应方法二和方法三,使用默认中断向量表,则无需此步骤);
3.使用关键词#pragmaCODE_SEGNON_BANKED将中断ISR放在MCU未分页存储器(P-Flash)中;
Tips:虽然本文是基于S12(X)讲的中断ISR的实现方法,但其同样适用于S08系列MCU和MagniVS12Z系列MCU以及相应的CodeWarrior6.x和CodeWarrior10.6/7IDE.