前言说明

在项目开发初期,很经常会需要临时操作某个GPIO来验证某些功能,可以通过编写一个简单的驱动程序来操作,但更方便的是可以通过命令行直接操作 GPIO ,这样不需要经过编写代码、编译驱动、推入文件、加载驱动那么繁琐的步骤。

以下为全志平台命令行操作 GPIO 的方法

启用功能

  • 挂载 debugfs
root@sun8i:/# mount -t debugfs debug /proc/sys/debug
mount -t debugfs debug /proc/sys/debug
  • 进入全志的引脚控制目录
root@sun8i:/# cd /proc/sys/debug/sunxi_pinctrl
cd /proc/sys/debug/sunxi_pinctrl root@sun8i:/proc/sys/debug/sunxi_pinctrl# ls -l
ls -l
-rw-rw-r-- 1 root root 0 Jan 1 1970 data
-rw-rw-r-- 1 root root 0 Jan 1 1970 dlevel
-rw-rw-r-- 1 root root 0 Jan 1 1970 function
-rw-rw-r-- 1 root root 0 Jan 1 1970 platform
-rw-rw-r-- 1 root root 0 Jan 1 1970 pull
-rw-rw-r-- 1 root root 0 Jan 1 1970 sunxi_pin
-rw-rw-r-- 1 root root 0 Jan 1 1970 sunxi_pin_configure
  • 节点介绍
data                    // 引脚的电平状态
dlevel                  // 引脚的驱动等级(结合芯片手册)
function                // 引脚的功能配置(结合芯片手册, 输入\输出\功能复用)
platform                // 当前平台
pull                    // 上下拉功能配置
sunxi_pin               // 指定引脚
sunxi_pin_configure     // 引脚所有的配置信息

GPIO 输入测试

  • 设置指定引脚(注意, PB2 不能写成 PB02, 本文结尾会通过代码分析来说明原因)
root@sun8i:/proc/sys/debug/sunxi_pinctrl# echo PB2 > sunxi_pin
echo PB2 > sunxi_pin
  • 查看引脚配置信息
root@sun8i:/proc/sys/debug/sunxi_pinctrl# cat sunxi_pin_configure
cat sunxi_pin_configure
printf register value...
pin[PB2] function: 6; register addr: 0xf1c20824
pin[PB2] data: 0(default value : 0); register addr: 0xf1c20834
pin[PB2] dleve: 1(default value : 1); register addr: 0xf1c20838
pin[PB2] pull: 0(default value : 0); register addr: 0xf1c20840 pin[PB2] trigger: 4; register addr: 0xf1c20a20
trigger mode in sunxi platform:
0 : Positive edge 1: Negative edge
2 : High level 3: Low level
4 : Double level 5: other
pin[PB2] mask : 1(0:disable 1:enable); register addr: 0xf1c20a30
pin[PB2] pending : 0(0:No Pending 1:Pending); register addr: 0xf1c20a34
  • 测试手动拉高(除了 cat sunxi_pin_configure,也可以 cat data 查看,注意 data 的值)
root@sun8i:/proc/sys/debug/sunxi_pinctrl# cat sunxi_pin_configure
cat sunxi_pin_configure
printf register value...
pin[PB2] function: 6; register addr: 0xf1c20824
pin[PB2] data: 1(default value : 0); register addr: 0xf1c20834
pin[PB2] dleve: 1(default value : 1); register addr: 0xf1c20838
pin[PB2] pull: 0(default value : 0); register addr: 0xf1c20840 pin[PB2] trigger: 4; register addr: 0xf1c20a20
trigger mode in sunxi platform:
0 : Positive edge 1: Negative edge
2 : High level 3: Low level
4 : Double level 5: other
pin[PB2] mask : 1(0:disable 1:enable); register addr: 0xf1c20a30
pin[PB2] pending : 0(0:No Pending 1:Pending); register addr: 0xf1c20a34

PB02 引脚我们用来作为检测触摸按键使用,当有触摸的时候,GPIO 会被拉高,如果没有触摸,GPIO 会被拉低,因此第一次查看 GPIO 配置信息的时候,data 为 0。

