本帖最后由 ypw 于 2016-1-13 22:04 编辑
首先奉上PDF资料,万变不离其宗,掌握核心科技才是最重要的:
我目前找到了三种方式控制NanoPi2的IO口:- 通过sysfs(/sys/class/gpio)来操作;
- 通过内核的gpio_set_value函数来操作;
- 通过配置寄存器(0xC001X000)来操作。
1. 通过sysfs来操作
这种方法是官方教程给出的办法,这里只给出链接,不再详细解释。
2. 通过内核函数来操作
首先,用户层需要和驱动层交流,目前我知道的是两种方式,一种是通过ioctl操作,另一种是基于文件操作。注意,不管是基于ioctl还是文件操作,速度都是在200us这个数量级上,也就是0.2ms。
2.1 基于ioctl操作
下面是一段我自己写的内核代码,以及操作这个内核模块的demo。
2.1.1 内核模块
这个内核模块控制的是GPIOC11引脚,通过ioctl发送SET_VALUE命令可以设置引脚输出电平的高低,也就是LED的亮灭。使用的函数是:
- gpio_request(GPIOC11, "test");
- gpio_direction_output(GPIOC11, 1);
- gpio_set_value(GPIOC11, HIGH);
复制代码
下面是全部代码:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #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 GPIOC11n");
- return 0;
- }
- static int gpio_close(struct inode *inode, struct file *file){
- printk("gpio_set_value LOWn");
- 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 HIGHn");
- }else if(arg == LOW){
- gpio_set_value(GPIOC11, LOW);
- printk("gpio_set_value LOWn");
- }
- }
- 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("initn");
- ret = misc_register(&gpio_dev);
- return ret;
- }
- static void gpio_exit(void){
- misc_deregister(&gpio_dev);
- printk("exitn");
- }
- module_init(gpio_init);
- module_exit(gpio_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("YPW");
复制代码
下面就是这个模块的Makefile:
- 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
安装和删除内核模块的办法:
- insmod gpio_ioctl.ko
- rmmod gpio_ioctl.ko
复制代码
安装好以后,如何使用呢?
- #include
- #include
- #include
- #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;
- }
复制代码
- gcc gpio_ioctl.c -o gpio_ioctl
- ./gpio_ioctl
复制代码
编译运行,即可看到灯闪三下。
http://yangpeiwen.com/wp-content/uploads/2016/01/gpio.mp4
2.2 基于文件操作
2.2.1 内核代码:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #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 GPIOC11n");
- return 0;
- }
- static int gpio_close(struct inode *inode, struct file *file){
- printk("gpio_set_value LOWn");
- 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 HIGHn");
- }else if(buf[0] == '0'){
- gpio_set_value(GPIOC11, LOW);
- printk("gpio_set_value LOWn");
- }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("initn");
- ret = misc_register(&gpio_dev);
- return ret;
- }
- static void gpio_exit(void){
- iounmap(GPIOCOUT);
- misc_deregister(&gpio_dev);
- printk("exitn");
- }
- module_init(gpio_init);
- module_exit(gpio_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("YPW");
复制代码
2.2.2 DEMO代码
- #include
- 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:
- #include
- volatile unsigned * GPIOCOUT;
- GPIOCOUT = ioremap(0xC001C000, 4);
- *GPIOCOUT = 0xFFFF;
- *GPIOCOUT = 0;
- iounmap(GPIOCOUT);
复制代码
3. 通过配置寄存器来操作
下面这个程序在用户层即可操作寄存器,原理是通过mmap将寄存器的地址通过映射到用户层,然后控制方法参考三星PDF即可。
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #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级别控制,而且很方便,不需要内核级的代码。
|