linux的GPIO驱动的使用(s5pv210)

开发板:TQ210         内核版本:2.6.35

#########################################################################################################

这段时间一直在学习linux的驱动,大部分的学习资料都是来自网络论坛、博客。这类资料往往不够系统,全面,且好多资料都是相互拷贝,重复的。因此,学了这么长时间,感觉好没有条理,总是东看一点西看一点,看完也说不出个所以然。不知道大家有没有好的学习方法,或者学习资料可以推荐一下,在此先谢谢各位。

回过头来,看了这么久的驱动,好像还没看GPIO的驱动。控制开发板的IO口应该是嵌入式开发最基础的操作了,那么,如何在应用程序中使用GPIO呢,即如何使用GPIO驱动?网上找资料学习了下,今天就把学到的东西做个总结。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

如何在应用程序中操作GPIO?这里有两种方法:

方法1:使用/sys/class/gpio目录下的文件。找到你想控制的gpio引脚对应的目录,对该目录下的文件进操作。

方法2:使用内核提供的gpio驱动的接口,自己再写一个驱动,利用自己写的驱动来控制GPIO(后面以LED驱动为例)。

下面详细介绍一下这两种方法。

方法1:详见http://www.tuicool.com/articles/mmaARfu 写的非常详细,很好的文章。

/sys/class/gpio目录下有两类目录:gpioxx和gpiochipxx        两个文件:export和unexport

介绍上面这几个目录和文件之前,先介绍一些s5pv210的GPIO引脚的分组与引脚编号。如下图所示:

210有好几百个GPIO口,并对这些IO口进行了分组,如属于通用IO组的GPA0、GPA0、GPB、GPC0、GPC1等,属于memory port引脚的MP0_1、MP0_2等。每个引脚都有自己的编号,且引脚编号按着上图的分组递增。在linux系统中,这些编号即在组内线性递增,也是跟随分组线性递增的。即num(GPA0_4) =num(GPA0_3)+1,且num(GPA1_0)=num(GPA0_7)+1。而方法一就是根据引脚在linux系统中的编号来控制引脚。想要控制某个IO口,就必须先知道它在linux系统中的编号。

每个组的第一个引脚的编号称为start 。由此可知,只要知道每组第一个引脚的编号start,加上这个引脚在组内的偏移量,就能得到该引脚的编号。例如,要确定GPB_3引脚的编号,首先得确定GPB_0引脚的编号start,start+3就是GPB_3引脚的编号。说到这里,可知,确定某个引脚的编号的关键是确定该引脚所在组的start。

如何计算每个组的start编号,可以参考内核源码的两个文件:\arch\arm\mach-s5pv210\include\mach\Gpio.h    和arch\arm\mach-s5pv210\Gpiolib.c。

210的GPIO口在linux中的分组情况可以看如下代码:

arch\arm\mach-s5pv210\Gpiolib.c:

/*
 * Following are the gpio banks in v210.
 *
 * The 'config' member when left to NULL, is initialized to the default
 * structure gpio_cfg in the init function below.
 *
 * The 'base' member is also initialized in the init function below.
 * Note: The initialization of 'base' member of s3c_gpio_chip structure
 * uses the above macro and depends on the banks being listed in order here.
 */
