实现过程
1 硬件平台搭建:
在Vivado 2018.1中,使用Xilinx
Nexys4开发板,搭建基于MicroBlaze软核的嵌入式系统硬件平台如下图8所示:
中断方式使用一个中断控制器:其中GPIO_0中断输出连接到Intr0,GPIO_2中断输出连接到Intr1,Timer_0中断输出连接到Intr2;中断控制器的中断向量输出连接到MicroBlaze微处理器的中断输入总线上。
其中对常用并行IO外设GPIO接口进行了配置(如图9):
16位开关和16位LED灯共用一个GPIO
IP核(设为GPIO_1),其中,开关使用GPIO通道,LED灯使用GPI2通道;四位七段数码管的位码与段码共用另一个GPIO
IP核(设为GPIO_0),其中,位码使用GPIO通道,段码使用GPIO2通道;两位按键使用另一个GPIO
IP核(设为GPIO_2)的GPIO通道;延时T0与T1使用Timer IP核(设为Timer_0)。
对并行IO中断系统进行了配置(如图10):
中断方式使用一个中断控制器:其中GPIO_1中断输出连接到Intr0;GPIO_2中断输出连接到Intr1;UART_0中断输出连接到Intr2;Timer_0中断输出连接到Intr3;
SPI_0中断输出连接到Intr4;
SPI_1中断输出连接到Intr5;;UART_1中断输出连接到Intr6;UART_2中断输出连接到Intr7.
中断控制器的中断向量输出连接到MicroBlaze微处理器的中断输入总线上。
如图所示:
对串行IO接口外设UART、SPI进行了配置(如图11):
生成HDL封装,查看硬件平台存储空间布局如下(如图12):
2. 程序控制方式实现任务1:
根据上面的分析,在主程序中设置按键对应GPIO通道工作在输入模式,开关对应的GPIO通道工作在输入模式,LED灯对应的GPIO通道工作在输出模式,设置并开始循环读取按键状态,如果按键状态改变,则根据按键的不同值,进入不同的if语句,来实现相应的功能:
· 当按键值为0x10,即BTNC被按下时,读取此时对应开关状态,存为csw1,并将其显示在LED灯上;
· 当按键值为0x10,即BTNR被按下时,读取此时对应开关状态,存为csw2,并将其显示在LED灯上;
· 当按键值为0x10,即BTNU被按下时,结果取r1 = csw1 +
csw2,并将结果r1显示在LED灯上;
· 当按键值为0x10,即BTND被按下时,结果取r2 = csw1
*csw2,并将结果r2显示在LED灯上.
\#include "stdio.h" \#include "xil_io.h" \#include "xgpio.h" int main() { short button; unsigned short csw1,csw2,r1,r2; Xil_Out8(XPAR_AXI_GPIO_2_BASEADDR+XGPIO_TRI_OFFSET,0x1f); Xil_Out16(XPAR_AXI_GPIO_1_BASEADDR+XGPIO_TRI2_OFFSET,0xffff); Xil_Out16(XPAR_AXI_GPIO_1_BASEADDR+XGPIO_TRI_OFFSET,0x0); while(1) while((Xil_In8(XPAR_AXI_GPIO_2_BASEADDR+XGPIO_DATA_OFFSET)&0x1f)!=0) { button = Xil_In8(XPAR_AXI_GPIO_2_BASEADDR+XGPIO_DATA_OFFSET)\&0x1f; while((Xil_In8(XPAR_AXI_GPIO_2_BASEADDR+XGPIO_DATA_OFFSET)\&0x1f)!=0); xil_printf("The pushed button's code is %d\\n" , button); if(button==0x10 ){ csw1 = Xil_In16(XPAR_AXI_GPIO_1_BASEADDR+XGPIO_DATA2_OFFSET)\&0xffff; Xil_Out16(XPAR_AXI_GPIO_1_BASEADDR+XGPIO_DATA_OFFSET,csw1); } if(button ==0x8){ csw2 = Xil_In16(XPAR_AXI_GPIO_1_BASEADDR+XGPIO_DATA2_OFFSET)\&0xffff; Xil_Out16(XPAR_AXI_GPIO_1_BASEADDR+XGPIO_DATA_OFFSET,csw2); } if(button ==0x1){ r1 = csw1 + csw2; Xil_Out16(XPAR_AXI_GPIO_1_BASEADDR+XGPIO_DATA_OFFSET,r1); } if(button ==0x4){ r2 = csw1 \* csw2; Xil_Out16(XPAR_AXI_GPIO_1_BASEADDR+XGPIO_DATA_OFFSET,r2); } } }
3. 并行IO接口中断控制方式实现任务2:
1.GPIO初始化:
对于程序控制方式,依据GPIO表格,写GPIO的TRI寄存器设置输入输出模式;
对于中断控制方式,还需要写IER寄存器以及GIER寄存器
实现代码如下:
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR+XGPIO_TRI_OFFSET,0x0);//设段码输出方式 Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR+XGPIO_TRI2_OFFSET,0x0);//设位码为输出方式 Xil_Out32(XPAR_AXI_GPIO_2_BASEADDR+XGPIO_TRI_OFFSET,0x1f);//设地BUTTON方输入方式 Xil_Out32(XPAR_AXI_GPIO_1_BASEADDR+XGPIO_TRI2_OFFSET,0xffff);//设地Switch方输入方式 Xil_Out32(XPAR_AXI_GPIO_2_BASEADDR+XGPIO_IER_OFFSET,XGPIO_IR_CH1_MASK);//允许中断 Xil_Out32(XPAR_AXI_GPIO_2_BASEADDR+XGPIO_GIE_OFFSET,XGPIO_GIE_GINTR_ENABLE_MASK);//GPIO中断输出 Xil_Out32(XPAR_AXI_TIMER_0_BASEADDR+XTC_TCSR_OFFSET,
2.Timer_0初始化:
Timer_0初始化的操作流程为:首先停止计时(通过写TCSR寄存器使使能定时器这一位为0实现),然后再是写预置值(通过写TLR寄存器实现),紧接着装载预置值(通过写TCSR寄存器使装载这一位为1实现)然后再是写TCSR寄存器,控制定时器使能。清除源中断状态、使能中断、使能自动装载、减计数。
实现代码如下:
Xil_Out32(XPAR_AXI_TIMER_0_BASEADDR+XTC_TCSR_OFFSET, Xil_In32(XPAR_AXI_TIMER_0_BASEADDR+XTC_TCSR_OFFSET)&~XTC_CSR_ENABLE_TMR_MASK);//写TCSR,停止计数 Xil_Out32(XPAR_AXI_TIMER_0_BASEADDR+XTC_TLR_OFFSET,RESET_VALUE);//TLR,预置计数值 Xil_Out32(XPAR_AXI_TIMER_0_BASEADDR+XTC_TCSR_OFFSET, Xil_In32(XPAR_AXI_TIMER_0_BASEADDR+XTC_TCSR_OFFSET)|XTC_CSR_LOAD_MASK);// Xil_Out32(XPAR_AXI_TIMER_0_BASEADDR+XTC_TCSR_OFFSET, (Xil_In32(XPAR_AXI_TIMER_0_BASEADDR+XTC_TCSR_OFFSET)&~XTC_CSR_LOAD_MASK)\ |XTC_CSR_ENABLE_TMR_MASK|XTC_CSR_AUTO_RELOAD_MASK|XTC_CSR_ENABLE_INT_MASK|XTC_CSR_DOWN_COUNT_MASK);
3.INTC初始化与微处理器开中断:
INTC内部寄存器如下:
对中断控制器开中断:首先清除原中断状态,然后使能intr1和intr3对应的中断输入并使能中断输出(分别通过写IAR、IER以及MER寄存器实现);
MicroBlaze微处理器开中断:要实现微处理器开中断,我们可通过调用microblaze_enable_interrupt实现:
实现代码如下:
//INTC初始化 Xil_Out32(XPAR_INTC_0_BASEADDR+XIN_IER_OFFSET,XPAR_AXI_GPIO_2_IP2INTC_IRPT_MASK\|XPAR_AXI_TIMER_0_INTERRUPT_MASK);// Xil_Out32(XPAR_INTC_0_BASEADDR+XIN_MER_OFFSET,0x3); //微处理器开中断 microblaze_enable_interrupts();
1.总中断服务程序:
总中断服务程序,首先读取中断控制器的中断状态寄存器;然后判断D3是否为1,如果是1则表示按键状态发生了变化,因此调用按键对应的GPIO中断事务处理函数,返回之后,判断D3是否为1如果是1则表示Timer_0定时器计时时间到,因此调用Timer_0对应的中断事务处理函数。
实现代码如下:
void My_ISR() { int status; status=Xil_In32(XPAR_INTC_0_BASEADDR+XIN_ISR_OFFSET);//续敢ISR if((status&0x8)==0x8) Seg_TimerCounterHandler();//l溉用用户中断服务狂序 else if((status&0x2)==0x2) BtnHandler();//l遴用按痉中断 Xil_Out32(XPAR_INTC_0_BASEADDR+XIN_IAR_OFFSET,status);//写IAR }
2.Timer_0的中断事务处理函数:
T0通过gpio_0的GPIO2通道控制一位LED灯点亮,并准备下一位的输出后直接退出。
实现代码如下:
void Seg_TimerCounterHandler() { Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR+XGPIO_DATA_OFFSET,segcode[j]); Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR+XGPIO_DATA2_OFFSET,pos); pos=pos\>\>1; j++; if(j==8) { j=0; pos=0xff7f; } Xil_Out32(XPAR_AXI_TIMER_0_BASEADDR+XTC_TCSR_OFFSET,Xil_In32(XPAR_AXI_TIMER_0_BASEADDR+XTC_TCSR_OFFSET));//满总中断 }
3.按键的中断事务处理函数:
首先读取按键的状态,根据按键的值来实现相应功能:
实现代码如下:
void BtnHandler() { int button; unsigned short sw; int i; int temp; int q,w,e,r,t; sw = Xil_In16(XPAR_AXI_GPIO_1_BASEADDR+XGPIO_DATA2_OFFSET)\&0xffff; button = Xil_In8(XPAR_AXI_GPIO_2_BASEADDR+XGPIO_DATA_OFFSET)\&0x1f; if(button == 0x10) //(1) { short pos1 = 0x0080; for(i=0;i\<8;i++) { if((sw&pos1) != 0) temp = 1; else temp = 0; segcode[i] = segtable[temp]; pos1=pos1\>\>1; } } else if(button == 0x1) //(2) { for(i=0;i\<8;i++) { if(i\>3){ temp = ( sw \>\>(4\*(7-i)))\&0xf; segcode[i]=segtable[temp]; } else segcode[i] = 0xff; } }else if(button == 0x4) //(3) { q = sw/10000; w = (sw-10000\*q)/1000; e = (sw-10000\*q-1000\*w)/100; r = (sw-10000\*q-1000\*w-100\*e)/10; t = (sw-10000\*q-1000\*w-100\*e-10\*r)/1; xil_printf("q=%d,w=%d,e=%d,r=%d,t=%d,",q,w,e,r,t); for(i=0;i\<8;i++) { if(i\>2){ switch(i) { case 3:temp =q;break; case 4:temp =w;break; case 5:temp =e;break; case 6:temp =r;break; case 7:temp =t;break; default: break; } segcode[i] = segtable[temp]; } else segcode[i] = 0xff; } } Xil_Out32(XPAR_AXI_GPIO_2_BASEADDR+XGPIO_ISR_OFFSET, Xil_In32(XPAR_AXI_GPIO_2_BASEADDR+XGPIO_ISR_OFFSET)); }
4.完整代码:
最终的实现代码如下(上面的中断事务处理函数已折叠):
\#include "xil_io.h" \#include "stdio.h" \#include "xgpio_l.h" \#include "xintc_l.h" \#include "xtmrctr_l.h" \#define RESET_VALUE 100000 void Seg_TimerCounterHandler(); void BtnHandler(); void My_ISR()__attribute__((interrupt_handler));//总中断服务雅序 char segtable[16] = { 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8, 0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e }; char segcode[8]={0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};//缓冲区 short pos=0xff7f; int j = 0; int main() { Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR+XGPIO_TRI_OFFSET,0x0);//设段码输出方式 Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR+XGPIO_TRI2_OFFSET,0x0);//设位码为输出方式 Xil_Out32(XPAR_AXI_GPIO_2_BASEADDR+XGPIO_TRI_OFFSET,0x1f);//设地BUTTON方输入方式 Xil_Out32(XPAR_AXI_GPIO_1_BASEADDR+XGPIO_TRI2_OFFSET,0xffff);//设地Switch方输入方式 Xil_Out32(XPAR_AXI_GPIO_2_BASEADDR+XGPIO_IER_OFFSET,XGPIO_IR_CH1_MASK);//允许中断 Xil_Out32(XPAR_AXI_GPIO_2_BASEADDR+XGPIO_GIE_OFFSET,XGPIO_GIE_GINTR_ENABLE_MASK);//GPIO中断输出 Xil_Out32(XPAR_AXI_TIMER_0_BASEADDR+XTC_TCSR_OFFSET, Xil_In32(XPAR_AXI_TIMER_0_BASEADDR+XTC_TCSR_OFFSET)\&\~XTC_CSR_ENABLE_TMR_MASK);//琛TCSR,停止计数 Xil_Out32(XPAR_AXI_TIMER_0_BASEADDR+XTC_TLR_OFFSET,RESET_VALUE);//TLR,预置计数值 Xil_Out32(XPAR_AXI_TIMER_0_BASEADDR+XTC_TCSR_OFFSET, Xil_In32(XPAR_AXI_TIMER_0_BASEADDR+XTC_TCSR_OFFSET)\|XTC_CSR_LOAD_MASK);// Xil_Out32(XPAR_AXI_TIMER_0_BASEADDR+XTC_TCSR_OFFSET, (Xil_In32(XPAR_AXI_TIMER_0_BASEADDR+XTC_TCSR_OFFSET)&\~XTC_CSR_LOAD_MASK)\\ \|XTC_CSR_ENABLE_TMR_MASK\|XTC_CSR_AUTO_RELOAD_MASK\|XTC_CSR_ENABLE_INT_MASK\|XTC_CSR_DOWN_COUNT_MASK); //INTC初始化 Xil_Out32(XPAR_INTC_0_BASEADDR+XIN_IER_OFFSET,XPAR_AXI_GPIO_2_IP2INTC_IRPT_MASK\|XPAR_AXI_TIMER_0_INTERRUPT_MASK);// Xil_Out32(XPAR_INTC_0_BASEADDR+XIN_MER_OFFSET,0x3); //微处理器开中断 microblaze_enable_interrupts(); return 0; } void My_ISR()…… void Seg_TimerCounterHandler()…… void BtnHandler()……