该引脚设置中断触发模式为 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,因此 pin[PB2] trigger 为 4,即 Double leve (双边沿触发模式)。

这里的 function 为 6,是因为我将这个 GPIO 设置为外部中断,根据 datasheet 描述,外部中断的功能号是 6。

GPIO 输出测试

  • 设置指定引脚
root@sun8i:/proc/sys/debug/sunxi_pinctrl# echo PE24 > sunxi_pin
echo PE24 > sunxi_pin
  • 查看引脚配置(注意 data 的值)
root@sun8i:/proc/sys/debug/sunxi_pinctrl# cat sunxi_pin_configure
cat sunxi_pin_configure
printf register value...
pin[PE24] function: 1; register addr: 0xf1c2089c
pin[PE24] data: 1(default value : 0); register addr: 0xf1c208a0
pin[PE24] dleve: 1(default value : 1); register addr: 0xf1c208a8
pin[PE24] pull: 0(default value : 0); register addr: 0xf1c208b0
  • 设置输出低电平(此时可以拿万用表或者示波器测量该引脚的实际状态)
root@sun8i:/proc/sys/debug/sunxi_pinctrl# echo PE24 0 > data
echo PE24 0 > data
  • 查看引脚配置信息(注意 data 的值)
root@sun8i:/proc/sys/debug/sunxi_pinctrl# cat sunxi_pin_configure
cat sunxi_pin_configure
printf register value...
pin[PE24] function: 1; register addr: 0xf1c2089c
pin[PE24] data: 0(default value : 0); register addr: 0xf1c208a0
pin[PE24] dleve: 1(default value : 1); register addr: 0xf1c208a8
pin[PE24] pull: 0(default value : 0); register addr: 0xf1c208b0
  • 设置输出高电平(此时可以拿万用表或者示波器测量该引脚的实际状态)
root@sun8i:/proc/sys/debug/sunxi_pinctrl# echo PE24 1 > data
echo PE24 1 > data
  • 查看引脚配置信息(注意 data 的值)
root@sun8i:/proc/sys/debug/sunxi_pinctrl# cat sunxi_pin_configure
cat sunxi_pin_configure
printf register value...
pin[PE24] function: 1; register addr: 0xf1c2089c
pin[PE24] data: 1(default value : 0); register addr: 0xf1c208a0
pin[PE24] dleve: 1(default value : 1); register addr: 0xf1c208a8
pin[PE24] pull: 0(default value : 0); register addr: 0xf1c208b0

内核配置

学会用还不行,还需要知道如何启用该功能,虽然全志大部分平台都会默认启用这个功能,万一遇到没有启用的呢?通过以 sunxi_pin_configure 关键词搜索代码, 以上调试功能是在该驱动中实现 lichee\linux-3.4\drivers\pinctrl\pinctrl-sunxi.c,并且依赖 CONFIG_DEBUG_FS 配置。

static int sunxi_pinctrl_probe(struct platform_device *pdev)
{
...... #ifdef CONFIG_DEBUG_FS
sunxi_pinctrl_debugfs();
#endif return 0;
......
} static struct dentry *debugfs_root;
static void sunxi_pinctrl_debugfs(void)
{
debugfs_root = debugfs_create_dir("sunxi_pinctrl", NULL);
if (IS_ERR(debugfs_root) || !debugfs_root) {
pr_debug("failed to create debugfs directory\n");
debugfs_root = NULL;
return;
}
debugfs_create_file("sunxi_pin_configure",(S_IRUGO | S_IWUSR | S_IWGRP),
debugfs_root, NULL, &sunxi_pin_configure_ops);
debugfs_create_file("sunxi_pin", (S_IRUGO | S_IWUSR | S_IWGRP),
debugfs_root, NULL, &sunxi_pin_ops);
debugfs_create_file("function", (S_IRUGO | S_IWUSR | S_IWGRP),
debugfs_root, NULL, &sunxi_pin_function_ops);
debugfs_create_file("data", (S_IRUGO | S_IWUSR | S_IWGRP),
debugfs_root, NULL, &sunxi_pin_data_ops);
debugfs_create_file("dlevel", (S_IRUGO | S_IWUSR | S_IWGRP),
debugfs_root, NULL, &sunxi_pin_dlevel_ops);
debugfs_create_file("pull", (S_IRUGO | S_IWUSR | S_IWGRP),
debugfs_root, NULL, &sunxi_pin_pull_ops);
debugfs_create_file("platform", (S_IRUGO | S_IWUSR | S_IWGRP),
debugfs_root, NULL, &sunxi_platform_ops); }

