跳转至

GPIO使用指南*

第一章 概述*

GPIO(GeneralPurposeinput/output),是通用输入输出端口的简称。可以通过软件控制其输出和输入,通俗来说就是常用引脚,可以控制引脚的高低电平,对其进行读取或者写入。且每一路都可独立配置为中断,支持多种中断类型。

目前驱动支持:

  • GPIO输出

  • GPIO输入(关闭内部上/下拉输入)

  • GPIO上拉输入

  • GPIO下拉输入

  • GPIO IDEL

上拉输入:

上拉输入模式下,I/O端口的电平信号直接进入输入数据寄存器。在I/O端口悬空(在无信号输入)的情况下,输入端的电平保持在高电平;并且在I/O端口输入为低电平的时候,输入端的电平也是低电平。

下拉输入:

下拉输入模式下,I/O端口的电平信号直接进入输入数据寄存器。在I/O端口悬空(在无信号输入)的情况下,输入端的电平保持在低电平;并且在I/O端口输入为高电平的时候,输入端的电平也是高电平。

GPIO IDEL:

如果管脚不用作任何功能,应该配置成 IDLE 状态,此时的状态为:输入无效;输出无效;上下拉电阻无效 如果管脚有复用模拟功能,在使用模拟功能的场景下,应配置成IDLE状态

第二章 IO PAD类型*

IO 要当作 GPIO 使用,需要支持复用为 GPIO功能。具体请查看芯片的 datesheet,看看哪些管脚支持可复用为 GPIO。

Apus 系列芯片中所有 IO PAD 大概可分为以下几类:电源 IO数字 IO模拟 IO。其中电源 IO是独占 IO,软件不需要关心,数字 IO模拟 IO可以在同一个 IO 上。数字/模拟 IO 如下表所示:

IDEL 代表悬空高阻态。如果管脚不用作任何功能,应该配置成 IDLE 状态,此时的状态为:输入无效;输出无效;上下拉电阻无效。

GPIO 号 PAD 名称 特殊功能 可否唤醒 默认功能
0 P0_0 IDLE
1 P0_1 IDLE
2 P0_2 IDLE
3 P0_3 IDLE
4 P0_4 IDLE
5 P0_5 UART0_RX
6 P0_6 UART0_TX
7 P0_7 BOOT PIN
8 P1_0 AIN0 IDLE
9 P1_1 AIN1 IDLE
10 P1_2 AIN2 JTAG_TMS
11 P1_3 AIN3 JTAG_TCK
12 P1_4 AIN4 IDLE
13 P1_5 AIN5 IDLE
14 P1_6 AIN6 IDLE
15 P1_7 AIN7 IDLE
16 P2_0 IDLE
17 P2_1 SYSI2C_SDA
18 P2_2 IDLE
19 P2_3 IDLE
20 P2_4 IDLE
21 P2_5 IDLE
22 P2_6 USB_DM IDLE
23 P2_7 USB_DP IDLE
24 P3_0 IDLE
25 P3_1 SYSI2C_SCL
26 P3_2 PWM_P
27 P3_3 PWM_N
28 P3_4 XO 32K IDLE
29 P3_5 XO 32K IDLE
30 P3_6 IDLE
31 P3_7 IDLE
32 P4_0 仅可作为 GPIO32 使用
33 P4_1 仅可作为 GPIO33 使用
34 P4_2 仅可作为 GPIO34 使用
35 P4_3 IR 仅可作为 IR、 GPIO35 使用
36 P4_4 ON-OFF 按键唤醒 仅可作为 ON-OFF、GPIO36 使用

第三章 功能接口*

除电源占用的管脚外,每个管脚都可以配置为GPIO。特例:P44不支持输出功能。

每个 GPIO 均可以独立响应中断,中断类型可以是以下五种模式:高电平中断、低电平中断、上升沿中断、下降沿中断和沿中断。

每个管脚需要通过芯片手册确认复用,是否可用来输出 PWM/调试信号。一共同时支持8路PWM。

API头文件:

