4. 定时器中断应用

任务目标

使用Zynq PS端私有定时器(SCU Private Timer)产生周期性中断,在中断服务函数(ISR)中翻转MIO连接的LED,掌握定时器寄存器配置、中断控制器(GIC)初始化及中断处理流程。

硬件连接

LED接在PS端MIO引脚(本例使用MIO0,对应Zynq开发板LED0),高电平点亮。定时器中断频率设定为1Hz,便于肉眼观察LED翻转。

核心代码

// timer_led.c - Zynq PS定时器中断控制LED
#include "xil_io.h"
#include "xil_exception.h"
#include "xscugic.h"
#include "xscutimer.h"

#define TIMER_DEVICE_ID      XPAR_XSCUTIMER_0_DEVICE_ID
#define INTC_DEVICE_ID       XPAR_XSCUGIC_0_DEVICE_ID
#define TIMER_IRPT_INTR      XPAR_SCUTIMER_INTR
#define LED_MIO_PIN          0
#define GPIO_DATA_REG        0xE000A000  // MIO引脚数据寄存器(SLCR已使能输出)

static XScuTimer TimerInstance;
static XScuGic IntcInstance;
static volatile int led_state = 0;

// 定时器中断服务函数:翻转LED
void Timer_ISR(void *CallbackRef) {
    XScuTimer *TimerPtr = (XScuTimer *)CallbackRef;
    if (XScuTimer_IsExpired(TimerPtr)) {
        XScuTimer_ClearInterruptStatus(TimerPtr);
        led_state = !led_state;
        Xil_Out32(GPIO_DATA_REG, led_state ? 0x1 : 0x0);  // MIO0输出
    }
}

// 初始化MIO0为GPIO输出
static void MIO_LED_Init(void) {
    // 设置MIO0为GPIO输出模式(SLCR寄存器)
    Xil_Out32(0xF8000008, 0x0);       // MIO_PIN_0: 选择GPIO功能
    Xil_Out32(0xF8000700, 0x1);       // GPIO_DIRM: MIO0输出
    Xil_Out32(0xF8000714, 0x1);       // GPIO_OEN: 使能输出
    Xil_Out32(GPIO_DATA_REG, 0x0);    // 初始LED灭
}

// 定时器中断初始化
static int Timer_Intc_Init(void) {
    XScuGic_Config *IntcConfig;
    XScuTimer_Config *TimerConfig;

    // GIC初始化
    IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
    XScuGic_CfgInitialize(&IntcInstance, IntcConfig, IntcConfig->CpuId);

    // 定时器初始化
    TimerConfig = XScuTimer_LookupConfig(TIMER_DEVICE_ID);
    XScuTimer_CfgInitialize(&TimerInstance, TimerConfig, TimerConfig->BaseAddr);

    // 设置定时器周期:CPU_1x_CLK(100MHz) / 1Hz = 100,000,000
    XScuTimer_LoadTimer(&TimerInstance, 100000000);
    XScuTimer_EnableAutoReload(&TimerInstance);
    XScuTimer_Start(&TimerInstance);

    // 连接中断并使能
    XScuGic_Connect(&IntcInstance, TIMER_IRPT_INTR,
                    (Xil_ExceptionHandler)Timer_ISR, &TimerInstance);
    XScuGic_Enable(&IntcInstance, TIMER_IRPT_INTR);

    // 使能全局中断
    Xil_ExceptionInit();
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
                    (Xil_ExceptionHandler)XScuGic_InterruptHandler, &IntcInstance);
    Xil_ExceptionEnable();
    return XST_SUCCESS;
}

int main(void) {
    MIO_LED_Init();
    Timer_Intc_Init();
    while(1);  // 等待中断
    return 0;
}

运行与验证

  1. 工程配置:在Vivado中创建Zynq工程,使能PS端MIO0作为GPIO输出,导出硬件并启动SDK。
  2. 编译下载:在SDK中新建Application Project,将上述代码替换main.c,编译后下载到开发板运行。
  3. 验证方法:观察LED0是否以1Hz频率(每秒翻转一次)闪烁。若LED不亮或频率异常,检查:
  • MIO0引脚是否被其他外设占用(SLCR配置需与Vivado中的MIO设置一致);
  • 定时器预装载值是否正确(CPU_1x_CLK频率需与硬件配置一致,常见为100MHz);
  • 中断号是否匹配(XPAR_SCUTIMER_INTR通常为29)。

预期结果:LED0以1秒为周期持续亮灭交替,程序不退出,无异常打印。