Chinaunix首页 | 论坛 | 博客
  • 博客访问: 268350
  • 博文数量: 74
  • 博客积分: 1336
  • 博客等级: 中尉
  • 技术积分: 1057
  • 用 户 组: 普通用户
  • 注册时间: 2009-07-02 09:33
文章分类

全部博文(74)

文章存档

2016年(6)

2015年(4)

2014年(20)

2013年(8)

2012年(16)

2010年(9)

2009年(11)

我的朋友

分类: LINUX

2012-08-03 17:45:44

设备有一组外部寄存器用来存储和控制设备的状态。存储设备状态的寄存器叫做数据寄存器;控制设备状态的寄存器叫做控制寄存器。这些寄存器可能位于内存空间,也可能位于IO空间。无论是内存地址空间还是IO地址空间,这些寄存器的访问都是连续的。一般台式机在设计时,因为内存地址空间比较紧张,所以一般将外部设备连接到IO地址空间上。对于嵌入式设备将外部设备连接到多余的内存空间上。

     当一个寄存器或内存位于IO空间时,称其为IO端口。当一个寄存器或内存位于内存空间时,称其为IO内存。

1、以IO端口方式控制LED

I/O端口的操作需按如下步骤完成:

1)    申请

n   struct resource *request_region(unsigned long first,unsigned long n, const char *name)

这个函数告诉内核,你要使用从 first 开始的n个端口,name参数是设备的名字。如果申请成功,返回非 NULL,申请失败,返回 NULL

2)    访问

I/O端口可分为8-, 16-位和 32-位端口。Linux内核头文件(体系依赖的头文件 ) 定义了下列内联函数来访问 I/O 端口:

n   unsigned inb(unsigned port)读字节端口( 8位宽 )

n   void outb(unsigned char byte,unsigned port)写字节端口( 8位宽 )

inboutb是读写8位端口的函数,inb()函数的第1个参数是端口号,outb()函数的第1个参数是要写入的数据,第2个参数是端口号。

n   unsigned inw(unsigned port)

n   void outw(unsigned short word,unsigned port)存取 16-位 端口。

inwoutw是读写16位端口的函数,inw()函数的第1个参数是端口号,outw()函数的第1个参数是要写入的数据,第2个参数是端口号。

n   unsigned inl(unsigned port)

n   void outl(unsigned longword,unsigned port)存取 32-位端口。

inloutl是读写32位端口的函数,inl()函数的第1个参数是端口号,outl()函数的第1个参数是要写入的数据,第2个参数是端口号。

3)    释放

当用完一组 I/O 端口(通常在驱动卸载时),应使用如下函数把它们返还给系统:

n   void release_region(unsigned long start, unsigned long n)

start是要使用的IO端口,第2个参数表示从start开始的n个端口。

4)    LED相关端口寄存器:

如图1-27所示,四个发光二极管接到了S3C2410F组端口GPF4~GPF7上面,当GPF4~GPF7引脚为低电平时,点亮发光二极管。

端口是具有有限存储容量的高速存储部件,也叫寄存器,存储容量一般为81632位。其可以用来存储指令,数据和地址。对硬件设备的操作一般是通过软件方法读取相应寄存器的状态来实现的。下面介绍与发光二极管相关的F端口寄存器,本内容也可以参考三星公司的S3C2410的数据手册。

F端口有三个控制寄存器,分别为GPFCONGPFDATGPGUP。该端口各寄存器的地址,读写要求如表4-1所示。

4-1 端口F控制寄存器

端口F控制寄存器

寄存器

地址

R/W

描述

复位值

GPFCON

0x56000050

R/W

端口F的配置寄存器

0x0

GPFDAT

0x56000054

R/W

端口F的数据寄存器

Undefined

GPFUP

0x56000058

R/W

端口F的上拉寄存器

0x0

   

a)    GPFCON是配置寄存器,在S3C2410中,大多数引脚是功能复用的。一个引脚可以配置成输入、输出或者其他功能。GPFCON就是选择一个功能,GPF组端口共有8个引脚,每一个引脚有4***能:分别是数据输入、数据输出、中断和保留。GPFCON的每两位可以取值00011011,表示不同的功能。