通过 Makefile 和 Kconfig 来确认 make menuconfig 的菜单选项和菜单位置

lichee\linux-3.4\drivers\pinctrl\Makefile:
obj-$(CONFIG_PINCTRL_SUNXI) += pinctrl-sunxi.o lichee\linux-3.4\drivers\pinctrl\Kconfig:
config PINCTRL_SUNXI
bool "SUNXI pin controller driver"
select PINMUX
select GENERIC_PINCONF

因此要使用此功能需要进行三个步骤:

  • 启用驱动程序 pinctrl-sunxi
make kernel_menuconfig 

Device Drivers ->
Pin controllers ->
[*] SUNXI pin controller driver
  • 启用 debugfs
make kernel_menuconfig

Kernel hacking ->
[*] Debug Filesystem
  • 挂载 debugfs
mount -t debugfs debug /proc/sys/debug

源码浅析

有时候我们需要操作的 GPIO 是已经被驱动程序申请占用,那么我们通过这种方式是否会有冲突呢?答案是,不会。

知其然,也要知其所以然。以下代码以操作 GPIO 电平的设备节点入手:/proc/sys/debug/sunxi_pinctrl/data

这里略过 debugfs 的知识点介绍,如果不知道为什么要从这里入手,可以先去了解一下 debugfs 。

注意代码里面的注释信息,该注释信息就是分析记录。

// 从操作 GPIO 的 data 入手
static void sunxi_pinctrl_debugfs(void)
{
...
debugfs_create_file("data", (S_IRUGO | S_IWUSR | S_IWGRP),
debugfs_root, NULL, &sunxi_pin_data_ops);
... } // 找到关联的 ops
static const struct file_operations sunxi_pin_data_ops = {
.open = sunxi_pin_data_open,
.write = sunxi_pin_data_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
}; // 分析实际写操作
static int sunxi_pin_data_write(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
int err;
unsigned int data;
long unsigned int config; // 从命令行解析得到引脚名称以及要设置的电平状态
err = sscanf(user_buf, "%s %u", sunxi_dbg_pinname,&data);
if(err != 2 )
return err;
if (data <= 1)
sunxi_dbg_data = data;
else{
pr_debug("Input Parameters data error!\n");
return -EINVAL;
}
config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT,data); // 实际操作,注意 SUNXI_PINCTRL ,并将引脚名字传入
// 如果是 echo PH02 1 > data, 那么引脚名字就是 "PH02" 
pin_config_set(SUNXI_PINCTRL,sunxi_dbg_pinname,config); return count; } // 传入给 pin_config_set() 第一个参数 dev_name
#define SUNXI_PINCTRL "sunxi-pinctrl" // 引脚配置设置 dev_name:sunxi-pinctrl name: PH02
int pin_config_set(const char *dev_name, const char *name,
unsigned long config)
{
struct pinctrl_dev *pctldev;
int pin, ret; mutex_lock(&pinctrl_mutex); // 这里关键,通过名字 pinctrl 设备驱动
pctldev = get_pinctrl_dev_from_devname(dev_name);
if (!pctldev) {
ret = -EINVAL;
goto unlock;
} // 通过 pinctrl 找名为 name 的引脚
pin = pin_get_from_name(pctldev, name);
if (pin < 0) {
ret = pin;
goto unlock;
} // 修改指定引脚配置
ret = pin_config_set_for_pin(pctldev, pin, config); unlock:
mutex_unlock(&pinctrl_mutex);
return ret;
}
EXPORT_SYMBOL(pin_config_set); // 通过名字找引脚的实现,其实质就是和预先定于好的名字对比
int pin_get_from_name(struct pinctrl_dev *pctldev, const char *name)
{
unsigned i, pin; /* The pin number can be retrived from the pin controller descriptor */
for (i = 0; i < pctldev->desc->npins; i++) {
struct pin_desc *desc; pin = pctldev->desc->pins[i].number;
desc = pin_desc_get(pctldev, pin);
/* Pin space may be sparse */
if (desc == NULL)
continue;
if (desc->name && !strcmp(name, desc->name))
return pin;
} return -EINVAL;
} // 修改指定引脚配置
static int pin_config_set_for_pin(struct pinctrl_dev *pctldev, unsigned pin,
unsigned long config)
{
const struct pinconf_ops *ops = pctldev->desc->confops;
int ret; if (!ops || !ops->pin_config_set) {
dev_err(pctldev->dev, "cannot configure pin, missing "
"config function in driver\n");
return -EINVAL;
} // 调用 pin_config_set 回调接口进行真正的操作
ret = ops->pin_config_set(pctldev, pin, config);
if (ret) {
dev_err(pctldev->dev,
"unable to set pin configuration on pin %d\n", pin);
return ret;
} return 0;
}

