嵌入式Linux設(shè)備驅(qū)動開發(fā)之:GPIO驅(qū)動程序?qū)嵗?/h1>
時間:2018-06-15 10:00:01
關(guān)鍵字:
基礎(chǔ)教程
嵌入式linux
操作系統(tǒng)
設(shè)備驅(qū)動
gpio驅(qū)動程序
手機看文章掃描二維碼
隨時隨地手機看文章
掃描二維碼
隨時隨地手機看文章
FS2410開發(fā)板的S3C2410處理器具有117個多功能通用I/O(GPIO)端口管腳,包括GPIO8個端口組,分別為GPA(23個輸出端口)、GPB(11個輸入/輸出端口)、GPC(16個輸入/輸出端口)、GPD(16個輸入/輸出端口)、GPE(16個輸入/輸出端口)、GPF(8個輸入/輸出端口)、GPH(11個輸入/輸出端口)。根據(jù)各種系統(tǒng)設(shè)計的需求,通過軟件方法可以將這些端口配置成具有相應(yīng)功能(例如:外部中斷或數(shù)據(jù)總線)的端口。
為了控制這些端口,S3C2410處理器為每個端口組分別提供幾種相應(yīng)的控制寄存器。其中最常用的有端口配置寄存器(GPACON~GPHCON)和端口數(shù)據(jù)寄存器(GPADAT~GPHDAT)。因為大部分I/O管腳可以提供多種功能,通過配置寄存器(PnCON)設(shè)定每個管腳用于何種目的。數(shù)據(jù)寄存器的每位將對應(yīng)于某個管腳上的輸入或輸出。所以通過對數(shù)據(jù)寄存器(PnDAT)的位讀寫,可以進行對每個端口的輸入或輸出。
在此主要以發(fā)光二極管(LED)和蜂鳴器為例,討論GPIO設(shè)備的驅(qū)動程序。它們的硬件驅(qū)動電路的原理圖如圖11.4所示。
圖11.4LED(左)和蜂鳴器(右)的驅(qū)動電路原理圖
在圖11.4中,可知使用S3C2410處理器的通用I/O口GPF4、GPF5、GPF6和GPF7分別直接驅(qū)動LEDD12、D11、D10以及D9,而使用GPB0端口驅(qū)動蜂鳴器。4個LED分別在對應(yīng)端口(GPF4~GPF7)為低電平時發(fā)亮,而蜂鳴器在GPB0為高電平時發(fā)聲。這5個端口的數(shù)據(jù)流方向均為輸出。
在表11.15中,詳細描述了GPF的主要控制寄存器。GPB的相關(guān)寄存器的描述與此類似,具體可以參考S3C2410處理器數(shù)據(jù)手冊。
表11.15 GPF端口(GPF0-GPF7)的主要控制寄存器
寄存器
地址
R/W
功能
初始值
GPFCON
0x56000050
R/W
配置GPF端口組
0x0
GPFDAT
0x56000054
R/W
GPF端口的數(shù)據(jù)寄存器
未定義
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]
每位對應(yīng)于相應(yīng)的端口,若端口用于輸入,則可以通過相應(yīng)的位讀取數(shù)據(jù);若端口用于輸出,則可以通過相應(yīng)的位輸出數(shù)據(jù);若端口用于其他功能,則其值無法確定。
GPFUP
位
描述
GPF[7:0]
[7:0]
0:向相應(yīng)端口管腳賦予上拉(pull-up)功能
1:取消上拉功能
為了驅(qū)動LED和蜂鳴器,首先通過端口配置寄存器將5個相應(yīng)寄存器配置為輸出模式。然后通過對端口數(shù)據(jù)寄存器的寫操作,實現(xiàn)對每個GPIO設(shè)備的控制(發(fā)亮或發(fā)聲)。在下一個小節(jié)中介紹的驅(qū)動程序中,s3c2410_gpio_cfgpin()函數(shù)和s3c2410_gpio_pullup()函數(shù)將進行對某個端口的配置,而s3c2410_gpio_setpin()函數(shù)實現(xiàn)向數(shù)據(jù)寄存器的某個端口的輸出。
11.3.2GPIO驅(qū)動程序GPIO驅(qū)動程序代碼如下所示:
/*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;/*采用字符設(shè)備號的動態(tài)分配*/
module_param(major,int,0);/*以參數(shù)的方式可以指定設(shè)備的主設(shè)備號*/
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);/*保存現(xiàn)場,保證下面一段是原子操作*/
con=__raw_readl(base+0x00);
con&=~mask;
con|=function;
__raw_writel(con,base+0x00);/*向配置寄存器寫入新配置數(shù)據(jù)*/
local_irq_restore(flags);/*恢復(fù)現(xiàn)場*/
}
voids3c2410_gpio_pullup(unsignedintpin,unsignedintto)
{/*配置上拉功能*/
unsignedlongbase=S3C2410_GPIO_BASE(pin);/*獲得端口的組基地址*/
unsignedlongoffs=S3C2410_GPIO_OFFSET(pin);/*獲得端口的組內(nèi)偏移地址*/
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);/*向上拉功能寄存器寫入新配置數(shù)據(jù)*/
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);/*向數(shù)據(jù)寄存器寫入新數(shù)據(jù)*/
local_irq_restore(flags);
}
intgpio_open(structinode*inode,structfile*filp)
{/*open操作函數(shù):進行寄存器配置*/
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操作函數(shù):沒有實際功能*/
return0;
}
ssize_tgpio_write(structfile*file,constchar__user*buff,
size_tcount,loff_t*offp)
{/*write操作函數(shù):沒有實際功能*/
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函數(shù)接口:主要接口的實現(xiàn)。對5個GPIO設(shè)備進行控制(發(fā)亮或發(fā)聲)*/
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操作函數(shù),熄滅所有燈和關(guān)閉蜂鳴器*/
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)
{/*字符設(shè)備的創(chuàng)建和注冊*/
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設(shè)備的file_operations結(jié)構(gòu)定義*/
.owner=THIS_MODULE,
.open=gpio_open,/*進行初始化配置*/
.release=gpio_release,/*關(guān)閉設(shè)備*/
.read=gpio_read,
.write=gpio_write,
.ioctl=gpio_ioctl,/*實現(xiàn)主要控制功能*/
};
staticstructcdevgpio_devs;
staticintgpio_init(void)
{
intresult;
dev_tdev=MKDEV(major,0);
if(major)
{/*設(shè)備號的動態(tài)分配*/
result=register_chrdev_region(dev,1,GPIO_DEVICE_NAME);
}
else
{/*設(shè)備號的動態(tài)分配*/
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);/*字符設(shè)備的注銷*/
unregister_chrdev_region(MKDEV(major,0),1);/*設(shè)備號的注銷*/
printk("Gpiodeviceuninstalledn");
}
module_init(gpio_init);
module_exit(gpio_cleanup);
MODULE_AUTHOR("David");
MODULE_LICENSE("DualBSD/GPL");
下面列出GPIO驅(qū)動程序的測試用例:
/*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發(fā)亮一段時間之后熄滅它*/
led_no%=4;
ioctl(dev_fd,LED_D09_SWT+led_no,LED_SWT_ON);/*發(fā)亮*/
sleep(time);
ioctl(dev_fd,LED_D09_SWT+led_no,LED_SWT_OFF);/*熄滅*/
}
intbeep_timer(intdev_fd,unsignedinttime)
{/*開蜂鳴器一段時間之后關(guān)閉*/
ioctl(dev_fd,BEEP_SWT,BEEP_SWT_ON);/*發(fā)聲*/
sleep(time);
ioctl(dev_fd,BEEP_SWT,BEEP_SWT_OFF);/*關(guān)閉*/
}
intmain()
{
inti=0;
intdev_fd;
/*打開gpio設(shè)備*/
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;
}
具體運行過程如下所示。首先編譯并加載驅(qū)動程序:
$makeclean;make/*驅(qū)動程序的編譯*/
$insmodgpio_drv.ko/*加載gpio驅(qū)動*/
$cat/proc/devices/*通過這個命令可以查到gpio設(shè)備的主設(shè)備號*/
$mknod/dev/gpioc2520/*假設(shè)主設(shè)備號為252,創(chuàng)建設(shè)備文件節(jié)點*/
然后編譯并運行驅(qū)動測試程序:
$arm-linux-gcc–ogpio_testgpio_test.c
$./gpio_test
運行結(jié)果為4個LED輪流閃爍,同時蜂鳴器以一定周期發(fā)出聲響。