首先奉上PDF资料,万变不离其宗,掌握核心科技才是最重要的:
SEC_Users_Manual_S5P4418_Users_Manual_Preliminary_Ver.0.10.pdf
我目前找到了三种方式控制NanoPi2的IO口:
- 通过sysfs(/sys/class/gpio)来操作;
- 通过内核的gpio_set_value函数来操作;
- 通过配置寄存器(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 88
| #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 10
| 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 22 23
| #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
|
编译运行,即可看到灯闪三下。
2.2 基于文件操作
2.2.1 内核代码:
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 88 89 90 91 92 93
| #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 ssize_t gpio_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { if (count > 1) return -EMSGSIZE; if(buf[0] == '1'){ gpio_set_value(GPIOC11, HIGH); printk("gpio_set_value HIGH\n"); }else if(buf[0] == '0'){ gpio_set_value(GPIOC11, LOW); printk("gpio_set_value LOW\n"); }else{ return -EMSGSIZE; } return -EMSGSIZE; }
static struct file_operations gpio_fops = { .owner = THIS_MODULE, .open = gpio_open, .release = gpio_close, .write = gpio_write, };
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){ iounmap(GPIOCOUT); misc_deregister(&gpio_dev); printk("exit\n"); }
module_init(gpio_init); module_exit(gpio_exit);
MODULE_LICENSE("GPL"); MODULE_AUTHOR("YPW");
|
2.2.2 DEMO代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <stdio.h>
int main() { FILE *p = fopen("/dev/gpio", "w"); int i; for(i=0;i<3;i++){ fprintf(p, "1");fflush(p); usleep(100000); fprintf(p, "0");fflush(p); usleep(100000); } fclose(p); return 0; }
|
这个比较简单,跟上面的一样,我就不过多讲了。如果不想自己编译,可以下载我编译好的内核模块:gpio_file.ko
不过有一点我想说的就是,可以通过寄存器加速控制IO口的速度,这里仅贴一个代码片段,只能在内核中操作,这种操作方式最快可以达到20ns:
1 2 3 4 5 6
| #include <asm/io.h> volatile unsigned * GPIOCOUT; GPIOCOUT = ioremap(0xC001C000, 4); *GPIOCOUT = 0xFFFF; *GPIOCOUT = 0; iounmap(GPIOCOUT);
|
3. 通过配置寄存器来操作
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
| #include <stdio.h> #include <stdlib.h> #include <time.h> #include <unistd.h> #include <fcntl.h> #include <unistd.h> #include <sys/mman.h>
#define GPIOC_BASE_ADDRESS (0xC001C000) #define MAP_SIZE 40 static int dev_fd;
int main(int argc, char **argv) { dev_fd = open("/dev/mem", O_RDWR | O_NDELAY); if (dev_fd < 0) { printf("open(/dev/mem) failed."); return 0; } unsigned int base = (unsigned int)mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dev_fd, GPIOC_BASE_ADDRESS ); #define GPIOCOUT *(unsigned int *)base #define GPIOCOUTENB *(unsigned int *)(base+0x04) #define GPIOCALTFN0 *(unsigned int *)(base+0x20) GPIOCALTFN0 &= ~(3<<22); GPIOCALTFN0 |= (1<<22); GPIOCOUTENB |= (1<<11); GPIOCOUT |= (1<<11); sleep(1); GPIOCOUT &= ~(1<<11); if(dev_fd) close(dev_fd); munmap((unsigned int *)base,MAP_SIZE); return 0; }
|
效果是灯亮一秒,然后熄灭。这种方法操作速度很快,可以在80ns级别控制,而且很方便,不需要内核级的代码。