1. LED流水灯控制

任务目标

在Zynq-7000系列开发板上,通过PS(Processing System)端的MIO GPIO或EMIO GPIO控制4个LED实现流水灯效果。本案例使用纯PS方案,不涉及PL(可编程逻辑)端逻辑设计,帮助掌握GPIO输出控制、延时函数和寄存器配置的基本方法。

硬件连接与前置条件

  • 开发板:Zynq-7020/7010(如ZedBoard、正点原子ZYNQ开发板)
  • 4个LED分别连接至PS端MIO引脚(具体引脚号因板卡而异,本案例以MIO0~MIO3为例,实际使用时需根据板卡原理图修改)
  • Vivado 2018.3+ / Vitis IDE,已创建Zynq最小系统(仅PS使能,无需PL配置)
  • 串口终端(115200波特率)用于观察运行状态

核心实现代码

以下代码在Vitis中作为独立应用程序运行,通过访问Zynq GPIO控制器寄存器实现LED流水灯控制:

#include <stdio.h>
#include "xil_io.h"
#include "sleep.h"
#include "xparameters.h"

// GPIO寄存器地址映射(Zynq-7000 PS GPIO基地址)
#define GPIO_BASE_ADDR  0xE000A000
// 寄存器偏移
#define DATA_OFFSET     0x00    // 数据寄存器(低16位对应MIO0~15)
#define DIRM_OFFSET     0x204   // 方向控制寄存器(1=输出)
#define OEN_OFFSET      0x208   // 输出使能寄存器(1=使能输出)

// 板卡LED连接引脚(根据实际硬件修改)
#define LED0_PIN        0       // MIO0
#define LED1_PIN        1       // MIO1
#define LED2_PIN        2       // MIO2
#define LED3_PIN        3       // MIO3

// 将4个LED对应引脚置为输出模式
void GPIO_Init(void) {
    u32 dir_reg, oen_reg;

    // 读取当前方向寄存器,将LED引脚对应位设为1(输出)
    dir_reg = Xil_In32(GPIO_BASE_ADDR + DIRM_OFFSET);
    dir_reg |= (1 << LED0_PIN) | (1 << LED1_PIN) |
               (1 << LED2_PIN) | (1 << LED3_PIN);
    Xil_Out32(GPIO_BASE_ADDR + DIRM_OFFSET, dir_reg);

    // 读取输出使能寄存器,将LED引脚对应位设为1(使能输出)
    oen_reg = Xil_In32(GPIO_BASE_ADDR + OEN_OFFSET);
    oen_reg |= (1 << LED0_PIN) | (1 << LED1_PIN) |
               (1 << LED2_PIN) | (1 << LED3_PIN);
    Xil_Out32(GPIO_BASE_ADDR + OEN_OFFSET, oen_reg);
}

// 点亮指定引脚对应的LED(低电平点亮,根据板卡极性调整)
void LED_On(int pin) {
    u32 data_reg = Xil_In32(GPIO_BASE_ADDR + DATA_OFFSET);
    data_reg |= (1 << pin);   // 置1点亮(若板卡为低电平点亮则改为置0)
    Xil_Out32(GPIO_BASE_ADDR + DATA_OFFSET, data_reg);
}

// 熄灭指定引脚对应的LED
void LED_Off(int pin) {
    u32 data_reg = Xil_In32(GPIO_BASE_ADDR + DATA_OFFSET);
    data_reg &= ~(1 << pin);  // 置0熄灭
    Xil_Out32(GPIO_BASE_ADDR + DATA_OFFSET, data_reg);
}

int main(void) {
    int led_pins[4] = {LED0_PIN, LED1_PIN, LED2_PIN, LED3_PIN};
    int i;

    printf("LED流水灯实验开始...\r\n");
    GPIO_Init();

    while (1) {
        // 依次点亮每个LED,形成流水效果
        for (i = 0; i < 4; i++) {
            LED_On(led_pins[i]);
            usleep(200000);    // 延时200ms
            LED_Off(led_pins[i]);
        }
    }
    return 0;
}

运行与验证

  1. 在Vitis中新建Application Project,平台选择已导出的硬件平台(包含Zynq PS配置)
  2. 将上述代码替换生成的helloworld.c,保存并编译
  3. 连接开发板JTAG和串口,下载程序运行
  4. 观察4个LED是否按顺序依次点亮并熄灭,形成循环流水效果

通过条件:LED0→LED1→LED2→LED3依次点亮,每个LED亮200ms后熄灭,循环往复;串口终端输出“LED流水灯实验开始...”。若LED不亮,检查引脚号是否与板卡原理图一致,以及LED极性(高/低电平点亮)是否需要反转LED_On/LED_Off中的置位逻辑。

常见问题

  • LED不亮:确认MIO引脚号正确,且方向寄存器和输出使能寄存器已正确配置;检查LED极性,部分板卡需要低电平点亮
  • 编译报错:确认已包含xil_io.h和xparameters.h头文件路径,Vitis BSP已正确生成
  • 流水速度过快/过慢:调整usleep()参数值(单位微秒)