GPIO控制程序说明


1.概述

本文档主要介绍PCUPID上如何通过配置padmux来实现gpio的复用功能,主要参考了《 GPIO使用说明》。


2.关键字

dts/dtsi:

Linux设备树文件,通常用于描述CPU所支持的外设,外设节点中包含的属性值可用于外设的配置。

padmux:

引脚复用,用于将模块功能引脚连接到具体的外部引脚上面,打通信号连接。

表1:PCUPID GPIO index与PAD对应表

Pad Name GPIO Index Pad Name GPIO Index Pad Name GPIO Index Pad Name GPIO Index
PAD_UART0_TX 0 PAD_GPIOE_28 37 PAD_GPIOB_04 74 PAD_SAR_ADC0_05 111
PAD_PWM_OUT00 1 PAD_GPIOA_00 38 PAD_GPIOB_05 75 PAD_SAR_ADC0_06 112
PAD_PWM_OUT01 2 PAD_GPIOA_01 39 PAD_GPIOB_06 76 PAD_SAR_ADC0_07 113
PAD_SPI_CK 3 PAD_GPIOA_02 40 PAD_GPIOB_07 77 PAD_SAR_ADC0_08 114
PAD_SPI_HLD 4 PAD_GPIOA_03 41 PAD_GPIOB_08 78 PAD_SAR_ADC0_09 115
PAD_SPI_CZ 5 PAD_GPIOA_04 42 PAD_OUTP_RX0_CH0 79 PAD_SAR_ADC0_10 116
PAD_SPI_WPZ 6 PAD_GPIOA_05 43 PAD_OUTN_RX0_CH0 80 PAD_SAR_ADC0_11 117
PAD_SPI_DI 7 PAD_GPIOA_06 44 PAD_OUTP_RX0_CH1 81 PAD_SAR_ADC0_12 118
PAD_SPI_DO 8 PAD_GPIOA_07 45 PAD_OUTN_RX0_CH1 82 PAD_SAR_ADC0_13 119
PAD_GPIOE_00 9 PAD_GPIOA_08 46 PAD_OUTP_RX0_CH2 83 PAD_SAR_ADC0_14 120
PAD_GPIOE_01 10 PAD_GPIOA_09 47 PAD_OUTN_RX0_CH2 84 PAD_PWM_ADC01 121
PAD_GPIOE_02 11 PAD_GPIOA_10 48 PAD_OUTP_RX0_CH3 85 PAD_PWM_ADC00 122
PAD_GPIOE_03 12 PAD_GPIOA_11 49 PAD_OUTN_RX0_CH3 86 PAD_GPIOD_01 123
PAD_GPIOE_04 13 PAD_EMMC_RST 50 PAD_GPIOC_00 87 PAD_GPIOD_02 124
PAD_GPIOE_05 14 PAD_EMMC_CLK 51 PAD_GPIOC_01 88 PAD_GPIOD_03 125
PAD_GPIOE_06 15 PAD_EMMC_CMD 52 PAD_GPIOC_02 89 PAD_GPIOD_00 126
PAD_GPIOE_07 16 PAD_EMMC_DS 53 PAD_GPIOC_03 90 PAD_UART0_RX 127
PAD_GPIOE_08 17 PAD_EMMC_D3 54 PAD_GPIOC_04 91 PAD_PM_GPIO4 128
PAD_GPIOE_09 18 PAD_EMMC_D4 55 PAD_GPIOC_05 92 PAD_PM_GPIO5 129
PAD_GPIOE_10 19 PAD_EMMC_D0 56 PAD_GPIOC_06 93 PAD_PM_PWM1_OUT 130
PAD_GPIOE_11 20 PAD_EMMC_D5 57 PAD_GPIOC_07 94 PAD_PM_I2C_SDA 131
PAD_GPIOE_12 21 PAD_EMMC_D1 58 PAD_GPIOC_08 95 PAD_PM_PWM0_OUT 132
PAD_GPIOE_13 22 PAD_EMMC_D6 59 PAD_OUTP_CH0 96 PAD_PM_I2C_SCL 133
PAD_GPIOE_14 23 PAD_EMMC_D2 60 PAD_OUTN_CH0 97 PAD_PM_GPIO1 134
PAD_GPIOE_15 24 PAD_EMMC_D7 61 PAD_OUTP_CH1 98 PAD_PM_GPIO3 135
PAD_GPIOE_16 25 PAD_GPIOA_12 62 PAD_OUTN_CH1 99 PAD_PM_UART1_TX 136
PAD_GPIOE_17 26 PAD_GPIOA_13 63 PAD_OUTP_CH2 100 PAD_PM_GPIO2 137
PAD_GPIOE_18 27 PAD_GPIOA_14 64 PAD_OUTN_CH2 101 PAD_PM_GPIO7 138
PAD_GPIOE_19 28 PAD_GPIOA_15 65 PAD_OUTP_CH3 102 PAD_PM_GPIO6 139
PAD_GPIOE_20 29 PAD_GPIOA_16 66 PAD_OUTN_CH3 103 PAD_PM_UART1_RX 140
PAD_GPIOE_21 30 PAD_GPIOA_17 67 PAD_OUTP_CH4 104 PAD_PM_ADC00_IN 141
PAD_GPIOE_22 31 PAD_GPIOA_18 68 PAD_OUTN_CH4 105 PAD_PM_GPIO0 142
PAD_GPIOE_23 32 PAD_GPIOA_19 69 PAD_SAR_ADC0_00 106
PAD_GPIOE_24 33 PAD_GPIOB_00 70 PAD_SAR_ADC0_01 107
PAD_GPIOE_25 34 PAD_GPIOB_01 71 PAD_SAR_ADC0_02 108
PAD_GPIOE_26 35 PAD_GPIOB_02 72 PAD_SAR_ADC0_03 109
PAD_GPIOE_27 36 PAD_GPIOB_03 73 PAD_SAR_ADC0_04 110

