三种方式控制NanoPi2的GPIO

18 篇文章 0 订阅
首先奉上PDF资料,万变不离其宗,掌握核心科技才是最重要的:

 SEC_Users_Manual_S5P4418_Users_Manual_Preliminary_Ver.0.10.pdf (12.59 MB, 下载次数: 37) 

我目前找到了三种方式控制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的亮灭。使用的函数是:
  1. gpio_request(GPIOC11, "test");
  2. gpio_direction_output(GPIOC11, 1);
  3. gpio_set_value(GPIOC11, HIGH);
复制代码

下面是全部代码:
  1. #include <linux/kernel.h>
  2. #include <linux/module.h>
  3. #include <linux/gpio.h>
  4. #include <linux/miscdevice.h>
  5. #include <linux/fs.h>
  6. #include <linux/moduleparam.h>
  7. #include <linux/slab.h>
  8. #include <linux/cdev.h>
  9. #include <linux/delay.h>
  10. #include <linux/compat.h>

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

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

  15. #define OUTPUT  1
  16. #define INPUT   0
  17. #define HIGH    1
  18. #define LOW     0

  19. #define SET_VALUE 123
  20. unsigned int GPIOC11 = PAD_GPIO_C + 11;
  21. #define DEVICE_NAME "gpio"

  22. static int gpio_open(struct inode *inode, struct file *file)
  23. {
  24.     gpio_request(GPIOC11, "test");
  25.     gpio_direction_output(GPIOC11, 1);
  26.     printk("request GPIOC11\n");
  27.     return 0;
  28. }

  29. static int gpio_close(struct inode *inode, struct file *file){
  30.     printk("gpio_set_value LOW\n");
  31.     gpio_set_value(GPIOC11, LOW);
  32.     gpio_free(GPIOC11);
  33.     return 0;
  34. }

  35. static long 
  36. gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
  37.     if(cmd == SET_VALUE){
  38.         if(arg == HIGH){
  39.             gpio_set_value(GPIOC11, HIGH);
  40.             printk("gpio_set_value HIGH\n");
  41.         }else if(arg == LOW){
  42.             gpio_set_value(GPIOC11, LOW);
  43.             printk("gpio_set_value LOW\n");
  44.         }
  45.     }
  46.     return -EMSGSIZE;
  47. }

  48. static struct file_operations gpio_fops = {
  49.     .owner = THIS_MODULE,
  50.     .open = gpio_open,
  51.     .release = gpio_close,
  52.     .unlocked_ioctl = gpio_ioctl,
  53. };

  54. static struct miscdevice gpio_dev = {
  55.     .minor          = MISC_DYNAMIC_MINOR,
  56.     .name          = DEVICE_NAME,
  57.     .fops          = &gpio_fops,
  58. };

  59. volatile unsigned * GPIOCOUT;

  60. static int gpio_init(void){
  61.     int ret = 0;
  62.     printk("init\n");
  63.     ret = misc_register(&gpio_dev);
  64.     return ret;
  65. }

  66. static void gpio_exit(void){
  67.     misc_deregister(&gpio_dev);
  68.     printk("exit\n");
  69. }

  70. module_init(gpio_init);
  71. module_exit(gpio_exit);

  72. MODULE_LICENSE("GPL");
  73. MODULE_AUTHOR("YPW");
复制代码

