dw_apb_gpio总结

1 概述

​ DW_apb_gpio是新思科技推出的基于AMBA 2.0APB总线‌的通用输入输出知识产权核,属于DesignWare库中的可合成组件。它广泛应用于SoC设计中,用于通过APB接口控制芯片引脚的输入/输出状态。

核心特性:

  • 总线接口:遵循AMBA 2.0APB协议,作为从设备挂载在APB总线上,适用于低带宽外设控制。
  • 端口配置:最多支持4个独立端口(Port A、B、C、D),每个端口最多可配置32个引脚,总引脚数可达128个,具体数量由IP配置决定。
  • 功能模式:每个引脚支持软件控制‌(通过寄存器读写)或硬件控制‌(外部信号直接驱动),部分引脚可混合配置。
  • 中断支持:仅Port A‌支持中断功能,包括电平/边沿触发、极性配置、去抖动(Debounce)及中断屏蔽/使能逻辑。

2 寄存器概述

端口操作寄存器(每个端口独立):

复制代码
基址 = base_addr + port_index * 0x0C
port_index: 0=Port A, 1=Port B, 2=Port C, 3=Port D
位 n(0 ≤ n ≤ 31)对应端口内的第 n 号引脚。
偏移量 寄存器名称 属性 功能描述
0x00 SWPORTx_DR R/W 软件端口数据寄存器(输出值,一般仅在输出时配置)
0x04 SWPORTx_DDR R/W 软件端口方向寄存器(1=输出,0=输入)
0x08 SWPORTx_CTL R/W 软件端口控制寄存器(0=GPIO模式,1=硬件功能模式)

外部端口寄存器:

偏移量 寄存器名称 属性 功能描述
0x50 + port_index*0x04 EXT_PORTx RO 读取引脚实际电平(一般在输入时使用,比如配置为输入时,实时查看引脚的输入值)
  • 位n为1表示引脚当前为高电平(3.3v/5v等),0为低电平。
  • 即使在输出模式下,也可以回读到引脚上的实际电平(可用于开漏输出时的状态确认)。

中断相关寄存器(仅端口A)

复制代码
基址 = base_addr(无需端口偏移)这些寄存器只对端口A有效,其他端口访问无效或返回0。
偏移量 寄存器名称 属性 功能描述
0x30 INTEN R/W 中断使能(1=使能中断)
0x34 INTMASK R/W 中断屏蔽(1=屏蔽中断,注意与 INTEN 逻辑不同)
0x38 INTTYPE_LEVEL R/W 中断类型(0=电平触发,1=边沿触发)
0x3c INT_POLARITY R/W 中断极性(0=低电平/下降沿,1=高电平/上升沿)
0x40 INTSTATUS RO 原始中断状态(未屏蔽前)
0x44 RAW_INTSTATUS RO 原始中断状态(同INTSTATUS)
0x48 DEBOUNCE R/W 去抖动使能(1=使能去抖动)
0x4c PORTA_EOI WO 写任意值清除已触发的中断(边沿模式必须写)

中断触发方式组合(INTTYPE_LEVEL与INT_POLARITY)

TYPE POLARITY 触发方式
0 0 低电平触发
0 1 高电平触发
1 0 下降沿触发
1 1 上升沿触发

去抖动:使能后,引脚电平需稳定超过2个APB时钟周期才会被采样,用于消除按键抖动。

中断清除:

  • 边沿触发模式:中断发生后必须写PORTA_EOI寄存器(任意值)来清除中断标志。
  • 电平触发模式:中断由外部电平决定,电平撤销后中断自动消失,但建议也写PORTA_EOI以保证统一处理。
  • 如果是双边沿触发时,INTTYPE_LEVEL和INT_POLARITY寄存器的值是不起作用的,此时以GPIO_INT_BOTHEDGE寄存器为主。

配置与识别寄存器

偏移量 寄存器名称 属性 功能描述
0x64 GPIO_ID_CODE RO IP版本的标识码
0x70 GPIO_CONFIG_REG2 RO 读该寄存器各端口引脚数等
0x74 GPIO_CONFIG_REG1 RO 读该寄存器的bit2-bit3确定端口数量
... ... ... ...