#ifndef __GX_GPIO_H__
#define __GX_GPIO_H__
/*** @brief gpio模式*/
typedef enum {
    GX_GPIO_MODE_IDLE = 0,        //未配置
    GX_GPIO_MODE_OUTPUT,          //输出
    GX_GPIO_MODE_INPUT,           //输入
    GX_GPIO_MODE_INPUT_PULLUP,    //上拉输入
    GX_GPIO_MODE_INPUT_PULLDOWN,  //下拉输入
}GX_GPIO_MODE;

/*** @brief gpio 电平状态*/
typedef enum {
    GX_GPIO_LEVEL_LOW = 0, ///< 低电平
    GX_GPIO_LEVEL_HIGH,     ///< 高电平
} GX_GPIO_LEVEL;

/*** @brief gpio中断触发方式*/
typedef enum {
    GX_GPIO_TRIGGER_EDGE_NONE      = 0x00, // 未配置
    GX_GPIO_TRIGGER_EDGE_FALLING   = 0x01, ///< 下降沿触发
    GX_GPIO_TRIGGER_EDGE_RISING    = 0x02, ///< 上升沿触发
    GX_GPIO_TRIGGER_EDGE_BOTH      = 0x03, ///< 降沿上升同时沿触发
    GX_GPIO_TRIGGER_LEVEL_HIGH     = 0x04, ///< 高电平触发
    GX_GPIO_TRIGGER_LEVEL_LOW      = 0x08, ///< 低电平触发
} GX_GPIO_TRIGGER_EDGE;

typedef int (*GPIO_CALLBACK)(unsigned int port);

/**
 * @brief 设置GPIO MODE*
 * @param port gpio端口
 * @param mode gpio模式*
 * @return int 设置是否成功
 * @retval 0 设置成功
 * @retval -1 设置失败*/
int gx_gpio_set_mode(unsigned int port, GX_GPIO_MODE mode);

/**
 * @brief 获取gpio输入电平状态*
 * @param port gpio端口
 * @return GX_GPIO_LEVEL gpio输入电平状态*/
GX_GPIO_LEVEL gx_gpio_get_level(unsigned int port);

/**
 * @brief 设置gpio输出电平状态*
 * @param port gpio端口
 * @param level 输出电平状态, 请参考GX_GPIO_LEVE*
 * @return int 设置是否成功
 * @retval 0 设置成功
 * @retval -1 设置失败*/
int gx_gpio_set_level(unsigned int port, GX_GPIO_LEVEL level);

/**
 * @brief gpio 使能中断*
 * @param port gpio端口
 * @param edge 触发方式, 请参考GX_GPIO_TRIGGER_EDGE
 * @param callback 中断回调函数*
 * @return int gpio中断使能是否成功
 * @retval 0 成功
 * @retval -1 失败*/
int gx_gpio_enable_trigger(unsigned int port, GX_GPIO_TRIGGER_EDGE edge, GPIO_CALLBACK callback);

/**
 * @brief 关闭gpio中断使能*
 * @param port gpio端口*
 * @return int 关闭gpio中断使能是否成功
 * @retval 0 成功
 * @retval -1 失败*/
int gx_gpio_disable_trigger(unsigned int port);

/*** gpio初始化*/
int gx_gpio_init(void);

/**
 * @brief gpio 使能休眠唤醒*
 * @param port gpio端口
 * @param wakeup_level  唤醒电平, 请参考GX_GPIO_LEVEL
 * @param wakeup_cb 唤醒回调函数*
 * @return int gpio中断使能是否成功
 * @retval 0 成功
 * @retval -1 失败*/

int gx_gpio_wakeup_enable(unsigned int port, GX_GPIO_LEVEL wakeup_level, GPIO_CALLBACK wakeup_cb);

/**
 * @brief 关闭gpio休眠唤醒*
 * @param port gpio端口 *
 * @return int 关闭gpio唤醒使能是否成功
 * @retval 0 成功
 * @retval -1 失败
 */
int gx_gpio_wakeup_disable(unsigned int port);

#endif /* __GX_GPIO_H__ */

第四章 使用例程*

4.1 GPIO输出使用*

支持 P0_0 - P4_2 一共 35 个 IO 管脚上下拉配置。

操作P4_3,作为GPIO,并设置为输出模式。

然后每隔一秒电平转换一次。

