最近要深一步用到GPIO口控制,写个博客记录下Kernel层的GPIO学习过程!

一、概念

General Purpose Input Output (通用输入/输出)简称为GPIO,或 总线扩展器。也就是芯片的引脚,当微控制器或芯片组没有足够的I/O端口,或当系统需要采用远端串行通信或控制时,GPIO产品能够提供额外的控制和监视功能。通常在ARM里,所有I/O都是通用的,

每个GPIO端口包含8个管脚,如PA端口是PA0~PA7,而且GPIO口至少有两个寄存器,即"通用IO控制寄存器"与"通用IO数据寄存器"。数据寄存器的各位都直接引到芯片外部,而对这种寄存器中每一位的作用,即每一位的信号流通方向,则可以通过控制寄存器中对应位独立地加以设置。比如,可以设置某个引脚的属性为输入、输出或其他特殊功能。

常用的应该是:高阻输入、推挽输出、开漏输出。

通俗理解为 :

高阻输入—— 保持高阻抗状态,彻底断开输出,避免干扰,对总线状态不起作用,此时总线可由其他器件占用。

推挽输出——可以输出高,低电平,连接数字器件。

开漏输出——输出端相当于三极管的集电极. 要得到高电平状态需要上拉电阻才行。

软件上就是通过设置IO口的模式,然后控制IO的上拉下拉,写入对应寄存器,通过寄存器控制电路:

上拉寄存器是控制对应端口上拉使能(DE)的。当对应位为0时,设置对应引脚上拉使能,为1时,禁止对应引脚上拉使能。如果上拉寄存器使能,无论引脚功能寄存器如何设置(输入,输出,数据,中断等),对应引脚输出高电平。

上拉是一个电阻接到一个电压,其实就是增强IO的驱动能力,IO口是高电平。
下拉是一个电阻接到地,保证IO口是低电平。

二、kernel层调用接口实现

GPIO操作,在Kernel 2.6.32版本以上提供了gpio口管理的库文件/kernel/drivers/gpio/gpiolib.c,里面就是我们需要的接口函数实现!

几个主要的方法:

申请一个pin脚作为gpio口,命名为 * label,如果经过判断空闲的 申请成功了做一些初始的bit位设置。

int gpio_request(unsigned gpio, const char *label)
{
struct gpio_desc *desc;
struct gpio_chip *chip;
int status = -EINVAL;
unsigned long flags; spin_lock_irqsave(&gpio_lock, flags);
...
if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0)
...
}

释放这个gpio口,还原bit。

void gpio_free(unsigned gpio)
{
unsigned long flags;
struct gpio_desc *desc;
struct gpio_chip *chip; might_sleep();
...
clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
clear_bit(FLAG_REQUESTED, &desc->flags);
clear_bit(FLAG_OPEN_DRAIN, &desc->flags);
clear_bit(FLAG_OPEN_SOURCE, &desc->flags);
...
}

设置gpio口为输入模式:

int gpio_direction_input(unsigned gpio)
{
unsigned long flags;
struct gpio_chip *chip;
struct gpio_desc *desc = &gpio_desc[gpio];
int status = -EINVAL; spin_lock_irqsave(&gpio_lock, flags);
if (!gpio_is_valid(gpio))
goto fail;
chip = desc->chip;
...
status = chip->direction_input(chip, gpio);
...
}

其中的chip->direction_input(chip, gpio)为实现!

 

设置gpio口为输出模式 value为初始值 0为高电平/1为低电平:

int gpio_direction_output(unsigned gpio, int value)
{
unsigned long flags;
struct gpio_chip *chip;
struct gpio_desc *desc = &gpio_desc[gpio];
int status = -EINVAL; /* Open drain pin should not be driven to 1 */
if (value && test_bit(FLAG_OPEN_DRAIN, &desc->flags))
return gpio_direction_input(gpio);
...
status = chip->direction_output(chip, gpio, value);
...
}

其中的chip->direction_output(chip, gpio, value)为实现!

设置gpio口的值:

void __gpio_set_value(unsigned gpio, int value)
{
struct gpio_chip *chip; chip = gpio_to_chip(gpio);
WARN_ON(chip->can_sleep);
trace_gpio_value(gpio, 0, value);
if (test_bit(FLAG_OPEN_DRAIN, &gpio_desc[gpio].flags))
_gpio_set_open_drain_value(gpio, chip, value);
else if (test_bit(FLAG_OPEN_SOURCE, &gpio_desc[gpio].flags))
_gpio_set_open_source_value(gpio, chip, value);
else
chip->set(chip, gpio - chip->base, value);
}

获取gpio口的值:

int __gpio_get_value(unsigned gpio)
{
struct gpio_chip *chip;
int value; chip = gpio_to_chip(gpio);
WARN_ON(chip->can_sleep);
value = chip->get ? chip->get(chip, gpio - chip->base) : 0;
trace_gpio_value(gpio, 1, value);
return value;
}

