在MCU/ARM/DSP中,都有1个启动过程,这里主要设计复位及中断的入口问题。 所以它们基本都有启动文件的支持,使程序上电后自动进入正确的程序位置及地址。 复位及中断的入口具有多个间隔固定的程序空间,它们可以是单独的跳转地址 及函数指针,如ARM的中断向量表。也可是一块小的固定程序空间,如51的n*8+3和DSP5402的4字。 DSP5402和51略有不同,它在硬件复位时bootloader将中断向量表映射到ROM的0xFF80处。
但我们实际运行时,中断入口是允许改变的,而且每个程序的中断地址和个数都是不同的。
故需要动态地改变中断向量表。
DSP5402在硬件复位时中断向量表存在SREGs.PMST.IPTR中,步进(大小)0x80.
SREGs.PMST.IPTR初始化为0b11111111 1,故中断向量表在0xff80处。 我喜欢将其动态映射到0x0080处,因为寄存器在0x00~0x5f,0x60~0x7f处。 这样连续,RAM程序可从0x100开始装载。
所以我们要做至少2方面的工作: 1.修改SREGs.PMST.IPTR //设置0x0080为中断向量表首址,在第2个128页内。 SREGs.PMST.Regs = (0x01 << PMST_IPTR) | (1 << PMST_MP_MC) | (1 << PMST_OVLY);
2.初始化中断向量表 在C语言的启动文件中,我们只需将中断向量表看成普通数组即可,因为CCS具有地址绝对定位功能。 这样我们就可以很方便地将中断向量表绝对定位到我们需要定位的位置。
在CCS中想绝对定位,必须修改CMD文件。 例如: MEMORY { /*(R读 W写 X运行 I初始化)*/ PAGE 0: /* Program Space */ MMRS (RWIX) : o=000000h l=000060h /* Memory-mapped registers */ SCRATCH (RW ) : o=000060h l=000020h /* scratch-pad DARAM */ VECT (RWIX) : o=000080h l=000080h /* Interrupt Vector Table */ IPROG (RW ) : o=000100h l=001f00h /* On-Chip DARAM */ PAGE 1: /* Data Space */ /* declaration for DARAM already made in PAGE 0 */ /*支持对SREGs在watch窗口中有效查看*/ IMMRS (RWIX) : o=000000h l=000060h /* Memory-mapped registers */ ISCRATCH(RW ) : o=000060h l=000020h /* scratch-pad DARAM */ /*支持对中断向量表的正确定位及改写*/ IVECT (RWIX) : o=000080h l=000080h /* Interrupt Vector Table */ IDATA (RW ) : o=002000h l=002000h /* On-Chip DARAM */ EDATA (RW ) : o=008000h l=008000h }
SECTIONS { /*支持对中断向量表的正确定位及改写*/ .vectors : > IVECT PAGE 1 /* interrupt vector table */ /*支持对SREGs在watch窗口中有效查看*/ .registers : > IMMRS PAGE 1 /* */ .text : > IPROG PAGE 0 /* User code */ .cinit : > IPROG PAGE 0 /* initialization tables */ .pinit : > IPROG PAGE 0 /* initialization functions */ .switch : > IPROG PAGE 0 /* for C-switch tables */
/* Normally, data would go to DMEM0, but OVLY=1 so put data in */ /* PMEM0 to keep loader from overwriting program with data. */ .sysmem : > IDATA PAGE 1 /*fill = 0DEADh*/ /* dynamic heap */ .stack : > IDATA PAGE 1 /*fill = 0BEEFh*/ /* system stack */ .const : > IDATA PAGE 1 /* C constant tables */ .cio : > IDATA PAGE 1 /* C-IO Buffer */ .bss : > IDATA PAGE 1 /* global & static vars */ .data : > IDATA PAGE 1 /* asm data area */ .flash : > EDATA PAGE 1 /**/ }
这里将VECT移入了PAGE 1,为不破坏以前的“规矩”,特将VECT重起名IVECT. 实际VECT以无用。
接下来就是对中断向量进行初始化。 #pragma DATA_SECTION("vectors")//将中断向量绝对定位到0x80处 uVectorEntry g_pfnVectors[] = { ISR_Reset(), //reset 0x0080 #0//软硬件复位nISR_REST ISR_Default(), //nmi 0x0084 #1//非屏蔽中断nISR_NMI ISR_Default(), //sint17 0x0088 #2//软件中断17 ISR_Default(), //sint18 0x008c #3//软件中断18 ISR_Default(), //sint19 0x0090 #4//软件中断19 ISR_Default(), //sint20 0x0094 #5//软件中断20 ISR_Default(), //sint21 0x0098 #6//软件中断21 ISR_Default(), //sint22 0x009c #7//软件中断22 ISR_Default(), //sint23 0x00a0 #8//软件中断23 ISR_Default(), //sint24 0x00a4 #9//软件中断24 ISR_Default(), //sint25 0x00a8 #10//软件中断25 ISR_Default(), //sint26 0x00ac #11//软件中断26 ISR_Default(), //sint27 0x00b0 #12//软件中断27 ISR_Default(), //sint28 0x00b4 #13//软件中断28 ISR_Default(), //sint29 0x00b8 #14//软件中断29 ISR_Default(), //sint30 0x00bc #15//软件中断30 ISR(Eint0Isr), //int0 0x00c0 #16//外部中断0 ISR(Eint1Isr), //int1 0x00c4 #17//外部中断1 ISR(Eint2Isr), //int2 0x00c8 #18//外部中断2 ISR(Timer0Isr),//tint 0x00cc #19//定时器中断0 ISR(McBSPIsr), //rint0 0x00d0 #20//McBSP0接收中断 ISR_Default(), //xint0 0x00d4 #21//McBSP0发送中断 ISR_Default(), //rint1 0x00d8 #22//McBSP1接收中断 ISR(Timer1Isr),//xint1 0x00dc #23//定时器中断1 ISR(Eint3Isr), //int3 0x00e0 #24//外部中断3 ISR_Default(), //hpint 0x00e4 #25//HPI中断 ISR_Default(), // 0x00e8 #26//软件中断10 ISR_Default(), // 0x00ec #27//软件中断11 ISR_Default(), // 0x00f0 #28//软件中断12 ISR_Default(), // 0x00f4 #29//软件中断13 ISR_Default(), // 0x00f8 #30//保留0 ISR_Default(), // 0x00fc #31//保留1 };
其中宏实际是“嵌入汇编”: #define ISR_Reset() {DSPCODE_BD, (pfnVectorEntry)c_int00, DSPCODE_SP_200L, DSPCODE_SP_200H} #define ISR_Return() {DSPCODE_RETE, (pfnVectorEntry)DSPCODE_NOP, DSPCODE_NOP, DSPCODE_NOP} #define ISR_Default() {DSPCODE_BD, (pfnVectorEntry)DefaultIsr, DSPCODE_NOP, DSPCODE_NOP} #define ISR(addr) {DSPCODE_BD, (pfnVectorEntry)addr, DSPCODE_NOP, DSPCODE_NOP} //注意:宏ISR_Load()调用前应该关闭中断,装载后再开放中断 #define ISR_Load(nISR, addr) {g_pfnVectors[nISR].Isr = (pfnVectorEntry)addr;} //注意:宏ISR_UnLoad()调用前应该关闭中断,卸载后再开放中断 #define ISR_UnLoad(nISR) {g_pfnVectors[nISR].Isr = (pfnVectorEntry)DefaultIsr;}
这里的汇编代码用枚举来替代: enum DspCodeEnum_Enum { DSPCODE_BD = (TI_IREG)0xf273u,//等效BD address指令 DSPCODE_B = (TI_IREG)0xf073u,,//等效B address指令 DSPCODE_RETE = (TI_IREG)0xf4fbu,,//等效rete指令 DSPCODE_NOP = (TI_IREG)0xf495u,//等效nop指令 DSPCODE_SP_200L = (TI_IREG)0x7718u,//STM #200,SP DSPCODE_SP_200H = (TI_IREG)0x00c8u//STM #200,SP };
例如ISR(Timer0Isr),它表示在19#中断向量nISR_TINT0的位置0xcc处为Timer0Isr的入口。
为了编写Timer0Isr,我们还要进行2方面的工作: 1.初始化定时器,设置100MHz时10mS中断的时间常数 void TimerObj::Timer0Init(void) { SREGs.TIMER0.TCR.Bits.TSS = 1;//关闭定时器0 SREGs.TIMER0.TCR.Bits.TDDR = 16 - 1;//16分频 SREGs.TIMER0.PRD = 62500 - 1;//设置定时周期(PRD->TIM) SREGs.TIMER0.TIM = 62500 - 1;//设置定时计数器 SREGs.TIMER0.TCR.Bits.TSS = 0;//启动定时器0 SREGs.IMR.Bits.TINT0 = 1;//允许TINT0中断(62500*16=10mS) }
2.编制定时器中断服务程序 extern "C" interrupt void Timer0Isr(void) {//每10mS中断1次 static unsigned int Count = 0; Count ++; if (Count > 50)//16*62500*50=0.5S {//Led工作灯XF半秒翻转1次,注意XF的变化不是改写ST1而是改写SP(1) SREGs.SP.Ptr[1] ^= (1 << ST1_XF);//改写SP(1) Count = 0; } SREGs.IFR.Bits.TINT0 = 1;//清除定时器0中断标志 }
到此,完整的关于C语言中DSP5402中断向量表的配置问题及实现过程叙述完毕
菜农HotPower@126.com 相关链接:https://bbs.21ic.com/upfiles/img/200812/200812219476776.rar |