当前位置:首页 > 嵌入式 > 嵌入式教程
[导读]FS2410开发板的S3C2410处理器具有117个多功能通用I/O(GPIO)端口管脚,包括GPIO 8个端口组,分别为GPA(23个输出端口)、GPB(11个输入/输出端口)、GPC(16个输入/输出端口)、GPD(16个输入/输出端口)、GPE(16个输入/输出端口)、GPF(8个输入/输出端口)、GPH(11个输入/输出端口)。

11.3GPIO驱动程序实例11.3.1GPIO工作原理

FS2410开发板的S3C2410处理器具有117个多功能通用I/O(GPIO)端口管脚,包括GPIO8个端口组,分别为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所示。

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

在图11.4中,可知使用S3C2410处理器的通用I/O口GPF4、GPF5、GPF6和GPF7分别直接驱动LEDD12、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=EINT711=保留

GPF6

[13:12]

00=输入01=输出10=EINT611=保留

GPF5

[11:10]

00=输入01=输出10=EINT511=保留

GPF4

[9:8]

00=输入01=输出10=EINT411=保留

GPF3

[7:6]

00=输入01=输出10=EINT311=保留

GPF2

[5:4]

00=输入01=输出10=EINT211=保留

GPF1

[3:2]

00=输入01=输出10=EINT111=保留

GPF0

[1:0]

00=输入01=输出10=EINT011=保留

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.2GPIO驱动程序

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

/*gpio_drv.h*/

#ifndefFS2410_GPIO_SET_H

#defineFS2410_GPIO_SET_H

#include<linux/ioctl.h>

#defineGPIO_DEVICE_NAME"gpio"

#defineGPIO_DEVICE_FILENAME"/dev/gpio"

#defineLED_NUM4

#defineGPIO_IOCTL_MAGIC'G'

#defineLED_D09_SWT_IOW(GPIO_IOCTL_MAGIC,0,unsignedint)

#defineLED_D10_SWT_IOW(GPIO_IOCTL_MAGIC,1,unsignedint)

#defineLED_D11_SWT_IOW(GPIO_IOCTL_MAGIC,2,unsignedint)

#defineLED_D12_SWT_IOW(GPIO_IOCTL_MAGIC,3,unsignedint)

#defineBEEP_SWT_IOW(GPIO_IOCTL_MAGIC,4,unsignedint)

#defineLED_SWT_ON0

#defineLED_SWT_OFF1

#defineBEEP_SWT_ON1

#defineBEEP_SWT_OFF0

#endif/*FS2410_GPIO_SET_H*/

/*gpio_drv.c*/

#include<linux/config.h>

#include<linux/module.h>

#include<linux/moduleparam.h>

#include<linux/init.h>

#include<linux/kernel.h>/*printk()*/

#include<linux/slab.h>/*kmalloc()*/

#include<linux/fs.h>/*everything...*/

#include<linux/errno.h>/*errorcodes*/

#include<linux/types.h>/*size_t*/

#include<linux/mm.h>

#include<linux/kdev_t.h>

#include<linux/cdev.h>

#include<linux/delay.h>

#include<linux/device.h>

#include<asm/io.h>

#include<asm/uaccess.h>

#include<asm/arch-s3c2410/regs-gpio.h>

#include"gpio_drv.h"

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

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

voids3c2410_gpio_cfgpin(unsignedintpin,unsignedintfunction)

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

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

unsignedlongshift=1;

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

unsignedlongcon;

unsignedlongflags;

if(pin<S3C2410_GPIO_BANKB)

{

shift=0;

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

}

mask<<=(S3C2410_GPIO_OFFSET(pin)<<shift);

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

con=__raw_readl(base+0x00);

con&=~mask;

con|=function;

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

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

}

voids3c2410_gpio_pullup(unsignedintpin,unsignedintto)

{/*配置上拉功能*/

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

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

unsignedlongflags;

unsignedlongup;

if(pin<S3C2410_GPIO_BANKB)

{

return;

}

local_irq_save(flags);

up=__raw_readl(base+0x08);

up&=~(1<<offs);

up|=to<<offs;

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

local_irq_restore(flags);

}

voids3c2410_gpio_setpin(unsignedintpin,unsignedintto)

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