static struct s3c_gpio_chip s5pv210_gpio_4bit[] = {
	{
		.chip	= {
			.base	= S5PV210_GPA0(0),
			.ngpio	= S5PV210_GPIO_A0_NR,
			.label	= "GPA0",
			.to_irq = s5p_gpiolib_gpioint_to_irq,
		},
	}, {
		.chip	= {
			.base	= S5PV210_GPA1(0),
			.ngpio	= S5PV210_GPIO_A1_NR,
			.label	= "GPA1",
			.to_irq = s5p_gpiolib_gpioint_to_irq,
		},
	}, {
		.chip	= {
			.base	= S5PV210_GPB(0),
			.ngpio	= S5PV210_GPIO_B_NR,
			.label	= "GPB",
			.to_irq = s5p_gpiolib_gpioint_to_irq,
		},
	}, {
		.chip	= {
			.base	= S5PV210_GPC0(0),
			.ngpio	= S5PV210_GPIO_C0_NR,
			.label	= "GPC0",
			.to_irq = s5p_gpiolib_gpioint_to_irq,
		},
	}, {
		.chip	= {
			.base	= S5PV210_GPC1(0),
			.ngpio	= S5PV210_GPIO_C1_NR,
			.label	= "GPC1",
			.to_irq = s5p_gpiolib_gpioint_to_irq,
		}........</span>

它定义了一个数组,数组里的每一个元素都代表一组引脚,即里面的每一个

<span style="font-size:14px;">.chip	= {
			.base	= S5PV210_GPA0(0),
			.ngpio	= S5PV210_GPIO_A0_NR,
</span><pre name="code" class="cpp"><span style="font-size:14px;"><span style="white-space:pre">			</span>.label	= "GPA0",</span>

.to_irq = s5p_gpiolib_gpioint_to_irq,},


都代表一组引脚。.base就是这组引脚的start编号(也是该组的编号,简称组号吧),ngpio表示这组包含的引脚个数,label表示linux系统中的组名,to_irq表示这组引脚包含的中断资源。显然,上面那个chip表示的是GPA0组引脚,.ngpio= S5PV210_GPIO_A0_NR   且#define S5PV210_GPIO_A0_NR(8)
  表示该组有8个引脚,.label= "GPA0"表示该组名称是GPA0。

重点是如何求出每个组的start编号,即上面chip中的base的值。以上面那个chip来说,就是要搞清楚 S5PV210_GPA0(0)所代表的值,它是一个宏定义。看如下代码:

\arch\arm\mach-s5pv210\include\mach\Gpio.h:

/* S5PV210 GPIO number definitions */
#define S5PV210_GPA0(_nr)	(S5PV210_GPIO_A0_START + (_nr))
#define S5PV210_GPA1(_nr)	(S5PV210_GPIO_A1_START + (_nr))
#define S5PV210_GPB(_nr)	(S5PV210_GPIO_B_START + (_nr))
#define S5PV210_GPC0(_nr)	(S5PV210_GPIO_C0_START + (_nr))
#define S5PV210_GPC1(_nr)	(S5PV210_GPIO_C1_START + (_nr))
#define S5PV210_GPD0(_nr)	(S5PV210_GPIO_D0_START + (_nr))
#define S5PV210_GPD1(_nr)	(S5PV210_GPIO_D1_START + (_nr))
#define S5PV210_GPE0(_nr)	(S5PV210_GPIO_E0_START + (_nr))
#define S5PV210_GPE1(_nr)	(S5PV210_GPIO_E1_START + (_nr))
#define S5PV210_GPF0(_nr)	(S5PV210_GPIO_F0_START + (_nr))
#define S5PV210_GPF1(_nr)	(S5PV210_GPIO_F1_START + (_nr))
#define S5PV210_GPF2(_nr)	(S5PV210_GPIO_F2_START + (_nr))
#define S5PV210_GPF3(_nr)	(S5PV210_GPIO_F3_START + (_nr))

可见,S5PV210_GPA0(0) = (S5PV210_GPIO_A0_START + 0),于是问题的关键是搞清S5PV210_GPIO_A0_START代表的值。

/* GPIO bank numbers */

/* CONFIG_S3C_GPIO_SPACE allows the user to select extra
 * space for debugging purposes so that any accidental
 * change from one gpio bank to another can be caught.
*/

<span style="color:#ff0000;">#define S5PV210_GPIO_NEXT(__gpio) 	((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 1)
</span>
enum s5p_gpio_number {
	S5PV210_GPIO_A0_START	= 0,
	S5PV210_GPIO_A1_START	= S5PV210_GPIO_NEXT(S5PV210_GPIO_A0),
	S5PV210_GPIO_B_START 	= S5PV210_GPIO_NEXT(S5PV210_GPIO_A1),
	S5PV210_GPIO_C0_START	= S5PV210_GPIO_NEXT(S5PV210_GPIO_B),
	S5PV210_GPIO_C1_START	= S5PV210_GPIO_NEXT(S5PV210_GPIO_C0),
	S5PV210_GPIO_D0_START	= S5PV210_GPIO_NEXT(S5PV210_GPIO_C1),
	S5PV210_GPIO_D1_START	= S5PV210_GPIO_NEXT(S5PV210_GPIO_D0),
	S5PV210_GPIO_E0_START	= S5PV210_GPIO_NEXT(S5PV210_GPIO_D1),
	S5PV210_GPIO_E1_START	= S5PV210_GPIO_NEXT(S5PV210_GPIO_E0),
	S5PV210_GPIO_F0_START	= S5PV210_GPIO_NEXT(S5PV210_GPIO_E1),
	S5PV210_GPIO_F1_START	= S5PV210_GPIO_NEXT(S5PV210_GPIO_F0),
	S5PV210_GPIO_F2_START	= S5PV210_GPIO_NEXT(S5PV210_GPIO_F1),
	S5PV210_GPIO_F3_START	= S5PV210_GPIO_NEXT(S5PV210_GPIO_F2),
	S5PV210_GPIO_G0_START	= S5PV210_GPIO_NEXT(S5PV210_GPIO_F3),
	S5PV210_GPIO_G1_START	= S5PV210_GPIO_NEXT(S5PV210_GPIO_G0),
...........

这里S5PV210_GPIO_A0_START恰好为0。如果要求S5PV210_GPIO_A1_START,则要求S5PV210_GPIO_NEXT(S5PV210_GPIO_A0)。

而S5PV210_GPIO_NEXT是宏定义:

#define S5PV210_GPIO_NEXT(__gpio)     ((__gpio##_START) + (__gpio##_NR) +CONFIG_S3C_GPIO_SPACE+ 1)

CONFIG_S3C_GPIO_SPACE是一个宏定义,它的值为0 。上面这句意思是:

该组的start编号 = 上一组的start编号 + 上一组的引脚数量 + 0 + 1。这也就印证了上文说的“引脚编号随着分组线性递增”。

讲完了引脚的分组与编号,接着分析/sys/class/gpio目录下的文件:

两类目录:gpioxx和gpiochipxx        两个文件:export和unexport

(1)gpioxx目录

gpioxx是第xx号引脚对应的文件夹,即代表第xx号引脚,xx是引脚编号。目录里是该引脚的一些属性文件,比如active_low、

value、direction、edge等文件,从文件名也能大概知道文件含义。若要操作该引脚,只要对这些属性文件进行相应的读写即可。

比如要控制3号引脚(假设为GPA0_3),找到gpio3目录,对该目录下的相关文件进行读写。例如要配置3号引脚为输出模式,则

向目录下的direction文件写"out",若要让3号引脚输出高电平,则向value文件写1。

(2)gpiochipxx目录

xx是组号,也等于该组第一个引脚的编号,即start编号。该目录是第xx组引脚的目录,里面含有第xx组引脚的一些属性文件。

比如:base、label、ngpio等。base文件里存放的是该组的组号,即该组第一个引脚的编号(GPX_0的编号);label文件里存放的是该组的名称;ngpio文件里存放的是该组的引脚数量。这些文件里的内容都能读出来。如下图所示,我在串口中断用cat 命令读出gpiochip0目录下的这些文件的内容

从上图可知,该组引脚时GPA0组引脚,GPA0组有8个引脚,GPA0_0的引脚编号是0。

(3)export文件

一个GPIO引脚,若要让他能在用户空间使用,必须先将其导出。如何导出?将该引脚编号写入导出文件即可。顾名思义,export就是这个导出文件。将引脚编号xx写入export文件后,/sys/class/gpio目录下会自动生成该引脚的目录gpioxx。

(4) unexport文件

该文件功能与export相反。若将引脚编号写入ubexport文件,则该引脚将无法在用户空间使用。将引脚编号xx写入unexport之后,/sys/class/gpio目录下的gpioxx目录会被自动删除。

注意:

有可能你的/sys/class/gpio目录下没有gpioxx目录,只有gpiochipxx目录、export文件、unexport文件。那是因为事先没有导出引脚编号,只要将要控制的引脚编号写入unexport文件,就会自动生成gpioxx目录。如下图,我在串口中断使用echo命令来导出0号引脚:

原本没有gpioxx目录,只有gpiochipxx目录。导出之后,出现了gpio0目录,只要读写该目录下的相关文件,就能控制第0号引脚。

也有可能出现这种情况:你将某引脚的编号xx写入了export文件,但是却没有出现gpioxx目录,多半因为这个引脚已经被占用了,当你用gpio_free(...)函数释放该引脚后,再将引脚编号写入export文件即可。

总结方法一:

(1)根据你要控制的引脚名称,确定该引脚编号。可以先确定所属组号,然后加上组内偏移量。

(2)确保你要控制的引脚已经导出了。若没有,将引脚编号写入/sys/class/gpio目录下的export文件。

(3)进入/sys/class/gpio/gpioxx目录,根据你的目的,打开相关文件,读写相关文件。

范例代码如下:

/* Copyright (c) 2011, RidgeRun
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by the RidgeRun.
 * 4. Neither the name of the RidgeRun nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY RIDGERUN ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL RIDGERUN BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>

 /****************************************************************
 * Constants
 ****************************************************************/

#define SYSFS_GPIO_DIR "/sys/class/gpio"
#define POLL_TIMEOUT (3 * 1000) /* 3 seconds */
#define MAX_BUF 64

/****************************************************************
 * gpio_export
 ****************************************************************/
int gpio_export(unsigned int gpio)
{
  int fd, len;
  char buf[MAX_BUF];

  fd = open(SYSFS_GPIO_DIR "/export", O_WRONLY);
  if (fd < 0) {
    perror("gpio/export");
    return fd;
  }

  len = snprintf(buf, sizeof(buf), "%d", gpio);
  write(fd, buf, len);
  close(fd);

  return 0;
}

/****************************************************************
 * gpio_unexport
 ****************************************************************/
int gpio_unexport(unsigned int gpio)
{
  int fd, len;
  char buf[MAX_BUF];

  fd = open(SYSFS_GPIO_DIR "/unexport", O_WRONLY);
  if (fd < 0) {
    perror("gpio/export");
    return fd;
  }

  len = snprintf(buf, sizeof(buf), "%d", gpio);
  write(fd, buf, len);
  close(fd);
  return 0;
}

/****************************************************************
 * gpio_set_dir
 ****************************************************************/
int gpio_set_dir(unsigned int gpio, unsigned int out_flag)
{
  int fd, len;
  char buf[MAX_BUF];

  len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR  "/gpio%d/direction", gpio);

  fd = open(buf, O_WRONLY);
  if (fd < 0) {
    perror("gpio/direction");
    return fd;
  }

  if (out_flag)
    write(fd, "out", 4);
  else
    write(fd, "in", 3);

  close(fd);
  return 0;
}

/****************************************************************
 * gpio_set_value
 ****************************************************************/
int gpio_set_value(unsigned int gpio, unsigned int value)
{
  int fd, len;
  char buf[MAX_BUF];

  len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);

  fd = open(buf, O_WRONLY);
  if (fd < 0) {
    perror("gpio/set-value");
    return fd;
  }

  if (value)
    write(fd, "1", 2);
  else
    write(fd, "0", 2);

  close(fd);
  return 0;
}

/****************************************************************
 * gpio_get_value
 ****************************************************************/
int gpio_get_value(unsigned int gpio, unsigned int *value)
{
  int fd, len;
  char buf[MAX_BUF];
  char ch;

  len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);

