NanoPi GPIO 控制(Nanopi-S2)

xiaoxiao2021-02-28  17

NanoPi GPIO 控制

1. sysfs 操作

前提:在编译内核时,查看是否加入 sysfs 支持,若没有则加上(这里默认使用NanoPi-S2测试,其最新固件已经加入sysfs支持)

$ make menuconfig Device Drivers —> GPIO Support —> /sys/class/gpio/… (sysfs interface)

1.1 查询编号

#查询gpio在内核中的编号 $ cd /sys/class/gpio $ for i in gpiochip* ; do echo `cat $i/label`: `cat $i/base` ; done GPIOA: 0 GPIOE: 128 GPIOALV: 160 GPIOB: 32 GPIOC: 64 GPIOD: 96

1.2 说明

$ cd /sys/class/gpio $ ls export gpiochip128 gpiochip32 gpiochip96 gpiochip0 gpiochip160 gpiochip64 unexport

export 用于通知系统需要导出控制的GPIO引脚编号 unexport 用于通知系统取消导出 gpiochipX 用于保存系统中GPIO寄存器的信息,包括每个寄存器控制引脚的起始编号base,寄存器名称,引脚总数,导出一个引脚的操作步骤

1.3 计算引脚编号

引脚编号 = 控制引脚的寄存器基数 + 控制引脚寄存器位数,以 gpioc9 为例:64+9=73

1.4 导出 gpio

#超级用户身份登录系统 $ cd /sys/class/gpio $ echo 73 > export 导出 $ ls gpio73

1.5 定义方向

$ cd /sys/class/gpio/gpio73 $ echo out > direction #输出 $ echo 1 > value #输出高 $ echo 0 > value #输出低 $ echo in > direction #输入 $ cat value #读取电平

2. 内核函数操作

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

2.1 基于 ioctl 操作

2.1.1 内核模块

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

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

以下是全部代码:ts_gpio.c

#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 #define DEVICE_NAME "gpio" unsigned int GPIOC11 = PAD_GPIO_C + 11; 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");
2.1.2 驱动编译

Makefile

obj-m:=ts_gpio.o mymodule-objs:=gpio #生成的驱动设备名 KDIR:=/home/fa/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 内核的源代码,详见 wiki 说明,然后将KDIR修改为你的内核地址,make 即可编译出 gpio.ko

2.1.3 编写应用
//gpio_ioctl.c #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; }
2.1.4 编译运行
$ gcc gpio_ioctl.c -o gpio_ioctl $ ./gpio_ioctl

2.2 基于文件操作

2.2.1 内核模块
//ts_gpio.c #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 #define DEVICE_NAME "gpio" unsigned int GPIOC11 = PAD_GPIO_C + 11; 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; } //这里区别于 ioctl 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("HJ");
2.2.2 驱动编译

Makefile 同 2.1.2

2.2.3 编写应用
#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; }

fflush用于清空缓冲流

2.2.4 编译运行

同 2.1.4

2.3 基于寄存器操作

程序在用户层即可操作寄存器,原理是通过mmap将寄存器的地址通过映射到用户层,然后控制,需要阅读芯片手册,了解最底层的 GPIO 控制。

#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 #define GPIOCOUT *(unsigned int *)base #define GPIOCOUTENB *(unsigned int *)(base+0x04) #define GPIOCALTFN0 *(unsigned int *)(base+0x20) 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 ); 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; }

3. 说明

静态加载:make menuconfig 选择模块是 [*] 编译进内核,然后make zImage编译内核,最后将 image 文件放到SD卡或NFS启动内核即可

动态加载:make menuconfig 选择模块是 [M] 编译进内核,然后 meke modules 编译模块,生成.ko文件,将其发送到ARM开发板里面,使用命令 insmod 进行加载,用 lsmod 进行查看,用 modinfo 查看详细信息,用rmmod 删除。

以上 GPIO 测试均为动态加载

转载请注明原文地址: https://www.6miu.com/read-2249996.html

最新回复(0)