下面就是这个模块的Makefile:
  1. obj-m:=gpio.o
  2. mymodule-objs:=gpio
  3. KDIR:=/home/ypw/Desktop/linux-3.4.y/
  4. MAKE:=make
  5. # EXTRA_CFLAGS += -I$(KDIR)arch/arm/mach-s5p4418/prototype/module
  6. default:
  7.         $(MAKE) -C $(KDIR) M=$(PWD) modules
  8. clean:
  9.         $(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. insmod gpio_ioctl.ko
  2. rmmod gpio_ioctl.ko
复制代码

安装好以后,如何使用呢?
  1. #include <stdio.h>
  2. #include <fcntl.h>
  3. #include <sys/ioctl.h>
  4. #define HIGH    1
  5. #define LOW     0

  6. #define SET_VALUE 123

  7. int main()
  8. {
  9.         int fd = open("/dev/gpio", O_RDWR);
  10.         int i;
  11.         for(i=0;i<3;i++){
  12.                 ioctl(fd, SET_VALUE, HIGH);
  13.                 usleep(100000);
  14.                 ioctl(fd, SET_VALUE, LOW);
  15.                 usleep(100000);
  16.         }
  17.         close(fd);
  18.         return 0;
  19. }
复制代码
  1. gcc gpio_ioctl.c -o gpio_ioctl
  2. ./gpio_ioctl
复制代码

编译运行,即可看到灯闪三下。
http://yangpeiwen.com/wp-content/uploads/2016/01/gpio.mp4

2.2 基于文件操作
2.2.1 内核代码
  1. #include <linux/kernel.h>
  2. #include <linux/module.h>
  3. #include <linux/gpio.h>
  4. #include <linux/miscdevice.h>
  5. #include <linux/fs.h>
  6. #include <linux/moduleparam.h>
  7. #include <linux/slab.h>
  8. #include <linux/cdev.h>
  9. #include <linux/delay.h>
  10. #include <linux/compat.h>

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

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

  15. #define OUTPUT  1
  16. #define INPUT   0
  17. #define HIGH    1
  18. #define LOW     0

  19. #define SET_VALUE 123
  20. unsigned int GPIOC11 = PAD_GPIO_C + 11;
  21. #define DEVICE_NAME "gpio"

  22. static int gpio_open(struct inode *inode, struct file *file)
  23. {
  24.     gpio_request(GPIOC11, "test");
  25.     gpio_direction_output(GPIOC11, 1);
  26.     printk("request GPIOC11\n");
  27.     return 0;
  28. }

  29. static int gpio_close(struct inode *inode, struct file *file){
  30.     printk("gpio_set_value LOW\n");
  31.     gpio_set_value(GPIOC11, LOW);
  32.     gpio_free(GPIOC11);
  33.     return 0;
  34. }

  35. static ssize_t 
  36. gpio_write(struct file *filp, const char __user *buf,
  37.     size_t count, loff_t *f_pos) {
  38.     if (count > 1)
  39.         return -EMSGSIZE;
  40.     if(buf[0] == '1'){
  41.         gpio_set_value(GPIOC11, HIGH);
  42.         printk("gpio_set_value HIGH\n");
  43.     }else if(buf[0] == '0'){
  44.         gpio_set_value(GPIOC11, LOW);
  45.         printk("gpio_set_value LOW\n");
  46.     }else{
  47.         return -EMSGSIZE;
  48.     }
  49.     return -EMSGSIZE;
  50. }

  51. static struct file_operations gpio_fops = {
  52.     .owner = THIS_MODULE,
  53.     .open = gpio_open,
  54.     .release = gpio_close,
  55.     .write = gpio_write,
  56. };

  57. static struct miscdevice gpio_dev = {
  58.     .minor          = MISC_DYNAMIC_MINOR,
  59.     .name          = DEVICE_NAME,
  60.     .fops          = &gpio_fops,
  61. };

  62. volatile unsigned * GPIOCOUT;

  63. static int gpio_init(void){
  64.     int ret = 0;
  65.     printk("init\n");
  66.     ret = misc_register(&gpio_dev);
  67.     return ret;
  68. }

  69. static void gpio_exit(void){
  70.     iounmap(GPIOCOUT);
  71.     misc_deregister(&gpio_dev);
  72.     printk("exit\n");
  73. }

  74. module_init(gpio_init);
  75. module_exit(gpio_exit);

  76. MODULE_LICENSE("GPL");
  77. MODULE_AUTHOR("YPW");
复制代码

2.2.2 DEMO代码
  1. #include <stdio.h>
  2. int main()
  3. {
  4.         FILE *p = fopen("/dev/gpio", "w");
  5.         int i;
  6.         for(i=0;i<3;i++){
  7.                 fprintf(p, "1");fflush(p);
  8.                 usleep(100000);
  9.                 fprintf(p, "0");fflush(p);
  10.                 usleep(100000);
  11.         }
  12.         fclose(p);
  13.         return 0;
  14. }
复制代码

这个比较简单,跟上面的一样,我就不过多讲了。如果不想自己编译,可以下载我编译好的内核模块:gpio_file.ko

不过有一点我想说的就是,可以通过寄存器加速控制IO口的速度,这里仅贴一个代码片段,只能在内核中操作,这种操作方式最快可以达到20ns:
  1. #include <asm/io.h>
  2. volatile unsigned * GPIOCOUT;
  3. GPIOCOUT = ioremap(0xC001C000, 4);
  4. *GPIOCOUT = 0xFFFF;
  5. *GPIOCOUT = 0;
  6. iounmap(GPIOCOUT);
复制代码

3. 通过配置寄存器来操作
下面这个程序在用户层即可操作寄存器,原理是通过mmap将寄存器的地址通过映射到用户层,然后控制方法参考三星PDF即可。
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <time.h>
  4. #include <unistd.h>
  5. #include <fcntl.h>
  6. #include <unistd.h>
  7. #include <sys/mman.h>

  8. #define GPIOC_BASE_ADDRESS   (0xC001C000)
  9. #define MAP_SIZE 40
  10. static int dev_fd;

  11. int main(int argc, char **argv)
  12. {
  13.     dev_fd = open("/dev/mem", O_RDWR | O_NDELAY);
  14.     if (dev_fd < 0)
  15.     {
  16.         printf("open(/dev/mem) failed.");
  17.         return 0;
  18.     }
  19.     unsigned int base = (unsigned int)mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dev_fd, GPIOC_BASE_ADDRESS );
  20.     #define GPIOCOUT *(unsigned int *)base
  21.     #define GPIOCOUTENB *(unsigned int *)(base+0x04)
  22.     #define GPIOCALTFN0 *(unsigned int *)(base+0x20)
  23.     GPIOCALTFN0 &= ~(3<<22);
  24.     GPIOCALTFN0 |= (1<<22);
  25.     GPIOCOUTENB |= (1<<11);
  26.     GPIOCOUT |= (1<<11);
  27.     sleep(1);
  28.     GPIOCOUT &= ~(1<<11);
  29.     if(dev_fd)
  30.         close(dev_fd);
  31.     munmap((unsigned int *)base,MAP_SIZE);
  32.     return 0;
  33. }
复制代码

效果是灯亮一秒,然后熄灭。这种方法操作速度很快,可以在80ns级别控制,而且很方便,不需要内核级的代码。
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值