对于有些挂载在I2C,SPI总线上的扩展GPIO,读写操作可能会导致睡眠,因此不能在中断函数中
  使用。使用下面的函数以区别于正常的GPIO

  int gpio_get_value_cansleep(unsigned gpio);//读GPIO
void gpio_set_value_cansleep(unsigned gpio, int value);//写GPIO

三、gpiolib.c关联芯片接口

以上为gpiolib.c的基本方法都是向下调用到对应芯片的gpio实现!

所以每个方法里面的实现都是通过 struct gpio_chip*chip 这个指针调用结构体中关联的相关接口!

下面是gplolib怎么关联到特定的芯片(mach-at91)的几个主要方法:

mach-at91的/kernel/arch/arm/mach-at91/gpio.c中的

static struct at91_gpio_chip gpio_chip[] = {
AT91_GPIO_CHIP("A", 0x00 + PIN_BASE, 32),
AT91_GPIO_CHIP("B", 0x20 + PIN_BASE, 32),
AT91_GPIO_CHIP("C", 0x40 + PIN_BASE, 32),
AT91_GPIO_CHIP("D", 0x60 + PIN_BASE, 32),
AT91_GPIO_CHIP("E", 0x80 + PIN_BASE, 32),
};

gpiolib.c中的接口与mach-at91函数接口对应关系如下:

#define AT91_GPIO_CHIP(name, base_gpio, nr_gpio)			\
{ \
.chip = { \
.label = name, \
.direction_input = at91_gpiolib_direction_input, \
.direction_output = at91_gpiolib_direction_output, \
.get = at91_gpiolib_get, \
.set = at91_gpiolib_set, \
.dbg_show = at91_gpiolib_dbg_show, \
.base = base_gpio, \
.ngpio = nr_gpio, \
}, \
}

从上面可以看到 在gpiolib.c中的方法调用芯片(mach-at91)的映射关系:

比如:gpio_direction_output() 设置gpio口为输出模式中最终的实现调用chip->direction_output(chip, gpio, value)。

从上面可以看出来,也就是调用mach-at91的at91_gpiolib_direction_output()!

其它的类似。

gpiolib.c中的

int gpiochip_add(struct gpio_chip *chip)
{
unsigned long flags;
int status = 0;
unsigned id;
int base = chip->base; if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1))
&& base >= 0) {
status = -EINVAL;
goto fail;
} spin_lock_irqsave(&gpio_lock, flags);
....
}

添加到结构体:

struct gpio_desc {
struct gpio_chip *chip;
unsigned long flags;
/* flag symbols are bit numbers */
#define FLAG_REQUESTED 0
#define FLAG_IS_OUT 1
#define FLAG_RESERVED 2
...
}

这就保存到了 chip 指针。

这一部分在Kernel初始化的时候调用执行!

四、芯片(mach-at91)gpio口的接口

由gpiolib.c根据映射调用对应接口

static int at91_gpiolib_direction_output(struct gpio_chip *chip,
unsigned offset, int val)
{
struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
void __iomem *pio = at91_gpio->regbase;
unsigned mask = 1 << offset; __raw_writel(mask, pio + (val ? PIO_SODR : PIO_CODR));
__raw_writel(mask, pio + PIO_OER);
return 0;
}

定义为输出模式,初始设置val 上拉还是下拉 , 写入数据寄存器。

#define PIO_SODR	0x30	/* Set Output Data Register */
#define PIO_CODR 0x34 /* Clear Output Data Register */
#define PIO_ODR 0x14 /* Output Disable Register */
#define PIO_PDSR 0x3c /* Pin Data Status Register */

同样设置为输入:

static int at91_gpiolib_direction_input(struct gpio_chip *chip,
unsigned offset)
{
struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
void __iomem *pio = at91_gpio->regbase;
unsigned mask = 1 << offset; __raw_writel(mask, pio + PIO_ODR);
return 0;
}

设置GPIO口的value 值 0或者1,已经设置为输出模式下:

static void at91_gpiolib_set(struct gpio_chip *chip, unsigned offset, int val)
{
struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
void __iomem *pio = at91_gpio->regbase;
unsigned mask = 1 << offset; __raw_writel(mask, pio + (val ? PIO_SODR : PIO_CODR));
}

读寄存器获取该GPIO口的值:

static int at91_gpiolib_get(struct gpio_chip *chip, unsigned offset)
{
struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
void __iomem *pio = at91_gpio->regbase;
unsigned mask = 1 << offset;
u32 pdsr; pdsr = __raw_readl(pio + PIO_PDSR);
return (pdsr & mask) != 0;
}

大体流程就这样了。

撰写不易,转载请注明出处http://blog.csdn.net/jscese/article/details/16823519

