[Linux][驱动学习笔记]--sysfs

在这里插入图片描述

文件作用
block包含所有的块设备
devices包含系统的所有设备并根据设备挂接的总线类型组织成层次结构
bus包含系统中的所有总线类型
drivers包含内核中已经注册的设备驱动程序
class包含系统中设备类型(网卡设备、声卡设备、输入设备等)。

 Linux2.6内核引入了sysfs(虚拟文件系统),sysfs的作用是将注册进系统中的设备、总线和驱动组织成一个个分级的文件,并直观地将驱动和设备的层次结构以文件的形式展示在用户空间,通过操作这些文件,系统向用户空间导出内核数据结构以及他们的属性,这样一来用户空间可以通过修改sysfs的文件属性来修改设备的属性值,进而改变设备的工作状态。比如通过sysfs文件系统可以直接向指定的gpio引脚输入和输出高低电平。
 给出一个通过sysfs控制led的实例,以下是led的原理图和引脚说明:
在这里插入图片描述

在这里插入图片描述
从原理图上可以看出,led低电平被点亮,高电平被熄灭。另外,对于sysfs文件系统,gpio的操作具有特殊的方式。首先计算出gpio对应的编号,编号等于group * 32 + pin,如:

描述名称计算编号
LED1GPIO3_263*32+26122
LED2GPIO3_223*32+22118
LED3GPIO3_203*32+20116
LED4GPIO2_72*32+771

一、终端模式下操作gpio

 进入到/sys/class/gpio/目录下,ls查看该目录下的文件。
在这里插入图片描述
 文件说明:

 1、export

  用于获取gpio的控制接口,比如echo 71 > export获取GPIO2_7的控制接口。
在这里插入图片描述
 进入gpio71下,该目录下的文件是控制GPIO2_7属性的接口,比如输入输出方向、电平、触发方式、电源、有效电平等。
在这里插入图片描述
 通过echo 命令可以改变gpio的属性,向direction文件写入in/out改变gpio输入输出方向;向edge文件写入none/rising/falling/both 改变gpio的触发方式;向value文件写入0/1就是向gpio写入高低电平,假如gpio为输入模式,gpio就是输入0/1,假如gpio为输出模式,gpio就是输出0/1。比如:想让GPIO2_7输出高电平,则echo out > directionecho 1 > value

 2、unexport

  用于取消gpio的控制接口,比如echo 71 > unexport 取消GPIO2_7的控制接口,此时gpio71这个文件会消失。
在这里插入图片描述

 3、gpiochipX

  保存系统中GPIO寄存器的信息,包括每个寄存器控制引脚的起始编号base、寄存器名称、引脚总数。每个寄存器控制32个引脚,一共有4组寄存器。

二、c语言操作sysfs gpio实现流水灯

 为实现这个功能,程序的整体设计思想其实很简单,我们不需要关心led底层驱动是如何实现的,我们只需要关心如何将gpio控制接口导出,然后对其进行一般的I/O操作即可。需要注意的是,不同版本的Linux系统,gpio控制接口的命名可能不同,但是对sysfs下gpio控制接口的操作是一样的。下文先给出操作gpio的各个功能函数,然后给出实现流水灯的测试函数,并附上整个工程的源码地址。

1、判断gpio控制接口是否被导出
/*
功能:判断gpio控制接口是否已经被导出
参数:
    gpio:传入gpio引脚编号(GPIO3_26 = 3*32 + 26 = 122)
返回值:
    -1 : 没有被导出
    0  :已经被导出
*/
int gpio_is_exported(int gpio)
{
    char buf[50];//class/gpio下的gpio文件

    sprintf(buf,"/sys/class/gpio/gpio%d",gpio);

    if(access(buf,F_OK) != -1)
    {
        return 0;
    }
    else
    {
        return -1;
    }
}
2、导出gpio控制接口
/*
功能:在sys下导出gpio控制接口
参数:               
    gpio:传入gpio引脚编号
返回值:
    0:导出成功
    -1:导出失败
*/
int gpio_export(int gpio)
{
    int fd = -1;
    char buf[4];
    int rv = -1;

    fd = open("/sys/class/gpio/export",O_RDWR);
    if(fd < 0)
    {
        return -1;
    }

    sprintf(buf,"%d",gpio);

    rv = write(fd,buf,strlen(buf));
    if(rv < 0)
    {
        return -1;
    }

    close(fd);

    return 0;

}
3、取消gpio控制接口

/*
功能:在sys下取消gpio
参数:
    gpio:gpio编号
返回值:
    成功返回0
    失败返回-1
*/
int gpio_unexport(int gpio)
{
    int fd = -1;
    char buf[4];
    int rv = -1;

    fd = open("/sys/class/gpio/unexport",O_RDWR);
    if(fd < 0)
    {
        return -1;
    }

    sprintf(buf,"%d",gpio);

    rv = write(fd,buf,strlen(buf));
    if(rv < 0)
    {
        return -1;
    }

    close(fd);

    return 0;

}
4、设置gpio的方向

/*
功能:设置gpio的方向
参数:
    gpio:gpio编号
    direction:gpio的方向(out/in)
返回值:
    -1:失败
    0:成功
*/