#include <gx_gpio.h>
#include <pad_def.h>

gx_gpio_init();  
gx_gpio_set_mode(P4_3, GX_GPIO_MODE_OUTPUT); 

while(1)  
{  
    rt_thread_mdelay(1000);  
    gx_gpio_set_level(P4_3, GX_GPIO_LEVEL_LOW);  
    rt_thread_mdelay(1000);  
    gx_gpio_set_level(P4_3, GX_GPIO_LEVEL_HIGH);  
}  

4.2 GPIO输入使用*

操作P4_3,作为GPIO,并设置为下拉输入模式。

然后每隔一秒读一次电平。

#include <gx_gpio.h>
#include <pad_def.h>

gx_gpio_init();
gx_gpio_set_mode(P4_3, GX_GPIO_MODE_INPUT_PULLDOWN);
while(1)    
{
    int ret = 0;
    rt_thread_mdelay(1000);
    ret = gx_gpio_get_level(P4_3);
    printf("P4_3 = %d\n", ret);
}

gx_gpio_set_mode 支持下面几种模式:

模式 说明
GX_GPIO_MODE_IDLE 浮空模式
GX_GPIO_MODE_OUTPUT 输出模式
GX_GPIO_MODE_INPUT 输入模式
GX_GPIO_MODE_INPUT_PULLUP 上拉输入
GX_GPIO_MODE_INPUT_PULLDOWN 下拉输入

4.3 中断使用*

注意,要先设置为GPIO输入模式。

#include <gx_gpio.h>
#include <pad_def.h>

static int gpio_callback (unsigned int port)
{
    printf("ISR port = %d\n", port);
    return 0;
}

gx_gpio_init();
gx_gpio_set_mode(P1_7, GX_GPIO_MODE_INPUT); // 注意,要先配置为输入模式
gx_gpio_enable_trigger(P1_7, GX_GPIO_TRIGGER_EDGE_FALLING, gpio_callback);

中断触发源支持:

中断触发源 说明
GX_GPIO_TRIGGER_EDGE_NONE 未配置
GX_GPIO_TRIGGER_EDGE_FALLING 下降沿触发
GX_GPIO_TRIGGER_EDGE_RISING 上升沿触发
GX_GPIO_TRIGGER_EDGE_BOTH 降沿上升同时沿触发
GX_GPIO_TRIGGER_LEVEL_HIGH 高电平触发
GX_GPIO_TRIGGER_LEVEL_LOW 低电平触发

4.4 GPIO PWM*

头文件说明:

/**
 * @brief pwm模式定时器初始化
 *
 * @param port gpio端口
 * @return pwm模式定时器初始化是否成功
 * @retval 0 成功
 * @retval -1 失败
 */
int gx_timer_pwm_init(unsigned int port);

/**
 * @brief pwm模式定时器反初始化
 *
 * @param port gpio端口
 * @return pwm模式定时器反初始化是否成功
 * @retval 0 成功
 * @retval -1 失败
 */
int gx_timer_pwm_deinit(unsigned int port);

/**
 * @brief pwm模式定时器启动,最小单位为 1000000000ns/PLCK,当前为32M,最小单位为1G/32M=32.15ns
 *
 * @param port gpio端口
 * @param period_ns PWM周期
 * @param duty_ns 高电平时间
 * @return pwm模式定时器启动是否成功
 * @retval 0 成功
 * @retval -1 失败
 */
int gx_timer_pwm_start(unsigned int port, unsigned long int period_ns, unsigned long int duty_ns);

/**
 * @brief pwm模式定时器停止
 *
 * @param port gpio端口
 * @return pwm模式定时器停止是否成功
 * @retval 0 成功
 * @retval -1 失败
 */
int gx_timer_pwm_stop(unsigned int port);

示例如下:

#define  PAD_ID  P1_5
void show_usage(void)
{
    printf("Usage: ./pwm_run Cycle_time [unit: ns] Duty_cycle [0 - 100]\n");
    printf("Example: pwm_run 1000000000 50\n");
    printf("Example: pwm_stop\n");
}