以上分析最关键的有两个地方,通过名字找到具体的 pinctrl 设备驱动,然后调用其回调来进行真正的GPIO配置操作:

get_pinctrl_dev_from_devname(dev_name);  和  ops->pin_config_set(pctldev, pin, config);

// pin_config_set 原型
struct pinconf_ops {
......
int (*pin_config_set) (struct pinctrl_dev *pctldev,
unsigned pin,
unsigned long config);
......
}; // 以名字寻找 pinctrl 设备驱动
struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *devname)
{
struct pinctrl_dev *pctldev = NULL;
bool found = false; if (!devname)
return NULL; // 遍历 pinctrldev_list 链表
// 从上面分析的 sunxi_pin_data_write() 传入的参数可知名字为 "sunxi-pinctrl"
list_for_each_entry(pctldev, &pinctrldev_list, node) {
if (!strcmp(dev_name(pctldev->dev), devname)) {
/* Matched on device name */
found = true;
break;
}
} return found ? pctldev : NULL;
}

要找到 pin_config_set(pctldev, pin, config); 真正实现,首先需要找到名为 "sunxi-pinctrl" 的 pinctrl 驱动,而要找到这个驱动,首先先找到操作 pinctrldev_list,增加节点的地方,即 pinctrl_register();

// 通过搜索 pinctrldev_list 来找增加链表的地方,找到这里。
struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
struct device *dev, void *driver_data)
{
...... mutex_lock(&pinctrl_mutex); // 找到加入 pinctrldev_list 的地方
list_add_tail(&pctldev->node, &pinctrldev_list); pctldev->p = pinctrl_get_locked(pctldev->dev);
if (!IS_ERR(pctldev->p)) {
struct pinctrl_state *s =
pinctrl_lookup_state_locked(pctldev->p,
PINCTRL_STATE_DEFAULT);
if (!IS_ERR(s))
pinctrl_select_state_locked(pctldev->p, s);
} mutex_unlock(&pinctrl_mutex);
......
}
EXPORT_SYMBOL_GPL(pinctrl_register);

回过头来检查 lichee\linux-3.4\drivers\pinctrl\pinctrl-sunxi.c 有没有调用  pinctrl_register();找到如下:


static int sunxi_pinctrl_probe(struct platform_device *pdev)
{
......
#if defined(CONFIG_OF)
pctl->membase = of_iomap(node, 0);
if (!pctl->membase)
return -ENOMEM;
// 如果采用 dts ,那么就会从 dts 获取引脚的描述信息
device = of_match_device(sunxi_pinctrl_match, &pdev->dev);
if (!device)
return -ENODEV; pctl->desc = (struct sunxi_pinctrl_desc *)device->data;
#else
// 一般是这里在 sun[?]iw[?].c 提供,如 R58 就是 sun8iw6.c
pctl->desc = (struct sunxi_pinctrl_desc *)(&sunxi_pinctrl_data);
#endif /* CONFIG_OF */ ret = sunxi_pinctrl_build_state(pdev);
if (ret) {
dev_err(&pdev->dev, "dt probe failed: %d\n", ret);
return ret;
} pins = devm_kzalloc(&pdev->dev,
pctl->desc->npins * sizeof(*pins),
GFP_KERNEL);
if (!pins)
return -ENOMEM; for (i = 0; i < pctl->desc->npins; i++)
pins[i] = pctl->desc->pins[i].pin; // 在这里配置, 这里的名字是关键,用的是平台设备的名字,其实就是 "sunxi-pinctrl"
sunxi_pctrl_desc.name = dev_name(&pdev->dev);
sunxi_pctrl_desc.owner = THIS_MODULE;
sunxi_pctrl_desc.pins = pins;
sunxi_pctrl_desc.npins = pctl->desc->npins;
pctl->dev = &pdev->dev; // 注册进去
pctl->pctl_dev = pinctrl_register(&sunxi_pctrl_desc,
&pdev->dev, pctl);
if (!pctl->pctl_dev) {
dev_err(&pdev->dev, "couldn't register pinctrl driver\n");
return -EINVAL;
}
......
}

从上述所知,pinctrl_register(); 所用的名字是平台设备驱动的名字,因此查看平台设备驱动的结构即可确认,其中 sunxi_pctrl_desc 这个结构的内容就包含了我们一直苦苦寻找的 pin_config_set(pctldev, pin, config); 真正实现所在。

static struct platform_driver sunxi_pinctrl_driver = {
.probe = sunxi_pinctrl_probe,
.driver = {
.name = SUNXI_PINCTRL, // 平台设备驱动匹配的名字
.owner = THIS_MODULE,
#if defined(CONFIG_OF)
.of_match_table = sunxi_pinctrl_match,
#endif
},
}; static struct platform_device sunxi_pinctrl_device = {
.name = SUNXI_PINCTRL, // 平台设备驱动匹配的名字
.id = PLATFORM_DEVID_NONE, /* this is only one device for pinctrl driver */
}; static int __init sunxi_pinctrl_init(void)
{
int ret; // 注册平台设备驱动
ret = platform_device_register(&sunxi_pinctrl_device);
if (IS_ERR_VALUE(ret)) {
pr_debug("register sunxi platform device failed\n");
return -EINVAL;
} ret = platform_driver_register(&sunxi_pinctrl_driver);
if (IS_ERR_VALUE(ret)) {
pr_debug("register sunxi platform device failed\n");
return -EINVAL;
}
return 0;
}
postcore_initcall(sunxi_pinctrl_init); // 这个宏定义就是 "sunxi-pinctrl"
#define SUNXI_PINCTRL "sunxi-pinctrl"

查看 sunxi_pctrl_desc 看看实际是怎么实现的。


