linux gpio设备驱动程序,嵌入式Linux设备驱动开发之:GPIO驱动程序实例-嵌入式系统-与非网...

11.3  GPIO驱动程序实例

11.3.1  GPIO工作原理

FS2410开发板的S3C2410处理器具有117个多功能通用I/O(GPIO)端口管脚,包括GPIO 8个端口组,分别为GPA(23个输出端口)、GPB(11个输入/输出端口)、GPC(16个输入/输出端口)、GPD(16个输入/输出端口)、GPE(16个输入/输出端口)、GPF(8个输入/输出端口)、GPH(11个输入/输出端口)。根据各种系统设计的需求,通过软件方法可以将这些端口配置成具有相应功能(例如:外部中断或数据总线)的端口。

为了控制这些端口,S3C2410处理器为每个端口组分别提供几种相应的控制寄存器。其中最常用的有端口配置寄存器(GPACON ~ GPHCON)和端口数据寄存器(GPADAT ~ GPHDAT)。因为大部分I/O管脚可以提供多种功能,通过配置寄存器(PnCON)设定每个管脚用于何种目的。数据寄存器的每位将对应于某个管脚上的输入或输出。所以通过对数据寄存器(PnDAT)的位读写,可以进行对每个端口的输入或输出。

在此主要以发光二极管(LED)和蜂鸣器为例,讨论GPIO设备的驱动程序。它们的硬件驱动电路的原理图如图11.4所示。

61f5bb12ea32cbcf09583e55fe3a8d86.png                 

图11.4  LED(左)和蜂鸣器(右)的驱动电路原理图

在图11.4中,可知使用S3C2410处理器的通用I/O口GPF4、GPF5、GPF6和GPF7分别直接驱动LED D12、D11、D10以及D9,而使用GPB0端口驱动蜂鸣器。4个LED分别在对应端口(GPF4~GPF7)为低电平时发亮,而蜂鸣器在GPB0为高电平时发声。这5个端口的数据流方向均为输出。

在表11.15中,详细描述了GPF的主要控制寄存器。GPB的相关寄存器的描述与此类似,具体可以参考S3C2410处理器数据手册。

表11.15 GPF端口(GPF0-GPF7)的主要控制寄存器

寄存器

地址

R/W

功能

初始值

GPFCON

0x56000050

R/W

配置GPF端口组

0x0

GPFDAT

0x56000054

R/W

GPF端口的数据寄存器

未定义

GPFUP

0x56000058

R/W

GPF端口的取消上拉寄存器

0x0

GPFCON

描述

GPF7

[15:14]

00 = 输入  01 = 输出  10 = EINT7  11 = 保留

GPF6

[13:12]

00 = 输入  01 = 输出  10 = EINT6  11 = 保留

GPF5

[11:10]

00 = 输入  01 = 输出  10 = EINT5  11 = 保留

GPF4

[9:8]

00 = 输入  01 = 输出  10 = EINT4  11 = 保留

GPF3

[7:6]

00 = 输入  01 = 输出  10 = EINT3  11 = 保留

GPF2

[5:4]

00 = 输入  01 = 输出  10 = EINT2  11 = 保留

GPF1

[3:2]

00 = 输入  01 = 输出  10 = EINT1  11 = 保留

GPF0

[1:0]

00 = 输入  01 = 输出  10 = EINT0  11 = 保留

GPFDAT

描述

GPF[7:0]

[7:0]

每位对应于相应的端口,若端口用于输入,则可以通过相应的位读取数据;若端口用于输出,则可以通过相应的位输出数据;若端口用于其他功能,则其值无法确定。

GPFUP

描述

GPF[7:0]

[7:0]

0:向相应端口管脚赋予上拉(pull-up)功能

1:取消上拉功能

为了驱动LED和蜂鸣器,首先通过端口配置寄存器将5个相应寄存器配置为输出模式。然后通过对端口数据寄存器的写操作,实现对每个GPIO设备的控制(发亮或发声)。在下一个小节中介绍的驱动程序中,s3c2410_gpio_cfgpin()函数和s3c2410_gpio_pullup()函数将进行对某个端口的配置,而s3c2410_gpio_setpin()函数实现向数据寄存器的某个端口的输出。

11.3.2  GPIO驱动程序

GPIO驱动程序代码如下所示:

/* gpio_drv.h */

#ifndef     FS2410_GPIO_SET_H

#define     FS2410_GPIO_SET_H

#include    

#define     GPIO_DEVICE_NAME       "gpio"

#define     GPIO_DEVICE_FILENAME  "/dev/gpio"

