首页 / 用AI开发

AG32 GPIO 控制与上下拉实现

2026年11月4日

AG32 (AGRV2K) 的 GPIO 控制完整指南:信号模型、寄存器、SDK API、上下拉实现、输入/输出/中断,以及工程级注意事项。


1. AG32 GPIO 信号模型

AG32 内部 GPIO 信号命名格式是 GPIOx_y,其中:
– x = GPIO 组 (0~9,共 10 组)
– y = bit 编号 (0~7,每组 8 bit)
– 寄存器基址: GPIO0_BASE = 0x40014000,每组 +0x1000

关键概念:GPIO 信号 ≠ 物理引脚。GPIO4_1 是 MCU 内部一个信号名,需要在 VE 文件里绑到具体物理引脚(如 PIN_34)才有用。

# my_board.ve
GPIO4_1 PIN_34    # 把 GPIO4_1 信号绑到物理引脚 34

2. GPIO 寄存器(GPIO_TypeDef)

typedef struct {
  __IO uint32_t GpioDATA[256];  // 0x000-0x3FC, 位带别名
  __IO uint32_t GpioDIR;        // 0x400, 方向: 1=output, 0=input
  __IO uint32_t GpioIS;         // 0x404, 中断: 1=电平 0=边沿
  __IO uint32_t GpioIBE;        // 0x408, 中断: 1=双边沿
  __IO uint32_t GpioIEV;        // 0x40C, 中断: 1=高/上升 0=低/下降
  __IO uint32_t GpioIE;         // 0x410, 中断屏蔽 (1=enable)
  __I  uint32_t GpioRIS;        // 0x414, 原始中断状态
  __I  uint32_t GpioMIS;        // 0x418, 屏蔽后中断状态
  __O  uint32_t GpioIC;         // 0x41C, 写 1 清中断
  __IO uint32_t GpioAFSEL;      // 0x420, 1=AF 模式 0=IO 模式
} GPIO_TypeDef;

2.1 寄存器地址(各组)

GPIO 组基址
GPIO00x40014000
GPIO10x40015000
GPIO20x40016000
GPIO30x40017000
GPIO40x40018000
GPIO50x40019000
GPIO60x4001A000
GPIO70x4001B000
GPIO80x4001C000
GPIO90x4001D000

2.2 GpioDATA 位带别名(关键)

GpioDATA[0..255] 是位带别名,写 GpioDATA[bits] 等价于”操作 bits 中为 1 的位”:
– 写 0xFF = 把 bits 中所有位拉高
– 写 0x00 = 把 bits 中所有位拉低
– 写 0xAB = 把 bits 中对应 1 的位设为 0xAB 对应位的值
– 读 GpioDATA[bits] = 读 bits 中所有位的当前值

位带别名的妙处:写操作只影响 bits 中为 1 的位,其他位保持不变。不需要 read-modify-write 就能原子地改单 bit。

// 例子:设 bit 0 高,bit 1 低,其他 bit 不动
GPIO0->GpioDATA[0x03] = 0x01;  // bit 0 写 1,bit 1 写 0

3. SDK GPIO API(gpio.h)

所有 GPIO 操作都通过 gpio.h 的 inline 函数,不要直接读写寄存器(可读性差、易错)。

3.1 时钟使能(必须先做)

#include "system.h"

SYS_EnableAPBClock(APB_MASK_GPIO0);  // GPIO0 时钟
SYS_EnableAPBClock(APB_MASK_GPIO4);  // GPIO4

不使能时钟就操作寄存器,寄存器读不到值,操作没效果。

3.2 基础 IO 操作

// 方向
GPIO_SetOutput(GPIO0, GPIO_BIT0);    // 设 bit 0 为输出
GPIO_SetInput(GPIO0, GPIO_BIT0);     // 设 bit 0 为输入

// 输出
GPIO_SetHigh(GPIO0, GPIO_BIT0);      // 拉高 (output 1)
GPIO_SetLow(GPIO0, GPIO_BIT0);       // 拉低 (output 0)
GPIO_SetValue(GPIO0, GPIO_BIT0, 1);  // 拉高
GPIO_Toggle(GPIO0, GPIO_BIT0);        // 翻转