3.Uboot配置

3.1、uboot config配置如下

[*] SigmaStar drivers  --->

    [*]   SigmaStar GPIO
    [*]   SigmaStar padmux

3.2、dts配置如下,compatible一般不会修改,status根据需要配置 okay/disabled.

      gpio: gpio {
            compatible = "sstar,gpio";
            tatus = "okay";
      };

3.3、uboot cmd使用

gpio input <gpio#>  --> gpio input 69  // gpio69 set as input
gpio set <gpio#>    --> gpio set 10    // gpio10 set as output high
gpio clear <gpio#>  --> gpio clear 49  // gpio49 set as output low
gpio toggle <gpio#> --> gpio toggle 49 // gpio49 level toggle
gpio status <gpio#> --> gpio status 20 // gpio20 status

4.kernel相关配置

4.1、kernel config配置说明

Device Drivers  --->

    [*] GPIO Support  --->
        [*]   /sys/class/gpio/... (sysfs interface)

    [*] SStar SoC platform drivers  --->
        [*]   Sstar GPIO driver
        [*]   Sstar PADMUX driver

4.2 、dts参数配置说明

        gpio: gpio {
            compatible = "sstar,gpio";
            #gpio-cells = <2>;
            status = "okay";
        };
属性 描述 备注
compatible 用于匹配驱动进行驱动注册 禁止修改
#gpio-cells 声明设备节点gpio cells的个数 配置为2表示第一个cell为gpio号,第二个cell表示gpio有效电平
status 驱动开关 根据需要配置,ok/disabled

5.复用功能介绍

当需要使用GPIO的复用功能时候,首先需要获取所要操作的PIN脚的name、所要复用的mux mode,将他们配置在xxx-padmux.dtsi中:

<PAD_GPIOC_02            PINMUX_FOR_I2S0_MCK_MODE_1       MDRV_PUSE_I2S0_MCK>,
<PAD_GPIOC_03            PINMUX_FOR_I2S0_RX_MODE_1        MDRV_PUSE_I2S0_RX_BCK>,
<PAD_GPIOC_04            PINMUX_FOR_I2S0_RX_MODE_1        MDRV_PUSE_I2S0_RX_WCK>,
<PAD_GPIOC_05            PINMUX_FOR_I2S0_RX_MODE_1        MDRV_PUSE_I2S0_RX_SDI>,
<PAD_GPIOC_06            PINMUX_FOR_I2S0_TX_MODE_1        MDRV_PUSE_I2S0_TX_SDO>,
<PAD_GPIOC_07            PINMUX_FOR_I2S0_TX_MODE_1        MDRV_PUSE_I2S0_TX_BCK>,
<PAD_GPIOC_08            PINMUX_FOR_I2S0_TX_MODE_1        MDRV_PUSE_I2S0_TX_WCK>,

如上I2S0的配置所示,第一列是pad name,可以在表1中获取(或者查阅/drivers/sstar/include/pcupid/gpio.h),第二列表示pad的复用模式(查阅/drivers/sstar/include/pcupid/padmux.h),第三列则是当前这组padmux配置的名字(查阅/drivers/sstar/include/drv_puse.h),也是该pad在当前mode下的功能。

以PAD_GPIOC_02为例,它支持的mode如表2,当该pad作为I2S0的MCLK输出主时钟信号时需要配置成 PINMUX_FOR_I2S0_MCK_MODE_1 ;而作为PWM2使用时需要配置为 PINMUX_FOR_PWM2_MODE_1

表2:PAD_GPIOC_02支持的mode一览

