Cortex-M3的中断架构和以往的ARM7、ARM9、ARM11有了很大的区别,IRQ、FIQ的概念的已经消失,随之而来的是NVIC中断管理(支持最多256个中断优先级,128级抢断)及中断向量表。这个中断向量表似有相识之感,在当时大学时期学习DOS平台下的C语言开发的时候,在设置BIOS时钟中断的时候,就曾把中断函数的指针地址设置到时钟中断的入口地址区,以期中断发生的时候,执行指定的函数。当然不仅是时钟中断,其它的中断的处理方式也类似。在DOS那个时代,估计最高深一点的编程就是程序驻留和钩子函数(姑且把修改中断地址以截获信息的这一类函数为钩子函数吧),以此看来,Cortex-M3的中断架构倒是借鉴了BIOS的中断处理机制。
ARM公司大力推出Cortex-M3系列CPU核,一改往日的命名方式,并且内存映射、中断架构等以往因不同芯片厂商而多变的架构,变得统一起来。看来ARM其志不小,想要在嵌入式领域开发技术层面一统江湖。这种统一意义巨大,大大降低了嵌入式软件移植的难度,不知道今后这种改变,能否在嵌入式领域中催生出类似昔日PC领域的王者组合:操作系统(Microsoft)+CPU(Intel)。
.Net Micro Framework的整个底层架构是建立在ARM7和ARM9基础之上的,而Cortex-M3的出现,大大的冲击了这一架构,这也是在MF3.0源码中就已经出现了CortexM3的目录,而到现在官方无一款可支持的Cortex-M3开发板的原因。因为要改动的代码太多,要保持ARM7、ARM9和Cortex-M3的代码统一太难。
所以在这里我单立了一个目录,大力进行修改而不遗余力(所谓不破不立),我觉得,这也是我们在一个划时代事物出现的面前,所应持有的态度和立场。
在第4回<修改启动代码&重写向量表>中我们所写的中断向量表,我们要进行一番调整,以期支持动态加入中断函数的功能。
新的中断函数表,我们仅需预先填写前三项:(1) 堆栈TOP地址,(2)复位地址,(3)不可屏蔽中断函数地址(NMIException)。VectorsTrampolines.s修改后的代码如下(和以前的代码相比,是不是简化了很多):
- EXPORT ARM_Vectors
- IMPORT StackTop
- IMPORT EntryPoint
- IMPORT NMIException
- ;********************************************************
- AREA |.text|, CODE, READONLY
- ;向量表
- ARM_Vectors
- DCD StackTop ; Top of Stack 栈顶
- DCD EntryPoint ; 复位
- DCD NMIException
- ;... ; 向量表定位在 RAM中,由程序动态生成
- SPACE 384 ; 预留空间 (76 - 3) * 4 = 292
- ;*********************************************************
- END
其中SPACE 384的代码也可以不要,不过要确保Scatterfile_tools_mdk.xml文件中,如下项没有<FileMapping Name="*" Options="(SectionForFlashOperations)" />
- <ExecRegion Name="ER_RAM_RO" Base="0x20000000" Options="ABSOLUTE" Size="">
- <FileMapping Name="VectorsTrampolines.obj" Options="(+RO, +FIRST)" />
- <FileMapping Name="*" Options="(SectionForFlashOperations)" />
- </ExecRegion>
此外还有一个改进就是删除我们原先在\DeviceCode\Targets\Native\CortexM3\DeviceCode目录下的建立的VectorsHandler_Temp库文件,这个库我们已经不需要了,我们随时根据需要,动态的在中断向量表中写入我们所需的中断函数地址。
好了,在\DeviceCode\Targets\Native\CortexM3\DeviceCode目录中建立NVIC目录(相当于以前体系的INTC),在写具体代码之前,我们现在写下如下代码(大概),这是对NVIC相关寄存器的描述。
- struct CortexM3_NVIC
- {
- static const UINT32 c_Base = 0xE000E100;
- //中断使能 置位复位
- /****/ volatile UINT32 SETENA[2];
- UINT32 RESERVED0[30];
- /****/ volatile UINT32 CLRENA[2];
- UINT32 RSERVED1[30];
- //中断悬起 置位复位
- /****/ volatile UINT32 SETPEN[2];
- UINT32 RESERVED2[30];
- /****/ volatile UINT32 CLRPEN[2];
- UINT32 RESERVED3[30];
- //中断活动状态
- /****/ volatile UINT32 ACTIVE[2];
- UINT32 RESERVED4[62];
- //中断优先级
- /****/ volatile UINT8 PRI[60];
- static const UINT32 c_IRQ_MAX_INDEX = 16+60;
- //IRQ Index Table
- static const UINT32 c_IRQ_Index_NMI = 0x2;
- static const UINT32 c_IRQ_Index_HardFault = 0x3;
- static const UINT32 c_IRQ_Index_MemManage = 0x4;
- static const UINT32 c_IRQ_Index_BusFault = 0x5;
- static const UINT32 c_IRQ_Index_UsageFault = 0x6;
- //7,8,9,A NULL
- static const UINT32 c_IRQ_Index_SVC = 0xB;
- static const UINT32 c_IRQ_Index_DebugMonitor = 0xC;
- //D NULL
- static const UINT32 c_IRQ_Index_PendSVC = 0xE;
- static const UINT32 c_IRQ_Index_SysTick = 0xF;
- //--
- static const UINT32 c_IRQ_Index_WWDG = 0x10;
- static const UINT32 c_IRQ_Index_PVD = 0x11;
- static const UINT32 c_IRQ_Index_TAMPER = 0x12;
- //略
- static const UINT32 c_IRQ_Index_DMA2_Channel3 = 0x4A;
- static const UINT32 c_IRQ_Index_DMA2_Channel4_5 = 0x4B;
- //IRQ Priority index Table
- static const UINT32 c_IRQ_Priority_NULL = 0xFF;
- static const UINT32 c_IRQ_Priority_Leve0 = 0x00;
- static const UINT32 c_IRQ_Priority_Leve1 = 0x10;
- static const UINT32 c_IRQ_Priority_Leve2 = 0x20;
- //略
- static const UINT32 c_IRQ_Priority_Leve14 = 0xE0;
- static const UINT32 c_IRQ_Priority_Leve15 = 0xF0;
- };
- 在NVIC目录下建立三个文件:NVIC.cpp、NVIC.h、doNetMF.proj
- NVIC.cpp的核心内容如下:
- void CortexM3_NVIC_Driver::Initialize()
- {
- //外部中断
- CortexM3_NVIC &NVIC = CortexM3::NVIC();
- NVIC.CLRENA[0]=0xFFFFFFFF; //清外部中断
- NVIC.CLRENA[1]=0xFFFFFFFF;
- NVIC.CLRPEN[0]=0xFFFFFFFF;
- NVIC.CLRPEN[1]=0xFFFFFFFF;
- //系统中断
- CortexM3_SCB &SCB = CortexM3::SCB();
- SCB.ICSR = 0x0A000000;
- SCB.VTOR = (UINT32)&IMAGE_RAM_RO_BASE; // 重定向中断向量表
- SCB.AIRCR = CortexM3_SCB::AIRCR_VECTKEY_MASK | CortexM3_SCB::NVIC_PriorityGroup_1;
- SCB.SCR = 0x00000000;
- SCB.CCR = 0x00000000;
- SCB.SHCSR = 0x00000000; //系统中断使能位
- SCB.CFSR = 0xFFFFFFFF;
- SCB.HFSR = 0xFFFFFFFF;
- SCB.DFSR = 0xFFFFFFFF;
- //优先级设定
- IRQ_VECTORING *IsrVector = s_IsrTable;
- for(int i = 0; i < CortexM3_NVIC::c_IRQ_MAX_INDEX; i++)
- {
- //略
- }
- }
- //激活中断
- BOOL CortexM3_NVIC_Driver::ActivateInterrupt(UINT32 Irq_Index,UINT32 ISR)
- {
- InterruptEnable(Irq_Index);
- UINT32 *p= (UINT32*)&IMAGE_RAM_RO_BASE + Irq_Index;
- *p=ISR;
- return TRUE;
- }
- //关闭中断
- BOOL CortexM3_NVIC_Driver::DeactivateInterrupt(UINT32 Irq_Index)
- {
- InterruptDisable(Irq_Index);
- return TRUE;
- }
- BOOL CortexM3_NVIC_Driver::InterruptEnable(UINT32 Irq_Index)
- {
- if(Irq_Index<16)
- {
- CortexM3_SCB &SCB = CortexM3::SCB();
- if(Irq_Index==CortexM3_NVIC::c_IRQ_Index_MemManage) //4
- SCB.SHCSR |= 0x1<<16;
- if(Irq_Index==CortexM3_NVIC::c_IRQ_Index_BusFault) //5
- SCB.SHCSR |= 0x1<<17;
- if(Irq_Index==CortexM3_NVIC::c_IRQ_Index_UsageFault) //6
- SCB.SHCSR |= 0x1<<18;
- if(Irq_Index==CortexM3_NVIC::c_IRQ_Index_SysTick) //15
- {
- CortexM3_SysTick &SysTick= CortexM3::SysTick();
- SysTick.CTRL |= CortexM3_SysTick::CTRL_TICKINT | CortexM3_SysTick::CTRL_ENABLE;
- }
- }
- else
- {
- CortexM3_NVIC &NVIC = CortexM3::NVIC();
- int irq=Irq_Index-16;
- NVIC.SETENA[irq/32] = 0x1 << (irq % 32);
- }
- return TRUE;
- }
- BOOL CortexM3_NVIC_Driver::InterruptDisable(UINT32 Irq_Index)
- {
- //略
- return TRUE;
- }
在其中,我们为了适应新架构的需要,增加了一个中断函数连接接口,声明如下:
- BOOL CPU_INTC_ActivateInterruptEx(UINT32 Irq_Index, UINT32 ISR)
- {
- return CortexM3_NVIC_Driver::ActivateInterrupt(Irq_Index, ISR);
- }
原先的(如下),已经废弃不用(暂保留)。
- BOOL CPU_INTC_ActivateInterrupt(UINT32 Irq_Index, HAL_CALLBACK_FPN ISR, void *ISR_Param)
- {
- return FALSE;
- }
以上代码中最重要的莫过于“激活中断”函数中的如下代码
UINT32 *p= (UINT32*)&IMAGE_RAM_RO_BASE + Irq_Index;
*p=ISR;
简单的两行代码,便实现了中断函数的动态注册(在GPIO和串口驱动中我们将详细讲这一点)。
此外,最后要记得在NativeSample.proj添加如下内容:
- <ItemGroup>
- <RequiredProjects Include="$(SPOCLIENT)\DeviceCode\Targets\Native\CortexM3\DeviceCode\Nvic\dotNetMF.proj" />
- <DriverLibs Include="nvic.$(LIB_EXT)" />
- </ItemGroup>
前一两个礼拜没有写相关文章,是因为代码修改量很大,一直在调试相关功能。目前GPIO、串口等功能均已经调试通过,所以后续的几天,我便一一介绍它们的实现过程。
本文出自 “叶帆工作室” 博客,转载请与作者联系!