杨培文

三种方式控制NanoPi2的GPIO

首先奉上PDF资料,万变不离其宗,掌握核心科技才是最重要的:

SEC_Users_Manual_S5P4418_Users_Manual_Preliminary_Ver.0.10.pdf

我目前找到了三种方式控制NanoPi2的IO口:

  1. 通过sysfs(/sys/class/gpio)来操作;
  2. 通过内核的gpio_set_value函数来操作;
  3. 通过配置寄存器(0xC001X000)来操作。 ## 1. 通过sysfs来操作

这种方法是官方教程给出的办法,这里只给出链接,不再详细解释。

http://bbs.elecfans.com/jishu_527063_1_1.html

2. 通过内核函数来操作

首先,用户层需要和驱动层交流,目前我知道的是两种方式,一种是通过ioctl操作,另一种是基于文件操作。注意,不管是基于ioctl还是文件操作,速度都是在200us这个数量级上,也就是0.2ms。

2.1 基于ioctl操作

下面是一段我自己写的内核代码,以及操作这个内核模块的demo。

2.1.1 内核模块

这个内核模块控制的是GPIOC11引脚,通过ioctl发送SET_VALUE命令可以设置引脚输出电平的高低,也就是LED的亮灭。使用的函数是:

1
2
3
gpio_request(GPIOC11, "test");
gpio_direction_output(GPIOC11, 1);
gpio_set_value(GPIOC11, HIGH);

下面是全部代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/compat.h>

#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>

#include <mach/platform.h>
#include <mach/devices.h>

#define OUTPUT 1
#define INPUT 0
#define HIGH 1
#define LOW 0

#define SET_VALUE 123
unsigned int GPIOC11 = PAD_GPIO_C + 11;
#define DEVICE_NAME "gpio"

static int gpio_open(struct inode *inode, struct file *file)
{
gpio_request(GPIOC11, "test");
gpio_direction_output(GPIOC11, 1);
printk("request GPIOC11\n");
return 0;
}

static int gpio_close(struct inode *inode, struct file *file){
printk("gpio_set_value LOW\n");
gpio_set_value(GPIOC11, LOW);
gpio_free(GPIOC11);
return 0;
}

static long
gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
if(cmd == SET_VALUE){
if(arg == HIGH){
gpio_set_value(GPIOC11, HIGH);
printk("gpio_set_value HIGH\n");
}else if(arg == LOW){
gpio_set_value(GPIOC11, LOW);
printk("gpio_set_value LOW\n");
}
}
return -EMSGSIZE;
}

static struct file_operations gpio_fops = {
.owner = THIS_MODULE,
.open = gpio_open,
.release = gpio_close,
.unlocked_ioctl = gpio_ioctl,
};

static struct miscdevice gpio_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &gpio_fops,
};

volatile unsigned * GPIOCOUT;

static int gpio_init(void){
int ret = 0;
printk("init\n");
ret = misc_register(&gpio_dev);
return ret;
}

static void gpio_exit(void){
misc_deregister(&gpio_dev);
printk("exit\n");
}

module_init(gpio_init);
module_exit(gpio_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("YPW");

下面就是这个模块的Makefile:

1
2
3
4
5
6
7
8
9
obj-m:=gpio.o
mymodule-objs:=gpio
KDIR:=/home/ypw/Desktop/linux-3.4.y/
MAKE:=make
# EXTRA_CFLAGS += -I$(KDIR)arch/arm/mach-s5p4418/prototype/module
default:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean

如果你需要编译这个内核模块,你首先需要下载linux内核的源代码:https://github.com/friendlyarm/linux-3.4.y,然后将KDIR修改为你的内核地址,make即可编译出gpio.ko。如果不想自己编译,只想使用GPIOC11,可以下载我编译好的内核模块:gpio_ioctl.ko

2.1.2 操作demo

安装和删除内核模块的办法:

1
2
insmod gpio_ioctl.ko
rmmod gpio_ioctl.ko

安装好以后,如何使用呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#define HIGH 1
#define LOW 0

#define SET_VALUE 123

int main()
{
int fd = open("/dev/gpio", O_RDWR);
int i;
for(i=0;i<3;i++){
ioctl(fd, SET_VALUE, HIGH);
usleep(100000);
ioctl(fd, SET_VALUE, LOW);
usleep(100000);
}
close(fd);
return 0;
}
1
2
gcc gpio_ioctl.c -o gpio_ioctl
./gpio_ioctl

编译运行,即可看到灯闪三下。