3 编程概要

寄存器偏移量定义:

复制代码
/* 寄存器偏移宏 */
#define DW_GPIO_SWPORT_DR      0x00
#define DW_GPIO_SWPORT_DDR     0x04
#define DW_GPIO_SWPORT_CTL     0x08
#define DW_GPIO_INTEN          0x30
#define DW_GPIO_INTMASK        0x34
#define DW_GPIO_INTTYPE_LEVEL  0x38
#define DW_GPIO_INT_POLARITY   0x3C
#define DW_GPIO_INTSTATUS      0x40
#define DW_GPIO_RAW_INTSTATUS  0x44
#define DW_GPIO_DEBOUNCE       0x48
#define DW_GPIO_PORTA_EOI      0x4C
#define DW_GPIO_EXT_PORT       0x50
#define DW_GPIO_ID_CODE        0x64
 
/* 端口数量及位宽(通常从 config 寄存器读取,此处先定义典型值) */
#define DW_GPIO_PORTS_MAX      4
#define DW_GPIO_PINS_MAX       32
 
/* 驱动句柄 */
typedef struct {
    uint32_t base_addr;         //模块基地址
    uint8_t  port_num;          //端口数量(实际硬件支持)
    uint8_t  pin_count[4];      //每个端口的引脚数
} dw_gpio_t;

/* ---------- 底层寄存器操作 ---------- */
static inline uint32_t dw_read32(uint32_t addr) 
{
    return *(volatile uint32_t *)addr;
}

static inline void dw_write32(uint32_t addr, uint32_t val)
{
    *(volatile uint32_t *)addr = val;
}

static inline uint32_t dw_port_base(dw_gpio_t *gpio, int port)
{
    return gpio->base_addr + port * 0x0C;
}

初始化:

复制代码
void dw_gpio_init(dw_gpio_t *gpio, uint32_t base) 
{
    gpio->base_addr = base;
    /* 读取 ID 码确认硬件存在 */
    uint32_t id = dw_read32(base + DW_GPIO_ID_CODE);
    if (id != 0x44573030) {  // "DW00"
        // 硬件不存在或错误,可设置标志或直接返回
        gpio->port_num = 0;
        return;
    }
    /* 从 GPIO_CONFIG_REG2 读取配置 (偏移 0x70) */
    uint32_t cfg2 = dw_read32(base + 0x70);
    //cfg2 位域: [3:0] = 端口数量-1; [7:4] = 端口A引脚数-1; 以此类推
    gpio->port_num = (cfg2 & 0x0F) + 1;
    if (gpio->port_num > DW_GPIO_PORTS_MAX)
        gpio->port_num = DW_GPIO_PORTS_MAX;

    /* 解析每个端口的引脚数量 (最多32) */
    for (int p = 0; p < gpio->port_num; p++) {
        int shift = 4 + p * 4;
        int bits = (cfg2 >> shift) & 0x0F;
        gpio->pin_count[p] = (bits == 0) ? 32 : bits; //0表示32
    }
}

配置/获取GPIO方向,配置mode等:

复制代码
void dw_gpio_set_direction(dw_gpio_t *gpio, int port, int pin, bool output) 
{
    if (port >= gpio->port_num) return;
    if (pin >= gpio->pin_count[port]) return;
    
    uint32_t reg = dw_port_base(gpio, port) + DW_GPIO_SWPORT_DDR;
    uint32_t val = dw_read32(reg);
    if (output)
        val |= (1U << pin);
    else
        val &= ~(1U << pin);
    dw_write32(reg, val);
}

void dw_gpio_set_function(dw_gpio_t *gpio, int port, int pin, bool hw_mode) 
{
    if (port >= gpio->port_num) return;
    if (pin >= gpio->pin_count[port]) return;
    
    uint32_t reg = dw_port_base(gpio, port) + DW_GPIO_SWPORT_CTL;
    uint32_t val = dw_read32(reg);
    if (hw_mode)
        val |= (1U << pin);
    else
        val &= ~(1U << pin);
    dw_write32(reg, val);
}