  fd = open(buf, O_RDONLY);
  if (fd < 0) {
    perror("gpio/get-value");
    return fd;
  }

  read(fd, &ch, 1);

  if (ch != '0') {
    *value = 1;
  } else {
    *value = 0;
  }

  close(fd);
  return 0;
}

/****************************************************************
 * gpio_set_edge
 ****************************************************************/

int gpio_set_edge(unsigned int gpio, char *edge)
{
  int fd, len;
  char buf[MAX_BUF];

  len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/edge", gpio);

  fd = open(buf, O_WRONLY);
  if (fd < 0) {
    perror("gpio/set-edge");
    return fd;
  }

  write(fd, edge, strlen(edge) + 1);
  close(fd);
  return 0;
}

/****************************************************************
 * gpio_fd_open
 ****************************************************************/

int gpio_fd_open(unsigned int gpio)
{
  int fd, len;
  char buf[MAX_BUF];

  len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);

  fd = open(buf, O_RDONLY | O_NONBLOCK );
  if (fd < 0) {
    perror("gpio/fd_open");
  }
  return fd;
}

/****************************************************************
 * gpio_fd_close
 ****************************************************************/

int gpio_fd_close(int fd)
{
  return close(fd);
}

/****************************************************************
 * Main
 ****************************************************************/