// 输入读取
uint8_t v = GPIO_GetValue(GPIO0, GPIO_BIT0);  // 读 0 或 1

多 bit 同时操作:

GPIO_SetOutput(GPIO4, GPIO_BIT0 | GPIO_BIT1);  // bit 0/1 都设 output
GPIO_SetHigh(GPIO4, GPIO_BIT0 | GPIO_BIT1);    // bit 0/1 都拉高

3.3 复用模式(AF)

当引脚要给外设用(UART / SPI / I2C 等),必须切到 AF 模式,否则外设无输出。

// 启用 UART0 复用 (SDK 自动操作 GPIO AFSEL)
GPIO_AF_ENABLE(GPIO_AF_PIN(UART, 0, UARTRXD));
GPIO_AF_ENABLE(GPIO_AF_PIN(UART, 0, UARTTXD));

// 关掉 AF (回到普通 GPIO 模式)
GPIO_AF_DISABLE(GPIO_AF_PIN(UART, 0, UARTTXD));

GPIO_AF_PIN(UART, 0, UARTTXD) 宏 内部自动算出:
– 哪个 GPIO group
– 哪个 bit
– AFSEL 寄存器要写什么

不要手动找 AF 寄存器,直接用 GPIO_AF_ENABLE / GPIO_AF_DISABLE。

3.4 中断

// 1. 清中断(防止上电残留)
GPIO_ClearInt(GPIO2, GPIO_BIT0);

// 2. 配置触发方式
GPIO_IntConfig(GPIO2, GPIO_BIT0, GPIO_INTMODE_FALLEDGE);
// GPIO_INTMODE 5 种:
//   LOWLEVEL  - 低电平触发
//   HIGHLEVEL - 高电平触发
//   FALLEDGE  - 下降沿
//   RISEEDGE  - 上升沿
//   BOTHEDGE  - 双边沿

// 3. 启用该 bit 的中断
GPIO_EnableInt(GPIO2, GPIO_BIT0);

// 4. 在 PLIC 注册 ISR
INT_EnableIRQ(GPIO2_IRQn, 1);  // priority 1

// 5. ISR handler — SDK 默认符号 GPIO<x>_isr
void GPIO2_isr(void) {
  GPIO_ClearInt(GPIO2, GPIO_BIT0);  // 必须在 ISR 入口清中断
  // ... 你的处理
}

注意:
– ISR 里必须先清中断(GPIO_ClearInt),否则 PLIC 会一直触发
– ISR 名字格式 GPIOx_isr 是 SDK weak 符号,直接重定义就覆盖
– 优先级 1 是最高有效优先级(数越小越高,看 interrupt.h 的 MIN_IRQ_PRIORITY)


4. 上下拉实现(硬件级)

AG32 上下拉是 引脚级硬件特性,在 Quartus P&R 阶段配置,运行时不能改。SDK 运行时函数 GPIO_SetInput / GPIO_SetOutput 只设方向,不改上下拉。

4.1 配置位置

通过 VE 文件的 .asf(Altera Settings File)在 Quartus 编译时配置:

# my_board.asf
puts "DESIGN ASF -- GPIO pull-up/down"

# 弱上拉 (推荐 5-100 kΩ,功耗低)
set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to PIN_16
set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to PIN_23

# 弱下拉
set_instance_assignment -name WEAK_PULL_DOWN_RESISTOR ON -to PIN_26

# 驱动电流(可选,默认 8mA,LED 驱动可加大到 16mA)
set_instance_assignment -name CURRENT_STRENGTH 16MA -to PIN_31

4.2 上下拉与输入模式的关系

GPIO 方向上下拉是否生效
Input (读 GPIO 值)✅ 生效
Output (驱动 GPIO 值)❌ 不生效(输出驱动)
AF (外设复用)⚠ 取决于外设,部分场景仍有效

关键:即使引脚是 Input 模式,没设上拉/下拉的话,引脚是浮空,读到电平是随机的(EMI / 静电干扰)。按键输入、开关检测、空闲状态确认 都需要上下拉。

4.3 工程集成

platformio.ini 设 design.asf = ./my_board.asf,pio run -t buildlogic 时 Supra 编译器会读 asf 配置:

[setup_logic]
logic_ve     = my_board.ve
design.asf   = ./my_board.asf
logic_device = AGRV2KL100

