前言说明

在项目开发初期,很经常会需要临时操作某个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. 【单元测试】Junit 4(二)--eclipse配置Junit+Junit基础注解

    1.0 前言 ​ 前面我们介绍了白盒测试方法,后面我们来介绍一下Junit 4,使用的是eclipse(用IDEA的小伙伴可以撤了) 1.1 配置Junit 4 1.1.1 安装包 我们需要三个jar ...

  2. 看了同事这10个IDEA神级插件,我也悄悄安装了

    昨天,有读者私信发我一篇文章,说里面提到的 Intellij IDEA 插件真心不错,基本上可以一站式开发了,希望能分享给更多的小伙伴,我在本地装了体验了一下,觉得确实值得推荐,希望小伙伴们有时间也可 ...

  3. 「浙江理工大学ACM入队200题系列」问题 H: 零基础学C/C++18——三位数反转

    本题是浙江理工大学ACM入队200题第二套中的H题 我们先来看一下这题的题面. 由于是比较靠前的题目,这里插一句.各位新ACMer朋友们,请一定要养成仔细耐心看题的习惯,尤其是要利用好输入和输出样例. ...

  4. created与mounted执行顺序(关于微任务与宏任务)

    1.ps:只要你只使用created或者mounted中的一个不就好了吗[dog].这样只要在第一个异步任务代码跳出前,嵌套第二个任务函数就好了 最后面的两个链接一个是微任务与宏任务的通俗例子,一个是 ...

  5. 云实例初始化工具cloud-init简介

    项目简介 cloud-init是一款用于初始化云服务器的工具,它拥有丰富的模块,能够为云服务器提供的能力有:初始化密码.扩容根分区.设置主机名.注入公钥.执行自定义脚本等等,功能十分强大. 目前为止c ...

  6. 读书笔记:A Philosophy of Software Design

    今天一位同事在斯坦福的博士生导师John Ousterhout (注,Tcl语言的设计者)来公司做了他的新书<A Philosophy of Software Design>的演讲,介绍了 ...

  7. 如何使用zx编写shell脚本

    前言 在这篇文章中,我们将学习谷歌的zx库提供了什么,以及我们如何使用它来用Node.js编写shell脚本.然后,我们将学习如何通过构建一个命令行工具来使用zx的功能,帮助我们为新的Node.js项 ...

  8. 1. scrapy 框架应该怎么学习(前言)

    其实 scrapy 框架并不难学习, 我觉得分为两部分: 命令 和 代码逻辑的构建 1. 如何学习命令 其实 scrapy 已经帮我们做好了很充足的说明了, 下面我来说如何好好利用这些说明 scrap ...

  9. 2022-2023年度必备宇宙最全Windows系统软件清单

    作为PC端的第一生产力工具,相信对于绝大部分人来说,Windows系统是一款不可替代的产品.既然如此,Pytrick今天就拿出珍藏多年的压箱底宝贝无偿分享给各位,给大家逐一介绍下这些体验一级棒的应用软 ...

  10. 30位以内随机产生时间戳加随机数id

    package com.zx.ps.web.gzdb; import java.text.SimpleDateFormat; import java.util.Date; public class c ...