static struct pinctrl_desc sunxi_pctrl_desc = {
.confops = &sunxi_pconf_ops,
......
}; static struct pinconf_ops sunxi_pconf_ops = {
.pin_config_get = sunxi_pinconf_get,
.pin_config_set = sunxi_pinconf_set,
.pin_config_group_get = sunxi_pinconf_group_get,
.pin_config_group_set = sunxi_pinconf_group_set,
}; // 真正的实现所在
static int sunxi_pinconf_set(struct pinctrl_dev *pctldev,
unsigned pin,
unsigned long config)
{
struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
struct sunxi_pin_bank *bank = sunxi_pin_to_bank(pctl, pin);
unsigned int pin_bias;
void __iomem *reg;
u32 val;
u32 mask;
u16 dlevel;
u16 data;
u16 func;
u16 pull; pin_reset_bias(&pin_bias, pin);
if (IS_ERR_OR_NULL(bank)) {
pr_debug("invalid pin number [%d] to set pinconf\n", pin);
return -EINVAL;
}
switch (SUNXI_PINCFG_UNPACK_TYPE(config)) {
case SUNXI_PINCFG_TYPE_DRV:
dlevel = SUNXI_PINCFG_UNPACK_VALUE(config);
val = pinctrl_readl_reg(bank->membase + sunxi_dlevel_reg(pin_bias));
mask = DLEVEL_PINS_MASK << sunxi_dlevel_offset(pin_bias);
val=(val & ~mask) | (dlevel << sunxi_dlevel_offset(pin_bias));
reg=bank->membase + sunxi_dlevel_reg(pin_bias);
pinctrl_write_reg(val,reg);
pr_debug("sunxi pconf set pin [%s] drive strength to [LEVEL%d]\n",
pin_get_name(pctl->pctl_dev, pin), dlevel);
break;
case SUNXI_PINCFG_TYPE_PUD:
pull = SUNXI_PINCFG_UNPACK_VALUE(config);
val = pinctrl_readl_reg(bank->membase + sunxi_pull_reg(pin_bias));
mask = PULL_PINS_MASK << sunxi_pull_offset(pin_bias);
val=(val & ~mask) | (pull << sunxi_pull_offset(pin_bias));
reg=bank->membase + sunxi_pull_reg(pin_bias);
pinctrl_write_reg(val,reg);
pr_debug("sunxi pconf set pin [%s] pull to [%d]\n",
pin_get_name(pctl->pctl_dev, pin), pull);
break;
case SUNXI_PINCFG_TYPE_DAT:
data = SUNXI_PINCFG_UNPACK_VALUE(config);
val = pinctrl_readl_reg(bank->membase + sunxi_data_reg(pin_bias));
mask = DATA_PINS_MASK << sunxi_data_offset(pin_bias);
val=(val & ~mask) | (data << sunxi_data_offset(pin_bias));
reg=bank->membase + sunxi_data_reg(pin_bias);
pinctrl_write_reg(val,reg);
pr_debug("sunxi pconf set pin [%s] data to [%d]\n",
pin_get_name(pctl->pctl_dev, pin), data);
break;
case SUNXI_PINCFG_TYPE_FUNC:
func = SUNXI_PINCFG_UNPACK_VALUE(config);
val = pinctrl_readl_reg(bank->membase + sunxi_mux_reg(pin_bias));
mask = MUX_PINS_MASK << sunxi_mux_offset(pin_bias);
val=(val & ~mask) | (func << sunxi_mux_offset(pin_bias));
reg=bank->membase + sunxi_mux_reg(pin_bias);
pinctrl_write_reg(val,reg);
pr_debug("sunxi pconf set pin [%s] func to [%d]\n",
pin_get_name(pctl->pctl_dev, pin), func);
break;
default:
pr_debug("invalid sunxi pconf type for set\n");
return -EINVAL;
}
return 0;
}

从上面可以看到,配置 GPIO 都是通过 pinctrl_write_reg(val,reg); 写寄存器的方式实现:

static inline void pinctrl_write_reg(u32 value,void __iomem *reg)
{
if(is_arisc_pin(reg)) {
sunxi_smc_writel(value, reg);
} else {
writel(value, reg);
}
......
}

综上分析思路,之所有使用这种方式操作 GPIO 不会对已有的驱动造成影响,是因为该方式是直接操作 SOC 的寄存器方式实现,直接绕过来 Linux 驱动的 GPIO 操作接口。

其实如果有学过 pinctrl 驱动,很容易就能定位到最终的 pinctrl_write_reg() 实现方式,没接触过的话,也是可以通过上述思路找到。虽然过程有点繁琐,但是这种分析思路,可以应对很多自己没有接触的驱动框架。

那为什么设定 GPIO 的时候,如 PB2 不能写成 PB02 呢?这是因为跟 GPIO 名字匹配有关,操作哪个 GPIO 是通过传入的名字确定的,注意上述的 pin_get_from_name() 和 sunxi_pinctrl_probe() 的实现,很容易找到下面的代码

