2. GPIO点灯控制

任务目标

在玄铁C906/C910开发板上,通过直接操作GPIO寄存器实现LED闪烁。本案例将引导你完成GPIO引脚的输出模式配置,并通过软件延时控制LED以约1Hz频率交替亮灭,从而掌握GPIO底层寄存器操作与基础延时方法。

硬件连接与前置条件

  • 开发板:玄铁C906/C910(以C906为例,寄存器地址基于CV1800B芯片手册)
  • LED连接:将LED正极通过220Ω限流电阻接至GPIOA_14(对应物理引脚),负极接GND
  • 编译工具链:RISC-V 64位交叉编译器(如riscv64-unknown-elf-gcc)
  • 调试方式:通过JTAG或串口下载程序,使用串口终端观察运行状态

GPIO寄存器映射

玄铁C906内部GPIO控制器基地址为0x03020000,本案例使用以下偏移寄存器(均为32位):

  • 0x00 — GPIO_SWPORT_DR(数据寄存器,写1输出高电平,写0输出低电平)
  • 0x04 — GPIO_SWPORT_DDR(方向寄存器,对应位置1为输出,置0为输入)
  • 0x30 — GPIO_CTRL(控制寄存器,需将对应位置1以启用软件控制,绕过硬件外设复用)

GPIOA_14对应上述寄存器中的第14位(bit 14)。

完整代码实现

#include <stdint.h>
#include <stdio.h>

// GPIO控制器基地址
#define GPIO_BASE      0x03020000UL

// 寄存器偏移
#define GPIO_DR        0x00
#define GPIO_DDR       0x04
#define GPIO_CTRL      0x30

// 目标引脚:GPIOA_14
#define LED_PIN        14

// 寄存器访问宏
#define REG32(addr)    (*(volatile uint32_t *)(addr))

// 软件延时函数(约1ms @ 500MHz CPU频率)
static void delay_ms(uint32_t ms) {
    // 粗略估算:500MHz下约125个周期/ms,取保守值200
    volatile uint32_t count;
    for (uint32_t i = 0; i < ms; i++) {
        for (count = 0; count < 200000; count++);
    }
}

int main(void) {
    uint32_t reg_val;

    // 1. 启用软件控制:将CTRL寄存器bit14置1
    reg_val = REG32(GPIO_BASE + GPIO_CTRL);
    reg_val |= (1UL << LED_PIN);
    REG32(GPIO_BASE + GPIO_CTRL) = reg_val;

    // 2. 配置方向为输出:DDR寄存器bit14置1
    reg_val = REG32(GPIO_BASE + GPIO_DDR);
    reg_val |= (1UL << LED_PIN);
    REG32(GPIO_BASE + GPIO_DDR) = reg_val;

    // 3. 初始状态:LED熄灭(输出低电平)
    reg_val = REG32(GPIO_BASE + GPIO_DR);
    reg_val &= ~(1UL << LED_PIN);
    REG32(GPIO_BASE + GPIO_DR) = reg_val;

    printf("GPIO LED Blink Test Started\r\n");

    while (1) {
        // LED点亮:输出高电平
        reg_val = REG32(GPIO_BASE + GPIO_DR);
        reg_val |= (1UL << LED_PIN);
        REG32(GPIO_BASE + GPIO_DR) = reg_val;
        delay_ms(500);

        // LED熄灭:输出低电平
        reg_val = REG32(GPIO_BASE + GPIO_DR);
        reg_val &= ~(1UL << LED_PIN);
        REG32(GPIO_BASE + GPIO_DR) = reg_val;
        delay_ms(500);
    }

    return 0;
}

编译与运行

  1. 保存代码为gpio_blink.c,执行交叉编译:
riscv64-unknown-elf-gcc -march=rv64imac -mabi=lp64 -O2 -o gpio_blink.elf gpio_blink.c
  1. 将生成的gpio_blink.elf通过调试器(如OpenOCD + GDB)加载到开发板内存并运行,或转换为bin文件烧录至Flash启动。

验证方法与判定标准

  • 静态检查:编译无报错,链接成功生成ELF文件。
  • 运行观察:程序运行后,串口终端打印GPIO LED Blink Test Started,同时LED以约1Hz频率闪烁(亮500ms、灭500ms)。
  • 判定通过:LED持续闪烁且周期稳定在1秒左右,说明GPIO输出配置正确、延时控制有效。若LED常亮或常灭,检查GPIO_CTRL是否已置1(外设复用冲突)及DDR方向配置。

常见问题

  • LED不亮:确认GPIO_CTRL寄存器bit14已置1,否则引脚被其他外设功能占用;检查DDR方向是否为输出。
  • 闪烁频率异常:延时函数基于CPU频率估算,若实际主频不同,调整内层循环次数200000即可。
  • 寄存器地址错误:不同型号(C906/C910)GPIO基地址可能不同,请以对应芯片手册为准。