这一部分用的都是基于PS的中断。
该部分中断分为两部分,
该部分最后会基于之前流水灯的实验继续展开:
需要使用的GPIO口有以下4组:
和上一次的实验一样:由A5、A6、A7、B8、C19组成一组流水灯;由T16、G17组成另外一组。
并且C19 对应的是MIO9、G17对应的是MIO0、B19对应的是MIO11。
这里主要是先完成第一部分,即完成一个计时器控制一组流水灯。
这里需要修改的设置如下:
该3类中断均由Generic Interrupt Controller(中断控制器)进行控制,
[参考ZYNQ7000 TRM (Page.255-256)]
[参考Zynq中 PS接收PL中断]
这里主要由两个部分需要修改:
这一部分需要6个GPIO口。
具体文件如下:
set_property IOSTANDARD LVCMOS33 [get_ports {leds_tri_o[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {leds_tri_o[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {leds_tri_o[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {leds_tri_o[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {leds_tri_o[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports key_tri_i]
set_property PACKAGE_PIN A5 [get_ports {leds_tri_o[0]}]
set_property PACKAGE_PIN A7 [get_ports {leds_tri_o[1]}]
set_property PACKAGE_PIN A6 [get_ports {leds_tri_o[2]}]
set_property PACKAGE_PIN B8 [get_ports {leds_tri_o[3]}]
set_property PACKAGE_PIN T16 [get_ports {leds_tri_o[4]}]
set_property PACKAGE_PIN AB12 [get_ports key_tri_i]
之后生成Bitstream文件后,导出硬件平台信息,开始编写ps程序。
这里首先还是应该先叙述一下中断需要使用的资源与具体的流程,主要有:
首先先引用一下必须引入的模块
#include "xparameters.h" // 常数模块
#include "xgpio.h" // PL GPIO模块
#include "xgpiops.h" // PS GPIO模块
#include "xscutimer.h" // 计时器模块
#include "xscugic.h" // 中断控制器模块
#include "xil_exception.h" // 中断模块
由于有两组流水灯,这一部分需要两个定时器,两组中断。所以这里没有办法使用单独的29号中断,因为需要不止一个中断信号。这里仅仅是略微修改一下下xscutimer_intr_example的例程,首先完成一组流水灯。
[参考ZYNQ7000 TRM (Page.229)]
具体需要用的函数比较多,一股脑列出来会比较乱,这里分一下类:
// PL GPIO口初始化
XGpio_Config *XGpio_LookupConfig(
u16 DeviceId
);
int XGpio_CfgInitialize(
XGpio * InstancePtr,
XGpio_Config * Config,
UINTPTR EffectiveAddr
);
//PS GPIO口初始化
XGpioPs_Config *XGpioPs_LookupConfig(
u16 DeviceId
);
s32 XGpioPs_CfgInitialize(
XGpioPs *InstancePtr,
XGpioPs_Config *ConfigPtr,
u32 EffectiveAddr
);
GPIO口配置
//PL GPIO口配置
void XGpio_SetDataDirection(
XGpio *InstancePtr,
unsigned Channel,
u32 DirectionMask
);
//PS GPIO口配置
void XGpioPs_SetDirection(
XGpioPs *InstancePtr,
u8 Bank,
u32 Direction
);
void XGpioPs_SetOutputEnable(
XGpioPs *InstancePtr,
u8 Bank,
u32 OpEnable
);
GPIO口写入
// PL GPIO写入
void XGpio_DiscreteWrite(
XGpio *InstancePtr,
unsigned Channel,
u32 Mask
);
// PS GPIO口写入
void XGpioPs_WritePin(
XGpioPs *InstancePtr,
u32 Pin,
u32 Data
);
XScuTimer_Config *XScuTimer_LookupConfig(
u16 DeviceId
);
s32 XScuTimer_CfgInitialize(
XScuTimer *InstancePtr,
XScuTimer_Config *ConfigPtr,
u32 EffectiveAddress
);
定时器配置
void XScuTimer_EnableAutoReload(
XScuTimer *InstancePtr
); // 设定定时器是否在一次计时完成后自动重新装载
void XScuTimer_LoadTimer(
XScuTimer *InstancePtr,
u32 value
); // 设定计时器时间
void XScuTimer_EnableInterrupt(
XScuTimer *InstancePtr
); // 设定计时器支持中断
这三个函数的本质就是往寄存器里直接写入数值,但是经过层层包装。
static INLINE void Xil_Out32(
UINTPTR Addr,
u32 Value
);
定时器启动
void XScuTimer_Start(
XScuTimer *InstancePtr
);
XScuGic_Config *XScuGic_LookupConfig(
u16 DeviceId
);
s32 XScuGic_CfgInitialize(
XScuGic *InstancePtr,
XScuGic_Config *ConfigPtr,
u32 EffectiveAddr
);
中断控制器
extern void Xil_ExceptionRegisterHandler(
u32 Exception_id,
Xil_ExceptionHandler Handler,
void *Data
); // 注册中断
s32 XScuGic_Connect(
XScuGic *InstancePtr,
u32 Int_Id,
Xil_InterruptHandler Handler,
void *CallBackRef
); // 将中断与中断回调函数连接起来
void XScuGic_Enable(
XScuGic *InstancePtr,
u32 Int_Id
); // 使能中断控制器
void Xil_ExceptionEnable(
); // 使能中断
其中,void Xil_ExceptionEnable() 本质上是将通用寄存器中的中断参量传到状态寄存器上。
程序编写:
#include "xparameters.h"
#include "xgpio.h"
#include "xgpiops.h"
#include "xscutimer.h"
#include "xscugic.h"
#include "xil_exception.h"
#define PS_GIC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define TIMER_IRPT_INTR XPAR_SCUTIMER_INTR
#define PS_TIMER_DEVICE_ID XPAR_XSCUTIMER_0_DEVICE_ID
#define TIMER_LOAD_VALUE (XPAR_PS7_CORTEXA9_0_CPU_CLK_FREQ_HZ/2 - 1)
#define PL_GPIO_DEVICE_ID XPAR_GPIO_0_DEVICE_ID
#define PS_GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
#define PL_LED_CHANNEL 1
#define PL_KEY_CHANNEL 2
#define PS_BANK XGPIOPS_BANK0
#define PL_LED0 0x01
#define PL_LED1 0x02
#define PL_LED2 0x04
#define PL_LED3 0x08
#define PS_LED0 0x09
volatile u8 led_status = 0x00;
volatile u8 state1;
XGpio plGpio;
XGpioPs psGpio;
XScuTimer psTimer;
XScuGic psGic;
int init();
void run();
int init_Gpio();
int init_Exti();
int init_Gpio_Pl();
int init_Gpio_Ps();
int init_Exti_Timer();
int init_Exti_Gic();
void psIntrTimerHandler(void*);
// 0
int main(){
int status;
status = init();
if(status != XST_SUCCESS) return XST_FAILURE;
run();
return 0;
}
// 1
int init(){
int status;
status = init_Gpio();
if(status != XST_SUCCESS) return XST_FAILURE;
status = init_Exti();
if(status != XST_SUCCESS) return XST_FAILURE;
return XST_SUCCESS;
}
void run(){
XScuTimer_Start(&psTimer);
while(1){
switch(state1){
case 1: {
XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, 0x0E);
XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
}break;
case 2: {
XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, 0x0D);
XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
}break;
case 3: {
XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, 0x0B);
XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
}break;
case 4: {
XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, 0x07);
XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
}break;
case 0: {
XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, 0x0F);
XGpioPs_WritePin(&psGpio, PS_LED0, 0x00);
}break;
default:{
XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, 0x0F);
XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
}
}
}
}
// 2
int init_Gpio(){
int status;
status = init_Gpio_Pl();
if(status != XST_SUCCESS) return XST_FAILURE;
status = init_Gpio_Ps();
if(status != XST_SUCCESS) return XST_FAILURE;
// PL
XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL, 0x00);
XGpio_SetDataDirection(&plGpio, PL_KEY_CHANNEL, 0x01);
// PS
XGpioPs_SetDirection(&psGpio, PS_BANK, 0x201);
XGpioPs_SetOutputEnable(&psGpio, PS_BANK, 0x201);
return XST_SUCCESS;
}
int init_Exti(){
int status;
status = init_Exti_Timer();
if(status != XST_SUCCESS) return XST_FAILURE;
status = init_Exti_Gic();
if(status != XST_SUCCESS) return XST_FAILURE;
// Timer //
XScuTimer_EnableAutoReload(&psTimer);
XScuTimer_LoadTimer(&psTimer, TIMER_LOAD_VALUE);
XScuTimer_EnableInterrupt(&psTimer);
// Gic
XScuGic* ptrGic = &psGic;
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
&psGic);
status = XScuGic_Connect(ptrGic, TIMER_IRPT_INTR,
(Xil_ExceptionHandler)psIntrTimerHandler,
(void *)ptrGic);
if(status != XST_SUCCESS) return XST_FAILURE;
XScuGic_Enable(&psGic, TIMER_IRPT_INTR);
Xil_ExceptionEnable();
return XST_SUCCESS;
}
// 3
int init_Gpio_Pl(){
XGpio_Config *xGpioCfg;
xGpioCfg = XGpio_LookupConfig(PL_GPIO_DEVICE_ID);
if(xGpioCfg == (XGpio_Config *)NULL) return XST_FAILURE;
return XGpio_CfgInitialize(&plGpio, xGpioCfg, xGpioCfg->BaseAddress);
}
int init_Gpio_Ps(){
XGpioPs_Config *xGpioCfg;
xGpioCfg = XGpioPs_LookupConfig(PS_GPIO_DEVICE_ID);
if(xGpioCfg == (XGpioPs_Config *)NULL) return XST_FAILURE;
return XGpioPs_CfgInitialize(&psGpio, xGpioCfg, xGpioCfg->BaseAddr);
}
int init_Exti_Timer(){
XScuTimer_Config *xGpioCfg;
xGpioCfg = XScuTimer_LookupConfig(PS_TIMER_DEVICE_ID);
if(xGpioCfg == (XScuTimer_Config *)NULL) return XST_FAILURE;
return XScuTimer_CfgInitialize(&psTimer, xGpioCfg, xGpioCfg->BaseAddr);
} //
int init_Exti_Gic(){
XScuGic_Config *xGpioCfg;
xGpioCfg = XScuGic_LookupConfig(PS_GIC_DEVICE_ID);
if(xGpioCfg == (XScuGic_Config *)NULL) return XST_FAILURE;
return XScuGic_CfgInitialize(&psGic, xGpioCfg, xGpioCfg->CpuBaseAddress);
} //
void psIntrTimerHandler(void *ref){
XScuTimer *ptr = (XScuTimer *) ref;
if(XScuTimer_IsExpired(ptr)){
XScuTimer_ClearInterruptStatus(ptr);
switch(state1){
case 0: state1 = 1; break;
case 1: state1 = 2; break;
case 2: state1 = 3; break;
case 3: state1 = 4; break;
case 4: state1 = 0; break;
default: state1 = 0;
}
}
}//
其实就是xscutimer_intr_example.c,只是重新整理修改了里面一丢丢的东西。
下一部分主要是如何使用两组定时器控制两组中断。