int main(int argc, char **argv, char **envp)
{
  struct pollfd fdset[2];
  int nfds = 2;
  int gpio_fd, timeout, rc;
  char *buf[MAX_BUF];
  unsigned int gpio;
  int len;

  if (argc < 2) {
    printf("Usage: gpio-int <gpio-pin>\n\n");
    printf("Waits for a change in the GPIO pin voltage level or input on stdin\n");
    exit(-1);
  }

  gpio = atoi(argv[1]);

  gpio_export(gpio);
  gpio_set_dir(gpio, 0);
  gpio_set_edge(gpio, "rising");
  gpio_fd = gpio_fd_open(gpio);

  timeout = POLL_TIMEOUT;

  while (1) {
    memset((void*)fdset, 0, sizeof(fdset));

    fdset[0].fd = STDIN_FILENO;
    fdset[0].events = POLLIN;

    fdset[1].fd = gpio_fd;
    fdset[1].events = POLLPRI;

    rc = poll(fdset, nfds, timeout);      

    if (rc < 0) {
      printf("\npoll() failed!\n");
      return -1;
    }

    if (rc == 0) {
      printf(".");
    }

    if (fdset[1].revents & POLLPRI) {
      len = read(fdset[1].fd, buf, MAX_BUF);
      printf("\npoll() GPIO %d interrupt occurred\n", gpio);
    }

    if (fdset[0].revents & POLLIN) {
      (void)read(fdset[0].fd, buf, 1);
      printf("\npoll() stdin read 0x%2.2X\n", (unsigned int) buf[0]);
    }

    fflush(stdout);
  }

  gpio_fd_close(gpio_fd);
  return 0;
}

