五、XPS中断控制器的使用
嵌入式应用中往往需要用到多个中断来提高系统响应的及时性,而MICROBLAZE处理器只提供一个中断输入信号来产生一个中断,因此一般基于MICROBLAZE的应用都需要进行外扩中断,XILINX提供的XPS中断控制器IP可以使我们十分方便地进行中断扩展。下面首先简单介绍下XPS中断控制器的特性与寄存器,然后在上一节的基础上结合一个实例来说明它的使用。
1.中断控制器的特性与寄存器(XPS Interrupt Controller,v1.00)
XPS中断控制器采用PLB接口,可以提供多达32个中断输入源、产生一个中断输出信号。每一中断输入源可以单独的使能和不使能,每一中断输入源可以配置为边缘触发中断或电平触发中断。边缘触发中断可配置为上沿触发或下沿触发,电平触发中断可配置为高电平触发或低电平触发。中断的优先级由矢量位置决定,最低位有最高优先级。中断控制器提供八个32位的寄存器用来编程控制(有些寄存器是可选的),分别为:
①中断状态寄存器(ISR:Interrupt Status Register):地址为C_BASEADDR + 0x0,可读写寄存器,读时每一位对应一个中断输入的标志位,当MER的HIE位为0时可写。
②中断挂起寄存器(IPR:Interrupt Pending Register):地址为C_BASEADDR + 0x4,可选的只读寄存器,读出结果为中断标志位与中断使能位的AND。
③中断使能寄存器(IER:Interrupt Enable Register):地址为C_BASEADDR + 0x8,可读写寄存器,每一位对应一个中断输入的使能位。
④中断应答寄存器(IAR:Interrupt Acknowledge Register):地址为C_BASEADDR + 0xC,只写寄存器,每位写1清对应的中断标志位。
⑤设置中断使能寄存器(SIE:Set Interrupt Enables):地址为C_BASEADDR + 0x10,只写寄存器,每位写1设对应的中断使能位。
⑥请除中断使能寄存器(CIE:Clear Interrupt Enables):地址为C_BASEADDR + 0x14,只写寄存器,每位写1清对应的中断使能位。
⑦中断矢量寄存器(IVR:Interrupt Vector Register):地址为C_BASEADDR + 0x18,可选的只读寄存器,读出结果为当前激活的最高优先级中断的中断矢量,无中断时读为全1。
⑧主使能寄存器(MER:Master Enable Register):地址为C_BASEADDR + 0x1C,可读写寄存器,只用到两位,最低位ME使能IRQ,次低位HIE为硬件中断使能。
2.一个实例
上一节我们基于μC/OS-II的延时函数实现了一个LED灯闪烁的实验,本节准备在上一节的基础上实现下面的功能:通过GPIO中断来激活或挂起一个比LED灯闪烁任务更高优先级的任务,以验证中断控制器的使用和任务的抢占,下面是具体步骤:
(1).打开上一节的工程,在XPS 界面右边的System Assembly View里点Port栏,再双击DIP_Switches_4Bit图标,在弹出的IP配置窗口里的Use栏里勾选 “GPIO Supports Interrupts”。点OK退出配置窗口。展开DIP_Switches_4Bit图标前的加号,选择IP2INTC_Irpt线为New Connection。
(2). 在XPS界面右边的System Assembly View里点Port栏,展开xps_intc_0图标前的加号,
在Intr里把DIP_Switches_4Bi_IP2INTC_Irpt_0加为中断源,并把优先级设为比xps_timer_0_Interrupt低,点OK退出窗口。
(3) 然后在XPS界面顺序选择:
①Hardware → Clean Netlist
②Hardware → Clean Bits
③Hardware → Generate Netlist
④Hardware → Generate Bitstream
(4) 修改C:\MICRIUM\Software\EvalBoards\Xilinx\Generic\GNU\EX1_OS\ app.c文件增加一个显示流水灯的任务。具体修改如下:
①. 在main函数前加入如下代码:
INT16U flag1;
OS_STK AppTaskSecondStk [256];
void AppTaskSecond(void *p_arg)
{
INT8U i;
INT32U j;
i=2;
BSP_InitIO();
while (1) {
LED_Off(0);
LED_On(i);
i++;
if(i>4)
{
i=2;
}
for(j=0;j<699999; j++); //Delay1
}
}
② 在main 函数里的OSInit();后面加入如下代码:
f lag1 =0;
OSTaskCreateExt(AppTaskSecond,
(void *)0,
&AppTaskSecondStk[255],
2,
2,
&AppTaskSecondStk[0],
256,
(void *)0,
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);
③ 把AppTaskFirst函数里调用的BSP_InitIO();语句删除,因为我们在更高优先级的AppTaskSecond任务里已经调用过了。
(5) 修改C:\MICRIUM\Software\EvalBoards\Xilinx\Generic\GNU\EX1_OS\app_cfg.h如下:
#define APP_TASK_FIRST_ID 3
#define APP_TASK_FIRST_PRIO 3
#define APP_TASK_FIRST_STK_SIZE 256
即降低第一个任务的优先级和减少堆栈容量。
(6) 修改C:\MICRIUM\Software\EvalBoards\Xilinx\Generic\GNU\BSP\bsp.c文件
①.在BSP_InitIntCtrl中断初始化函数前加入GPIO的中断函数如下:
extern INT16U flag1;
void BSP_GpioHandler(void *baseaddr_p)
{
for(gpio_status=0; gpio_status<69999; gpio_status++); //Delay2
if(flag1==0)
{
OSTaskSuspend(2);
flag1=1;
}
else
{
OSTaskResume(2);
flag1=0;
}
XGpio_mWriteReg(XPAR_DIP_SWITCHES_4BIT_BASEADDR, 0x120, 0x1);
}
②在BSP_InitIntCtrl函数内的
init_status = XIntc_Connect(&int_ctl, BSP_INTC_TIMER1_ID,BSP_Timer1Handler,(void *)0);语句下面添加下列语句:
init_status = XIntc_Connect(&int_ctl, XPAR_XPS_INTC_0_DIP_SWITCHES_4BIT_IP2INTC_IRPT_INTR,BSP_GpioHandler,(void *)0);
XIntc_Enable(&int_ctl, XPAR_XPS_INTC_0_DIP_SWITCHES_4BIT_IP2INTC_IRPT_INTR);
XGpio_mWriteReg(XPAR_DIP_SWITCHES_4BIT_BASEADDR, XGPIO_TRI_OFFSET, 0xffffffff);
XGpio_mWriteReg(XPAR_DIP_SWITCHES_4BIT_BASEADDR, XGPIO_GIE_OFFSET, XGPIO_GIE_GINTR_ENABLE_MASK);
XGpio_mWriteReg(XPAR_DIP_SWITCHES_4BIT_BASEADDR, XGPIO_IER_OFFSET, 0x1);
(7) 在XPS界面选择:
①Software → Clean Libraries
②Software → Generate Libraries and BSPs
③点击TEST工程名,在右键弹出菜单选择Set Compiler Options...,
在Debug and Optimization选择 No Optimization。
再在右键弹出菜单选择选择Clean Project 后在选择Build Project编译工程。
④Device Configuration → Update Bitstream
⑤Device Configuration → Download Bitstream
下载成功后可以看到系统首先运行高优先级的任务使LED[2:4]跑流水灯,拨下DIP开关产生GPIO中断,进入中断函数内把高优先级任务挂起,则系统运行次优先级的任务使LED1闪烁,在拨下DIP开关,进入中断后恢复高优先级任务,又可以看到LED[2:4]跑流水灯,
次优先级任务由于抢占已经不在运行(LED1不再闪烁)。
3.最后总结一下调试中遇到问题:
(1).要用XPS中断控制器进行中断扩展,有三个地方相应的使能位都要打开,否则中断进不来,一个MICROBLAZE的中断使能位,二是中断控制器各个输入的使能位和输出使能位,三是具体中断源的使能位(GPIO的单独位的使能位和总使能位),而硬件上也要确认连接好。
(2).进入中断后要清标志位,包括中断控制器和中断源两个地方的标志位,否则出现中断退出后又立刻进来的现象。
(3). DIP开关由手工拨动,一般有按键反弹现象,所以要延时下消抖,这就是上面代码//Delay2的作用,如果不消抖,可能出现拨一下进入中断多次的现象。
(4) 带了操作系统后,如果任务多点或堆栈大一点,往往不能把全部代码放在BRAM里,这时在Device Configuration → Update Bitstream步骤时会报错说容量不够,所以这里我把堆栈改为256了,实际项目使用的话往往要外扩FLASH和RAM(扩展方法待续^_^)。 |