最近在调试安霸SDK,这个SDK中并没有提供直接操作GPIO口的驱动设置,在使用GPIO 的时候一般是通过echo命令的导出gpio口,设置其属性,具体方法我在另一文章中写过,并且网上也有很多的例子可以查看就不多写了。今天主要是对于芯片的GPIO的datasheet和驱动程序进行分析,在Linux系统中内核已经提供了很多接口去操作GPIO
内核中gpio 的使用
      1  测试 gpio 端口是否合法  int gpio_is_valid(int number); 
     
     2  申请某个 gpio 端口 当然在申请之前需要显示的配置该gpio 端口的 pinmux
        int gpio_request(unsigned gpio, const char *label)
     
     3  标记 gpio 的使用方向包括输入还是输出
       /* 成功返回零失败返回负的错误值 */ 
       int gpio_direction_input(unsigned gpio); 
       int gpio_direction_output(unsigned gpio, int value); 
     
     4  获得 gpio 引脚的值和设置 gpio 引脚的值 ( 对于输出 )
        int gpio_get_value(unsigned gpio);
        void gpio_set_value(unsigned gpio, int value); 
     
     5 gpio 当作中断口使用
        int gpio_to_irq(unsigned gpio); 
        返回的值即中断编号可以传给request_irq() free_irq()
        内核通过调用该函数将gpio端口转换为中断,在用户空间也有类似方法
  
     6  导出 gpio 端口到用户空间
        int gpio_export(unsigned gpio, bool direction_may_change); 
        内核可以对已经被gpio_request() 申请的 gpio 端口的导出进行明确的管理,
        参数direction_may_change 表示用户程序是否允许修改 gpio 的方向,假如可以
        则参数direction_may_change 为真
        /*  撤销 GPIO 的导出  */ 
        void gpio_unexport(); 
内核实现的GPIO接口,在用户空间得到了很好的体现,即使用echo的命令操作。具体的代码,在kernel/linux-3.10/drivers/gpio中实现,这是整个gpio子系统。话说这次写驱动并没有使用过上述接口,基本都是自己根据datasheet实现的接口。GPIO的驱动是个非常简单的驱动,只要是学会如何去阅读datasheet,然后去操作GPIO的寄存器就可以。
接下看看datasheet的,如下截图
这是GPIO的寄存器的说明,限于篇幅问题,就只截上述图,安霸芯片的GPIO的地址是不连续的,总共分为四组,定义如下
#define GPIO0_StartAddr 0xE8009000
#define GPIO1_StartAddr 0xE800A000
#define GPIO2_StartAddr 0xE800E000
#define GPIO3_StartAddr 0xE8010000
总共114个引脚,分为四组,每组32个引脚,每个引脚对应12个寄存器,换言之,每个寄存器对应32个引脚。确定基地址,每个bit位对应一个引脚。
关于寄存器的介绍:

如上术分别对应每个寄存器。只要把这些引脚和寄存器等之间的对应关系搞清楚,gpio的驱动实现就简单多了。
好了,datasheet很多东西自己看的理解的可能有些清楚,但在叙述过程中可能有些繁琐或是限于本人语文老师的原因,写的有点乱,多多理解。
接下来直接附上GPIO驱动代码:
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/gpio.h>
#include<linux/device.h>
#define GPIO0_StartAddr 0xE8009000
#define GPIO1_StartAddr 0xE800A000
#define GPIO2_StartAddr 0xE800E000
#define GPIO3_StartAddr 0xE8010000
#define GPIO_MAPSIZE 0x2C
#define GPIO_OUT 1
#define GPIO_IN 0
#define GPIO_HIGH 1
#define GPIO_LOW 0
#define REG(Addr) (*(volatile unsigned int*) (Addr))
#define DEVICE_NAME "AM_GPIO" //定义设备名

//定义操作
#define SET_OUTPUT_LOW 0 //设置io口为输出低电平模式
#define SET_OUTPUT_HIGH 1 //设置io口为输出高电平模式
#define SET_INPUT 2 //设置io口为输入模式
#define GET_VALUE 3 //获取io口的状态
#define SET_IRQ_LEVEL_LOW 4 //设置io口为低电平触发中断模式
#define SET_IRQ_LEVEL_HIGH 5 //设置io口为高电平触发中断模式
#define SET_IRQ_EDGES_FAILL 6 //设置io口为下降沿触发模式
#define SET_IRQ_EDGES_RISI 7 //设置io口为上升沿触发模式
//定义设备结构体
static struct class *cdev_class;
struct cdev cdev;
//定义设备号参数
dev_t dev = 0;
//open函数
static int gpio_open(struct inode *inode, struct file *filp)
{
return 0;
}
//release函数
int gpio_release(struct inode *inode, struct file *filp)
{
printk("gpio device released\n");
return 0;
}
//ioctl函数
static void s2l_gpio_setpin(void *Regaddr, unsigned int Addr_offset,
unsigned int MaskBit, unsigned int to)
{
unsigned int tmp=0;
tmp = REG((unsigned int)Regaddr +Addr_offset);
tmp &= ~(1<<MaskBit);
tmp |= (!!to<<MaskBit);
REG((unsigned int)Regaddr +Addr_offset) = tmp;
}

unsigned int s2l_gpio_getpin(void *Regaddr, unsigned int Addr_offset, unsigned int MaskBit)
{
unsigned int tmp=0;
tmp=REG((unsigned int)Regaddr +Addr_offset);
tmp &= (1<<MaskBit);
return tmp?1:0;
}