参考文章:

http://www.tuicool.com/articles/mmaARfu  这里讲的很清楚,也很全面。严重推荐。

关于方法二,下篇文章介绍。

###################################################################################################################转载请注明出处:

地址:http://blog.csdn.net/andoubi/article/details/51872781

作者:Andoubi

时间: 2024-10-11 02:16:01

linux的GPIO驱动的使用(s5pv210)的相关文章

Linux下GPIO驱动

编写驱动程序,首先要了解是什么类型的设备.linux下的设备分为三类,分别为:字符设备,块设备和网络设备.字符设备类型是根据是否以字符流为数据的交换方式,大部分设备都是字符设备,如键盘,串口等,块设备则是以块为单位进行管理的设备,如,磁盘.网络设备就是网卡等. 其次要了解应用程序和驱动程序的区别,两者的主要区别分为以下三点: 1入口函数的任务不相同,应用程序完成一个任务,驱动只完成初始化工作,比如中断 申请,寄存器设置,定时器设置. 2运行时的cpu模式不相同,驱动具有很高的权限,应用程序是在用

linux通用GPIO驱动

Linux开发平台实现了通用GPIO的驱动,用户通过,SHell或者系统调用能控制GPIO的输出和读取其输入值.其属性文件均在/sys/class/gpio/目录下,该目录下有export和unexport两个属性文件,其余都是连接文件,如gpiochipN等. export文件导出某个GPIO,unexport将导出的GPIO从/sysfs中删除.向export文件写入要操作的GPIO序号N可以导出对应的GPIO设备目录,例如: #echo 68>/sys/class/gpio/export

