?
11.3??GPIO驅(qū)動程序?qū)嵗?/h2>
11.3.1??GPIO工作原理
FS2410開發(fā)板的S3C2410處理器具有117個多功能通用I/O(GPIO)端口管腳,包括GPIO?8個端口組,分別為GPA(23個輸出端口)、GPB(11個輸入/輸出端口)、GPC(16個輸入/輸出端口)、GPD(16個輸入/輸出端口)、GPE(16個輸入/輸出端口)、GPF(8個輸入/輸出端口)、GPH(11個輸入/輸出端口)。根據(jù)各種系統(tǒng)設(shè)計(jì)的需求,通過軟件方法可以將這些端口配置成具有相應(yīng)功能(例如:外部中斷或數(shù)據(jù)總線)的端口。
為了控制這些端口,S3C2410處理器為每個端口組分別提供幾種相應(yīng)的控制寄存器。其中最常用的有端口配置寄存器(GPACON?~?GPHCON)和端口數(shù)據(jù)寄存器(GPADAT?~?GPHDAT)。因?yàn)榇蟛糠諭/O管腳可以提供多種功能,通過配置寄存器(PnCON)設(shè)定每個管腳用于何種目的。數(shù)據(jù)寄存器的每位將對應(yīng)于某個管腳上的輸入或輸出。所以通過對數(shù)據(jù)寄存器(PnDAT)的位讀寫,可以進(jìn)行對每個端口的輸入或輸出。
在此主要以發(fā)光二極管(LED)和蜂鳴器為例,討論GPIO設(shè)備的驅(qū)動程序。它們的硬件驅(qū)動電路的原理圖如圖11.4所示。
?????????????????
圖11.4??LED(左)和蜂鳴器(右)的驅(qū)動電路原理圖
在圖11.4中,可知使用S3C2410處理器的通用I/O口GPF4、GPF5、GPF6和GPF7分別直接驅(qū)動LED?D12、D11、D10以及D9,而使用GPB0端口驅(qū)動蜂鳴器。4個LED分別在對應(yīng)端口(GPF4~GPF7)為低電平時發(fā)亮,而蜂鳴器在GPB0為高電平時發(fā)聲。這5個端口的數(shù)據(jù)流方向均為輸出。
在表11.15中,詳細(xì)描述了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?=?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] |
每位對應(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ù)寄存器的寫操作,實(shí)現(xiàn)對每個GPIO設(shè)備的控制(發(fā)亮或發(fā)聲)。在下一個小節(jié)中介紹的驅(qū)動程序中,s3c2410_gpio_cfgpin()函數(shù)和s3c2410_gpio_pullup()函數(shù)將進(jìn)行對某個端口的配置,而s3c2410_gpio_setpin()函數(shù)實(shí)現(xiàn)向數(shù)據(jù)寄存器的某個端口的輸出。
?
11.3.2??GPIO驅(qū)動程序
GPIO驅(qū)動程序代碼如下所示:
/*?gpio_drv.h?*/
#ifndef?????FS2410_GPIO_SET_H
#define?????FS2410_GPIO_SET_H
#include????<linux/ioctl.h>
#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?<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>????/*?error?codes?*/
#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"
static?int?major?=?0;?/*?采用字符設(shè)備號的動態(tài)分配?*/
module_param(major,?int,?0);?/*?以參數(shù)的方式可以指定設(shè)備的主設(shè)備號*/
void?s3c2410_gpio_cfgpin(unsigned?int?pin,?unsigned?int?function)
{?/*?對某個管腳進(jìn)行配置(輸入/輸出/其他功能)*/
?????unsigned?long?base?=?S3C2410_GPIO_BASE(pin);?/*?獲得端口的組基地址*/
?????unsigned?long?shift?=?1;
?????unsigned?long?mask?=?0x03;?/*?通常用配置寄存器的兩位表示一個端口*/
?????unsigned?long?con;
?????unsigned?long?flags;
?????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)場?*/
}
void?s3c2410_gpio_pullup(unsigned?int?pin,?unsigned?int?to)?
{?/*?配置上拉功能?*/
????unsigned?long?base?=?S3C2410_GPIO_BASE(pin);?/*?獲得端口的組基地址*/
????unsigned?long?offs?=?S3C2410_GPIO_OFFSET(pin);/*?獲得端口的組內(nèi)偏移地址?*/
????unsigned?long?flags;
????unsigned?long?up;?
????
????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);
}
void?s3c2410_gpio_setpin(unsigned?int?pin,?unsigned?int?to)
{?/*?向某個管腳進(jìn)行輸出?*/
????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?<<?offs);
????dat?|=?to?<<?offs;
????__raw_writel(dat,?base?+?0x04);?/*?向數(shù)據(jù)寄存器寫入新數(shù)據(jù)*/
????local_irq_restore(flags);
}
int?gpio_open?(struct?inode?*inode,?struct?file?*filp)
{?/*?open操作函數(shù):進(jìn)行寄存器配置*/
????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操作函數(shù):沒有實(shí)際功能*/
????return?0;
}
ssize_t?gpio_write(struct?file?*file,?const?char?__user?*buff,?
?????????????????????????????????????????????????????size_t?count,?loff_t?*offp)
{?/*?write操作函數(shù):沒有實(shí)際功能*/
????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函數(shù)接口:主要接口的實(shí)現(xiàn)。對5個GPIO設(shè)備進(jìn)行控制(發(fā)亮或發(fā)聲)?*/
????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?commandn");
????????????break;
????????}
????}
????return?0;
}
static?int?gpio_release(struct?inode?*node,?struct?file?*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);
????return?0;
}
static?void?gpio_setup_cdev(struct?cdev?*dev,?int?minor,
????????struct?file_operations?*fops)
{?/*?字符設(shè)備的創(chuàng)建和注冊?*/
????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設(shè)備的file_operations結(jié)構(gòu)定義?*/
????.owner???=?THIS_MODULE,
????.open????=?gpio_open,????????/*?進(jìn)行初始化配置*/
????.release?=?gpio_release,????/*?關(guān)閉設(shè)備*/
????.read????=?gpio_read,????????
????.write???=?gpio_write,
????.ioctl???=?gpio_ioctl,????????/*?實(shí)現(xiàn)主要控制功能*/
};
static?struct?cdev?gpio_devs;
static?int?gpio_init(void)
{
????int?result;
????dev_t?dev?=?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:?unable?to?get?major?%dn",?major);
????????return?result;
????}
????gpio_setup_cdev(&gpio_devs,?0,?&gpio_fops);
????printk("The?major?of?the?gpio?device?is?%dn",?major);
????return?0;
}
?
static?void?gpio_cleanup(void)
{
????cdev_del(&gpio_devs);?/*?字符設(shè)備的注銷?*/
????unregister_chrdev_region(MKDEV(major,?0),?1);?/*?設(shè)備號的注銷*/
????printk("Gpio?device?uninstalledn");
}
module_init(gpio_init);
module_exit(gpio_cleanup);
MODULE_AUTHOR("David");
MODULE_LICENSE("Dual?BSD/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"
int?led_timer(int?dev_fd,?int?led_no,?unsigned?int?time)
{?/*指定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);?/*?熄滅?*/
}
int?beep_timer(int?dev_fd,?unsigned?int?time)
{/*?開蜂鳴器一段時間之后關(guān)閉*/
????ioctl(dev_fd,?BEEP_SWT,?BEEP_SWT_ON);?/*?發(fā)聲*/
????sleep(time);
????ioctl(dev_fd,?BEEP_SWT,?BEEP_SWT_OFF);????/*?關(guān)閉?*/
}
int?main()
{
????int?i?=?0;
????int?dev_fd;
????????/*?打開gpio設(shè)備?*/
????dev_fd?=?open(GPIO_DEVICE_FILENAME,?O_RDWR?|?O_NONBLOCK);
????if?(?dev_fd?==?-1?)?
????{
????????printf("Cann't?open?gpio?device?filen");
????????exit(1);
????}
????while(1)
????{
????????i?=?(i?+?1)?%?4;
????????led_timer(dev_fd,?i,?1);
????????beep_timer(dev_fd,?1);????????
????}
????close(dev_fd);
????return?0;
}???????????
具體運(yùn)行過程如下所示。首先編譯并加載驅(qū)動程序:
$?make?clean;make?/*?驅(qū)動程序的編譯*/
$?insmod?gpio_drv.ko?/*?加載gpio驅(qū)動?*/
$?cat?/proc/devices?/*?通過這個命令可以查到gpio設(shè)備的主設(shè)備號?*/
$?mknod?/dev/gpio??c??252??0??/*?假設(shè)主設(shè)備號為252,?創(chuàng)建設(shè)備文件節(jié)點(diǎn)*/
然后編譯并運(yùn)行驅(qū)動測試程序:
$?arm-linux-gcc?–o?gpio_test??gpio_test.c
$?./gpio_test
運(yùn)行結(jié)果為4個LED輪流閃爍,同時蜂鳴器以一定周期發(fā)出聲響。