int pwm_run(int argc, const char * argv[])
{
    long long duty_ns = 0;

    if (argc < 3){
        show_usage();
        return -1;
    }

    char *endptr;
    long long period_ns = strtoll(argv[1], &endptr, 10);
    long long duty_cycle = strtoll(argv[2], &endptr, 10);

    duty_ns = period_ns * duty_cycle / 100;

    gx_timer_pwm_start(PAD_ID, period_ns, duty_ns);
    return 0;
}

void pwm_stop(void)
{
    gx_timer_pwm_stop(PAD_ID);
}

int main(void)
{
    printf("Apus PMW Sample App\n");

    gx_timer_pwm_init(PAD_ID);

    return 0;
}

COMMAND_EXPORT_ALIAS(pwm_run, pwm_run, "pwm run");
COMMAND_EXPORT_ALIAS(pwm_stop, pwm_stop, "pwm stop");

注意,管脚要选择支持 “Muti-functionIO with wake-up function” 功能的,这样才能复用于 PWM 功能和中断以及支持GPIO低功耗唤醒。

最多同时支持 8 路 PWM功能。

4.5 GPIO唤醒*

注意:要查看芯片手册,确认该管脚支持 “wake-up function” 功能。

apus的休眠唤醒,主要是两类:

  • 外部的电压触发(GPIO唤醒属于这一类)

  • 内部的RTC触发

唤醒可配置为 IO 上升沿唤醒、IO 下降沿唤醒。在深度休眠或休眠状态下,IO 仅保留输入功能,上下拉由"唤醒极性"控制。

IO 唤醒极性 休眠后上下拉状态
GX_GPIO_LEVEL_HIGH 上升沿 下拉
GX_GPIO_LEVEL_LOW 下降沿 上拉

下面的例子,演示了P0_4 GPIO 既做中断又支持休眠唤醒的功能。

static pm_wakelock_t _blehr_wakelock;

static int gpio_callback (unsigned int port)
{
    printf("ISR port = %d\n", port);
    pm_wakelock_release(&_blehr_wakelock); // 响应了中断后,就允许机器进入休眠
    return 1;
}

static int wake_gpio_callback (unsigned int port)
{
    printf("wake port = %d\n", port);
    pm_wakelock_acquire(&_blehr_wakelock); // 低功耗唤醒后,不允许机器再次休眠下去,主要是为了验证GPIO中断
    return 1;
}

pm_wakelock_init(&_blehr_wakelock, "ble heartrate");
gx_gpio_init();
gx_gpio_set_mode(P0_4, GX_GPIO_MODE_INPUT);
gx_gpio_wakeup_enable(P0_4, GX_GPIO_LEVEL_LOW, wake_gpio_callback); // GPIO唤醒

// 如果不使用了,可以关闭GPIO唤醒
gx_gpio_wakeup_disable(P0_4)

4.6 GPIO引脚的驱动强度配置*

GPIO 引脚的驱动强度是指用于驱动它的电流量。

通常,当前量设置为默认值。默认是:2ma

但是,某些方案(例如更亮的 LED 或传感器功率增加)需要调整驱动器强度到对应的 GPIO 引脚上。

支持 P0_0 - P4_2 一共 35 个 IO 管脚驱动强度配置配置。

/**
 * @brief IO PAD 驱动强度
 */
typedef enum {
    GX_HAL_PADMUX_DRIVE_STRENGTH_2MA = 0x0,      /*!< IO 驱动强度 2mA        */
    GX_HAL_PADMUX_DRIVE_STRENGTH_4MA = 0x1,      /*!< IO 驱动强度 4mA        */
    GX_HAL_PADMUX_DRIVE_STRENGTH_8MA = 0x2,      /*!< IO 驱动强度 8mA        */
    GX_HAL_PADMUX_DRIVE_STRENGTH_12MA = 0x3,     /*!< IO 驱动强度 12mA       */
} GX_HAL_PADMUX_DRIVE_STRENGTH_CFG;

/**
 * @brief 设置 IO 驱动强度
 *
 * @param dev 设备句柄
 * @param io  IO 号 取值范围[0, 34]
 * @param io_pull_cfg  驱动强度, 取值范围参考 \ref GX_HAL_PADMUX_DRIVE_STRENGTH_CFG
 *
 * @return hal_status 设置是否成功
 * @retval HAL_OK 成功
 * @retval HAL_ERROR 失败
 */