void dw_gpio_write(dw_gpio_t *gpio, int port, int pin, bool high) 
{
    if (port >= gpio->port_num) return;
    if (pin >= gpio->pin_count[port]) return;
    
    uint32_t reg = dw_port_base(gpio, port) + DW_GPIO_SWPORT_DR;
    uint32_t val = dw_read32(reg);
    if (high)
        val |= (1U << pin);
    else
        val &= ~(1U << pin);
    dw_write32(reg, val);
}

bool dw_gpio_read(dw_gpio_t *gpio, int port, int pin) 
{
    if (port >= gpio->port_num) return false;
    if (pin >= gpio->pin_count[port]) return false;
    
    uint32_t reg = gpio->base_addr + DW_GPIO_EXT_PORT + port * 4;
    uint32_t val = dw_read32(reg);
    return (val >> pin) & 0x1;
}

void dw_gpio_debounce_enable(dw_gpio_t *gpio, int pin, bool enable) 
{
    if (pin >= gpio->pin_count[0]) return;
    
    uint32_t reg = gpio->base_addr + DW_GPIO_DEBOUNCE;
    uint32_t val = dw_read32(reg);
    if (enable)
        val |= (1U << pin);
    else
        val &= ~(1U << pin);
    dw_write32(reg, val);
}

中断相关的函数操作:

复制代码
void dw_gpio_int_enable(dw_gpio_t *gpio, int pin, bool enable) 
{
    if (pin >= gpio->pin_count[0]) return;  //仅端口A
    
    uint32_t reg = gpio->base_addr + DW_GPIO_INTEN;
    uint32_t val = dw_read32(reg);
    if (enable)
        val |= (1U << pin);
    else
        val &= ~(1U << pin);
    dw_write32(reg, val);
}

void dw_gpio_int_set_trigger(dw_gpio_t *gpio, int pin, bool level, bool high_polarity) 
{
    if (pin >= gpio->pin_count[0]) return;
    
    uint32_t type_reg = gpio->base_addr + DW_GPIO_INTTYPE_LEVEL;
    uint32_t pol_reg  = gpio->base_addr + DW_GPIO_INT_POLARITY;
    uint32_t type_val = dw_read32(type_reg);
    uint32_t pol_val  = dw_read32(pol_reg);
    
    if (level)
        type_val |= (1U << pin);
    else
        type_val &= ~(1U << pin);
    
    if (high_polarity)
        pol_val |= (1U << pin);
    else
        pol_val &= ~(1U << pin);
    
    dw_write32(type_reg, type_val);
    dw_write32(pol_reg, pol_val);
}

uint32_t dw_gpio_get_raw_int_status(dw_gpio_t *gpio)
{
    return dw_read32(gpio->base_addr + DW_GPIO_RAW_INTSTATUS);
}

void dw_gpio_clear_int(dw_gpio_t *gpio, int pin)
{
    //对于边沿触发,写EOI寄存器清除
    //对于电平触发,需要外部信号撤销才能清除,但写EOI也无害
    (void)pin; //忽略pin,EOI写入任意值清除所有已响应的中断
    dw_write32(gpio->base_addr + DW_GPIO_PORTA_EOI, 0x0);
}
相关推荐
wunaiqiezixin1 小时前
MIT 6.S081 file system 实验篇(lab9):Large files (moderate)
linux·unix·os·xv6·mit6.s081
GeW1 小时前
RHCE 考试通过率提升:核心考点、实验环境与排错技巧
linux
吴声子夜歌2 小时前
Shell编程实例——与解析相关的任务(二)
linux·运维·shell
mengge.cloud2 小时前
0917Docker 小白实验教程(理论+实操)
linux·服务器·网络·docker
Escalating_xu3 小时前
【内存管理发展史】从 8086 实模式到分页:CPU 如何把虚拟地址变成物理地址
linux·缓存
亿朝亿夕3 小时前
linux服务排查步骤
linux
酸菜。3 小时前
dw_apb_uart总结
linux
倔强的石头1063 小时前
【Linux指南】动静态库系列(八):ELF 加载与进程地址空间:程序还没运行,为什么已经有地址
linux·运维
第十人i4 小时前
Linux 一键重装系统脚本:reinstall 工具详解
linux·服务器