本篇主要讲在Android系统中的linux下控制GPIO的方法,android 通过jni控制GPIO可基于本篇内容继续开展;
linux控制GPIO基本上有两种方法,一是通过pinctrl体系,直接使用/sys下的文件进行控制,另一种就是编写驱动,在驱动中进行控制,下面分别进行介绍:
一、是用sys文件系统控制(更深入的介绍请移步:
http://blog.chinaunix.net/uid-27717694-id-3701921.html);
Sysfs路径 /sys/class/gpio有3个入口条目: - 控制接口用于用户空间获取GPIO控制 (export和unexport) - GPIO自己(gpioxx) - GPIO控制器(gpiochipxx)
这是对于标准文件的补充,包括“device”符号
控制接口是只写的: /sys/class/gpio/
export
例如:“echo 19 > export”将创建一个GPIO #19的“gpio19”节点(假设内核代码未申请此GPIO号)。 注意,此处返回错误基本上是因为内核驱动中使用了gpio_request占用或者获取了该GPIO
“unexport”————与“export”效果相反
例如:"echo 19 > unexport"将移除一个由“export”文件导出的“gpio19”节点。
GPIO信号拥有如/sys/class/gpio/gpio42/(对应于GPIO#42)的路径,并且具有下列读写属性: /sys/class/gpio/gpioN/
“direction”————读为“in”或是“out”。这个值通常可写。写“out”默认初始化此值为低。为了确定无障碍操作,值“low”和“high”可以被写入以配置GPIO的输出初始化值。
注意这个属性“将不存在”如果内核不支持改变一个GPIO的方向,或者它不能被内核代码导出(不能显式的允许用户空间来重新配置GPIO的选项。)
“value”—————读作“0”(低)或“1”(高)。如果GPIO被配置为一个输出,这个值可写;
任何非零值均被视为高。 如果管脚可以被配置为中断产生中断管脚,且如果它已经被配置为产生中断(参考“edge”描述),你可以poll(2)此文件并且当中断触发时poll(2)将返回。如果你使用了poll(2),设置POLLPRI和POLLERR事件。如果你使用select(2),在exceptfds中设置文件描述符。在poll(2)返回之后,有两个选择一是lseek(2)到sysfs文件的开始且读新的值,另一个是关闭文件且重打开它来读取新的值。(为何这样设置?)
“edge”————读作“none”、“rising”、“falling”或是“both”。写这些字符串以选择边沿信号,他将使得“value”文件上的poll(2)操作返回。 这个文件只在管脚可以配置为中断产生输入管脚时存在。
“active_low”————读为0(false)或1(true)。写任何非零值都会反转读或写的值。目前和后来的poll(2)支持经由edge属性配置为“rising”或“falling”上升沿或下降沿将遵循这个设置。
GPIO控制器具有如/sys/class/gpio/gpiochip42/(针对控制器,实现GPIO开始于#42)的路径,且具有下列制度属性: /sys/class/gpio/gpiochipN/
“base”————与N相等,是第一个被此芯片管理的GPIO “label”————提供用于诊断(并不总是独一无二的) “ngpio”————管理的GPIO数(N到N+ngpio-1)
从内核代码中导出 --------------------------
内核代码可以显式管理那些使用gpio_request()申请的GPIO的导出 /* export the GPIO to userspace */ int gpio_export(unsigned gpio, bool direction_may_change);
/* reverse gpio_export() */ void gpio_unexport();
/* create a sysfs link to an exported GPIO node */ int gpio_export_link(struct device *dev, const char *name, unsigned gpio)
/* change the polarity of a GPIO node in sysfs */ int gpio_sysfs_set_active_low(unsigned gpio, int value);
一个内核驱动申请一个GPIO后,它可以使用gpio_export()使得sysfs接口有效。驱动可以控制信号方向是否可以改变。这使得驱动可以防止用户空间代码不小心冲击重要的系统状态。
明确的exporting有助于调试(使得一些实验更简单),或是提供一个总是可以使用的接口,适合于bsp文档。
GPIO被导出后,gpio_export_link()允许在sysfs的任何地方创建GPIO sysfs节点的符号链接。驱动可以用此在它们自己设备sysfs目录下提供指定名字的接口(链接到GPIO节点)
驱动可以使用gpio_sysfs_set_active_low()隐藏GPIO在用户空间和单板之间的线极性不同。这仅影响sysfs接口。极性变换可以在gpio_export()之前和之后完成,并且前面使能的poll(2) (支持上升沿或下降沿事件)将被重新配置为遵循此设置。
二、编写驱动(rk3128为例,转载自:
http://developer.t-firefly.com/forum.php?mod=viewthread&tid=2436&highlight=gpio)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include static struct UserData{
int gpio;
int state;
};
static struct of_device_id luobogpio_of_match[] = {
{ .compatible = "luobogpio" },
{ }
};
MODULE_DEVICE_TABLE(of, luobogpio_of_match);
static int luobogpio_open(struct inode *inode, struct file *filp)
{
printk("luobogpio_open\n");
return 0;
}static ssize_t luobogpio_read(struct file *filp, char __user *ptr, size_t size, loff_t *pos)
{
if (ptr == NULL)
printk("%s: user space address is NULL\n", __func__);
return sizeof(int);
}static long luobogpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
long ret = 0;
struct UserData userdata;
unsigned char label[10];
printk("luobogpio_ioctl: cmd = %d arg = %ld\n",cmd, arg);
switch (cmd){case 0:
printk("gpio_request\n");
if (copy_from_user((void*)&userdata,(void __user *)arg, sizeof(struct UserData)))
return -EFAULT;
printk("copy_from_usergpio=%d ,state=%d\n",userdata.gpio,userdata.state);
sprintf(label,"gpio-%d",userdata.gpio);
printk("----->%s\n",label);
ret = gpio_request(userdata.gpio, label);
if (ret) {
printk("failed to request GPIO%d for you ret:%d\n",userdata.gpio,ret);
}
break;
case 1:printk("gpio_direction_output\n");
if (copy_from_user((void*)&userdata,(void __user *)arg, sizeof(struct UserData)))
return -EFAULT;
printk("copy_from_usergpio=%d ,state=%d\n",userdata.gpio,userdata.state);
ret=gpio_direction_output(userdata.gpio, userdata.state);
if (ret) {
printk("failed to gpio_direction_outputfor you ret:%d\n",userdata.gpio);
}
break;
case 5:
printk("gpio_free\n");
if (copy_from_user((void*)&userdata,(void __user *)arg, sizeof(struct UserData)))
return -EFAULT;
printk("copy_from_usergpio=%d ,state=%d\n",userdata.gpio,userdata.state);
gpio_free(userdata.gpio);
break;
default:
printk("unknown ioctl cmd!\n");
ret = -EINVAL;
break;
}
return ret;
}static int luobogpio_release(struct inode *inode, struct file *filp)
{
printk("luobogpio_release\n");
return 0;
}static struct file_operations luobogpio_fops = {
.owner= THIS_MODULE,
.open= luobogpio_open,
.read= luobogpio_read,
.unlocked_ioctl= luobogpio_ioctl,
.release = luobogpio_release,
};
static struct miscdevice luobogpio_dev =
{
.minor = MISC_DYNAMIC_MINOR,
.name = "luobogpio",
.fops = &luobogpio_fops,
};
static int luobogpio_probe(struct platform_device *pdev)
{
int ret=-1;
ret = misc_register(&luobogpio_dev);
if (ret < 0){
printk("ac_usb_switch register err!\n");
return ret;
}printk("func: %s\n", __func__);
return 0;
}static int luobogpio_remove(struct platform_device *pdev)
{
//printk("func: %s\n", __func__);
return 0;
}#ifdef CONFIG_PM_SLEEP
static int luobogpio_suspend(struct device *dev)
{
//printk("func: %s\n", __func__);
return 0;
}static int luobogpio_resume(struct device *dev)
{
//printk("func: %s\n", __func__);
return 0;
}
#endifstatic const struct dev_pm_ops luobogpio_pm_ops = {
#ifdef CONFIG_PM_SLEEP
.suspend = luobogpio_suspend,
.resume = luobogpio_resume,
.poweroff = luobogpio_suspend,
.restore = luobogpio_resume,
#endif
};
static struct platform_driver luogpio_driver = {
.driver= {
.name= "luobogpio",
.owner= THIS_MODULE,
.pm= &luobogpio_pm_ops,
.of_match_table= of_match_ptr(luobogpio_of_match),
},
.probe= luobogpio_probe,
.remove= luobogpio_remove,
};
module_platform_driver(luogpio_driver);
MODULE_DESCRIPTION("luobogpio Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:luobogpio");
【rk3128 控制GPIO】