unsignedlongbase=S3C2410_GPIO_BASE(pin);

unsignedlongoffs=S3C2410_GPIO_OFFSET(pin);

unsignedlongflags;

unsignedlongdat;

local_irq_save(flags);

dat=__raw_readl(base+0x04);

dat&=~(1<<offs);

dat|=to<<offs;

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

local_irq_restore(flags);

}

intgpio_open(structinode*inode,structfile*filp)

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

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

s3c2410_gpio_pullup(S3C2410_GPF4,1);/*LEDD12*/

s3c2410_gpio_pullup(S3C2410_GPF5,1);/*LEDD11*/

s3c2410_gpio_pullup(S3C2410_GPF6,1);/*LEDD10*/

s3c2410_gpio_pullup(S3C2410_GPF7,1);/*LEDD9*/

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);

return0;

}

ssize_tgpio_read(structfile*file,char__user*buff,

size_tcount,loff_t*offp)

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

return0;

}

ssize_tgpio_write(structfile*file,constchar__user*buff,

size_tcount,loff_t*offp)

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

return0;

}

intswitch_gpio(unsignedintpin,unsignedintswt)

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

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

&&(pin!=S3C2410_GPB0))

{

printk("Unsupportedpin");

return1;

}

s3c2410_gpio_setpin(pin,swt);

return0;

}

staticintgpio_ioctl(structinode*inode,structfile*file,

unsignedintcmd,unsignedlongarg)

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

unsignedintswt=(unsignedint)arg;

switch(cmd)

{

caseLED_D09_SWT:

{

switch_gpio(S3C2410_GPF7,swt);

}

break;

caseLED_D10_SWT:

{

switch_gpio(S3C2410_GPF6,swt);

}

break;

caseLED_D11_SWT:

{

switch_gpio(S3C2410_GPF5,swt);

}

break;

caseLED_D12_SWT:

{

switch_gpio(S3C2410_GPF4,swt);

}

break;

caseBEEP_SWT:

{

switch_gpio(S3C2410_GPB0,swt);

break;

}

default:

{

printk("Unsupportedcommandn");

break;

}

}

return0;

}

staticintgpio_release(structinode*node,structfile*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);

return0;

}

staticvoidgpio_setup_cdev(structcdev*dev,intminor,

structfile_operations*fops)

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

interr,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%daddinggpio%d",err,minor);

}

}

staticstructfile_operationsgpio_fops=

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

.owner=THIS_MODULE,

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

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

.read=gpio_read,

.write=gpio_write,

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

};

staticstructcdevgpio_devs;

staticintgpio_init(void)

{

intresult;

dev_tdev=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<0)

{

printk(KERN_WARNING"Gpio:unabletogetmajor%dn",major);

returnresult;

}

gpio_setup_cdev(&gpio_devs,0,&gpio_fops);

printk("Themajorofthegpiodeviceis%dn",major);

return0;

}

staticvoidgpio_cleanup(void)

{

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

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

printk("Gpiodeviceuninstalledn");

}

module_init(gpio_init);

module_exit(gpio_cleanup);

MODULE_AUTHOR("David");

MODULE_LICENSE("DualBSD/GPL");

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

/*gpio_test.c*/

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<fcntl.h>

#include<string.h>

#include<sys/types.h>

#include<sys/stat.h>

#include"gpio_drv.h"

intled_timer(intdev_fd,intled_no,unsignedinttime)

{/*指定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);/*熄灭*/

}

intbeep_timer(intdev_fd,unsignedinttime)

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

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

sleep(time);

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

}

intmain()

{

inti=0;

intdev_fd;

/*打开gpio设备*/

dev_fd=open(GPIO_DEVICE_FILENAME,O_RDWR|O_NONBLOCK);

if(dev_fd==-1)

{

printf("Cann'topengpiodevicefilen");

exit(1);

}

while(1)

{

i=(i+1)%4;

led_timer(dev_fd,i,1);

beep_timer(dev_fd,1);

}

close(dev_fd);

return0;

}

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

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

$insmodgpio_drv.ko/*加载gpio驱动*/

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

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

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

$arm-linux-gcc–ogpio_testgpio_test.c

$./gpio_test

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

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