Kernel 中的 GPIO 定义和控制的更多相关文章

  1. linux内核中的GPIO系统之(1):软件框架

    一.前言 作为一个工作多年的系统工程师,免不了做两件事情:培训新员工和给新员工分配任务.对于那些刚刚从学校出来的学生,一般在开始的时候总是分配一些非常简单的任务,例如GPIO driver.LED d ...

  2. linux下对/sys/class/gpio中的gpio的控制 (转)

        在嵌入式设备中对GPIO的操作是最基本的操作.一般的做法是写一个单独驱动程序,网上大多数的例子都是这样的.其实linux下面有一个通用的GPIO操作接口,那就是我要介绍的 “/sys/clas ...

  3. Linux内核中的GPIO系统之(3):pin controller driver代码分析

    一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道,pin control subsystem也不例外,被它驱动的硬件叫做pin controller(一般ARM soc的datash ...

  4. linux内核中的GPIO系统之(2):pin control subsystem

    一.前言 在linux2.6内核上工作的嵌入式软件工程师在pin control上都会遇到这样的状况: (1)启动一个新的项目后,需要根据硬件平台的设定进行pin control相关的编码.例如:在b ...

  5. Linux内核中的GPIO系统之(3):pin controller driver代码分析--devm_kzalloc使用【转】

    转自:http://www.wowotech.net/linux_kenrel/pin-controller-driver.html 一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道 ...

  6. 为AM335x移植Linux内核主线代码(35)使用platform中的GPIO

    http://www.eefocus.com/marianna/blog/15-02/310352_46e8f.html 使用GPIO,当然可以自己编写驱动,比如之前的第34节,也可以使用Kernel ...

  7. Linux kernel中常见的宏整理

    0x00 宏的基本知识 // object-like #define 宏名 替换列表 换行符 //function-like #define 宏名 ([标识符列表]) 替换列表 换行符 替换列表和标识 ...

  8. ArcGIS中的坐标系定义与转换 (转载)

    原文:ArcGIS中的坐标系定义与转换 (转载) 1.基准面概念:  GIS中的坐标系定义由基准面和地图投影两组参数确定,而基准面的定义则由特定椭球体及其对应的转换参数确定,因此欲正确定义GIS系统坐 ...

  9. linux kernel中timer的使用

    linux kernel中timer的使用 http://blog.csdn.net/njuitjf/article/details/16888821 在kernel中如果想周期性的干些什么事情,或者 ...

随机推荐

  1. Java 多线程 简单实例 (Runnable)

    1.多线程实例 package second; public class A implements Runnable { public char stat = '*'; public void run ...

  2. easy ui easyui-linkbutton 禁用、启用

    <a id="btn_update_Shop" name="btn_update_Shop" class="easyui-linkbutton& ...

  3. python学习笔记20(字符串格式化)

    Python中内置有对字符串进行格式化的操作% 模板 格式化字符串时,Python使用一个字符串作为模板.模板中有格式符,这些格式符为真实值预留位置,并说明真实数值应该呈现的格式.Python用一个t ...

  4. kindeditor-4.1.10 结合 Asp.Net MVC 添加图片功能

    KindEditor是一套开源的HTML可视化编辑器,现在我要结合Asp.Net MVC4 上传图片功能,做相应的配置和修改, 其实网上也有人写过类似的文章了,我写出来是以防以后使用的时候出现这样的问 ...

  5. bnuoj 20832 Calculating Yuan Fen(暴力模拟)

    http://www.bnuoj.com/bnuoj/problem_show.php?pid=20832 [题意]: 给你一串字符串,求一个ST(0<ST<=10000),对字符串中字符 ...

  6. JavaScript与DOM的关系

    JavaScript与浏览器的工作 1.浏览器获取并加载你的页面,从上至下解析它的内容. 遇到JavaScript时,浏览器会解析代码,检查它的正确性,然后执行代码. 浏览器还会建立一个HTML页面的 ...

  7. (转载)shell日志分析常用命令

    shell日志分析常用命令总结 时间:2016-03-09 15:55:29来源:网络 导读:shell日志分析的常用命令,用于日志分析的shell脚本,统计日志中百度蜘蛛的抓取量.抓取最多的页面.抓 ...

  8. window live writer的曲折安装过程

           之前一直使用windows live writer2012写日志,由于之前重装了系统,所以需要重新安装writer,本以为是一个很简单的过程,你就是安装个软件吗.... 然而事实是... ...

  9. Mybatis代码生成器 xml配置文件 连接SQL SERVER 2005

    <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE generatorConfiguratio ...

  10. thinkphp 减少文件目录

    配置 'TMPL_FILE_DEPR'=>'_' 于是模板文件的格式为如:index_index.html,index_show.html .代替原来的目录结构:/index/index.htm ...