很好的linux下GPIO驱动详解文章

原文地址  http://blog.csdn.net/llxmedici/article/details/6282372 打算跟着友善之臂的<mini2440 linux移植开发指南>来做个LED驱动,虽然LED的原理简单得不能再简单了,但是要把kernel中针对于s3c24**的GPIO的一些数据结构,还有函数搞清楚也不是那么轻松的事,所以本文主要简单地说明下LED驱动中的相关数据结构以及函数/宏的定义,并对驱动加以验证 ***********************************

飞思卡尔imx6开发板Linux下GPIO驱动

控制GPIO_1_28的输出: #define MY_BOMB_GPIO       IMX_GPIO_NR(1, 28) 配置为输出方式: gpio_direction_output (MY_BOMB_GPIO,0); 设置电平状态 gpio_set_value(MY_BOMB_GPIO,1); 读取电平状态 tem=  gpio_get_value(MY_BOMB_GPIO);

Linux的i2c驱动详解

目录(?)[-] 简介 架构 设备注册 I2C关键数据结构和详细注册流程 关键数据结构 详细注册流程 使用I2C子系统资源函数操作I2C设备 Gpio模拟i2c总线的通用传输算法 总结 理清i2c中的个结构体关系 i2c驱动的编写建议 1 简介 I2C 总线仅仅使用 SCL . SDA 两根信号线就实现了设备之间的数据交互,极大地简化对硬件资源和 PCB 板布线空间的占用.因此, I2C 总线被非常广泛地应用在 EEPROM .实时钟.小型 LCD 等设备与 CPU 的接口中. Linux I2

嵌入式Linux裸机开发(九)——S5PV210定时器

嵌入式Linux裸机开发(九)--S5PV210定时器 S5PV210内部一共有四类定时器. 一.PWM定时器 1.PWM定时简介 S5PV210内部共有5个32bit的PWM定时器.PWM定时器可以生成内部中断.PWM定时器0.1.2.3具有PWM功能,可以驱动外部I/O信号.PWM定时器4是一个无外部引脚的内部定时器.PWM 定时器使用 PCLK_PSYS 作为时钟源. 每个定时器有一个由定时器时钟驱动的32位递减计数器.递减计数器的初始值是由TCNTBn自动装载而获得的.如果递减计数器减到

linux字符设备驱动

一.字符设备.字符设备驱动与用户空间访问该设备的程序三者之间的关系. 如图,在Linux内核中使用cdev结构体来描述字符设备,通过其成员dev_t来定义设备号(分为主.次设备号)以确定字符设备的唯一性.通过其成员file_operations来定义字符设备驱动提供给VFS的接口函数,如常见的open().read().write()等. 在Linux字符设备驱动中,模块加载函数通过register_chrdev_region( ) 或alloc_chrdev_region( )来静态或者动态获

TQ2440学习笔记——Linux上I2C驱动的两种实现方法(1)

作者:彭东林 邮箱:[email protected] 内核版本:Linux-3.14 u-boot版本:U-Boot 2015.04 硬件:TQ2440 (NorFlash:2M   NandFlash:256M  内存:64M) 摘要 这里并不深入分析Linux下I2C驱动的实现,只是以TQ2440硬件平台为例分析I2C驱动的两种方法. 第一种方法: 使用S3C2440自带的I2C控制器实现,这个kernel已经支持,我们只需要配置即可. 第二种方法: 使用GPIO模拟,这个在kernel中

友坚4412开发板基于Timed_out框架的GPIO驱动分析

Timed GPIO驱动程序分析 Timed GPIO驱动程序是android系统基于linux内核新增加的一类驱动程序,这类驱动程序主要是运用了内核定时器,与内核定时器进行绑定,使得控制GPIO口的高低电平与时间打上关系,既可以实现在一定的时间实现GPIO口为高或者低电平.Timed GPIO驱动被实现为平台设备驱动,Timed GPIO驱动源码位于如下目录:\kernel\drivers\staging\android Timed GPIO驱动程序主要包括如下几个文件: Timed_gpio