struct sunxi_pinctrl_desc sunxi_pinctrl_data = {
.pins = sun8i_w8_pins,
.npins = ARRAY_SIZE(sun8i_w8_pins),
.banks = sun8i_w8_banks,
.nbanks = ARRAY_SIZE(sun8i_w8_banks),
}; static struct sunxi_desc_pin sun8i_w8_pins[]={
......
SUNXI_PIN(SUNXI_PINCTRL_PIN_PB2,
SUNXI_FUNCTION(0x0, "gpio_in"),
SUNXI_FUNCTION(0x1, "gpio_out"),
SUNXI_FUNCTION(0x2, "uart2"), /* RTS */
SUNXI_FUNCTION(0x6, "eint"), /* EINT2 */
SUNXI_FUNCTION(0x7, "io_disable")),
...... }; // 可以看到,这里定义的名字是 PB2 而不是 PB02, 如果传入的是 PB02 就会匹配失败
#define SUNXI_PINCTRL_PIN_PB0 PINCTRL_PIN(SUNXI_PB_BASE + 0, "PB0")
#define SUNXI_PINCTRL_PIN_PB1 PINCTRL_PIN(SUNXI_PB_BASE + 1, "PB1")
#define SUNXI_PINCTRL_PIN_PB2 PINCTRL_PIN(SUNXI_PB_BASE + 2, "PB2")
#define SUNXI_PINCTRL_PIN_PB3 PINCTRL_PIN(SUNXI_PB_BASE + 3, "PB3")
#define SUNXI_PINCTRL_PIN_PB4 PINCTRL_PIN(SUNXI_PB_BASE + 4, "PB4")
#define SUNXI_PINCTRL_PIN_PB5 PINCTRL_PIN(SUNXI_PB_BASE + 5, "PB5")
#define SUNXI_PINCTRL_PIN_PB6 PINCTRL_PIN(SUNXI_PB_BASE + 6, "PB6")

总结说明

该命令行方式通过 debugfs 暴露操作接口,通过 pinctrl 提供具体的 GPIO 操作接口,最终通过读写寄存器的方式实现。