注意改 ASF 后必须 pio run -t logic_clean + pio run -t buildlogic:sconsign cache 不跟踪 ASF 修改。

4.4 验证上下拉生效

读 pin 状态:

uint8_t v = GPIO_GetValue(GPIO, GPIO_BIT);  // 高 = 上拉生效,低 = 下拉生效
配置没接外设时读到
Pull-up1 (高)
Pull-down0 (低)
浮空随机

debug 推荐:在 main loop 里周期性读 input pin,打印 pull-up OK / pull-down OK / FAIL。如果读到跟预期相反,说明 ASF 配置没生效(看 buildlog 里 Processing my_board.asf 是否出现)。


5. GPIO 实战模式

5.1 输出模式(LED / 驱动外部电路)

// 1. 使能时钟
SYS_EnableAPBClock(APB_MASK_GPIO4);

// 2. 设方向 output
GPIO_SetOutput(GPIO4, GPIO_BIT0 | GPIO_BIT1 | GPIO_BIT2 | GPIO_BIT3);

// 3. 写值
GPIO_SetHigh(GPIO4, GPIO_BIT0 | GPIO_BIT1);  // LED1+2 灭 (active-low)
GPIO_SetLow(GPIO4, GPIO_BIT0);                // LED1 亮

// 4. 翻转
GPIO_Toggle(GPIO4, GPIO_BIT0);

active-low LED 用法:GPIO 输出 0 = 亮,1 = 灭。

5.2 输入模式(按键 / 开关检测)

// 1. 使能时钟
SYS_EnableAPBClock(APB_MASK_GPIO0);

// 2. 设方向 input
GPIO_SetInput(GPIO0, GPIO_BIT0);

// 3. 在 ASF 里设上拉(否则浮空)
set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to PIN_16

// 4. 读
uint8_t pressed = GPIO_GetValue(GPIO0, GPIO_BIT0) == 0;  // active-low 按下

active-low 按键:按键一端接 MCU,另一端接 GND。没按时引脚被上拉拉高,按下时拉低 → 读到 0 = 按下。

5.3 中断模式(低功耗场景)

// pin_init()
SYS_EnableAPBClock(APB_MASK_GPIO2);
GPIO_SetInput(GPIO2, GPIO_BIT0);
GPIO_ClearInt(GPIO2, GPIO_BIT0);
GPIO_IntConfig(GPIO2, GPIO_BIT0, GPIO_INTMODE_FALLEDGE);  // 下降沿
INT_EnableIRQ(GPIO2_IRQn, 1);
GPIO_EnableInt(GPIO2, GPIO_BIT0);

// 中断服务程序
void GPIO2_isr(void) {
  GPIO_ClearInt(GPIO2, GPIO_BIT0);  // 必须先清
  // 处理
}

5.4 流水灯 demo

// 4 LED active-low,主循环 500ms 切换
static const uint8_t led_bits[] = { GPIO_BIT0, GPIO_BIT1, GPIO_BIT2, GPIO_BIT3 };
static const uint8_t led_mask  = GPIO_BIT0 | GPIO_BIT1 | GPIO_BIT2 | GPIO_BIT3;

void led_init(void) {
  SYS_EnableAPBClock(APB_MASK_GPIO4);
  GPIO_SetOutput(GPIO4, led_mask);
  GPIO_SetHigh(GPIO4, led_mask);  // 全灭
}

void led_waterfall(uint8_t phase) {
  for (uint8_t i = 0; i < 4; i++) {
    if (i == phase) GPIO_SetLow(GPIO4, led_bits[i]);   // ON
    else            GPIO_SetHigh(GPIO4, led_bits[i]);  // OFF
  }
}

// main
while (1) {
  led_waterfall(phase);
  phase = (phase + 1) & 0x03;
  UTIL_IdleUs(500e3);  // 500ms
}

6. 常见错误

6.1 GPIO 操作没效果

症状:GPIO_SetHigh 后读 pin 还是 0。

排查:
1. 没使能时钟 — SYS_EnableAPBClock(APB_MASK_GPIOx) 必须调
2. 方向设错 — Output 模式才能拉高拉低,Input 模式读不到
3. VE 没绑引脚 — 寄存器操作改了,但 bit 对应的物理引脚没绑,外部看不到
4. VE 路由了 AF 模式 — GPIO_AFSEL 寄存器被 SDK 改了,被外设占用