#define     LED_NUM                  4

#define     GPIO_IOCTL_MAGIC       'G'

#define     LED_D09_SWT             _IOW(GPIO_IOCTL_MAGIC, 0, unsigned int)

#define     LED_D10_SWT             _IOW(GPIO_IOCTL_MAGIC, 1, unsigned int)

#define     LED_D11_SWT             _IOW(GPIO_IOCTL_MAGIC, 2, unsigned int)

#define     LED_D12_SWT             _IOW(GPIO_IOCTL_MAGIC, 3, unsigned int)

#define     BEEP_SWT                 _IOW(GPIO_IOCTL_MAGIC, 4, unsigned int)

#define     LED_SWT_ON              0

#define     LED_SWT_OFF             1

#define     BEEP_SWT_ON             1

#define     BEEP_SWT_OFF            0

#endif /* FS2410_GPIO_SET_H */

/* gpio_drv.c */

#include 

#include 

#include 

#include 

#include    /* printk() */

#include         /* kmalloc() */

#include        /* everything... */

#include     /* error codes */

#include     /* size_t */

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include "gpio_drv.h"

static int major = 0; /* 采用字符设备号的动态分配 */

module_param(major, int, 0); /* 以参数的方式可以指定设备的主设备号*/

void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function)

{ /* 对某个管脚进行配置(输入/输出/其他功能)*/

unsigned long base = S3C2410_GPIO_BASE(pin); /* 获得端口的组基地址*/

unsigned long shift = 1;

unsigned long mask = 0x03; /* 通常用配置寄存器的两位表示一个端口*/

unsigned long con;

unsigned long flags;

if (pin 

{

shift = 0;

mask  = 0x01; /* 在GPA端口中用配置寄存器的一位表示一个端口*/

}

mask <<= (S3C2410_GPIO_OFFSET(pin) <

local_irq_save(flags); /* 保存现场,保证下面一段是原子操作 */

con = __raw_readl(base + 0x00);

con &= ~mask;

con |= function;

__raw_writel(con, base + 0x00); /* 向配置寄存器写入新配置数据 */

local_irq_restore(flags); /* 恢复现场 */

}

void s3c2410_gpio_pullup(unsigned int pin, unsigned int to)

{ /* 配置上拉功能 */

unsigned long base = S3C2410_GPIO_BASE(pin); /* 获得端口的组基地址*/

unsigned long offs = S3C2410_GPIO_OFFSET(pin);/* 获得端口的组内偏移地址 */

unsigned long flags;

unsigned long up;

if (pin 

{

return;

}

local_irq_save(flags);

up = __raw_readl(base + 0x08);

up &= ~(1 <

up |= to <

__raw_writel(up, base + 0x08); /* 向上拉功能寄存器写入新配置数据*/

local_irq_restore(flags);

}

void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)

{ /* 向某个管脚进行输出 */

unsigned long base = S3C2410_GPIO_BASE(pin);

unsigned long offs = S3C2410_GPIO_OFFSET(pin);

unsigned long flags;

unsigned long dat;

local_irq_save(flags);

dat = __raw_readl(base + 0x04);

dat &= ~(1 <

dat |= to <

__raw_writel(dat, base + 0x04); /* 向数据寄存器写入新数据*/

local_irq_restore(flags);

}

int gpio_open (struct inode *inode, struct file *filp)

{ /* open操作函数:进行寄存器配置*/

s3c2410_gpio_pullup(S3C2410_GPB0, 1); /* BEEP*/

s3c2410_gpio_pullup(S3C2410_GPF4, 1); /* LED D12 */

s3c2410_gpio_pullup(S3C2410_GPF5, 1); /* LED D11 */

s3c2410_gpio_pullup(S3C2410_GPF6, 1); /* LED D10 */

s3c2410_gpio_pullup(S3C2410_GPF7, 1); /* LED D9 */

s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_OUTP);

s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP);

s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF5_OUTP);

s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF6_OUTP);

s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF7_OUTP);

return 0;

}

ssize_t gpio_read(struct file *file, char __user *buff,

size_t count, loff_t *offp)

{ /* read操作函数:没有实际功能*/

return 0;

}

ssize_t gpio_write(struct file *file, const char __user *buff,

size_t count, loff_t *offp)

{ /* write操作函数:没有实际功能*/

return 0;

}

int switch_gpio(unsigned int pin, unsigned int swt)