b)    GPFDAT是数据寄存器。用于记录引脚的状态,寄存器的每一位表示一个引脚的状态,当引脚被GPFCON设置为输入时,读取该寄存器可以获得相应位的状态值;当引脚被GPFCON设置为输出时,写此寄存器的相应位可以令此引脚输出高电平或者低电平。当引脚被设置为中断时,此引脚会被设置为中断信号源。

c)    GPFUP是端口上拉寄存器,当对应位为1时,表示相应的引脚没有内部上拉电阻;为0时,相应的引脚使用上拉电阻。当需要上拉或者下拉电阻时,外围电路没有加上拉或下拉电阻,那么就可以使用内部上拉或者下拉电阻来代替。

各个寄存器的配置如下表4-2,表4-3,表4-4所示。

4-2 GPFCON寄存器设置

4-3 GPFDAT寄存器设置

4-4 GPFUP寄存器设置

2IO端口方式控制LED实例

#include

#include

#include

#include

#include

#include

#include //inl,outl.,inb,outb,inw......

#include //mb,rmb,wmb

#include //access_ok,get_user,put_user

#include //for  udev

#include //s3c2410_gpio_cfgpin,s3c2410_gpio_setpin......

#include //request_region,request_mem_region......

#include //s3c2410_gpxcon.........

#define VIRTUALMEM_MAJOR 0    /*预设的ioport的主设备号*/

//用来保存原始的寄存器配置

static unsigned int gpfcon_old = 0;

static unsigned int gpfdat_old = 0;

static unsigned int gpfup_old  = 0;

static int Virtualmem_major = VIRTUALMEM_MAJOR;

struct resource *IO_port_resource;//io口是否可用结果返回

/*Virtualmem设备结构体*/

struct Virtualmem_dev                                    

{

     struct cdev cdev; /*cdev结构体*/                            

};

struct Virtualmem_dev *Virtualmem_devp; /*设备结构体指针*/

/*文件打开函数*/

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

{

     /*将设备结构体指针赋值给文件私有数据指针*/

     filp->private_data = Virtualmem_devp;

     printk("In the open process! turn off the led!\n");

     outl(0x5500 | inl((unsigned long )S3C2410_GPFCON),(unsigned long )S3C2410_GPFCON);

     outl(0xF0 | inl((unsigned long )S3C2410_GPFUP),(unsigned long)S3C2410_GPFUP);

     outl(0xF0 | inl((unsigned long )S3C2410_GPFDAT),(unsigned long)S3C2410_GPFDAT);

     return 0;

}

/*文件释放函数*/

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

{

     return 0;

}

/*读函数*/

static ssize_t Virtualmem_read(struct file *filp, char __user *buf, size_t size,

                                   loff_t *ppos)

{

     int ret = 0;

     struct Virtualmem_dev *dev = filp->private_data;   /*获得设备结构体指针*/

     return ret;

}

/*写函数*/

static ssize_t Virtualmem_write(struct file *filp, const char __user *buf,

                                    size_t size, loff_t *ppos)

{

     unsigned int count = size;

     int ret = 0;

     struct Virtualmem_dev *dev = filp->private_data;   /*获得设备结构体指针*/

     unsigned char * userbuf;

     /*用户空间->内核空间*/

//   get_user (*userbuf,(unsigned char*)buf); 

     if (copy_from_user(userbuf, (unsigned char *)buf, count))

         ret =  - EFAULT;

     else

     {

         outl ((*userbuf), (unsigned long)S3C2410_GPFDAT);

//       writel (*userbuf, (unsigned long)S3C2410_GPFDAT); 

         printk("write data from user to ioport!\n");

     }

     return ret;

}

/*文件操作结构体*/

static const struct file_operations Virtualmem_fops =

{

     .owner = THIS_MODULE,

     .read = Virtualmem_read,

     .write = Virtualmem_write,

     .open = Virtualmem_open,

     .release = Virtualmem_release,

};

