NanoPi GPIO 控制(Nanopi-S2)

本文详细介绍NanoPi设备上的GPIO控制方法,包括sysfs接口操作、内核函数操作(ioctl和文件操作)以及寄存器操作。同时介绍了静态加载与动态加载内核模块的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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 测试均为动态加载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值