hal_status gx_hal_padmux_set_io_drive_strength(GX_HAL_PADMUX *dev, int io, GX_HAL_PADMUX_DRIVE_STRENGTH_CFG io_drive_strength_cfg);

/**
 * @brief 获取 IO 驱动强度
 *
 * @param dev 设备句柄
 * @param io  IO 号 取值范围[0, 34]
 * @param io_pull_cfg  驱动强度, 获取到的值参考 \ref GX_HAL_PADMUX_DRIVE_STRENGTH_CFG
 *
 * @return hal_status 获取驱动强度是否成功
 * @retval HAL_OK 成功
 * @retval HAL_ERROR 失败
 */
hal_status gx_hal_padmux_get_io_drive_strength(GX_HAL_PADMUX *dev, int io, GX_HAL_PADMUX_DRIVE_STRENGTH_CFG *io_drive_strength_cfg);

4.7 IO管脚复用*

padmux 模块是数字管脚复用模块,采用全功能用用的矩阵机制设计,即每一个具有通用复用功能的 IO(P0_0 - P3_7) 都可以复用成某个外设模块的功能。可以通过 padmux_set 设置 IO 复用功能,通过 padmux_get 获取 IO 复用功能。

可复用的功能有:

NUM FUNC NUM FUNC NUM FUNC NUM FUNC
0 IDLE 1 GPIOn 2 UART0_TX 3 UART0_RX
4 UART0_CTS 5 UART0_RTS 6 UART1_TX 7 UART1_RX
8 UART1_CTS 9 UART1_RTS 10 I2C0_SCL 11 I2C0_SDA
12 I2C1_SCL 13 I2C1_SDA 14 I2C2_SCL 15 I2C2_SDA
16 PWM0 17 PWM1 18 PWM2 19 PWM3
20 PWM4 21 PWM5 22 PWM6 23 PWM7
24 QUAD_X_A 25 QUAD_X_B 26 QUAD_Y_A 27 QUAD_Y_B
28 QUAD_Z_A 29 QUAD_Z_B 30 KEY_COL_0 31 KEY_COL_1
32 KEY_COL_2 33 KEY_COL_3 34 KEY_COL_4 35 KEY_COL_5
36 KEY_COL_6 37 KEY_COL_7 38 KEY_COL_8 39 KEY_COL_9
40 KEY_COL_10 41 KEY_COL_11 42 KEY_COL_12 43 KEY_COL_13
44 KEY_COL_14 45 KEY_COL_15 46 KEY_COL_16 47 KEY_COL_17
48 KEY_COL_18 49 KEY_COL_19 50 KEY_ROW_0 51 KEY_ROW_1
52 KEY_ROW_2 53 KEY_ROW_3 54 KEY_ROW_4 55 KEY_ROW_5
56 KEY_ROW_6 57 KEY_ROW_7 58 KEY_ROW_8 59 KEY_ROW_9
60 KEY_ROW_10 61 KEY_ROW_11 62 SPI2_CS0(Master) 63 SPI2_CS1(Master)
64 SPI2_CS2(Master) 65 SPI1_CLK(Master) 66 SPI1_D0(Master) 67 SPI1_D1(Master)
68 SPI1_D2(Master) 69 SPI1_D3(Master) 70 SPI1_CS(Master) 71 SPI2_CLK(Master)
72 SPI2_D0(Master) 73 SPI2_D1(Master) 74 SPI2_D2(Master) 75 SPI2_D3(Master)
76 SPI2_CS(Slave) 77 SPI2_CLK(Slave) 78 SPI2_D0(Slave) 79 SPI2_D1(Slave)
80 SPI2_D2(Slave) 81 SPI2_D3(Slave) 82 I2S_BCLK(Master) 83 I2S_LRCLK(Master)
84 I2S_DIN 85 I2S_DOUT 86 I2S_BCLK(Slave) 87 I2S_LRCLK(Slave)
88 SD_CLK 89 SD_CMD 90 SD_D0 91 SD_D1
92 SD_D2 93 SD_D3 94 95 MCLK
96 JTAG_RST 97 JTAG_TMS 98 JTAG_TCK 99 JTAG_TDI
100 JTAG_TDO 101 PDM_CLK 102 PDM0_DATA 103 PDM1_DATA
104 ANT0 105 ANT1 106 ANT2 107 ANT3
108 109 110 111 PWM_P
112 PMW_N 113 SD_CARD_DETECT 114 IRC_RX_TEST1(I) 115 IRC_TX_TEST1(I)
116 IRC_RX_TEST2(O) 117 118 119
120 121 122 123
124 125 126 IRC_TX_TEST2(O) 127 Reg Control