/*初始化并注册cdev*/

static void Virtualmem_setup_cdev(struct Virtualmem_dev *dev, int index)

{

     int err, devno = MKDEV(Virtualmem_major, index);

     cdev_init(&dev->cdev, &Virtualmem_fops);

     dev->cdev.owner = THIS_MODULE;

     dev->cdev.ops = &Virtualmem_fops;

     err = cdev_add(&dev->cdev, devno, 1);

     if (err)

         printk(KERN_ALERT "Error %d adding Virtualmem%d", err, index);

}

/*设备驱动模块加载函数*/

int Virtualmem_init(void)

{

     int result;

     dev_t devno = MKDEV(Virtualmem_major, 0);

     /* 申请设备号*/

     if (Virtualmem_major)

         result = register_chrdev_region(devno, 1, "ioport");

     else  /* 动态申请设备号 */

     {

         result = alloc_chrdev_region(&devno, 0, 1, "ioport");

         Virtualmem_major = MAJOR(devno);

     } 

     if (result < 0)

         return result;

     /* 动态申请设备结构体的内存*/

     Virtualmem_devp = kmalloc(sizeof(struct Virtualmem_dev), GFP_KERNEL);

     if (!Virtualmem_devp)  /*申请失败*/

     {

         result =  - ENOMEM;

         goto fail;

     }

     memset(Virtualmem_devp, 0, sizeof(struct Virtualmem_dev));

     Virtualmem_setup_cdev(Virtualmem_devp, 0);

     if ((IO_port_resource=request_region((unsigned long)S3C2410_GPFCON, 0x0c,"ioport"))==NULL)

         goto fail;

     else

     {

         printk("In the init process! \n");

         gpfcon_old = (unsigned int) inl((unsigned long)S3C2410_GPFCON);

         gpfdat_old = (unsigned int) inl((unsigned long)S3C2410_GPFDAT);

         gpfup_old = (unsigned int) inl((unsigned long)S3C2410_GPFUP);

         printk("S3C2410_GPFCON is %8X\n",gpfcon_old);

         printk("S3C2410_GPFUP  is %8X\n",gpfup_old);

         printk("S3C2410_GPFDAT is %8X\n",gpfdat_old);

         return 0;

     }

     fail: unregister_chrdev_region(devno, 1);

     return result;

}

/*模块卸载函数*/

void Virtualmem_exit(void)

{

     if (IO_port_resource!=NULL) release_region((unsigned long)S3C2410_GPFCON, 0x0c);

     cdev_del(&Virtualmem_devp->cdev);   /*注销cdev*/

     kfree(Virtualmem_devp);    /*释放设备结构体内存*/

     unregister_chrdev_region(MKDEV(Virtualmem_major, 0), 1);     /*释放设备号*/

}

MODULE_AUTHOR("AK-47");

MODULE_LICENSE("Dual BSD/GPL");

module_param(Virtualmem_major, int, S_IRUGO);

module_init(Virtualmem_init);

module_exit(Virtualmem_exit);

3IO端口方式测试代码

#include

#include

#include

#include

#include

#include

#define DEVICE_FILENAME "/dev/ioport"

int main ( )

{

     int dev;

     int loop;

     char buf[128];

     dev = open(DEVICE_FILENAME,O_RDWR|O_NDELAY);

     if (dev >= 0)

     {

         printf("\nwait? input\n");

         printf("write the 0x5f to the Port F!\n");

         buf[0]=0x5f;

         write (dev, buf, 1);

         sleep (2);

         printf("write the 0xaf to the Port F!\n");

         buf[0]=0xaf;

         write (dev, buf, 1);

         sleep (1);

     }

     else

     {

         printf("open failure!\n");

     }

     close (dev);

     return 0;

}

4、驱动程序调试

加载驱动程序后,创建设备节点,然后运行测试程序,由于向LED所在端口先后写了0x5f0xaf,因此会观察到4个发光二极管交替闪亮一次。

阅读(2213) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~