mode mode mode
PINMUX_FOR_GPIO_MODE PINMUX_FOR_FUART_MODE_2 PINMUX_FOR_FUART_2W_MODE_2
PINMUX_FOR_I2S0_MCK_MODE_1 PINMUX_FOR_BT656_OUT_MODE_1 PINMUX_FOR_TTL24_MODE_1
PINMUX_FOR_TTL18_MODE_1 PINMUX_FOR_TTL18_MODE_2 PINMUX_FOR_TTL16_MODE_1
PINMUX_FOR_TTL16_MODE_2 PINMUX_FOR_TTL_SRGB8_MODE_1 PINMUX_FOR_LCD8_MODE_1
PINMUX_FOR_LCD16_MODE_1 PINMUX_FOR_LCD18_MODE_1 PINMUX_FOR_PWM2_MODE_1
PINMUX_FOR_IR2_IN_MODE_1 PINMUX_FOR_PSPI0_G_MODE_1 PINMUX_FOR_PSPI1_4W_MODE_2

当配置完成后需要检查下是否有其他pad配置成了相同的mode,或者是相同的pad配置成其他的mode,这样会引发冲突,可能会导致配置的padmux失效,需要注释掉或者重新选择pad。

// 以DMIC为例,演示下冲突的情况
// SDMMC1
...
<PAD_GPIOA_07            PINMUX_FOR_GPIO_MODE             MDRV_PUSE_SDIO0_PWR>,
...
// DMIC
<PAD_GPIOA_07            PINMUX_FOR_DMIC_8CH_MODE_3       MDRV_PUSE_DMIC0_CLK>,
...

如上所示,dtsi中同时配置了PAD_GPIO_07这个引脚,分别配置成GPIO模式和DMIC,用做SDIO的pwr和DMIC的clk,如果没有注释掉其中一个配置,由于引脚冲突注定有一个配置的功能是无法使用的。

总结一下需要注意的事项:

  • 一个pad只能配置成一种mode,如果配置了多个mode会冲突;

  • 一个MDRV_PUSE_XXX只能对应一组pad配置,不可以存在多组相同的MDRV_PUSE_XXX配置,否则也会冲突;

  • 配置的pad和mode需要对应。


6.Sample code

用户空间控制gpio代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

#define GPIO_93_PIN   "93"
#define GPIO_DIR_VAL  "out"
#define GPIO_VAL_H    "1"
#define GPIO_VAL_L    "0"

int main(int argc, char *argv[])
{
    char buff[2] = {0};
    int gpio_val_fd = 0;
    int gpio_dir_fd = 0;
    int gpio_export_fd = 0;
    int pin_val = 0;

    gpio_export_fd = open("/sys/class/gpio/export", O_WRONLY);
    if (gpio_export_fd < 0)
    {
        printf("open gpio export failed\n");
        return -1;
    }
    else
    {
        write(gpio_export_fd, GPIO_93_PIN, sizeof(GPIO_93_PIN));
    }

    gpio_dir_fd = open("/sys/devices/gpiochip0/gpio/gpio93/direction", O_RDWR);
    if (gpio_val_fd < 0)
    {
        close(gpio_export_fd);
        printf("open direction failed\n");
        return -2;
    }

    write(gpio_dir_fd, GPIO_DIR_VAL, sizeof(GPIO_DIR_VAL));

    gpio_val_fd = open("/sys/class/gpio/gpio93/value", O_RDWR);
    if (gpio_val_fd == -1)
    {
        printf("gpio open failed\n");
        close(gpio_export_fd);
        close(gpio_dir_fd);
        return -3;
    }
    else
    {
        printf("/sys/class/gpio/gpio93/value\n");
    }

    memset(buff, 0, 2);
    read(gpio_val_fd, buff, 1);
    pin_val = atoi(buff);
    printf("first read gpio val %d, %s\n", pin_val, buff);
    write(gpio_val_fd, GPIO_VAL_H, sizeof(GPIO_VAL_H));

    close(gpio_val_fd);
    gpio_val_fd = open("/sys/class/gpio/gpio93/value", O_RDWR);

    memset(buff, 0, 2);
    read(gpio_val_fd, buff, 1);
    pin_val = atoi(buff);
    printf("after write 1,second read gpio val %d, %s\n", pin_val, buff);

    write(gpio_val_fd, GPIO_VAL_L, sizeof(GPIO_VAL_L));
    close(gpio_val_fd);
    gpio_val_fd = open("/sys/class/gpio/gpio93/value", O_RDWR);

    memset(buff, 0, 2);
    read(gpio_val_fd, buff, 1);
    pin_val = atoi(buff);
    printf("after write 0, third read gpio val %d, %s\n", pin_val, buff);

    close(gpio_export_fd);
    close(gpio_dir_fd);
    close(gpio_val_fd);

    return 0;
}

GPIO 93对应PAD_GPIOC_06,padmux配置如下所示的PINMUX_FOR_GPIO_MODE,

<PAD_GPIOC_06            PINMUX_FOR_GPIO_MODE             MDRV_PUSE_PSPI1_CS>,

demo中先给/sys/class/gpio/export写入控制的GPIO引脚,然后再设置/sys/devices/gpiochip0/gpio/gpio93/direction为输出,向/sys/class/gpio/gpio93/value写入输出的电平值为1,读取pin值后再写入0,最后再读取电平值。