{ /* 向5个端口中的一个输出ON/OFF值 */

if (!((pin <= S3C2410_GPF7) && (pin >= S3C2410_GPF4))

&& (pin != S3C2410_GPB0))

{

printk("Unsupported pin");

return 1;

}

s3c2410_gpio_setpin(pin, swt);

return 0;

}

static int gpio_ioctl(struct inode *inode, struct file *file,

unsigned int cmd, unsigned long arg)

{ /* ioctl函数接口:主要接口的实现。对5个GPIO设备进行控制(发亮或发声) */

unsigned int swt = (unsigned int)arg;

switch (cmd)

{

case LED_D09_SWT:

{

switch_gpio(S3C2410_GPF7, swt);

}

break;

case LED_D10_SWT:

{

switch_gpio(S3C2410_GPF6, swt);

}

break;

case LED_D11_SWT:

{

switch_gpio(S3C2410_GPF5, swt);

}

break;

case LED_D12_SWT:

{

switch_gpio(S3C2410_GPF4, swt);

}

break;

case BEEP_SWT:

{

switch_gpio(S3C2410_GPB0, swt);

break;

}

default:

{

printk("Unsupported command\n");

break;

}

}

return 0;

}

static int gpio_release(struct inode *node, struct file *file)

{ /* release操作函数,熄灭所有灯和关闭蜂鸣器 */

switch_gpio(S3C2410_GPB0, BEEP_SWT_OFF);

switch_gpio(S3C2410_GPF4, LED_SWT_OFF);

switch_gpio(S3C2410_GPF5, LED_SWT_OFF);

switch_gpio(S3C2410_GPF6, LED_SWT_OFF);

switch_gpio(S3C2410_GPF7, LED_SWT_OFF);

return 0;

}

static void gpio_setup_cdev(struct cdev *dev, int minor,

struct file_operations *fops)

{ /* 字符设备的创建和注册 */

int err, devno = MKDEV(major, minor);

cdev_init(dev, fops);

dev->owner = THIS_MODULE;

dev->ops = fops;

err = cdev_add (dev, devno, 1);

if (err)

{

printk (KERN_NOTICE "Error %d adding gpio %d", err, minor);

}

}

static struct file_operations gpio_fops =

{ /* gpio设备的file_operations结构定义 */

.owner   = THIS_MODULE,

.open    = gpio_open,        /* 进行初始化配置*/

.release = gpio_release,    /* 关闭设备*/

.read    = gpio_read,

.write   = gpio_write,

.ioctl   = gpio_ioctl,        /* 实现主要控制功能*/

};

static struct cdev gpio_devs;

static int gpio_init(void)