debug:

// 读 GpioDIR,看是不是 output
uint32_t dir = GPIO4->GpioDIR;
printf("GPIO4 DIR = 0x%X\n", dir);  // bit 0/1/2/3 应该是 1

// 读 GpioDATA,看实际值
uint32_t val = GPIO4->GpioDATA[0x0F];
printf("GPIO4 DATA = 0x%X\n", val);

6.2 上拉/下拉不生效

症状:Input pin 读到 0(应该上拉读到 1),或读到 1(应该下拉读到 0)。

排查:
1. ASF 配置没生效 — 跑 pio run -t logic_clean + pio run -t buildlogic,看 buildlog 里 Processing my_board.asf 出现
2. asf 文件最后一行有换行符 — 没换行的话最后一条 set_instance_assignment 不生效
3. 引脚 VE 绑错 — set_instance_assignment -to PIN_x 中的 PIN_x 跟 VE 里信号绑的引脚要一致
4. 外部干扰 — 拉一根线到 VCC / GND 验证,排除外部干扰

6.3 中断一直触发

症状:ISR 一秒触发几十次,看起来像”幽灵触发”。

排查:
1. 没清中断 — ISR 入口必须 GPIO_ClearInt,否则 PLIC 一直 pending
2. 中断模式设错 — GPIO_INTMODE_FALLEDGE 但没设上拉,引脚浮空,电平在高低之间漂移触发假中断
3. 多个 bit 共享一个 IRQ — GPIO2_IRQn 是整个 GPIO2 组的中断,GpioRIS 寄存器有具体哪个 bit 触发。ISR 里只清触发的那一个 bit,别 ClearInt 整个 group

6.4 编译错误 APB_MASK_GPIOGPIO_IDX undeclared

症状:用了 PERIPHERAL_ENABLE(GPIO, GPIO_IDX) 宏,SDK peripherals.h 字符串拼接问题。

修法:直接调 SYS_EnableAPBClock(APB_MASK_GPIO4),不要用 PERIPHERAL_ENABLE(GPIO, IDX) 宏。


7. SDK API 速查

// 时钟
void SYS_EnableAPBClock(uint32_t mask);     // APB_MASK_GPIOx 之类
void SYS_DisableAPBClock(uint32_t mask);

// 方向
void GPIO_SetOutput(GPIO_TypeDef *gpio, uint8_t bits);
void GPIO_SetInput(GPIO_TypeDef *gpio, uint8_t bits);

// 输出
void GPIO_SetHigh(GPIO_TypeDef *gpio, uint8_t bits);
void GPIO_SetLow(GPIO_TypeDef *gpio, uint8_t bits);
void GPIO_SetValue(GPIO_TypeDef *gpio, uint8_t bits, uint8_t value);
void GPIO_Toggle(GPIO_TypeDef *gpio, uint8_t bits);

// 输入
uint8_t GPIO_GetValue(GPIO_TypeDef *gpio, uint8_t bits);

// AF
void GPIO_AF_ENABLE(uint32_t pin);   // pin = GPIO_AF_PIN(UART, 0, UARTTXD)
void GPIO_AF_DISABLE(uint32_t pin);

// 中断
void GPIO_ClearInt(GPIO_TypeDef *gpio, uint8_t bits);
void GPIO_IntConfig(GPIO_TypeDef *gpio, uint8_t bits, uint32_t mode);
void GPIO_EnableInt(GPIO_TypeDef *gpio, uint8_t bits);
void GPIO_DisableInt(GPIO_TypeDef *gpio, uint8_t bits);
uint8_t GPIO_IsRawIntActive(GPIO_TypeDef *gpio, uint8_t bits);
uint8_t GPIO_IsMaskedIntActive(GPIO_TypeDef *gpio, uint8_t bits);

// 模式常量
GPIO_INTMODE_LOWLEVEL
GPIO_INTMODE_HIGHLEVEL
GPIO_INTMODE_FALLEDGE
GPIO_INTMODE_RISEEDGE
GPIO_INTMODE_BOTHEDGE

.

返回用AI开发