int gpio_set_direction(int gpio,char *direction)
{
    int len = 0;
    char buf[50];
    int fd = -1;
    int rv = -1;

    if(direction == NULL)
    {
        //字符串指针为空
        return -1;
    }
    len = strlen(direction);
    if(len == 0)
    {
        //字符串长度为0
        return -1;
    }

    sprintf(buf,"/sys/class/gpio/gpio%d/direction",gpio);

    fd = open(buf,O_RDWR);
    if(fd < 0)
    {
        return -1;
    }
     rv = write(fd,direction,len);
    if(rv < 0)
    {
        //写入方向出错
        return -1;
    }

    close(fd);

    return 0;

}
5、设置触发方式
/*
功能:设置触发方式
参数:
    gpio:gpio编号
    edge:
        none表示引脚为输入,不是中断引脚
        rising表示引脚为中断输入,上升沿触发
        falling表示引脚为中断输入,下降沿触发
        both表示引脚为中断输入,边沿触发
        0-->none, 1-->rising, 2-->falling, 3-->both
返回值:
    成功返回0
    失败返回-1
*/
int gpio_set_edge(int gpio,int edge)
{
    char buf[50];
    int  fd = -1;

    sprintf(buf,"/sys/class/gpio/gpio%d/edge",gpio);

    fd = open(buf,O_RDWR);
    if(fd < 0)
    {
        return -1;
    }

    switch(edge)
    {
        case 0:
            write(fd,"none",4);
            break;
        case 1:
            write(fd,"rising",6);
            break;
        case 2:
            write(fd,"falling",7);
            break;
        case 3:
            write(fd,"both",4);
        default:
            break;
    }

    close(fd);

    return 0;
}
6、设置gpio电平
功能:向gpio写入高低电平(0/1)
参数:
    gpio:引脚编号
    value:高低电平(0/1)
返回值:
   成功返回0
   失败返回-1
*/

int gpio_set_value(int gpio,int value)
{
    char buf[50];
    int rv = -1;
    int fd = -1;

    sprintf(buf,"/sys/class/gpio/gpio%d/value",gpio);

    fd = open(buf,O_RDWR);
    if(fd < 0)
    {
        return -1;
    }

    if(value)
    {

        write(fd,"1",1);
    }
    else
    {
        write(fd,"0",1);
    }

    close(fd);

    return 0;
}
7、获取gpio电平
/*
功能:获取gpio的电平
参数:
    gpio:gpio编号
返回值:
    成功返回gpio的值
    失败返回-1
*/
int gpio_get_value(int gpio)
{
    char buf[50];
    int rv = -1;
    int fd = -1;
    char value;

    sprintf(buf,"/sys/class/gpio/gpio%d/value",gpio);

    fd = open(buf,O_RDWR);
    if(fd < 0)
    {
        return -1;
    }

    rv = read(fd,&value,1);
    if(rv < 0)
    {
        return -1;
    }

    close(fd);

    return (value - '0');

}
8、主函数

#include <stdio.h>
#include <signal.h>
#include <sys/select.h>
#include "imx283A_gpio.h"

//定义LED数组
int led[5] =
{
    0,
    3*32+26,//LED1
    3*32+22,//LED2
    3*32+20,//LED3
    2*32+7//LED4
};

static int g_stop = 0;

//信号函数
void sig_stop(int signo)
{
    if(signo == SIGINT)
    {
        g_stop = 1;
    }
}


//毫秒级延时
static void sleep_ms(unsigned int ms)
{

    struct timeval t;

    t.tv_sec=ms/1000;

    t.tv_usec=(ms*1000)%1000000;

    select(0,NULL,NULL,NULL,&t);

}

int main(int argc,char **argv)
{
    int rv = -1;

    int i = 0;


    //安装信号
    signal(SIGINT,sig_stop);

    //导入gpio
    for(i = 1; i < 5; i++)
    {
        if(gpio_is_exported(led[i]) < 0)
        {
            gpio_export(led[i]);
        }
    }

    //设置gpio输出方向
    for(i = 1; i < 5; i++)
    {
        gpio_set_direction(led[i],"out");
        gpio_set_value(led[i],1);//关掉所有led
    }

    //循环流水灯

    i = 1;

    while(!g_stop)
    {
        sleep_ms(500);
        gpio_set_value(led[i],0);
        sleep_ms(500);//延时500ms
        gpio_set_value(led[i],1);

        if( i == 4)
        {
            i = 0;
        }
        i++;
    }

    //导出gpio
    for(i = 1; i < 5; i++)
    {
        gpio_set_value(led[i],1);
        gpio_unexport(led[i]);
    }

    return 0;
}

9、交叉编译Makefile

 使用开发板对应的交叉编译器编译上面程序,make 之后会在当前目录下生成可执行程序led,然后通过tftp -gr 命令下载到开发板上运行。需要注意的是在开发板上运行led之前,需要给/sys目录以及子目录赋给可读写权限,否则会出现权限限制错误。

objects = $(patsubst %.c,%.o,$(wildcard *.c))
CC = /opt/xtools/EasyArm-imax280A/gcc-4.4.4-glibc-2.11.1-multilib-1.0/arm-fsl-linux-gnueabi/bin/arm-linux-cc
CFLAGS = -g
TARGET = led
$(TARGET):$(objects)
    $(CC) $(CFLAGS) -o $@ $^ 
#声明为伪目标,防止出现史料未及的错误,比如硬盘中存在一个名为clean的文件。
.PHONY:clean
clean:
    rm -rf *.o 
10、效果

在这里插入图片描述
 gpio的应用不仅仅是点亮几个led而已,它可以焊接很多传感器,通过gpio的输入输出控制传感器,进而获取传感器的数据。引入sysfs文件系统后,Linux应用开发不需要关心底层驱动的实现,使得用户空间操作gpio变得简单。

工 程 源 码 : \color{red}{工程源码:} git地址
微信公众号:
微信公众号

  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值