【分析笔记】全志方案通过命令行操作 GPIO 口(带源码分析)的更多相关文章

  1. shiro实现无状态的会话,带源码分析

    转载请在页首明显处注明作者与出处 朱小杰      http://www.cnblogs.com/zhuxiaojie/p/7809767.html 一:说明 在网上都找不到相关的信息,还是翻了大半天 ...

  2. drf复习(一)--原生djangoCBV请求生命周期源码分析、drf自定义配置文件、drf请求生命周期dispatch源码分析

    admin后台注册model  一.原生djangoCBV请求生命周期源码分析 原生view的源码路径(django/views/generic/base.py) 1.从urls.py中as_view ...

  3. 观察者模式—jdk自带源码分析

    一:观察者模式简介 二:jdk实现观察者模式的源码 三:实际例子 四:观察者模式的优点和不足 五:总结 一:观察者模式简介 有时又被称为发布(publish )-订阅(Subscribe)模式.模型- ...

  4. [原创]java WEB学习笔记70:Struts2 学习之路-- struts2拦截器源码分析,运行流程

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  5. jQuery-1.9.1源码分析系列完毕目录整理

    jQuery 1.9.1源码分析已经完毕.目录如下 jQuery-1.9.1源码分析系列(一)整体架构 jQuery-1.9.1源码分析系列(一)整体架构续 jQuery-1.9.1源码分析系列(二) ...

  6. Android源码分析(十六)----adb shell 命令进行OTA升级

    一: 进入shell命令界面 adb shell 二:创建目录/cache/recovery mkdir /cache/recovery 如果系统中已有此目录,则会提示已存在. 三: 修改文件夹权限 ...

  7. v76.01 鸿蒙内核源码分析(共享内存) | 进程间最快通讯方式 | 百篇博客分析OpenHarmony源码

    百篇博客分析|本篇为:(共享内存篇) | 进程间最快通讯方式 进程通讯相关篇为: v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志 v27.05 鸿蒙内核源码分析(互斥锁) | 同样 ...

  8. 一个普通的 Zepto 源码分析(三) - event 模块

    一个普通的 Zepto 源码分析(三) - event 模块 普通的路人,普通地瞧.分析时使用的是目前最新 1.2.0 版本. Zepto 可以由许多模块组成,默认包含的模块有 zepto 核心模块, ...

  9. ArrayList源码分析超详细

    ArrayList源码分析超详解 想要分析下源码是件好事,但是如何去进行分析呢?以我的例子来说,我进行源码分析的过程如下几步: 找到类:利用 IDEA 找到所需要分析的类(ztrl+N查找ArraLi ...

  10. ArrayList源码分析超详细(转载)

    ArrayList源码分析超详细   ArrayList源码分析超详解 想要分析下源码是件好事,但是如何去进行分析呢?以我的例子来说,我进行源码分析的过程如下几步: 找到类:利用 IDEA 找到所需要 ...

随机推荐

  1. 一个实用的 vite + vue3 组件库脚手架工具,提升开发效率

    无论是 vue2 全家桶还是 vue3 + vite + TypeScript,组件库的使用几乎大家都会,但自己开发一个独立组件库就不是每个人都掌握的,因为搭建组件库的基础开发环境,就会让很多同学望而 ...

  2. JIRA操作之 基本说明

    官方说明:https://docs.atlassian.com/software/jira/docs/api/7.6.1/ 项目(Project) Project是一组问题单(Issue)的集合,每个 ...

  3. Spring Boot 中使用 tkMapper

    说明:基于 MyBatis 有很多第三方功能插件,这些插件可以完成数据操作方法的封装.数据库逆向工程的生成等. tkMapper 和 MyBatis-plus 都是基于 MyBatis 提供的第三方插 ...

  4. 【安装文档】TRex流量分析仪保姆级安装指南--基于VMware虚拟机(ubantu18.04@Intel 82545EM)

    前言 既然你已经知道TRex并尝试搜索它的安装教程,这意味着你有一定的基础知识(至少知道自己需要什么).因此本文对于TRex的介绍部分会偏少 本次主要为TRex安装过程的一次记录(版本为v3.0.0) ...

  5. CH58X/CH57X/V208 Observer(观察者)例程讨论讲解

    使用的是沁恒的CH582M的Observer例程与官方的demo板. 本例程的功能是主机扫描到从机的MAC地址并打印出来. 先对宏定义进行理解讨论. 最大响应扫描数为8,在串口调试助手那里可以看到打印 ...

  6. 微信小程序的学习(二)

    一.数据绑定 1.数据绑定的基本原则 在 data 中定义数据 在 wxml 中使用数据 2.如何在 data 里面定义数据? 在页面对应的 .js 文件中,把数据定义到 data 对象中即可: 3. ...

  7. IIS部署WebApi跨域不生效

    在IIS8.5上部署了WebApi程序,但是跨域不生效检查了前端和后端都没有问题. 后面才发现在应用程序池中需要设置为集成模式.经典模式下不能正常使用

  8. C# Panel动态添加滚动条

    /// <summary> /// panel控件的事件:在向该控件添加控件时发生 /// </summary> private void panel1_ControlAdde ...

  9. 使用Python实现多线程、多进程、异步IO的socket通信

    多线程实现socket通信服务器端代码 import socket import threading class MyServer(object): def __init__(self): # 初始化 ...

  10. 【Day02】Spring Cloud组件的使用--Nacos配置中心、sentinel流量控制、服务网关Gateway、RocketMQ、服务调用链路(Sleuth、zipkin)

    今日内容 一.配置中心 1.遗留问题 yml配置,每一次都需要重启项目 需要不重启项目拿到更新的结果 引出:配置中心 选择:Spring Cloud Config组件 / Alibaba的Nacos( ...