gpio

对linux c app里面的一个gpio操作云里雾里半天,是时候好好理理了。

主要是这gpio number是如何同pin number 一一对应的。

256
root@mxj-zbcs-310:#tree /sys/class/gpio
.
|-- export
|-- gpio120 -> ../../devices/soc.0/1c20800.pinctrl/gpio/gpio120
|-- gpio228 -> ../../devices/soc.0/1c20800.pinctrl/gpio/gpio228
|-- gpio229 -> ../../devices/soc.0/1c20800.pinctrl/gpio/gpio229
|-- gpio232 -> ../../devices/soc.0/1c20800.pinctrl/gpio/gpio232
|-- gpio32 -> ../../devices/soc.0/1c20800.pinctrl/gpio/gpio32
|-- gpio33 -> ../../devices/soc.0/1c20800.pinctrl/gpio/gpio33
|-- gpio359 -> ../../devices/soc.0/1f02c00.pinctrl/gpio/gpio359
|-- gpio64 -> ../../devices/soc.0/1c20800.pinctrl/gpio/gpio64
|-- gpiochip0 -> ../../devices/soc.0/1c20800.pinctrl/gpio/gpiochip0
|-- gpiochip1024 -> ../../devices/soc.0/pinctrl.1/gpio/gpiochip1024
|-- gpiochip352 -> ../../devices/soc.0/1f02c00.pinctrl/gpio/gpiochip352
`-- unexport

奇怪,今天发现sys/class/gpio 下面的gpio没有了。只剩下gpiochip了。原来需要在应用export 对应io number。

    fd = open("/sys/class/gpio/export", O_WRONLY);
    len = snprintf(buffer, sizeof(buffer), "%d", pin);
    if (write(fd, buffer, len) < 0) {
    }

这里的exportunport以及之后对 io number value的read write 操作都是在gpiolibc 上实现的。

    snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin);
    fd = open(path, O_WRONLY);
    if (fd < 0) {
    }

    if (write(fd, &values_str[value == 0 ? 0 : 1], 1) < 0) {
    }        

接下来需要理一理gpiolibc 的实现,gpiolibc 所有操作都是基于gpio_desc *desc 变量。改变量通过gpio_desc 结构体定义这里的数组。数组索引就对应我们这里的io number。

static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];

这个结构体的实现主要是是通过gpio_chip来操作的,例如gpiod_set_value 是通过gpio_desc 获取到gpio_chip 然后在通过gpio_chip->set() 实现IO的读写。

static void gpiod_set_value(struct gpio_desc *desc, int value) {
    struct gpio_chip    *chip;

    if (!desc)
        return;
    chip = desc->chip;
    /* Should be using gpio_set_value_cansleep() */
    WARN_ON(chip->can_sleep);
    trace_gpio_value(desc_to_gpio(desc), 0, value);
    if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
        _gpio_set_open_drain_value(desc, value);
    else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
        _gpio_set_open_source_value(desc, value);
    else
        chip->set(chip, gpio_chip_hwgpio(desc), value);
}

所以对于gpio_chip 的初始化就很重要了。然而我并没有找。

static inline u32 sunxi_data_reg(u16 pin)  {
    u8 bank = pin / PINS_PER_BANK;
    u32 offset = bank * BANK_MEM_SIZE;
    offset += DATA_REGS_OFFSET;
    offset += pin % PINS_PER_BANK / DATA_PINS_PER_REG * 0x04;
    return round_down(offset, 4);
}

#define PINS_PER_BANK    32     //其中 PINS_PER_BANK  为32

对于如上计算的bank 1-7依次对应 PB-PH
8 对应PL

Page.396 3.21. Port Controller(CPUx-PORT)

pin % PINS_PER_BANK 对于Port 尾号,例如pin=32,对应 PB0

#define  IO_NUMBER_CARD_READER0     228     //!< PH4 (IC-D0_L)    读卡器信号0 
#define  IO_NUMBER_CARD_READER1     229     //!< PH5 (IC-D1_L)    读卡器信号1 
#define  IO_NUMBER_DOOR_CTRL        33      //!< PB1 (Core_OPEN)门禁控制
#define  IO_NUMBER_DOOR_CHECK       64      //!< PC0 (button)按键选择(非门控制)
#define  IO_NUMBER_USER_KEY         32      //!< PB0 (Userkey)用户按键
#define  IO_NUMBER_A64_SHUTDOWN     359     //!< PL7 (Core_SHUTDOWN) 用MCU 控制 A64 软件开关机
#define  IO_NUMBER_BODY_RADAR       232     //!< PH8 (Body_Sensor)人体雷达    
#define  IO_NUMBER_A64_SENSE                //!< PH4 (Core_SENSE) 未知功能

shell 脚本转换

num2name(){
  local num=$1
  local h=$[$num/32]
  local l=$[$num%32]

  local H=$(echo -e `printf '\\\x%02x' $[0x41+h]`)
  echo P$H$l
}
name2num(){
local name=$1
name=${name#P}
local H=${name:0:1}
local l=${name:1}
local h=$[`printf '%d' \'$H`-0x41]

echo $[h*32+l]
}