static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long gpio_num)
{
int ret;
unsigned int MaskBit;
unsigned int group_num;
void * GpioBase = NULL;
group_num = gpio_num/32;
MaskBit = gpio_num%32;

switch (group_num)
{
case 0:
GpioBase = ioremap(GPIO0_StartAddr,GPIO_MAPSIZE);
break;
case 1:
GpioBase = ioremap(GPIO1_StartAddr,GPIO_MAPSIZE);
break;
case 2:
GpioBase = ioremap(GPIO2_StartAddr,GPIO_MAPSIZE);
break;
case 3:
GpioBase = ioremap(GPIO3_StartAddr,GPIO_MAPSIZE);
break;
default:
break;
}
switch (cmd)
{
case SET_OUTPUT_LOW://0
{
s2l_gpio_setpin(GpioBase, 0x04, MaskBit , GPIO_OUT); //output
s2l_gpio_setpin(GpioBase, 0x00, MaskBit , GPIO_LOW);//low
break;
}
case SET_OUTPUT_HIGH:
{
s2l_gpio_setpin(GpioBase, 0x04, MaskBit , GPIO_OUT); //output
s2l_gpio_setpin(GpioBase, 0x00, MaskBit , GPIO_HIGH);//high
break;
}
case SET_INPUT:
{
s2l_gpio_setpin(GpioBase, 0x04, MaskBit , GPIO_IN); //output
break;
}
case GET_VALUE:
{
ret = s2l_gpio_getpin(GpioBase,0x00,MaskBit);
return ret;
printk("ret = %d\n",ret);
break;
}
case SET_IRQ_LEVEL_LOW:
{
s2l_gpio_setpin(GpioBase,0x08,MaskBit,GPIO_LOW);//设置中断为电平触发模式
s2l_gpio_setpin(GpioBase,0x10,MaskBit,GPIO_LOW);//设置低电平触发
break;
}
case SET_IRQ_LEVEL_HIGH:
{
s2l_gpio_setpin(GpioBase,0x08,MaskBit,GPIO_LOW);//设置中断为电平触发模式
s2l_gpio_setpin(GpioBase,0x10,MaskBit,GPIO_HIGH);//设置高电平触发
break;
}
case SET_IRQ_EDGES_FAILL:
{
s2l_gpio_setpin(GpioBase,0x08,MaskBit,GPIO_HIGH);//设置中断为边沿触发模式
s2l_gpio_setpin(GpioBase,0x10,MaskBit,GPIO_LOW);//设置下降沿触发
break;
}
case SET_IRQ_EDGES_RISI:
{
s2l_gpio_setpin(GpioBase,0x08,MaskBit,GPIO_HIGH);//设置中断为边沿触发模式
s2l_gpio_setpin(GpioBase,0x10,MaskBit,GPIO_LOW);//设置上升沿触发
break;
}
default:
break;
}
return 0;
}
//file_operations
static struct file_operations gpio_fops =
{
.owner = THIS_MODULE,
.open = gpio_open,
.release = gpio_release,
.unlocked_ioctl = gpio_ioctl,
};
//初始化并注册cdev
static int gpio_init(void)
{
int result;
int err;
result = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME);
if (result)
{
printk("globalvar register failure\n");
unregister_chrdev_region(dev,1);
return result;
}
else
{
printk(" register success\n");
}
cdev_init(&cdev, &gpio_fops);
err = cdev_add(&cdev, dev, 1);
if (err < 0)
{
printk("add register error\n");
unregister_chrdev_region(dev, 1);
return err;
}
cdev_class = class_create(THIS_MODULE, DEVICE_NAME);
if(IS_ERR(cdev_class))
{
printk("ERR:cannot create a cdev_class\n");
unregister_chrdev_region(dev, 1);
return -1;
}
device_create(cdev_class,NULL, dev, 0, DEVICE_NAME);
return result;
}
//exit函数
static void gpio_exit(void)
{
device_destroy(cdev_class, dev);
class_destroy(cdev_class);
unregister_chrdev_region(dev,1);
printk("exit success\n");
}
module_init(gpio_init);
module_exit(gpio_exit);
MODULE_AUTHOR("wangmutian");
MODULE_DESCRIPTION("GPIO Driver"); // 一些描述信息
MODULE_LICENSE("GPL");

所有的代码不到三百行,最主要的就是实现了一个字符设备驱动的结构体
static struct file_operations gpio_fops =
{
.owner = THIS_MODULE,
.open = gpio_open,
.release = gpio_release,
.unlocked_ioctl = gpio_ioctl,
};
特别说明的一点就是 在Linux内核2.6版本之后的,ioctl全都变成了unlocked_ioctl , 我现在使用的系统内核是3.10的,原先在上家公司做pos机,主要是使用2.6内核,一直没在意过这个事,这次这个问题导致我在编译过程,始终出现错误,然后才发现是这个关键字导致的问题,希望这个对大家有所帮助。
还有一个可以学习的地方就是这两个函数,用来操作GPIO的寄存器
static void s2l_gpio_setpin(void *Regaddr, unsigned int Addr_offset,
unsigned int MaskBit, unsigned int to)
{
unsigned int tmp=0;
tmp = REG((unsigned int)Regaddr +Addr_offset);
tmp &= ~(1<<MaskBit);
tmp |= (!!to<<MaskBit);
REG((unsigned int)Regaddr +Addr_offset) = tmp;
}

unsigned int s2l_gpio_getpin(void *Regaddr, unsigned int Addr_offset, unsigned int MaskBit)
{
unsigned int tmp=0;
tmp=REG((unsigned int)Regaddr +Addr_offset);
tmp &= (1<<MaskBit);
return tmp?1:0;
}
其他都是一下Linux驱动提供的标准接口,在其他地方都可以找个解释。
Logo

更多推荐