支持 P0_0 - P3_7 一共 32 个 IO 管脚复用设置。P4_0 - P4_2 不支持管脚复用配置。

支持 P0_0 - P4_2 一共 35 个 IO 管脚上下拉配置。

支持 P0_0 - P4_2 一共 35 个 IO 管脚驱动强度配置配置。

/** \addtogroup <scpu>
 *  @{
 */

#ifndef __GX_PADMUX_H__
#define __GX_PADMUX_H__

/**
 * @brief 管脚复用配置
 */
typedef struct pin_config {
    unsigned char pin_id;   ///< pin 脚号
    unsigned char function; ///< 复用功能
} GX_PIN_CONFIG;

/**
 * @brief PIN 内部上下拉配置
 */
typedef enum {
    GX_PADMUX_IO_PULL_INV  = 0x0,      /*!< IO 部上下拉无效        */
    GX_PADMUX_IO_PULL_DOWN = 0x1,      /*!< IO 内部下拉            */
    GX_PADMUX_IO_PULL_UP   = 0x3,      /*!< IO 内部上拉            */
} GX_PADMUX_IO_PULL_CFG;


/**
 * @brief 配置管脚复用功能
 *
 * @param pad_id pin 脚号
 * @param function 复用功能
 * @return int 是否成功
 * @retval 0 成功
 * @retval -1 失败
 */
int padmux_set(int pad_id, int function);

/**
 * @brief 获取管脚复用功能
 *
 * @param pad_id pin 脚号
 * @return int 是否成功
 * @retval 0 成功
 * @retval -1 失败
 */
int padmux_get(int pad_id);

/**
 * @brief 管脚复用功能检查
 *
 * @param pad_id pin 脚号
 * @param function 复用功能
 * @return int 检测是否正常
 * @retval 0 正常
 * @retval -1 异常
 */
int padmux_check(int pad_id, int function);

/**
 * @brief 管脚复用初始化
 *
 * @param pin_table 管脚复用表, 详细说明请参考 gxdocref GX_PIN_CONFIG
 * @param size 管脚复用表大小
 * @return int 是否成功
 * @retval 0 正常
 * @retval -1 异常
 */
int padmux_init(const GX_PIN_CONFIG *pin_table, int size);

/**
 * @brief 设置PIN 内部上下拉
 *
 * @param pad_in    pin 脚号
 * @param pull_cfg  上下拉状态
 *
 * @return int 检测是否正常
 * @retval 0 正常
 * @retval -1 异常
 */
int padmux_set_io_pull(int pad_id, GX_PADMUX_IO_PULL_CFG pull_cfg);

/**
 * @brief 获取PIN 内部上下拉
 *
 * @param pad_in    pin 脚号
 * @param pull_cfg  上下拉状态
 *
 * @return int 检测是否正常
 * @retval 0 正常
 * @retval -1 异常
 */
int padmux_get_io_pull(int pad_id, GX_PADMUX_IO_PULL_CFG *pull_cfg);


#endif

/** @}*/

例子

padmux_set(P0_7, 2); // 设置 P0_7 复用功能为 UART0_TX
padmux_set(P1_0, 3); // 设置 P1_0 复用功能为 UART0_RX

4.8 特殊IO口说明*

管脚 说明
P4_0 只能用于GPIO输入和输出,不能用于PWM,不能用于中断
p4_1 只能用于GPIO输入和输出,不能用于PWM,不能用于中断
P4_3 只能用于GPIO输入和输出,不能用于PWM,不能用于中断
p4_4 只能用于GPIO输入,不能用于PWM,不能用于中断