{

int result;

dev_t dev = MKDEV(major, 0);

if (major)

{ /* 设备号的动态分配 */

result = register_chrdev_region(dev, 1, GPIO_DEVICE_NAME);

}

else

{ /* 设备号的动态分配 */

result = alloc_chrdev_region(&dev, 0, 1, GPIO_DEVICE_NAME);

major = MAJOR(dev);

}

if (result 

{

printk(KERN_WARNING "Gpio: unable to get major %d\n", major);

return result;

}

gpio_setup_cdev(&gpio_devs, 0, &gpio_fops);

printk("The major of the gpio device is %d\n", major);

return 0;

}

static void gpio_cleanup(void)

{

cdev_del(&gpio_devs); /* 字符设备的注销 */

unregister_chrdev_region(MKDEV(major, 0), 1); /* 设备号的注销*/

printk("Gpio device uninstalled\n");

}

module_init(gpio_init);

module_exit(gpio_cleanup);

MODULE_AUTHOR("David");

MODULE_LICENSE("Dual BSD/GPL");

下面列出GPIO驱动程序的测试用例:

/* gpio_test.c */

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include "gpio_drv.h"

int led_timer(int dev_fd, int led_no, unsigned int time)

{ /*指定LED发亮一段时间之后熄灭它*/

led_no %= 4;

ioctl(dev_fd, LED_D09_SWT + led_no, LED_SWT_ON); /* 发亮*/

sleep(time);

ioctl(dev_fd, LED_D09_SWT + led_no, LED_SWT_OFF); /* 熄灭 */

}

int beep_timer(int dev_fd, unsigned int time)

{/* 开蜂鸣器一段时间之后关闭*/

ioctl(dev_fd, BEEP_SWT, BEEP_SWT_ON); /* 发声*/

sleep(time);

ioctl(dev_fd, BEEP_SWT, BEEP_SWT_OFF);    /* 关闭 */

}

int main()

{

int i = 0;

int dev_fd;

/* 打开gpio设备 */

dev_fd = open(GPIO_DEVICE_FILENAME, O_RDWR | O_NONBLOCK);

if ( dev_fd == -1 )

{

printf("Cann't open gpio device file\n");

exit(1);

}

while(1)

{

i = (i + 1) % 4;

led_timer(dev_fd, i, 1);

beep_timer(dev_fd, 1);

}

close(dev_fd);

return 0;

}

具体运行过程如下所示。首先编译并加载驱动程序:

$ make clean;make /* 驱动程序的编译*/

$ insmod gpio_drv.ko /* 加载gpio驱动 */

$ cat /proc/devices /* 通过这个命令可以查到gpio设备的主设备号 */

$ mknod /dev/gpio  c  252  0  /* 假设主设备号为252, 创建设备文件节点*/

然后编译并运行驱动测试程序:

$ arm-linux-gcc –o gpio_test  gpio_test.c

$ ./gpio_test

运行结果为4个LED轮流闪烁,同时蜂鸣器以一定周期发出声响。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/486165.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

它将改变一切:AI解决了生物学50年来的重大难题

图片来源&#xff1a;Pixabay来源 公众号“机器之心”CASP14 组织者、年近七旬的 UC Davis 科学家 Andriy Kryshtafovych 在大会上感叹道&#xff0c;I wasnt sure that I would live long enough to see this&#xff08;我活久见了&#xff09;[1]。11 月 30 日&#xff0c;…

教你彻底学会动态规划——入门篇

动态规划相信大家都知道&#xff0c;动态规划算法也是新手在刚接触算法设计时很苦恼的问题&#xff0c;有时候觉得难以理解&#xff0c;但是真正理解之后&#xff0c;就会觉得动态规划其实并没有想象中那么难。网上也有很多关于讲解动态规划的文章&#xff0c;大多都是叙述概念…

linux终端密码星星,如何获得您的sudo密码在Ubuntu中显示为星号 | MOS86

我的一个朋友最近从Windows切换到Ubuntu Linux。在新操作系统花了一个星期左右的时间&#xff0c;他提出了一个问题109mh1112虽然这确实导致了这里和那里的一些打字错误&#xff0c;一次输入正确的sudo密码已被证明是非常有挑战性的&#xff0c;主要是因为在输入密码时没有显示…

正则 null_正则表达式exec、match、test的区别

一、定义的不同RegExp.prototype.test()RegExp.prototype.exec()String.prototype.match()从MDN的定义可以看出&#xff0c;test和exec是正则实例的API&#xff0c;match是String的&#xff0c;这一点决定了调用方式的不同。二、应用场景的不同如果只是想要判断正则表达式和字符…

Gartner发布2021年重要战略科技趋势!

来源&#xff1a;Gartner不久前&#xff0c;全球领先的信息技术研究和顾问公司Gartner发布企业机构在2021年需要深挖的重要战略科技趋势。分析师们在举行的Gartner IT Symposium/Xpo大会美洲站虚拟会议上展示了自己的发现。Gartner研究副总裁Brian Burke表示&#xff1a;“各企…

linux at24测试程序,linux 2.6下eeprom at24c08 i2c设备驱动(new style probe方式)

1 修改bsp_以便支持probe1.1 AT24C08地址的确定原理图上将A2、A1、A0都接地了&#xff0c;所以地址是0x50。注意到是7位(bit).1.2 修改bsp采用友善之臂的, 2.6.32.2内核[rootlocalhost mach-s3c2440]# vim/opt/FriendlyARM/mini2440/linux-2.6.32.2/arch/arm/mach-s3c2440/mach…

Java中注释的使用

如何在Java中使用注释 在编写程序时&#xff0c;经常需要添加一些注释&#xff0c;用以描述某段代码的作用。 一般来说&#xff0c;对于一份规范的程序源代码而言&#xff0c;注释应该占到源代码的 1/3 以上。因此&#xff0c;注释是程序源代码的重要组成部分&#xff0c;一定要…

关于动态规划,你想知道的都在这里了!

作者 | Your DevOps Guy翻译| 火火酱~&#xff0c;责编 | 晋兆雨出品 | AI科技大本营头图 | 付费下载于视觉中国什么是动态规划&#xff1f;它又有什么重要的呢&#xff1f;在本文中&#xff0c;我将介绍由Richard Bellman在20世纪50年代提出的动态规划&#xff08;dynamic pro…

linux修改永久ip地址,centos设置IP地址,永久修改ipv4

# ifconfig #查看下本机的IP地址。eth0Link encap:Ethernet HWaddr 00:50:56:0A:0B:0Cinet addr:192.168.0.3 Bcast:192.168.0.255 Mask:255.255.255.0inet6 addr: fe80::250:56ff:fe0a:b0c/64 Scope:LinkUP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1RX packets:172220…

详细介绍MySQL/MariaDB的锁

官方手册&#xff1a;https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-transaction-model.html 1.事务提交的方式 在MariaDB/MySQL中有3种事务提交的方式。 1.显式开启和提交。 使用begin或者start transaction来显式开启一个事务&#xff0c;显式开启的事务必须使用c…

美国专利商标局发布人工智能专利扩散分析报告

以下文章来源&#xff1a;中科院知识产权信息&#xff0c;2020-11-23报告显示&#xff0c;从2002到2018年&#xff0c;美国人工智能专利的年申请量增长超过100%&#xff0c;从每年3万件增加到6万多件&#xff0c;含人工智能的专利申请所占份额从9%上升到近16%。同时&#xff0c…

java 开发环境的搭建

这里主要说的是在windows 环境下怎么配置环境。 1.首先安装JDK java的sdk简称JDK &#xff0c;去其官方网站下载最近的JDK即可。。http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html点击下载好的exe文件安装即可。 2.接下来我们需要配置环…

linux内核等价多路径路由,Linux内核分析 - 网络[四]:路由表

路由表的创建inet_init() -> ip_init() -> ip_fib_init() -> fib_net_init() -> ip_fib_net_init()[net\ipv4\fib_frontend.c]首先为路由表分配空间&#xff0c;这里的每个表项hlist_head实际都会链接一个单独的路由表&#xff0c;FIB_TABLE_HASHSZ表示了分配多少个…

2017级面向对象程序设计 作业二

以下均以扫描方式为例&#xff0c;即电梯只会在最底层和最高层选择掉头&#xff0c;路途中遇到路径方向相同的乘客将他带上电梯。 文字描述面向过程实现的步骤&#xff1a; 一. 定义有关电梯的变量&#xff0c;如&#xff1a;1.电梯当前所在楼层.&#xff0c;2. 电梯内的人数&a…

新型支架状电极允许人类思想操作计算机

Illustration: Synchron来源&#xff1a;IEEE电气电子工程师据悉&#xff0c;两名患有神经肌肉疾病的澳大利亚人在他们的大脑中植入了支架状的电极&#xff0c;使他们能够利用自己的思想操作电脑&#xff0c;从而恢复了一些个人独立性。据发明者介绍&#xff0c;这是这种被称为…

java中的foreach

foreach 并不是java中的关键字&#xff0c;是for语句的特殊简化版&#xff0c;在比那里数组&#xff0c;集合时&#xff0c;foreach更加简单快捷&#xff0c;从字面上的意思理解 foreach 也就是 “ for每一个 ”的意思&#xff0c;那么到底怎么使用 foreach语句呢&#xff1f; …

ACM数论-素数

ACM数论——素数 素数定义&#xff1a; 质数&#xff08;prime number&#xff09;又称素数&#xff0c;有无限个。质数定义为在大于1的自然数中&#xff0c;除了1和它本身以外不再有其他因数&#xff0c;这样的数称为质数。例 子&#xff1a;2、3、5、7、11、13、17、19。&am…

机器视觉中彩色成像必须考虑的十个问题

来源&#xff1a;Imagination Tech在为你的产品开发最适合的机器视觉系统时&#xff0c;需要考虑很多因素&#xff0c;以下列出开发过程中需要考虑的一些问题&#xff1a;颜色准确性/差异化首先要考虑的是应用程序所需的颜色精度和差异程度。在某些应用中&#xff0c;机器视觉相…

嫦娥“挖土”归来有多难?看看中国首颗返回式卫星的故事

本文转载自“科技日报&#xff08;kjrbwx&#xff09;”&#xff0c;原标题《嫦娥“挖土”归来有多难&#xff1f;看看中国首颗返回式卫星的故事》&#xff0c;作者 | 吕炳宏 付毅飞2020年11月30日&#xff0c;嫦娥五号探测器在环月轨道上&#xff0c;成功实施着陆器上升器组合…

重磅,2020年度第十届吴文俊人工智能科学技术奖获奖名单公示

来源&#xff1a;科奖圈根据《吴文俊人工智能科学技术奖励条例》和《吴文俊人工智能科学技术奖励实施细则》相关规定&#xff0c;经全国各地方人工智能学会、协会及联盟&#xff0c;各高校及科研&#xff08;院&#xff09;所&#xff0c;学会各专业委员会及工作委会&#xff0…